anatomical planes and its movements

| Axis | Direction | Lies at intersection of |
|---|---|---|
| Vertical (Longitudinal) | Craniocaudal (head to feet) | Sagittal + Coronal planes |
| Sagittal (Anteroposterior) | Front to back | Sagittal + Transverse planes |
| Transverse (Horizontal) | Side to side | Coronal + Transverse planes |
| Movement | Description |
|---|---|
| Flexion | Forward/anterior movement of a limb or trunk; decreases the angle at a joint |
| Extension | Backward/posterior movement; increases the angle at a joint; returns from flexion |
| Lateral flexion | Bending of the head or trunk to either side (in the sagittal plane of the spine) |
| Dorsiflexion | Lifting the foot upward (ankle - foot toward shin) |
| Plantarflexion | Pointing the foot downward (ankle - foot away from shin) |
| Movement | Description |
|---|---|
| Abduction | Movement away from the midline of the body |
| Adduction | Movement toward the midline of the body |
| Lateral flexion | Spine bending sideways |
| Inversion | Movement of the foot that directs the sole medially (inward) |
| Eversion | Movement of the foot that directs the sole laterally (outward) |
| Movement | Description |
|---|---|
| Medial (internal) rotation | Rotation toward the midline |
| Lateral (external) rotation | Rotation away from the midline |
| Supination | Forearm rotates so palm faces anteriorly (upward) |
| Pronation | Forearm rotates so palm faces posteriorly (downward) |
| Horizontal abduction/adduction | Movements of the shoulder in the horizontal plane |
| Movement | Description |
|---|---|
| Circumduction | A combination of flexion, abduction, extension, and adduction - the limb traces a cone shape. Does not include rotation |
| Opposition | Pad of thumb meets pad of other fingers (thumb only) |
| Retraction | Backward movement of the head, jaw, or shoulder blades |
| Protraction | Forward movement of head, jaw, or shoulder blades |

| Joint Type | Planes | Movements Permitted | Example |
|---|---|---|---|
| Plane | Gliding | Sliding/gliding only | Acromioclavicular joint |
| Hinge | 1 (sagittal) | Flexion, extension | Elbow (humero-ulnar) |
| Pivot | 1 (transverse) | Rotation only | Atlanto-axial joint |
| Bicondylar | ~1 + limited rotation | Mainly flexion/extension + limited rotation | Knee joint |
| Condylar (Ellipsoid) | 2 | Flexion, extension, abduction, adduction, circumduction (limited) | Wrist joint |
| Saddle | 2 | Flexion, extension, abduction, adduction, circumduction | Carpometacarpal joint of thumb |
| Ball and Socket | 3 (multiaxial) | Flexion, extension, abduction, adduction, circumduction, rotation | Hip, shoulder |
| Plane | Divides body into | Axis of movement | Key movements |
|---|---|---|---|
| Sagittal | Right / Left | Transverse | Flexion, Extension, Dorsiflexion, Plantarflexion |
| Coronal (Frontal) | Anterior / Posterior | Sagittal | Abduction, Adduction, Lateral flexion, Inversion, Eversion |
| Transverse (Axial) | Superior / Inferior | Longitudinal | Rotation (medial/lateral), Supination, Pronation |
| All three | - | - | Circumduction (multi-planar) |
Create an anatomy reference PDF with plane/movement diagrams
ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"
mkdir -p /tmp/workspace/anatomy-pdf/assets
cd /tmp/workspace/anatomy-pdf/assets && curl -sL "https://cdn.orris.care/cdss_images/a6821ed545aa1fb79ab82e9b83a926f549f85bed1add3edc7d6e458dff65071d.png" -o planes_diagram.png && curl -sL "https://cdn.orris.care/cdss_images/2e8211de7642c967cadc186a6f354d03d46990420817127516787e954f3a1dc5.png" -o axis_deviation.png && echo "Downloaded: $(ls -lh *.png)"
~/anatomy-pdf/generate_pdf.py
"""
Anatomy Reference PDF: Anatomical Planes & Movements
Generated with ReportLab
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
Image, HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Circle, Polygon
from reportlab.graphics import renderPDF
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor
import os
# ─── Paths ───────────────────────────────────────────────────────────────────
BASE = "/tmp/workspace/anatomy-pdf"
ASSETS = os.path.join(BASE, "assets")
OUT_PDF = os.path.join(BASE, "Anatomy_Planes_Movements_Reference.pdf")
# ─── Color Palette ────────────────────────────────────────────────────────────
NAVY = HexColor("#1B3A6B")
TEAL = HexColor("#0F7B8C")
CORAL = HexColor("#E8643A")
GOLD = HexColor("#F5A623")
LIGHT_BG = HexColor("#F0F4F8")
MED_GREY = HexColor("#6B7A8D")
WHITE = colors.white
BLACK = colors.black
SAGE = HexColor("#4A7C59")
PURPLE = HexColor("#6B4C9A")
PINK_BG = HexColor("#FFF0EB")
BLUE_BG = HexColor("#EBF4F7")
GREEN_BG = HexColor("#EBF5EE")
PAGE_W, PAGE_H = A4
MARGIN = 2.0 * cm
# ─── Doc Setup ───────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUT_PDF,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=MARGIN, bottomMargin=MARGIN,
title="Anatomy Reference: Planes & Movements",
author="Orris Medical AI",
subject="Anatomical Planes, Axes, and Joint Movements"
)
styles = getSampleStyleSheet()
# Custom styles
def make_style(name, parent="Normal", **kw):
return ParagraphStyle(name, parent=styles[parent], **kw)
S_TITLE = make_style("S_TITLE", fontSize=28, textColor=WHITE, alignment=TA_CENTER, leading=34, fontName="Helvetica-Bold")
S_SUBTITLE = make_style("S_SUBTITLE", fontSize=13, textColor=HexColor("#D0E8F0"), alignment=TA_CENTER, leading=18)
S_H1 = make_style("S_H1", fontSize=16, textColor=WHITE, fontName="Helvetica-Bold", leading=20, spaceAfter=2)
S_H2 = make_style("S_H2", fontSize=13, textColor=NAVY, fontName="Helvetica-Bold", leading=17, spaceBefore=8, spaceAfter=4)
S_H3 = make_style("S_H3", fontSize=11, textColor=TEAL, fontName="Helvetica-Bold", leading=14, spaceBefore=6, spaceAfter=3)
S_BODY = make_style("S_BODY", fontSize=9.5,textColor=HexColor("#2C3E50"), leading=14, alignment=TA_JUSTIFY, spaceAfter=4)
S_CAPTION = make_style("S_CAPTION", fontSize=8, textColor=MED_GREY, alignment=TA_CENTER, leading=11, fontName="Helvetica-Oblique")
S_BULLET = make_style("S_BULLET", fontSize=9.5,textColor=HexColor("#2C3E50"), leading=14, leftIndent=12, bulletIndent=2, spaceAfter=2)
S_SMALL = make_style("S_SMALL", fontSize=8, textColor=MED_GREY, leading=11, alignment=TA_CENTER)
S_NOTE = make_style("S_NOTE", fontSize=8.5,textColor=HexColor("#555555"), leading=12, leftIndent=8)
CONTENT_W = PAGE_W - 2 * MARGIN
# ─── Header / Footer callbacks ────────────────────────────────────────────────
def on_page(cvs, doc):
cvs.saveState()
# Top band (pages > 1)
if doc.page > 1:
cvs.setFillColor(NAVY)
cvs.rect(0, PAGE_H - 1.0*cm, PAGE_W, 1.0*cm, fill=1, stroke=0)
cvs.setFillColor(WHITE)
cvs.setFont("Helvetica-Bold", 9)
cvs.drawString(MARGIN, PAGE_H - 0.65*cm, "ANATOMY REFERENCE")
cvs.setFont("Helvetica", 9)
cvs.drawRightString(PAGE_W - MARGIN, PAGE_H - 0.65*cm, "Planes & Movements")
# Footer
cvs.setFillColor(LIGHT_BG)
cvs.rect(0, 0, PAGE_W, 0.9*cm, fill=1, stroke=0)
cvs.setFillColor(MED_GREY)
cvs.setFont("Helvetica", 7.5)
cvs.drawString(MARGIN, 0.30*cm, "Sources: Gray's Anatomy for Students (Elsevier) | Thieme Atlas of Anatomy | Bailey & Love's Surgery")
cvs.setFont("Helvetica-Bold", 8)
cvs.drawRightString(PAGE_W - MARGIN, 0.30*cm, f"Page {doc.page}")
cvs.restoreState()
def on_first_page(cvs, doc):
on_page(cvs, doc)
# ─── Helper: section header box ──────────────────────────────────────────────
def section_header(title, color=NAVY, text_color=WHITE):
data = [[Paragraph(f'<font color="white"><b>{title}</b></font>', S_H1)]]
tbl = Table(data, colWidths=[CONTENT_W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("ROUNDEDCORNERS", [6]),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 12),
]))
return tbl
# ─── Helper: info box ────────────────────────────────────────────────────────
def info_box(text, bg=LIGHT_BG, border_color=TEAL):
data = [[Paragraph(text, S_BODY)]]
tbl = Table(data, colWidths=[CONTENT_W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0), (-1,-1), 8),
("LINEAFTER", (0,0), (0,-1), 2, border_color), # left accent bar actually
("LINEBEFORE", (0,0), (0,-1), 4, border_color),
]))
return tbl
# ─── Helper: two-column table ─────────────────────────────────────────────────
def two_col_table(rows, headers=None, col_widths=None, row_colors=None):
if col_widths is None:
col_widths = [CONTENT_W*0.35, CONTENT_W*0.65]
table_data = []
if headers:
table_data.append([Paragraph(f'<b>{h}</b>', make_style("TH", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold", leading=12)) for h in headers])
for r in rows:
table_data.append([Paragraph(str(c), make_style("TC", fontSize=9, textColor=HexColor("#2C3E50"), leading=13)) for c in r])
tbl = Table(table_data, colWidths=col_widths, repeatRows=1 if headers else 0)
cmds = [
("GRID", (0,0), (-1,-1), 0.4, HexColor("#CBD5E0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 7),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
if headers:
cmds += [
("BACKGROUND", (0,0), (-1,0), NAVY),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
]
# alternating rows
start = 1 if headers else 0
for i in range(start, len(table_data), 2):
cmds.append(("BACKGROUND", (0,i), (-1,i), LIGHT_BG))
tbl.setStyle(TableStyle(cmds))
return tbl
# ─── Helper: 3-column movement table ─────────────────────────────────────────
def three_col_table(rows, headers=None, col_widths=None):
if col_widths is None:
col_widths = [CONTENT_W*0.28, CONTENT_W*0.36, CONTENT_W*0.36]
return two_col_table(rows, headers=headers, col_widths=col_widths)
# ─── Draw anatomical plane diagram (custom ReportLab drawing) ─────────────────
def make_planes_drawing():
"""Draw a schematic figure showing the 3 planes intersecting a body silhouette."""
W, H = 320, 280
d = Drawing(W, H)
# Body silhouette (simplified oval + lines)
cx, cy = W // 2, H // 2 + 10
# --- Transverse plane (pink horizontal band) ---
d.add(Rect(cx - 75, cy - 8, 150, 16, fillColor=HexColor("#FFB3B3"), strokeColor=CORAL, strokeWidth=1.2))
# --- Coronal plane (blue vertical band) ---
d.add(Rect(cx - 8, cy - 90, 16, 180, fillColor=HexColor("#B3D4FF"), strokeColor=NAVY, strokeWidth=1.2))
# --- Sagittal plane (green diagonal/depth band) ---
# Represented as a parallelogram
pts = [cx-75, cy-90, cx+10, cy-90, cx+10, cy+90, cx-75, cy+90]
# Skewed to give 3D feel
d.add(Polygon([cx+5, cy-90, cx+80, cy-80, cx+80, cy+80, cx+5, cy+90],
fillColor=HexColor("#B3FFD4"), strokeColor=SAGE, strokeWidth=1.2))
# Body silhouette
d.add(Circle(cx, H - 30, 18, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.5)) # head
# Torso
d.add(Rect(cx-22, cy - 40, 44, 80, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.5))
# Legs
d.add(Rect(cx-20, cy - 90, 17, 50, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.2))
d.add(Rect(cx+3, cy - 90, 17, 50, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.2))
# Arms
d.add(Rect(cx-40, cy-30, 16, 60, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.2))
d.add(Rect(cx+24, cy-30, 16, 60, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.2))
# Labels
label_style = dict(fillColor=BLACK, fontSize=9, fontName="Helvetica-Bold")
d.add(String(cx + 85, cy - 3, "TRANSVERSE", **label_style, fillColor=CORAL))
d.add(String(cx + 85, cy - 13, "(Axial)", fillColor=MED_GREY, fontSize=8, fontName="Helvetica"))
d.add(String(cx - 70, H - 18, "SAGITTAL", **label_style, fillColor=SAGE))
d.add(String(8, cy + 100, "CORONAL", **label_style, fillColor=NAVY))
d.add(String(8, cy + 88, "(Frontal)", fillColor=MED_GREY, fontSize=8, fontName="Helvetica"))
# Arrows for planes
# Superior / Inferior
d.add(Line(cx, cy + 95, cx, H - 5, strokeColor=NAVY, strokeWidth=1.5))
d.add(String(cx + 3, H - 8, "S", fillColor=NAVY, fontSize=8, fontName="Helvetica-Bold"))
d.add(Line(cx, cy - 95, cx, 5, strokeColor=NAVY, strokeWidth=1.5))
d.add(String(cx + 3, 2, "I", fillColor=NAVY, fontSize=8, fontName="Helvetica-Bold"))
return d
# ─── Draw movement arrows diagram ─────────────────────────────────────────────
def make_movement_diagram():
"""Simple schematic showing flex/ext, abd/add, rotation arrows."""
W, H = 420, 160
d = Drawing(W, H)
# Three panels: sagittal | coronal | transverse
panel_w = W / 3
titles = ["SAGITTAL PLANE", "CORONAL PLANE", "TRANSVERSE PLANE"]
moves = ["Flexion / Extension", "Abduction / Adduction", "Medial / Lateral Rotation"]
bg_cols = [HexColor("#FFF5EB"), HexColor("#EBF4F7"), HexColor("#EBF5EE")]
txt_cols = [CORAL, NAVY, SAGE]
arrow_dirs = [ # (x1,y1,x2,y2) pairs for the movement arrows in each panel
[(60, 60, 60, 100), (60, 60, 60, 20)], # up/down = flex/ext
[(30, 60, 55, 60), (90, 60, 65, 60)], # left/right = abd/add
[(40, 40, 80, 80), (80, 40, 40, 80)], # rotation arc suggestion
]
for i, (title, move, bg, tc) in enumerate(zip(titles, moves, bg_cols, txt_cols)):
ox = i * panel_w
d.add(Rect(ox + 2, 2, panel_w - 4, H - 4, fillColor=bg, strokeColor=HexColor("#CBD5E0"), strokeWidth=0.8))
d.add(String(ox + 8, H - 16, title, fillColor=tc, fontSize=7.5, fontName="Helvetica-Bold"))
d.add(String(ox + 8, H - 28, move, fillColor=MED_GREY, fontSize=7, fontName="Helvetica"))
# Draw body center dot
d.add(Circle(ox + panel_w/2, H/2, 12, fillColor=HexColor("#F5CBA7"), strokeColor=HexColor("#D98C55"), strokeWidth=1.2))
# Arrows
cx2 = ox + panel_w / 2
cy2 = H / 2
if i == 0: # Sagittal - flex/ext (up-down arc)
d.add(Line(cx2, cy2 + 15, cx2, cy2 + 45, strokeColor=CORAL, strokeWidth=2))
d.add(String(cx2 + 3, cy2 + 45, "Flex", fillColor=CORAL, fontSize=7))
d.add(Line(cx2, cy2 - 15, cx2, cy2 - 45, strokeColor=HexColor("#FF8C42"), strokeWidth=2))
d.add(String(cx2 + 3, cy2 - 50, "Ext", fillColor=HexColor("#FF8C42"), fontSize=7))
elif i == 1: # Coronal - abd/add
d.add(Line(cx2 + 15, cy2, cx2 + 50, cy2, strokeColor=NAVY, strokeWidth=2))
d.add(String(cx2 + 50, cy2 + 3, "Abd", fillColor=NAVY, fontSize=7))
d.add(Line(cx2 - 15, cy2, cx2 - 50, cy2, strokeColor=HexColor("#4A90D9"), strokeWidth=2))
d.add(String(cx2 - 55, cy2 + 3, "Add", fillColor=HexColor("#4A90D9"), fontSize=7))
else: # Transverse - rotation
# Draw curved arrows approximated as lines at angles
import math
for angle_deg, col, lbl, lbl_dx, lbl_dy in [(45, SAGE, "Med\nRot", 5, 5), (225, HexColor("#2E8B57"), "Lat\nRot", -30, -20)]:
angle = math.radians(angle_deg)
x2 = cx2 + 45 * math.cos(angle)
y2 = cy2 + 45 * math.sin(angle)
d.add(Line(cx2 + 14*math.cos(angle), cy2 + 14*math.sin(angle), x2, y2, strokeColor=col, strokeWidth=2))
d.add(String(x2 + lbl_dx, y2 + lbl_dy, lbl, fillColor=col, fontSize=6.5))
return d
# ─── Build story ──────────────────────────────────────────────────────────────
story = []
# ══════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════
def cover_page(cvs, doc):
cvs.saveState()
# Full navy background
cvs.setFillColor(NAVY)
cvs.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
# Teal accent bar top
cvs.setFillColor(TEAL)
cvs.rect(0, PAGE_H - 2.5*cm, PAGE_W, 2.5*cm, fill=1, stroke=0)
# Gold accent bar bottom
cvs.setFillColor(GOLD)
cvs.rect(0, 0, PAGE_W, 1.5*cm, fill=1, stroke=0)
# Diagonal decorative stripe
cvs.setFillColor(HexColor("#0D2B54"))
cvs.setStrokeColor(HexColor("#0D2B54"))
# Title
cvs.setFillColor(WHITE)
cvs.setFont("Helvetica-Bold", 36)
cvs.drawCentredString(PAGE_W/2, PAGE_H*0.62, "ANATOMICAL PLANES")
cvs.setFont("Helvetica-Bold", 28)
cvs.drawCentredString(PAGE_W/2, PAGE_H*0.54, "& MOVEMENTS")
# Subtitle
cvs.setFont("Helvetica", 14)
cvs.setFillColor(HexColor("#A8D8EA"))
cvs.drawCentredString(PAGE_W/2, PAGE_H*0.46, "A Clinical Anatomy Quick Reference")
# Divider
cvs.setStrokeColor(GOLD)
cvs.setLineWidth(2)
cvs.line(MARGIN*2, PAGE_H*0.43, PAGE_W - MARGIN*2, PAGE_H*0.43)
# Plane labels as decorative badges
badge_texts = ["SAGITTAL", "CORONAL", "TRANSVERSE"]
badge_colors = [SAGE, CORAL, TEAL]
badge_x = [MARGIN*2 + 10, PAGE_W/2 - 45, PAGE_W - MARGIN*2 - 100]
for txt, col, bx in zip(badge_texts, badge_colors, badge_x):
cvs.setFillColor(col)
cvs.roundRect(bx, PAGE_H*0.36, 90, 22, 5, fill=1, stroke=0)
cvs.setFillColor(WHITE)
cvs.setFont("Helvetica-Bold", 9)
cvs.drawCentredString(bx + 45, PAGE_H*0.36 + 7, txt)
# Body copy
cvs.setFont("Helvetica", 10)
cvs.setFillColor(HexColor("#BDD5EA"))
cvs.drawCentredString(PAGE_W/2, PAGE_H*0.29, "Planes · Axes · Movements · Joint Types · Clinical Applications")
# Source note
cvs.setFont("Helvetica-Oblique", 8)
cvs.setFillColor(HexColor("#7A9CC7"))
cvs.drawCentredString(PAGE_W/2, PAGE_H*0.23, "Gray's Anatomy for Students | Thieme Atlas of Anatomy | Bailey & Love's Surgery")
# Footer
cvs.setFillColor(NAVY)
cvs.setFont("Helvetica", 8)
cvs.drawCentredString(PAGE_W/2, 0.55*cm, "Orris Medical AI · Reference Edition · 2026")
cvs.restoreState()
# Insert cover as first page using a Spacer + onFirstPage trick
# We use a trick: draw a full-page canvas on page 1
story.append(Spacer(1, PAGE_H - 2*MARGIN)) # placeholder - covered by cover_page
# ══════════════════════════════════════════════
# PAGE 2: ANATOMICAL POSITION & PLANES
# ══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("1. ANATOMICAL POSITION & THE THREE PLANES", color=NAVY))
story.append(Spacer(1, 6))
story.append(info_box(
"<b>Anatomical Position (Reference Standard)</b> — All planes and movements are described with the body "
"standing upright, feet together, arms by the sides, <b>palms facing forward</b>, face looking ahead, "
"toes pointing forward. This is the universal reference from which all anatomical descriptions are made.",
bg=HexColor("#EBF4F7"), border_color=TEAL
))
story.append(Spacer(1, 8))
# Planes diagram from textbook
img_planes = Image(os.path.join(ASSETS, "planes_diagram.png"), width=CONTENT_W * 0.85, height=9*cm, kind="proportional")
story.append(img_planes)
story.append(Paragraph("Fig. 1 — Anatomical planes with directional terms. (Gray's Anatomy for Students, Elsevier)", S_CAPTION))
story.append(Spacer(1, 10))
# Planes table
planes_rows = [
["<b>Sagittal Plane</b>\n(Anteroposterior)",
"Vertical; runs <b>front-to-back</b>. Divides body into <b>Right and Left</b> parts.\n"
"• <b>Median sagittal plane</b>: divides body into equal right and left halves.\n"
"• Named after the sagittal suture of the skull.\n"
"• Axis of movement: <b>Transverse (side-to-side) axis</b>"],
["<b>Coronal Plane</b>\n(Frontal Plane)",
"Vertical; runs <b>side-to-side</b>. Divides body into <b>Anterior (front) and Posterior (back)</b> parts.\n"
"• Parallel to the forehead / coronal suture.\n"
"• Axis of movement: <b>Sagittal (front-to-back) axis</b>"],
["<b>Transverse Plane</b>\n(Horizontal / Axial)",
"Horizontal; perpendicular to the long body axis. Divides body into <b>Superior and Inferior</b> parts.\n"
"• Standard plane for CT scan cross-sections.\n"
"• Axis of movement: <b>Longitudinal (vertical) axis</b>"],
]
col_w = [CONTENT_W * 0.28, CONTENT_W * 0.72]
story.append(two_col_table(planes_rows, headers=["PLANE", "DESCRIPTION & AXIS"], col_widths=col_w))
story.append(Spacer(1, 6))
story.append(Paragraph("Source: Gray's Anatomy for Students, p.18–19 | Thieme Atlas of General Anatomy", S_SMALL))
# ══════════════════════════════════════════════
# PAGE 3: CARDINAL AXES & AXIS DEVIATIONS
# ══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("2. CARDINAL BODY AXES", color=TEAL))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Each plane intersects with another to define a <b>cardinal axis</b> around which movement occurs. "
"The three axes are perpendicular to each other, matching the three spatial dimensions.",
S_BODY
))
story.append(Spacer(1, 6))
axes_rows = [
["<b>Vertical (Longitudinal) Axis</b>",
"Runs <b>craniocaudally</b> (head to feet).",
"Intersection of <b>Sagittal + Coronal</b> planes"],
["<b>Sagittal (Anteroposterior) Axis</b>",
"Runs <b>front to back</b>.",
"Intersection of <b>Sagittal + Transverse</b> planes"],
["<b>Transverse (Horizontal) Axis</b>",
"Runs <b>side to side</b>.",
"Intersection of <b>Coronal + Transverse</b> planes"],
]
story.append(three_col_table(axes_rows,
headers=["AXIS", "DIRECTION", "FORMED BY"],
col_widths=[CONTENT_W*0.32, CONTENT_W*0.30, CONTENT_W*0.38]))
story.append(Spacer(1, 10))
# Axis deviation section
story.append(section_header("3. AXIS DEVIATIONS (Clinical)", color=CORAL))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Joint deformities deviate the long axis of bones from normal in predictable planes. "
"Understanding the plane of deviation is key to clinical diagnosis and surgical correction.",
S_BODY
))
story.append(Spacer(1, 6))
# Axis deviation image
img_axis = Image(os.path.join(ASSETS, "axis_deviation.png"), width=CONTENT_W * 0.9, height=8*cm, kind="proportional")
story.append(img_axis)
story.append(Paragraph("Fig. 2 — Valgus/Varus (coronal plane) and Recurvation/Antecurvation (sagittal plane) deformities. (Thieme Atlas of Anatomy)", S_CAPTION))
story.append(Spacer(1, 8))
deviation_rows = [
["<b>Valgus</b>", "Coronal (Frontal)",
"Distal bone angled <b>away from midline</b>. E.g., genu valgum (knock knees)."],
["<b>Varus</b>", "Coronal (Frontal)",
"Distal bone angled <b>toward midline</b>. E.g., genu varum (bow legs)."],
["<b>Recurvation</b>", "Sagittal",
"<b>Backward bowing</b> at a joint."],
["<b>Antecurvation</b>", "Sagittal",
"<b>Forward bowing</b> at a joint."],
]
story.append(three_col_table(deviation_rows,
headers=["DEFORMITY", "PLANE", "DESCRIPTION"],
col_widths=[CONTENT_W*0.22, CONTENT_W*0.25, CONTENT_W*0.53]))
# ══════════════════════════════════════════════
# PAGE 4: MOVEMENTS BY PLANE
# ══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("4. MOVEMENTS BY ANATOMICAL PLANE", color=NAVY))
story.append(Spacer(1, 6))
story.append(make_movement_diagram())
story.append(Paragraph("Fig. 3 — Schematic overview of movements occurring in each anatomical plane.", S_CAPTION))
story.append(Spacer(1, 10))
# ── Sagittal Plane ──
story.append(KeepTogether([
Table([[Paragraph("SAGITTAL PLANE MOVEMENTS", make_style("BH", fontSize=10, textColor=WHITE, fontName="Helvetica-Bold", leading=13))]],
colWidths=[CONTENT_W],
style=TableStyle([("BACKGROUND",(0,0),(-1,-1),CORAL),("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),("LEFTPADDING",(0,0),(-1,-1),10)])),
Spacer(1, 4),
two_col_table([
["<b>Flexion</b>", "Forward/anterior movement; <b>decreases</b> the joint angle. E.g., bending the elbow."],
["<b>Extension</b>", "Backward/posterior movement; <b>increases</b> the joint angle. Returns from flexion."],
["<b>Hyperextension</b>","Extension beyond the anatomical position (e.g., neck tilt back)."],
["<b>Lateral Flexion</b>","Bending of the spine to either side (specific to the vertebral column)."],
["<b>Dorsiflexion</b>", "At the ankle: foot lifted toward the shin."],
["<b>Plantarflexion</b>","At the ankle: foot pointed away from shin."],
], headers=["MOVEMENT", "DESCRIPTION"], col_widths=[CONTENT_W*0.28, CONTENT_W*0.72]),
]))
story.append(Spacer(1, 8))
# ── Coronal Plane ──
story.append(KeepTogether([
Table([[Paragraph("CORONAL PLANE MOVEMENTS", make_style("BH2", fontSize=10, textColor=WHITE, fontName="Helvetica-Bold", leading=13))]],
colWidths=[CONTENT_W],
style=TableStyle([("BACKGROUND",(0,0),(-1,-1),NAVY),("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),("LEFTPADDING",(0,0),(-1,-1),10)])),
Spacer(1, 4),
two_col_table([
["<b>Abduction</b>", "Movement <b>away</b> from the midline of the body."],
["<b>Adduction</b>", "Movement <b>toward</b> the midline of the body."],
["<b>Lateral Flexion</b>","Spine/neck bending sideways."],
["<b>Inversion</b>", "Foot movement: sole directed <b>medially</b> (inward)."],
["<b>Eversion</b>", "Foot movement: sole directed <b>laterally</b> (outward)."],
["<b>Elevation / Depression</b>","Scapula moving superiorly or inferiorly (shoulder girdle)."],
], headers=["MOVEMENT", "DESCRIPTION"], col_widths=[CONTENT_W*0.28, CONTENT_W*0.72]),
]))
story.append(Spacer(1, 8))
# ── Transverse Plane ──
story.append(KeepTogether([
Table([[Paragraph("TRANSVERSE PLANE MOVEMENTS", make_style("BH3", fontSize=10, textColor=WHITE, fontName="Helvetica-Bold", leading=13))]],
colWidths=[CONTENT_W],
style=TableStyle([("BACKGROUND",(0,0),(-1,-1),SAGE),("TOPPADDING",(0,0),(-1,-1),5),("BOTTOMPADDING",(0,0),(-1,-1),5),("LEFTPADDING",(0,0),(-1,-1),10)])),
Spacer(1, 4),
two_col_table([
["<b>Medial (Internal) Rotation</b>","Rotation <b>toward</b> the midline."],
["<b>Lateral (External) Rotation</b>","Rotation <b>away</b> from the midline."],
["<b>Supination</b>", "Forearm: palm rotates to face <b>anteriorly</b> (upward when arm is raised)."],
["<b>Pronation</b>", "Forearm: palm rotates to face <b>posteriorly</b> (downward)."],
["<b>Horizontal Abduction</b>","Arm swings backward in the horizontal plane."],
["<b>Horizontal Adduction</b>","Arm swings forward/across in the horizontal plane."],
], headers=["MOVEMENT", "DESCRIPTION"], col_widths=[CONTENT_W*0.35, CONTENT_W*0.65]),
]))
# ══════════════════════════════════════════════
# PAGE 5: MULTI-PLANAR MOVEMENTS & JOINT TYPES
# ══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("5. MULTI-PLANAR & SPECIAL MOVEMENTS", color=PURPLE))
story.append(Spacer(1, 6))
story.append(two_col_table([
["<b>Circumduction</b>", "Combination of flexion, abduction, extension, and adduction — limb traces a cone. Does <b>NOT</b> include rotation."],
["<b>Opposition</b>", "Pad of the thumb meets the pad of another finger (thumb only at carpometacarpal joint)."],
["<b>Protraction</b>", "Forward movement of the head, jaw, or scapula."],
["<b>Retraction</b>", "Backward movement of the head, jaw, or scapulae."],
["<b>Elevation</b>", "Upward movement (e.g., shrugging the shoulders, closing the mouth)."],
["<b>Depression</b>", "Downward movement (e.g., pulling shoulders down)."],
], headers=["MOVEMENT", "DESCRIPTION"], col_widths=[CONTENT_W*0.28, CONTENT_W*0.72]))
story.append(Spacer(1, 12))
story.append(section_header("6. SYNOVIAL JOINT TYPES & PERMITTED MOVEMENTS", color=TEAL))
story.append(Spacer(1, 6))
story.append(Paragraph(
"Synovial joints are classified by the <b>shape of their articular surfaces</b>, which determines the axes "
"of movement and the types of motion permitted.",
S_BODY
))
story.append(Spacer(1, 6))
joint_rows = [
["<b>Plane</b>", "Gliding/sliding", "Acromioclavicular, intercarpal, intertarsal"],
["<b>Hinge</b>", "Flexion, Extension", "Elbow (humero-ulnar), interphalangeal, ankle"],
["<b>Pivot</b>", "Rotation", "Atlanto-axial (C1-C2), proximal radio-ulnar"],
["<b>Bicondylar</b>", "Flex/Ext + limited rotation","Knee, temporomandibular"],
["<b>Condylar (Ellipsoid)</b>", "Flex, Ext, Abd, Add, Circumduction (limited)", "Wrist (radiocarpal), MCP joints"],
["<b>Saddle</b>", "Flex, Ext, Abd, Add, Circumduction", "Carpometacarpal joint of thumb"],
["<b>Ball and Socket</b>", "Flex, Ext, Abd, Add, Circumduction, Rotation (multiaxial)", "Hip, Glenohumeral (shoulder)"],
]
story.append(three_col_table(joint_rows,
headers=["JOINT TYPE", "MOVEMENTS", "EXAMPLES"],
col_widths=[CONTENT_W*0.22, CONTENT_W*0.40, CONTENT_W*0.38]))
story.append(Spacer(1, 10))
story.append(Paragraph(
"<b>Axes of movement by joint type:</b> Uniaxial (hinge, pivot) — 1 plane. "
"Biaxial (condylar, saddle) — 2 planes. Multiaxial (ball and socket) — 3 planes.",
S_NOTE
))
# ══════════════════════════════════════════════
# PAGE 6: SUMMARY QUICK REFERENCE TABLE
# ══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header("7. QUICK REFERENCE SUMMARY", color=NAVY))
story.append(Spacer(1, 8))
# Master summary table
summary_data = [
[Paragraph("<b>PLANE</b>", make_style("SH", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold")),
Paragraph("<b>DIVIDES BODY</b>", make_style("SH", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold")),
Paragraph("<b>AXIS OF MOVEMENT</b>", make_style("SH", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold")),
Paragraph("<b>KEY MOVEMENTS</b>", make_style("SH", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold")),
],
[Paragraph("<b>Sagittal</b>", make_style("SC", fontSize=9, textColor=CORAL, fontName="Helvetica-Bold")),
Paragraph("Right / Left", make_style("SC2", fontSize=9)),
Paragraph("Transverse (side-to-side)", make_style("SC2", fontSize=9)),
Paragraph("Flexion, Extension, Dorsiflexion, Plantarflexion", make_style("SC2", fontSize=9)),
],
[Paragraph("<b>Coronal / Frontal</b>", make_style("SC3", fontSize=9, textColor=NAVY, fontName="Helvetica-Bold")),
Paragraph("Anterior / Posterior", make_style("SC2", fontSize=9)),
Paragraph("Sagittal (front-to-back)", make_style("SC2", fontSize=9)),
Paragraph("Abduction, Adduction, Lateral Flexion, Inversion, Eversion", make_style("SC2", fontSize=9)),
],
[Paragraph("<b>Transverse / Axial</b>", make_style("SC4", fontSize=9, textColor=SAGE, fontName="Helvetica-Bold")),
Paragraph("Superior / Inferior", make_style("SC2", fontSize=9)),
Paragraph("Longitudinal (vertical)", make_style("SC2", fontSize=9)),
Paragraph("Medial / Lateral Rotation, Supination, Pronation", make_style("SC2", fontSize=9)),
],
[Paragraph("<b>Multi-planar</b>", make_style("SC5", fontSize=9, textColor=PURPLE, fontName="Helvetica-Bold")),
Paragraph("–", make_style("SC2", fontSize=9)),
Paragraph("Multiple axes", make_style("SC2", fontSize=9)),
Paragraph("Circumduction (flex + abd + ext + add; no rotation)", make_style("SC2", fontSize=9)),
],
]
col_ws = [CONTENT_W*0.18, CONTENT_W*0.18, CONTENT_W*0.27, CONTENT_W*0.37]
tbl_summary = Table(summary_data, colWidths=col_ws)
tbl_summary.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), NAVY),
("GRID", (0,0), (-1,-1), 0.5, HexColor("#CBD5E0")),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 7),
("VALIGN", (0,0), (-1,-1), "TOP"),
("BACKGROUND", (0,1), (-1,1), HexColor("#FFF5EB")),
("BACKGROUND", (0,2), (-1,2), LIGHT_BG),
("BACKGROUND", (0,3), (-1,3), HexColor("#EBF5EE")),
("BACKGROUND", (0,4), (-1,4), HexColor("#F5EBF7")),
]))
story.append(tbl_summary)
story.append(Spacer(1, 14))
# Terms of orientation box
story.append(section_header("8. TERMS OF ORIENTATION", color=MED_GREY))
story.append(Spacer(1, 6))
orient_rows = [
["<b>Anterior (Ventral)</b>", "Toward the front of the body. E.g., nose is anterior."],
["<b>Posterior (Dorsal)</b>", "Toward the back of the body. E.g., vertebral column is posterior."],
["<b>Superior</b>", "Toward the head. E.g., head is superior to the shoulders."],
["<b>Inferior</b>", "Toward the feet. E.g., knee is inferior to the hip."],
["<b>Medial</b>", "Toward the midline (median sagittal plane)."],
["<b>Lateral</b>", "Away from the midline / toward the side."],
["<b>Proximal</b>", "Closer to the origin/attachment. E.g., hand is distal to elbow."],
["<b>Distal</b>", "Farther from origin. E.g., fingers are distal to wrist."],
["<b>Superficial</b>", "Closer to the surface of the body."],
["<b>Deep</b>", "Further from the surface; toward the interior."],
["<b>Ipsilateral</b>", "On the same side."],
["<b>Contralateral</b>", "On the opposite side."],
]
story.append(two_col_table(orient_rows, headers=["TERM", "DEFINITION"], col_widths=[CONTENT_W*0.28, CONTENT_W*0.72]))
story.append(Spacer(1, 8))
story.append(Paragraph(
"Sources: Gray's Anatomy for Students (Elsevier, 2023) | General Anatomy and Musculoskeletal System, "
"Thieme Atlas of Anatomy | Bailey and Love's Short Practice of Surgery, 28th Edition",
S_SMALL
))
# ─── Build PDF ────────────────────────────────────────────────────────────────
def make_cover_first(fn):
"""Build the doc but intercept the first page to draw the cover."""
_first = [True]
def on_page_dispatch(cvs, doc):
if _first[0]:
cover_page(cvs, doc)
_first[0] = False
else:
on_page(cvs, doc)
doc.build(story, onFirstPage=on_page_dispatch, onLaterPages=on_page)
make_cover_first(OUT_PDF)
print(f"PDF created: {OUT_PDF}")
import os
sz = os.path.getsize(OUT_PDF)
print(f"File size: {sz/1024:.1f} KB")
cd /tmp/workspace/anatomy-pdf && python generate_pdf.py 2>&1
~/anatomy-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
~/anatomy-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
~/anatomy-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
~/anatomy-pdf/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
cd /tmp/workspace/anatomy-pdf && python generate_pdf.py 2>&1
Anatomy Planes Movements Reference
PDF Document · PDF
| Page | Content |
|---|---|
| 1 | Full-color cover — navy/teal/gold design with plane badges |
| 2 | Anatomical position + the 3 planes with the Gray's Anatomy diagram (coronal, sagittal, transverse) |
| 3 | Cardinal body axes table + Thieme Atlas axis deviation diagram (valgus/varus, recurvation/antecurvation) |
| 4 | All movements by plane — sagittal (flexion/extension), coronal (abduction/adduction), transverse (rotation/supination/pronation) with color-coded tables |
| 5 | Multi-planar movements (circumduction, opposition, protraction/retraction) + all 7 synovial joint types with permitted movements and examples |
| 6 | Master quick-reference summary table + full terms of orientation glossary (anterior/posterior, medial/lateral, proximal/distal, etc.) |