Explain internuclearophtholmoplegia in a simple way

| Feature | What it means simply |
|---|---|
| Lesion site | Damage to the MLF (a nerve tract in the brainstem) |
| Which side is "affected" | Named for the eye that can't adduct (move inward) |
| Classic sign | Weak adduction + nystagmus in the OTHER eye |
| Convergence | Usually preserved (both eyes can still cross for near vision) |
| Symptoms | Double vision, blurry vision - but surprisingly many patients feel nothing! |
Create a diagnostic flowchart for eye movement disorders
ls /home/daytona/workspace/ 2>/dev/null || echo "empty"
mkdir -p /home/daytona/workspace/eye-flowchart && python3 -c "import matplotlib;…
~/eye-flowchart/flowchart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
fig, ax = plt.subplots(figsize=(22, 32))
ax.set_xlim(0, 22)
ax.set_ylim(0, 32)
ax.axis('off')
fig.patch.set_facecolor('#F0F4F8')
# ─── Color palette ───────────────────────────────────────────────────────────
C_START = '#1A1A2E' # deep navy - start node
C_QUES = '#16213E' # dark blue - decision diamonds
C_CAT1 = '#0F3460' # medium blue - category boxes
C_MONO = '#533483' # purple - monocular
C_BINO = '#E94560' # red - binocular / serious
C_DIAG = '#1A7431' # green - diagnosis boxes
C_INV = '#B8621A' # orange - investigation boxes
C_TEXT = 'white'
C_ARROW = '#2C3E50'
C_BG = '#F0F4F8'
def box(ax, x, y, w, h, text, color, fontsize=9, text_color='white', style='round,pad=0.1', alpha=1.0, bold=False):
fancy = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=style,
linewidth=1.2, edgecolor='white',
facecolor=color, alpha=alpha, zorder=3)
ax.add_patch(fancy)
weight = 'bold' if bold else 'normal'
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color=text_color, zorder=4, weight=weight,
wrap=True, multialignment='center',
fontfamily='DejaVu Sans')
def diamond(ax, x, y, w, h, text, color, fontsize=8.5):
dx, dy = w/2, h/2
xs = [x, x+dx, x, x-dx, x]
ys = [y+dy, y, y-dy, y, y+dy]
ax.fill(xs, ys, color=color, zorder=3, alpha=0.95)
ax.plot(xs, ys, color='white', lw=1.2, zorder=4)
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color='white', zorder=5, weight='bold', multialignment='center')
def arrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.5):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color,
lw=lw, connectionstyle='arc3,rad=0.0'),
zorder=2)
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.15, my, label, fontsize=7.5, color=color, zorder=5,
va='center', style='italic')
def harrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.5):
# Horizontal then vertical connector
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=lw,
connectionstyle='angle,angleA=0,angleB=90'),
zorder=2)
if label:
ax.text((x1+x2)/2, (y1+y2)/2 + 0.15, label, fontsize=7.5,
color=color, ha='center', zorder=5, style='italic')
# ══════════════════════════════════════════════════════════════════════
# TITLE
# ══════════════════════════════════════════════════════════════════════
ax.text(11, 31.3, 'Diagnostic Flowchart: Eye Movement Disorders',
ha='center', va='center', fontsize=16, weight='bold',
color=C_START, fontfamily='DejaVu Sans')
ax.text(11, 30.85, 'Based on Bradley & Daroff\'s Neurology | Kanski\'s Clinical Ophthalmology | Wills Eye Manual',
ha='center', va='center', fontsize=8, color='#666666')
# ══════════════════════════════════════════════════════════════════════
# NODE 1 — Start
# ══════════════════════════════════════════════════════════════════════
box(ax, 11, 30.2, 5.5, 0.7,
'PATIENT PRESENTS WITH\nEYE MOVEMENT COMPLAINT',
C_START, fontsize=10, bold=True)
arrow(ax, 11, 29.85, 11, 29.25)
# ══════════════════════════════════════════════════════════════════════
# Q1 — Diplopia?
# ══════════════════════════════════════════════════════════════════════
diamond(ax, 11, 28.85, 4.2, 0.8,
'Is there DIPLOPIA\n(double vision)?', C_QUES)
# YES → down
arrow(ax, 11, 28.45, 11, 27.8, 'YES')
# NO → right branch (nystagmus / oscillopsia / gaze palsy)
harrow(ax, 13.1, 28.85, 17.5, 28.85, '')
ax.text(15.3, 28.95, 'NO', fontsize=7.5, color=C_ARROW, ha='center', style='italic')
box(ax, 17.5, 28.85, 3.8, 0.7,
'OTHER: Nystagmus,\nOscillopsia, Gaze Palsy',
C_CAT1, fontsize=8.5)
arrow(ax, 17.5, 28.5, 17.5, 27.8)
# ──────────────────────────────────────────────────────────────────────
# NYSTAGMUS branch (right side)
# ──────────────────────────────────────────────────────────────────────
diamond(ax, 17.5, 27.4, 3.8, 0.7,
'Type of\nabnormal movement?', C_QUES, fontsize=8)
# Nystagmus down
arrow(ax, 17.5, 27.05, 17.5, 26.45, 'Nystagmus')
box(ax, 17.5, 26.1, 3.8, 0.65,
'Central vs Peripheral?\n(direction, fixation, onset)',
C_CAT1, fontsize=8)
arrow(ax, 17.5, 25.78, 17.5, 25.2)
box(ax, 17.5, 24.9, 3.8, 0.55,
'Peripheral: BPPV, Labyrinthitis\nCentral: Brainstem/Cerebellum',
C_DIAG, fontsize=7.5)
# Gaze palsy → left
harrow(ax, 15.6, 27.4, 13.8, 27.4, '')
ax.text(14.7, 27.5, 'Gaze Palsy', fontsize=7, color=C_ARROW, ha='center', style='italic')
box(ax, 13, 27.4, 2.8, 0.55,
'See: Gaze Palsy\nSection Below',
C_CAT1, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# Q2 — Monocular or Binocular?
# ══════════════════════════════════════════════════════════════════════
diamond(ax, 11, 27.4, 4.2, 0.7,
'Cover one eye —\nDiplopia RESOLVES?', C_QUES)
# YES (binocular) → down
arrow(ax, 11, 27.05, 11, 26.45, 'YES → Binocular')
# NO (monocular) → left
harrow(ax, 8.9, 27.4, 5.5, 27.4, '')
ax.text(7.2, 27.55, 'NO → Monocular', fontsize=7.5, color=C_ARROW, ha='center', style='italic')
box(ax, 5.5, 27.4, 3.2, 0.55,
'MONOCULAR DIPLOPIA\nRefractive error, Cataract,\nCorneal irregularity',
C_MONO, fontsize=7.5)
arrow(ax, 5.5, 27.13, 5.5, 26.55)
box(ax, 5.5, 26.25, 3.2, 0.55,
'Refer Ophthalmology\nRefraction / Slit Lamp',
C_INV, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# Q3 — Painful?
# ══════════════════════════════════════════════════════════════════════
diamond(ax, 11, 26.1, 4.2, 0.65,
'Is the diplopia\nPAINFUL?', C_QUES)
# YES → right (painful binocular)
harrow(ax, 13.1, 26.1, 15.5, 26.1, '')
ax.text(14.3, 26.22, 'YES', fontsize=7.5, color=C_BINO, ha='center', style='italic')
box(ax, 15.5, 26.1, 2.8, 0.55,
'PAINFUL DIPLOPIA\n⚠ Red Flag',
C_BINO, fontsize=8, bold=True)
arrow(ax, 15.5, 25.83, 15.5, 25.35)
box(ax, 15.5, 25.05, 2.8, 0.55,
'Consider:\nCavernous Sinus Thrombosis\nPCA Aneurysm\nTolosa-Hunt Syndrome\nOrbital Cellulitis',
C_BINO, fontsize=7)
arrow(ax, 15.5, 24.78, 15.5, 24.25)
box(ax, 15.5, 24.0, 2.8, 0.45,
'URGENT: MRI/MRA Brain\n+ CTA if aneurysm suspected',
C_INV, fontsize=7.5)
# NO → down (painless binocular)
arrow(ax, 11, 25.78, 11, 25.2, 'NO')
# ══════════════════════════════════════════════════════════════════════
# Q4 — Which direction is diplopia worse?
# ══════════════════════════════════════════════════════════════════════
diamond(ax, 11, 24.85, 4.4, 0.65,
'Direction of\nMAXIMAL diplopia?', C_QUES, fontsize=8.5)
# Horizontal → left
harrow(ax, 8.8, 24.85, 6.2, 24.85, '')
ax.text(7.5, 25.0, 'Horizontal', fontsize=7.5, color=C_ARROW, ha='center', style='italic')
# Vertical → down
arrow(ax, 11, 24.52, 11, 23.95, 'Vertical')
# Oblique / mixed → right
harrow(ax, 13.2, 24.85, 15.0, 24.85, '')
ax.text(14.1, 25.0, 'Oblique', fontsize=7.5, color=C_ARROW, ha='center', style='italic')
# ══════════════════════════════════════════════════════════════════════
# HORIZONTAL BRANCH (left side)
# ══════════════════════════════════════════════════════════════════════
box(ax, 6.2, 24.85, 3.2, 0.55,
'HORIZONTAL DIPLOPIA',
C_CAT1, fontsize=8.5, bold=True)
arrow(ax, 6.2, 24.57, 6.2, 24.0)
diamond(ax, 6.2, 23.65, 3.2, 0.65,
'Adduction\ndefect?', C_QUES, fontsize=8)
# Yes adduction → left = INO
harrow(ax, 4.6, 23.65, 3.0, 23.65, '')
ax.text(3.8, 23.8, 'YES', fontsize=7, color=C_ARROW, style='italic')
box(ax, 3.0, 23.65, 2.4, 0.55,
'INO\n(MLF Lesion)\nMS / Stroke',
C_DIAG, fontsize=7.5)
arrow(ax, 3.0, 23.38, 3.0, 22.85)
box(ax, 3.0, 22.6, 2.4, 0.45,
'MRI Brain\n(thin brainstem cuts)',
C_INV, fontsize=7.5)
# No adduction → down = CN VI
arrow(ax, 6.2, 23.33, 6.2, 22.8, 'NO')
box(ax, 6.2, 22.5, 3.2, 0.55,
'Abduction defect?\n→ CN VI (Abducens) Palsy\nLateral rectus weakness',
C_DIAG, fontsize=7.5)
arrow(ax, 6.2, 22.23, 6.2, 21.7)
box(ax, 6.2, 21.45, 3.2, 0.5,
'MRI Brain/Orbit\nRule out raised ICP\nRule out Wernicke\'s',
C_INV, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# VERTICAL BRANCH (center-down)
# ══════════════════════════════════════════════════════════════════════
box(ax, 11, 23.65, 3.6, 0.55,
'VERTICAL DIPLOPIA',
C_CAT1, fontsize=8.5, bold=True)
arrow(ax, 11, 23.38, 11, 22.8)
diamond(ax, 11, 22.45, 3.6, 0.65,
'Head tilt test\n(3-step test)?', C_QUES, fontsize=8)
# Positive → CN IV
harrow(ax, 12.8, 22.45, 14.8, 22.45, '')
ax.text(13.8, 22.6, 'Positive', fontsize=7, color=C_ARROW, style='italic')
box(ax, 14.8, 22.45, 2.8, 0.55,
'CN IV (Trochlear) Palsy\nSuperior oblique weak\nHead tilt to opposite side',
C_DIAG, fontsize=7.5)
arrow(ax, 14.8, 22.18, 14.8, 21.65)
box(ax, 14.8, 21.4, 2.8, 0.5,
'MRI / Check BP\nRule out diabetic\nneuropathy, trauma',
C_INV, fontsize=7.5)
# Negative → other
arrow(ax, 11, 22.13, 11, 21.6, 'Negative')
box(ax, 11, 21.3, 3.6, 0.55,
'Skew Deviation\nMyasthenia Gravis\nThyroid Eye Disease',
C_DIAG, fontsize=7.5)
arrow(ax, 11, 21.03, 11, 20.5)
box(ax, 11, 20.25, 3.6, 0.5,
'AChR Ab / Tensilon test\nTFTs / Orbital imaging\nMRI Brainstem',
C_INV, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# OBLIQUE / MIXED BRANCH (right side, down)
# ══════════════════════════════════════════════════════════════════════
box(ax, 15.0, 24.85, 2.8, 0.55,
'OBLIQUE / MIXED\nDIPLOPIA',
C_CAT1, fontsize=8.5, bold=True)
arrow(ax, 15.0, 24.57, 15.0, 24.05)
box(ax, 15.0, 23.75, 2.8, 0.55,
'CN III Palsy?\nPtosis + dilated pupil\n+ "down and out" eye',
C_DIAG, fontsize=7.5)
arrow(ax, 15.0, 23.48, 15.0, 22.95)
diamond(ax, 15.0, 22.6, 2.8, 0.65,
'Pupil\ninvolved?', C_QUES, fontsize=8)
# YES pupil → aneurysm
harrow(ax, 16.4, 22.6, 18.0, 22.6, '')
ax.text(17.2, 22.73, 'YES', fontsize=7, color=C_BINO, style='italic')
box(ax, 18.0, 22.6, 2.5, 0.55,
'⚠ URGENT\nPCA Aneurysm\nuntil proven otherwise',
C_BINO, fontsize=7.5, bold=True)
arrow(ax, 18.0, 22.33, 18.0, 21.8)
box(ax, 18.0, 21.55, 2.5, 0.5,
'CTA / MRA BRAIN\nNeurosurgery consult',
C_INV, fontsize=7.5)
# NO pupil → medical CN III
arrow(ax, 15.0, 22.28, 15.0, 21.75, 'NO')
box(ax, 15.0, 21.5, 2.8, 0.5,
'Medical CN III\nDiabetes, HTN\nSpare pupil',
C_DIAG, fontsize=7.5)
arrow(ax, 15.0, 21.25, 15.0, 20.75)
box(ax, 15.0, 20.5, 2.8, 0.5,
'HbA1c / BP control\nMRI if no improvement\nin 3 months',
C_INV, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# GAZE PALSY SECTION (bottom center)
# ══════════════════════════════════════════════════════════════════════
box(ax, 11, 19.4, 6.0, 0.65,
'GAZE PALSY (Conjugate gaze disorder — no diplopia)',
C_START, fontsize=9, bold=True)
# connect from "See: Gaze Palsy Section Below"
arrow(ax, 13.0, 27.13, 13.0, 25.0)
harrow(ax, 13.0, 25.0, 11.0, 19.73)
diamond(ax, 11, 18.75, 5.0, 0.7,
'Direction of gaze palsy?', C_QUES, fontsize=9)
# Horizontal left
harrow(ax, 8.5, 18.75, 5.5, 18.75, '')
ax.text(7.0, 18.9, 'Horizontal', fontsize=7.5, color=C_ARROW, ha='center', style='italic')
# Vertical down
arrow(ax, 11, 18.4, 11, 17.85, 'Vertical')
# One-and-half → right
harrow(ax, 13.5, 18.75, 16.5, 18.75, '')
ax.text(15.0, 18.9, 'One-and-half\nsyndrome', fontsize=7, color=C_ARROW, ha='center', style='italic')
# ── Horizontal gaze palsy
box(ax, 5.5, 18.75, 3.2, 0.6,
'HORIZONTAL GAZE PALSY',
C_CAT1, fontsize=8, bold=True)
arrow(ax, 5.5, 18.45, 5.5, 17.9)
box(ax, 5.5, 17.6, 3.4, 0.6,
'Frontal (FEF) Lesion:\nEyes deviate TOWARD lesion\n(ipsilateral gaze preference)',
C_DIAG, fontsize=7.5)
arrow(ax, 5.5, 17.3, 5.5, 16.8)
box(ax, 5.5, 16.55, 3.4, 0.45,
'Pontine (PPRF) Lesion:\nEyes deviate AWAY from lesion\n(contralateral gaze preference)',
C_DIAG, fontsize=7.5)
arrow(ax, 5.5, 16.33, 5.5, 15.85)
box(ax, 5.5, 15.6, 3.0, 0.45,
'MRI Brain\n(Stroke / MS / Tumor)',
C_INV, fontsize=7.5)
# ── Vertical gaze palsy
box(ax, 11, 17.55, 3.6, 0.6,
'VERTICAL GAZE PALSY',
C_CAT1, fontsize=8, bold=True)
arrow(ax, 11, 17.25, 11, 16.7)
box(ax, 11, 16.4, 3.8, 0.65,
'Parinaud Syndrome\n(Dorsal Midbrain)\nUpgaze palsy + convergence-\nretraction nystagmus + lid lag',
C_DIAG, fontsize=7.5)
arrow(ax, 11, 16.08, 11, 15.6)
box(ax, 11, 15.35, 3.8, 0.45,
'MRI Midbrain\n(Pineal tumor, Hydrocephalus, MS)',
C_INV, fontsize=7.5)
# ── One-and-half syndrome
box(ax, 16.5, 18.75, 3.0, 0.6,
'ONE-AND-HALF\nSYNDROME',
C_CAT1, fontsize=8, bold=True)
arrow(ax, 16.5, 18.45, 16.5, 17.9)
box(ax, 16.5, 17.6, 3.2, 0.65,
'Ipsilateral gaze palsy\n+ INO\nOnly: contralateral eye\nabducts (+ nystagmus)',
C_DIAG, fontsize=7.5)
arrow(ax, 16.5, 17.28, 16.5, 16.8)
box(ax, 16.5, 16.55, 3.0, 0.45,
'Pontine lesion (PPRF + MLF)\nMRI brainstem\nMS / Stroke',
C_INV, fontsize=7.5)
# ══════════════════════════════════════════════════════════════════════
# LEGEND
# ══════════════════════════════════════════════════════════════════════
legend_items = [
(C_QUES, 'Decision Point'),
(C_CAT1, 'Category / Pathway'),
(C_DIAG, 'Diagnosis'),
(C_INV, 'Investigation'),
(C_MONO, 'Monocular cause'),
(C_BINO, 'Urgent / Red Flag'),
]
lx, ly = 0.6, 15.5
ax.text(lx, ly, 'LEGEND', fontsize=8, weight='bold', color=C_START)
for i, (col, label) in enumerate(legend_items):
ry = ly - 0.5 - i * 0.5
rect = FancyBboxPatch((lx, ry - 0.17), 0.5, 0.35,
boxstyle='round,pad=0.05',
facecolor=col, edgecolor='white', lw=0.8, zorder=3)
ax.add_patch(rect)
ax.text(lx + 0.65, ry, label, fontsize=7.5, va='center', color=C_START)
plt.tight_layout(pad=0.5)
plt.savefig('/home/daytona/workspace/eye-flowchart/eye_movement_flowchart.png',
dpi=180, bbox_inches='tight', facecolor=C_BG)
print("Done")
cd /home/daytona/workspace/eye-flowchart && python3 flowchart.py 2>&1
eye movement flowchart
Image · PNG
| Direction | Likely Lesion | Key Clue |
|---|---|---|
| Horizontal | CN VI palsy (abduction defect) or INO (adduction defect) | INO = adduction weak + nystagmus in other eye |
| Vertical | CN IV palsy (trochlear) | Park's 3-step test positive; head tilt |
| Oblique/Mixed | CN III palsy | Ptosis + dilated pupil + "down and out" eye |
| Type | Lesion | Features |
|---|---|---|
| Horizontal | Frontal (FEF) = eyes toward lesion; Pontine (PPRF) = eyes away from lesion | |
| Vertical | Parinaud syndrome (dorsal midbrain) | Upgaze palsy + convergence-retraction nystagmus |
| One-and-half | PPRF + MLF (same side) | Only contralateral abduction preserved |