codexcytokinesis-toroidal-egg-positioning.py
cytokinesis-toroidal-egg-positioning.py
"""Original apparatus and reasoning diagram for Rappaport's 1961 experiment.
Python 3.14; matplotlib 3.10.7 and numpy 2.3.5. Geometry is illustrative,
not a tracing of the paper's figures or a record of measured dimensions.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Ellipse, Polygon, Rectangle
import numpy as np
BLUE = "#265b79"
GREEN = "#287b43"
ORANGE = "#c65b13"
PURPLE = "#7154a5"
def aster(ax, x, y, size=0.20):
for angle in np.linspace(0, 2 * np.pi, 9, endpoint=False):
ax.plot((x, x + size * np.cos(angle)), (y, y + size * np.sin(angle)), color=GREEN, lw=1)
ax.add_patch(Circle((x, y), 0.045, fc=GREEN, ec=GREEN))
def spindle(ax, first, second):
ax.plot((first[0], second[0]), (first[1], second[1]), color=GREEN, lw=2.6)
aster(ax, *first)
aster(ax, *second)
def main():
fig = plt.figure(figsize=(11, 6.8), dpi=100, facecolor="white")
fig.suptitle("Where can a furrow form?", y=0.965, fontsize=17)
axes = [fig.add_axes((x, 0.33, 0.28, 0.46)) for x in (0.045, 0.36, 0.675)]
for ax in axes:
ax.set(xlim=(-1.7, 1.7), ylim=(-1.65, 1.9), aspect="equal")
ax.axis("off")
ax = axes[0]
ax.set_title("Manipulation", fontsize=13)
ax.add_patch(Rectangle((-1.50, -0.62), 3, 0.17, fc="#dce4e8", ec=BLUE, lw=1.2))
ax.add_patch(Ellipse((0, -0.10), 2.20, 0.75, fc="#eef6fa", ec=BLUE, lw=2))
ax.plot((0, 0), (0.1, 1.8), color="#69747c", lw=3)
ax.add_patch(Circle((0, -0.02), 0.43, fc="#c9d2d9", ec="#69747c", lw=1.5))
ax.annotate("Sphere\non needle", (0.33, 0.1), (0.6, 0.86), ha="center", fontsize=11,
arrowprops={"arrowstyle": "->", "color": "#69747c"})
ax.annotate("", (-0.47, 0.15), (-0.47, 1.18), arrowprops={"arrowstyle": "->", "color": ORANGE, "lw": 2})
ax.text(0, -0.92, "Floor", ha="center", fontsize=11)
ax.add_patch(Polygon([(-0.35, -1.55), (0.35, -1.55), (0.22, -1.20), (-0.22, -1.20)], fc="#dce4e8", ec=BLUE))
ax.text(0.48, -1.40, "Objective\nbelow", ha="left", va="center", fontsize=10)
ax = axes[1]
ax.set_title("First division", fontsize=13)
ax.add_patch(Circle((0, 0), 1.30, fc="#eef6fa", ec=BLUE, lw=2))
ax.add_patch(Circle((0, 0), 0.70, fc="#c9d2d9", ec="#69747c", lw=1.3))
spindle(ax, (-0.48, 0.91), (0.48, 0.91))
ax.annotate("", (0, 0.80), (0, 1.56), arrowprops={"arrowstyle": "->", "color": ORANGE, "lw": 2})
ax.text(0, -1.62, "Torus → horseshoe", ha="center", fontsize=11)
ax = axes[2]
ax.set_title("Later division", fontsize=13)
angles = np.linspace(0.65 * np.pi, 2.35 * np.pi, 241)
outer = np.column_stack((1.30 * np.cos(angles), 1.30 * np.sin(angles)))
inner = np.column_stack((0.70 * np.cos(angles[::-1]), 0.70 * np.sin(angles[::-1])))
ax.add_patch(Polygon(np.vstack((outer, inner)), fc="#eef6fa", ec=BLUE, lw=2))
for sign in (-1, 1):
spindle(ax, (sign * 1.00, 0.56), (sign * 0.78, -0.66))
ax.add_patch(Circle((sign * 0.97, -0.04), 0.12, fc="#e8ddf6", ec=PURPLE))
ax.plot((0, 0), (-1.31, -0.70), color=ORANGE, lw=4)
ax.annotate("Extra furrow", (0, -0.95), (0, -1.60), ha="center", fontsize=11, color=ORANGE,
arrowprops={"arrowstyle": "->", "color": ORANGE})
ax.text(0, 1.70, "End daughters omitted", ha="center", fontsize=10)
fig.text(0.5, 0.255, "Control timing", ha="center", fontsize=12)
fig.text(0.5, 0.165, "Different spindles → extra furrow\nNo intervening chromosomes", ha="center", fontsize=13,
bbox={"boxstyle": "round,pad=0.55", "facecolor": "#eef6fa", "edgecolor": BLUE})
fig.text(0.5, 0.055, "Original apparatus and logic schematic; rays symbolic; not to scale.", ha="center", fontsize=11)
fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()