anatomical planes and its movements

Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Finding Sources
Loading Image
Reading File
Loading Image
Reading File
Reading File
Finding Sources
Reading File
Reading File
I now have comprehensive content. Let me compile the full answer.

Anatomical Planes and Movements

Anatomical Position (Reference Standard)

All planes and movements are described relative to the anatomical position: standing upright, feet together, arms by the sides, palms facing forward, face looking ahead, toes pointing forward. This is the universal reference from which all anatomical descriptions are made.
  • Gray's Anatomy for Students, p. 18

The Three Cardinal Anatomical Planes

Anatomical Planes - Gray's Anatomy
Fig. 1.1 from Gray's Anatomy for Students - The three anatomical planes with directional terms

1. Sagittal Plane (Anteroposterior Plane)

  • Orientation: Vertical; runs front-to-back (anteroposterior)
  • Divides: The body into right and left parts
  • Median sagittal plane (midsagittal / median plane): passes through the exact center of the body, dividing it into equal right and left halves
  • Any sagittal plane parallel to the median plane is simply called a "sagittal plane"
  • Named after the sagittal suture of the skull, which runs in this plane
  • Axis of rotation: Transverse (side-to-side) axis

2. Coronal Plane (Frontal Plane)

  • Orientation: Vertical; runs side-to-side
  • Divides: The body into anterior (front) and posterior (back) parts
  • Parallel to the forehead / coronal suture of the skull
  • Axis of rotation: Sagittal (front-to-back) axis

3. Transverse Plane (Horizontal / Axial Plane)

  • Orientation: Horizontal; at right angles to the long axis of the body
  • Divides: The body into superior (upper) and inferior (lower) parts
  • Also called "cross-sectional plane" - the standard plane for CT scan slices
  • Axis of rotation: Longitudinal (vertical) axis
  • Thieme Atlas of General Anatomy and Musculoskeletal System, p. (body planes section); Gray's Anatomy for Students, p. 18-19

The Three Cardinal Body Axes

Each plane is associated with a perpendicular axis around which movement occurs:
AxisDirectionLies at intersection of
Vertical (Longitudinal)Craniocaudal (head to feet)Sagittal + Coronal planes
Sagittal (Anteroposterior)Front to backSagittal + Transverse planes
Transverse (Horizontal)Side to sideCoronal + Transverse planes
  • Thieme Atlas, General Anatomy section

Movements and the Planes They Occur In

Sagittal Plane Movements (about the transverse axis)

MovementDescription
FlexionForward/anterior movement of a limb or trunk; decreases the angle at a joint
ExtensionBackward/posterior movement; increases the angle at a joint; returns from flexion
Lateral flexionBending of the head or trunk to either side (in the sagittal plane of the spine)
DorsiflexionLifting the foot upward (ankle - foot toward shin)
PlantarflexionPointing the foot downward (ankle - foot away from shin)

Coronal (Frontal) Plane Movements (about the sagittal axis)

MovementDescription
AbductionMovement away from the midline of the body
AdductionMovement toward the midline of the body
Lateral flexionSpine bending sideways
InversionMovement of the foot that directs the sole medially (inward)
EversionMovement of the foot that directs the sole laterally (outward)

Transverse Plane Movements (about the longitudinal/vertical axis)

MovementDescription
Medial (internal) rotationRotation toward the midline
Lateral (external) rotationRotation away from the midline
SupinationForearm rotates so palm faces anteriorly (upward)
PronationForearm rotates so palm faces posteriorly (downward)
Horizontal abduction/adductionMovements of the shoulder in the horizontal plane

Multi-planar Movements

MovementDescription
CircumductionA combination of flexion, abduction, extension, and adduction - the limb traces a cone shape. Does not include rotation
OppositionPad of thumb meets pad of other fingers (thumb only)
RetractionBackward movement of the head, jaw, or shoulder blades
ProtractionForward movement of head, jaw, or shoulder blades
  • Bailey and Love's Short Practice of Surgery, 28th Ed, Table 35.3; Gray's Anatomy for Students

Axis Deviations in the Planes (Clinical Relevance)

Valgus/Varus and Recurvation/Antecurvation - Thieme Atlas
Joint deformities deviate bones from the normal axis:
  • Frontal (coronal) plane deviations:
    • Valgus: distal bone angles away from midline (e.g., genu valgum = knock knees)
    • Varus: distal bone angles toward midline (e.g., genu varum = bow legs)
  • Sagittal plane deviations:
    • Recurvation: backward bowing
    • Antecurvation: forward bowing
  • Thieme Atlas, General Anatomy

Movement Classification by Joint Type

Different joint types allow movements in different planes:
Joint TypePlanesMovements PermittedExample
PlaneGlidingSliding/gliding onlyAcromioclavicular joint
Hinge1 (sagittal)Flexion, extensionElbow (humero-ulnar)
Pivot1 (transverse)Rotation onlyAtlanto-axial joint
Bicondylar~1 + limited rotationMainly flexion/extension + limited rotationKnee joint
Condylar (Ellipsoid)2Flexion, extension, abduction, adduction, circumduction (limited)Wrist joint
Saddle2Flexion, extension, abduction, adduction, circumductionCarpometacarpal joint of thumb
Ball and Socket3 (multiaxial)Flexion, extension, abduction, adduction, circumduction, rotationHip, shoulder
  • Gray's Anatomy for Students, p. 35-36

Quick Summary Table

PlaneDivides body intoAxis of movementKey movements
SagittalRight / LeftTransverseFlexion, Extension, Dorsiflexion, Plantarflexion
Coronal (Frontal)Anterior / PosteriorSagittalAbduction, Adduction, Lateral flexion, Inversion, Eversion
Transverse (Axial)Superior / InferiorLongitudinalRotation (medial/lateral), Supination, Pronation
All three--Circumduction (multi-planar)
Sources: Gray's Anatomy for Students (Elsevier); General Anatomy and Musculoskeletal System, Thieme Atlas; Bailey and Love's Short Practice of Surgery, 28th Ed

Create an anatomy reference PDF with plane/movement diagrams

Reading File
Running Command

ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"

Running Command

mkdir -p /tmp/workspace/anatomy-pdf/assets

Running Command

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)"

Writing File

~/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")
Running Command

cd /tmp/workspace/anatomy-pdf && python generate_pdf.py 2>&1

Editing File

~/anatomy-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
Editing File

~/anatomy-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
Editing File

~/anatomy-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
Editing File

~/anatomy-pdf/generate_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anatomy-pdf/generate_pdf.py'
Running Command

cd /tmp/workspace/anatomy-pdf && python generate_pdf.py 2>&1

Your anatomy reference PDF is ready:

Anatomy Planes Movements Reference

PDF Document · PDF

8 pages | ~4.9 MB

Here's what's inside each section:
PageContent
1Full-color cover — navy/teal/gold design with plane badges
2Anatomical position + the 3 planes with the Gray's Anatomy diagram (coronal, sagittal, transverse)
3Cardinal body axes table + Thieme Atlas axis deviation diagram (valgus/varus, recurvation/antecurvation)
4All movements by plane — sagittal (flexion/extension), coronal (abduction/adduction), transverse (rotation/supination/pronation) with color-coded tables
5Multi-planar movements (circumduction, opposition, protraction/retraction) + all 7 synovial joint types with permitted movements and examples
6Master quick-reference summary table + full terms of orientation glossary (anterior/posterior, medial/lateral, proximal/distal, etc.)
Sources cited throughout: Gray's Anatomy for Students (Elsevier), Thieme Atlas of General Anatomy, and Bailey & Love's Short Practice of Surgery, 28th Ed.
This is a shared conversation. Sign in to Orris to start your own chat.