fig, ax = plt.subplots(figsize=(14, 6.4))
ax.set(xlim=(0, 16), ylim=(0, 7.4)); ax.axis("off")
ax.set_facecolor("white")
vp_x = [0.3, 4.2, 8.1, 12.0]
VW, VY, VH = 3.2, 3.5, 3.2
# connector wedges between viewports
for i in range(3):
x0 = vp_x[i] + VW; x1 = vp_x[i + 1]
ax.add_patch(Polygon([(x0, VY), (x0, VY + VH), (x1, VY + VH * 0.86), (x1, VY + VH * 0.14)],
closed=True, fc=pale, ec=gray, lw=0.8, alpha=0.85, zorder=0))
ax.text((x0 + x1) / 2, VY + VH / 2, "zoom", ha="center", va="center",
color=gray, fontsize=8, style="italic", rotation=90)
for x in vp_x:
ax.add_patch(FancyBboxPatch((x, VY), VW, VH, boxstyle="round,pad=0.04",
fc="white", ec=navy, lw=1.6, zorder=1))
# 1 · colon segment with an inflamed field
cx = vp_x[0] + VW / 2
ax.add_patch(FancyBboxPatch((cx - 1.25, VY + 1.0), 2.5, 1.35, boxstyle="round,pad=0.06",
fc="#F6E3DE", ec=coral, lw=1.6, zorder=2))
ax.add_patch(FancyBboxPatch((cx - 1.05, VY + 1.25), 2.1, 0.85, boxstyle="round,pad=0.04",
fc="white", ec=gray, lw=1.0, zorder=3))
for dx in (-0.62, 0.0, 0.62):
ax.add_patch(Ellipse((cx + dx, VY + 1.67), 0.42, 0.30, fc=coral, ec="none", alpha=0.55, zorder=4))
ax.text(cx, VY + 2.72, "Long-standing pancolitis", ha="center", color=navy, weight="bold", fontsize=9.5)
ax.text(cx, VY + 0.55, "the endoscopist's view", ha="center", color=gray, fontsize=8.5, style="italic")
# 2 · mucosa and crypts
cx = vp_x[1] + VW / 2
for j, dx in enumerate((-1.05, -0.53, 0.0, 0.53, 1.05)):
distorted = (j == 3)
ax.add_patch(FancyBboxPatch((cx + dx - 0.16, VY + 0.85), 0.32, 1.5 if not distorted else 1.15,
boxstyle="round,pad=0.03",
fc="#DDEDEA" if not distorted else "#F6E3DE",
ec=teal if not distorted else coral, lw=1.4, zorder=2))
ax.add_patch(Rectangle((cx - 1.35, VY + 2.42), 2.7, 0.16, fc=gold, ec="none", alpha=0.7, zorder=2))
ax.annotate("branched, shortened crypt", xy=(cx + 0.53, VY + 2.0), xytext=(cx + 0.1, VY + 3.05),
ha="center", color=coral, fontsize=8, weight="bold",
arrowprops=dict(arrowstyle="-|>", color=coral, lw=1.2))
ax.text(cx, VY + 0.55, "mucosa · architecture", ha="center", color=gray, fontsize=8.5, style="italic")
# 3 · epithelial cell and nucleus
cx = vp_x[2] + VW / 2
ax.add_patch(Ellipse((cx, VY + 1.65), 2.5, 1.85, fc="#DDEDEA", ec=teal, lw=1.6, zorder=2))
ax.add_patch(Circle((cx, VY + 1.65), 0.66, fc=navy, ec="none", zorder=3))
for _ in range(9):
a = rng.uniform(0, 2 * np.pi); r = rng.uniform(0.08, 0.48)
ax.plot([cx + r * np.cos(a) - 0.07, cx + r * np.cos(a) + 0.07],
[VY + 1.65 + r * np.sin(a), VY + 1.65 + r * np.sin(a) + 0.05],
color=gold, lw=1.6, zorder=4)
ax.annotate("nucleus", xy=(cx + 0.5, VY + 2.05), xytext=(cx + 1.0, VY + 2.95),
color=navy, fontsize=8, weight="bold",
arrowprops=dict(arrowstyle="-|>", color=navy, lw=1.2))
ax.text(cx, VY + 0.55, "cell state · cell of origin", ha="center", color=gray, fontsize=8.5, style="italic")
# 4 · CpG island at a panel promoter
cx = vp_x[3] + VW / 2
ax.plot([cx - 1.35, cx + 1.35], [VY + 1.55, VY + 1.55], color=navy, lw=2.4, zorder=2, solid_capstyle="round")
ax.add_patch(FancyBboxPatch((cx - 1.15, VY + 1.32), 1.5, 0.46, boxstyle="round,pad=0.03",
fc=gold, ec="none", alpha=0.35, zorder=1))
ax.text(cx - 0.40, VY + 1.06, "CpG island", ha="center", color="#8a6a1f", fontsize=8, weight="bold")
ax.add_patch(FancyArrowPatch((cx + 0.45, VY + 1.55), (cx + 1.3, VY + 1.55), arrowstyle="-|>",
mutation_scale=13, color=teal, lw=2.2, zorder=3))
ax.text(cx + 0.9, VY + 1.06, "gene body", ha="center", color=teal, fontsize=8, weight="bold")
meth = [1, 1, 0, 1, 1, 1, 0, 1]
for k, m in enumerate(meth):
px = cx - 1.10 + k * 0.21
ax.plot([px, px], [VY + 1.55, VY + 2.02], color=navy, lw=1.1, zorder=3)
ax.add_patch(Circle((px, VY + 2.10), 0.088, fc=coral if m else "white",
ec=coral if m else navy, lw=1.3, zorder=4))
ax.text(cx, VY + 2.62, "one EM-seq read, CpG by CpG", ha="center", color=navy, weight="bold", fontsize=9)
ax.text(cx, VY + 0.55, "methylation · the measured layer", ha="center", color=gray, fontsize=8.5, style="italic")
legend = [Line2D([0], [0], marker="o", color="none", markerfacecolor=coral, markeredgecolor=coral,
markersize=8, label="methylated CpG"),
Line2D([0], [0], marker="o", color="none", markerfacecolor="white", markeredgecolor=navy,
markersize=8, label="unmethylated CpG")]
ax.legend(handles=legend, loc="upper left", bbox_to_anchor=(0.005, 1.0), frameon=False,
fontsize=8.8, ncol=1, handletextpad=0.5)
titles = ["1 · Colon segment", "2 · Mucosa and crypts", "3 · Epithelial cell", "4 · Promoter CpGs"]
notes = ["Visible inflammation.\nDysplasia is often flat\nand not visible at all.",
"Histology sees architecture\nand grade, one biopsy site\nat a time.",
"Cell identity and state.\nAlso what cfDNA carries\nout of the tissue.",
"Read-level methylation:\nmeasurable in tissue DNA\nand in plasma cfDNA."]
for x, t, n in zip(vp_x, titles, notes):
ax.text(x + VW / 2, VY - 0.32, t, ha="center", color=navy, weight="bold", fontsize=10.5)
ax.text(x + VW / 2, VY - 1.55, n, ha="center", va="center", color="#3c4a52", fontsize=8.8, linespacing=1.45)
ax.add_patch(FancyArrowPatch((0.5, 0.55), (15.5, 0.55), arrowstyle="-|>", mutation_scale=16,
color=gray, lw=1.4))
for x, s in zip([vp_x[i] + VW / 2 for i in range(4)], ["≈ 30 cm", "≈ 1 mm", "≈ 10 µm", "≈ 300 bp"]):
ax.text(x, 0.78, s, ha="center", color=gray, fontsize=8.5)
ax.text(8.0, 7.05, "One participant, four scales — the endoscope resolves the first two",
ha="center", color=navy, weight="bold", fontsize=12.5)
fig.tight_layout(); fig.savefig(OUT / "figure-1-zoom-cascade.png", dpi=220, bbox_inches="tight")
plt.show()