codexpast-exam-of-the-computer-science-course-of-the-university-of-oxford2015geometric-modelling-2015-seat.py
past-exam-of-the-computer-science-course-of-the-university-of-oxford/2015/geometric-modelling-2015-seat.py
"""Oxford 2015 geometric modelling Q1: primitives and the initial box.
Run from any directory; writes geometric-modelling-2015-seat.png there.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
"""
import os
import tempfile
os.environ.setdefault('MPLCONFIGDIR', tempfile.mkdtemp(prefix='gmod-seat-mpl-'))
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle
import numpy as np
s = np.sqrt(3.)
points = np.array([[2-s, 0], [2, 1], [1, 1], [1, 2], [0, 2], [0, 0]])
fig, axes = plt.subplots(1, 2, figsize=(9, 4.6), layout='constrained')
for ax in axes:
ax.add_patch(Polygon(points, facecolor='#dbeaf6', edgecolor='#123e67', lw=2, zorder=3))
ax.set(xlim=(-.5, 2.55), ylim=(-.5, 2.55), xlabel='x', ylabel='y')
ax.set_aspect('equal')
ax.grid(alpha=.18)
for label, p, offset in zip('ABCDEO', points,
[(4, -17), (8, 0), (-15, -16), (8, 5), (-15, 5), (-15, -16)]):
ax.plot(*p, 'o', color='#123e67', ms=3, zorder=5)
ax.annotate(label, p, xytext=offset, textcoords='offset points', fontsize=11, zorder=6)
ax = axes[0]
x = np.linspace(-.5, 2.55, 100)
ax.plot(x, (x-(2-s))/s, '--', color='.55', lw=1, zorder=1)
for y in [0, 1, 2]:
ax.axhline(y, ls='--', color='.65', lw=1, zorder=1)
for xx in [0, 1]:
ax.axvline(xx, ls='--', color='.65', lw=1, zorder=1)
normals = np.array([[1, -s], [0, 1], [1, 0], [0, 1], [-1, 0], [0, -1]])
for i in range(6):
mid = (points[i] + points[(i+1) % 6])/2
v = .33*normals[i]/np.linalg.norm(normals[i])
ax.annotate('', mid+v, mid, arrowprops=dict(arrowstyle='->', lw=1.6, color='#9b3514'), zorder=6)
label_position = {1: (1.62, 1.4), 2: (1.43, 1.56), 4: (-.25, 1.13)}.get(i, mid+1.35*v)
ax.text(*label_position, f'n{i+1}', fontsize=9, color='#9b3514', ha='center', va='center', zorder=6)
ax.set_title('Supporting lines and outward normals')
ax = axes[1]
ax.add_patch(Rectangle((1, 0), 1, 2, facecolor='#f4b942', alpha=.16, zorder=4))
ax.add_patch(Rectangle((0, 0), 2, 2, fill=False, edgecolor='#a93535', lw=1.6, zorder=5))
ax.plot([1, 1], [0, 2], '--', color='#a93535', lw=1.7, zorder=5)
ax.text(1.6, 1.7, 'R: [1,2] × [0,2]', ha='center', fontsize=9)
ax.text(.5, 2.3, 'Q: [0,2] × [0,2]', ha='center', fontsize=10)
ax.plot(1.25, .75, 'o', color='#287845', zorder=7)
ax.annotate('inside', (1.25, .75), xytext=(6, -13), textcoords='offset points', fontsize=9)
ax.plot(1.5, 1.5, 'x', color='#a93535', ms=7, zorder=7)
ax.annotate('outside', (1.5, 1.5), xytext=(6, -13), textcoords='offset points', fontsize=9)
ax.set_title('First vertical subdivision')
fig.savefig('geometric-modelling-2015-seat.png', dpi=125, facecolor='white', transparent=False)
plt.close(fig)