codexvalve-tether-angle.py
valve-tether-angle.py
"""Schematic force balance for equal valve tethers, not an anatomical drawing.
Python 3.14 / Matplotlib 3.10. Run in _media to generate cwd PNG.
Each panel balances a 1 N upward load with two equal tensile forces.
"""
from pathlib import Path
from math import sin, cos, radians
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(7.4, 3.9), dpi=100, facecolor='white')
for ax, degrees in zip(axes, (30, 60)):
theta = radians(degrees)
tension = 1 / (2 * cos(theta))
ax.plot([0, 0], [0, -1.05], '--', color='#aaaaaa', linewidth=1)
for side in (-1, 1):
x, y = side * sin(theta), -cos(theta)
ax.plot([0, x], [0, y], color='#1766a5', linewidth=2.5)
ax.plot(x, y, 'o', color='#333333', markersize=6)
ax.annotate('', xy=(.68*x, .68*y), xytext=(.28*x, .28*y),
arrowprops=dict(arrowstyle='->', color='#1766a5', lw=2))
ax.plot(0, 0, 'o', color='#222222', markersize=7)
ax.annotate('', xy=(0, .62), xytext=(0, .05),
arrowprops=dict(arrowstyle='->', color='#c15b18', lw=2.4))
ax.text(.07, .5, 'Load = 1 N', color='#9d4308', fontsize=10)
ax.text(.02, -.33, rf'$\theta={degrees}^\circ$', fontsize=11)
ax.text(0, -1.17, rf'Each tension: $T={tension:.3f}$ N', ha='center', fontsize=11)
ax.set(xlim=(-1.05, 1.05), ylim=(-1.3, .78), aspect='equal')
ax.axis('off')
fig.suptitle('Tether angle changes tension at the same applied load', fontsize=12)
fig.text(.5, .055, r'$2T\cos\theta=1\ \mathrm{N}$; schematic point attachment, not anatomical geometry',
ha='center', fontsize=10)
fig.subplots_adjust(left=.03, right=.97, top=.84, bottom=.18, wspace=.15)
fig.savefig(Path('valve-tether-angle.png'), facecolor='white', dpi=100)
plt.close(fig)