make me flow charts in download pdf form

Reading File
Running Command

cd /home/daytona/workspace/attachments/36b3fb2a-09c9-4318-a7df-da48bdbd2f00/ && python3 -c " import pdfplumber with pdfplumber.open('I now have all the content needed Let me compile t.pdf') as pdf: print(f'Pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages): text = page.extract_text() print(f'--- PAGE {i+1} ---') print(text[:3000] if text else '[no text]') print() "

Running Command

mkdir -p /home/daytona/workspace/joints-flowcharts && python3 -c "import matplotlib; import reportlab; print('libs ok')"

Writing File

~/joints-flowcharts/make_flowcharts.py

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import matplotlib.patheffects as pe
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
import io, os

OUT_DIR = "/home/daytona/workspace/joints-flowcharts"

# ── colour palette ────────────────────────────────────────────────────────────
C_TITLE   = "#1A3A5C"   # deep navy
C_FALSE   = "#C0392B"   # crimson  (false joints)
C_TRUE    = "#1565C0"   # royal blue (true joints)
C_SUB1    = "#E57373"   # light red
C_SUB2    = "#64B5F6"   # light blue
C_LEAF    = "#E3F2FD"   # very light blue leaf
C_LEAFR   = "#FFEBEE"   # very light red leaf
C_AMP     = "#6A1B9A"   # purple (amphiarthroses)
C_AMPL    = "#F3E5F5"   # light purple
C_MECH    = "#2E7D32"   # green (mechanics)
C_MECHL   = "#E8F5E9"   # light green
C_BG      = "#F9FAFB"
C_TEXT    = "#212121"
C_WHITE   = "#FFFFFF"
C_ARROW   = "#546E7A"

def text_box(ax, x, y, w, h, text, bg, fg=C_TEXT, fs=8, bold=False, radius=0.02,
             edge="#90A4AE", lw=1.2, wrap=True):
    box = FancyBboxPatch((x - w/2, y - h/2), w, h,
                         boxstyle=f"round,pad=0.01,rounding_size={radius}",
                         linewidth=lw, edgecolor=edge, facecolor=bg, zorder=3)
    ax.add_patch(box)
    weight = "bold" if bold else "normal"
    ax.text(x, y, text, ha="center", va="center", fontsize=fs,
            color=fg, fontweight=weight, zorder=4,
            wrap=wrap, multialignment="center",
            linespacing=1.3)

def arrow(ax, x0, y0, x1, y1, color=C_ARROW, lw=1.5, style="-|>"):
    ax.annotate("", xy=(x1, y1), xytext=(x0, y0),
                arrowprops=dict(arrowstyle=style, color=color,
                                lw=lw, connectionstyle="arc3,rad=0.0"),
                zorder=2)

def v_line(ax, x, y0, y1, color=C_ARROW, lw=1.5):
    ax.plot([x, x], [y0, y1], color=color, lw=lw, zorder=2)

def h_line(ax, x0, x1, y, color=C_ARROW, lw=1.5):
    ax.plot([x0, x1], [y, y], color=color, lw=lw, zorder=2)

def save_fig(fig, name):
    path = os.path.join(OUT_DIR, name)
    fig.savefig(path, dpi=180, bbox_inches="tight", facecolor=C_BG)
    plt.close(fig)
    return path

# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 1 – Master Classification Tree
# ══════════════════════════════════════════════════════════════════════════════
def chart1():
    fig, ax = plt.subplots(figsize=(14, 11))
    ax.set_xlim(0, 14); ax.set_ylim(0, 11)
    ax.axis("off")
    fig.patch.set_facecolor(C_BG)
    ax.set_facecolor(C_BG)

    # ── title banner ─────────────────────────────────────────────────────────
    banner = FancyBboxPatch((0.3, 9.9), 13.4, 0.9,
                            boxstyle="round,pad=0.02",
                            linewidth=0, facecolor=C_TITLE, zorder=3)
    ax.add_patch(banner)
    ax.text(7, 10.35, "JOINTS — MASTER CLASSIFICATION",
            ha="center", va="center", fontsize=14, color=C_WHITE,
            fontweight="bold", zorder=4)
    ax.text(7, 10.0, "General Anatomy & Musculoskeletal System  |  THIEME Atlas of Anatomy",
            ha="center", va="center", fontsize=7.5, color="#B0BEC5", zorder=4)

    # ── ROOT ─────────────────────────────────────────────────────────────────
    text_box(ax, 7, 9.0, 2.8, 0.55, "JOINTS", C_TITLE, C_WHITE, fs=11, bold=True,
             edge=C_TITLE)

    # ── Two main branches ────────────────────────────────────────────────────
    # FALSE JOINTS
    text_box(ax, 3.0, 7.7, 3.2, 0.6, "FALSE JOINTS\n(Synarthroses)", C_FALSE,
             C_WHITE, fs=9.5, bold=True, edge=C_FALSE)
    # TRUE JOINTS
    text_box(ax, 11.0, 7.7, 3.2, 0.6, "TRUE JOINTS\n(Diarthroses)", C_TRUE,
             C_WHITE, fs=9.5, bold=True, edge=C_TRUE)

    # arrows root -> branches
    arrow(ax, 7, 8.72, 3.0, 8.01)
    arrow(ax, 7, 8.72, 11.0, 8.01)

    # False joints sub-branches
    false_subs = [
        (1.5, 6.3, "Syndesmoses\n(Fibrous)", C_SUB1),
        (3.0, 6.3, "Synchondroses\n(Cartilaginous)", C_SUB1),
        (4.6, 6.3, "Synostoses\n(Bony Fusion)", C_SUB1),
    ]
    for (bx, by, btxt, bc) in false_subs:
        text_box(ax, bx, by, 1.7, 0.6, btxt, bc, C_WHITE, fs=8.5, bold=True, edge=C_FALSE)
        arrow(ax, 3.0, 7.38, bx, by+0.32)

    # Syndesmoses leaves
    synd_leaves = ["Interosseous\nmembranes", "Tibiofibular\nsyndesmosis",
                   "Fontanelles", "Gomphosis\n(teeth)"]
    sx = [0.55, 1.05, 1.55, 2.05]
    for i, (lx, lt) in enumerate(zip(sx, synd_leaves)):
        text_box(ax, lx, 5.05, 0.85, 0.58, lt, C_LEAFR, C_TEXT, fs=7, edge=C_SUB1)
        arrow(ax, 1.5, 5.97, lx, 5.35)

    # Synchondroses leaves
    synch_leaves = ["Epiphyseal\ngrowth plates", "Costal\ncartilage",
                    "Symphyses\n(pubic/IV discs)"]
    syx = [2.35, 3.0, 3.65]
    for lx, lt in zip(syx, synch_leaves):
        text_box(ax, lx, 5.05, 1.0, 0.58, lt, C_LEAFR, C_TEXT, fs=7, edge=C_SUB1)
        arrow(ax, 3.0, 5.97, lx, 5.35)

    # Synostoses leaves
    syn2_leaves = ["Sacrum", "Hip bone", "Ossified\nepliphyses"]
    s2x = [4.0, 4.6, 5.2]
    for lx, lt in zip(s2x, syn2_leaves):
        text_box(ax, lx, 5.05, 0.95, 0.55, lt, C_LEAFR, C_TEXT, fs=7, edge=C_SUB1)
        arrow(ax, 4.6, 5.97, lx, 5.34)

    # True joints sub-branches
    true_subs = [
        (9.5,  6.3, "Diarthroses\n(variable DoF)", C_SUB2, C_TRUE),
        (12.5, 6.3, "Amphiarthroses\n(stiff, limited)", "#AB47BC", C_AMP),
    ]
    for (bx, by, btxt, bc, ec) in true_subs:
        text_box(ax, bx, by, 2.4, 0.6, btxt, bc, C_WHITE, fs=8.5, bold=True, edge=ec)
        arrow(ax, 11.0, 7.38, bx, by+0.32)

    # Diarthroses -> axis sub-branches
    axis_subs = [
        (8.1,  4.9, "1-Axis\n(Hinge / Pivot)", "#42A5F5"),
        (9.5,  4.9, "2-Axis\n(Ellipsoidal / Saddle)", "#42A5F5"),
        (10.9, 4.9, "3-Axis\n(Spheroidal)", "#42A5F5"),
        (12.4, 4.9, "Translational\n(Plane joints)", "#42A5F5"),
    ]
    for (bx, by, btxt, bc) in axis_subs:
        text_box(ax, bx, by, 1.9, 0.6, btxt, bc, C_WHITE, fs=7.5, bold=True,
                 edge=C_TRUE)
        arrow(ax, 9.5, 5.97, bx, by+0.32)

    # Axis leaves
    ax1_lv = [("Elbow", "8.0"), ("Knee", "8.45"), ("Atlanto-axial", "8.9"),
              ("Proximal\nradioulnar", "9.35")]
    ax1_x = [7.6, 8.05, 8.5, 8.95]
    for lx, lt in zip(ax1_x, [("Elbow\njoint"), ("Knee\njoint"),
                                ("Atlanto-axial\njoint"), ("Proximal\nRadioulnar")]):
        text_box(ax, lx, 3.7, 1.0, 0.58, lt, C_LEAF, C_TEXT, fs=7, edge=C_SUB2)
        arrow(ax, 8.1, 4.58, lx, 4.0)

    ax2_x = [9.05, 9.95]
    ax2_lv = ["Radiocarpal\n(wrist) joint", "CMC joint\nof thumb"]
    for lx, lt in zip(ax2_x, ax2_lv):
        text_box(ax, lx, 3.7, 1.15, 0.58, lt, C_LEAF, C_TEXT, fs=7, edge=C_SUB2)
        arrow(ax, 9.5, 4.58, lx, 4.0)

    ax3_x = [10.25, 11.55]
    ax3_lv = ["Hip joint\n(deep socket)", "Shoulder joint\n(shallow socket)"]
    for lx, lt in zip(ax3_x, ax3_lv):
        text_box(ax, lx, 3.7, 1.3, 0.58, lt, C_LEAF, C_TEXT, fs=7, edge=C_SUB2)
        arrow(ax, 10.9, 4.58, lx, 4.0)

    text_box(ax, 12.4, 3.7, 1.4, 0.58, "Femoropatellar\nFacet joints\n(spine)",
             C_LEAF, C_TEXT, fs=7, edge=C_SUB2)
    arrow(ax, 12.4, 4.58, 12.4, 4.0)

    # Amphiarthroses leaf
    text_box(ax, 12.5, 4.9, 1.8, 0.58,
             "Sacroiliac joint\n(iliosacral)", C_AMPL, C_AMP, fs=7.5, edge=C_AMP)
    # already drawn above as sub-branch

    # ── legend ───────────────────────────────────────────────────────────────
    leg_items = [
        (mpatches.Patch(color=C_FALSE, label="False Joints / Synarthroses")),
        (mpatches.Patch(color=C_TRUE,  label="True Joints / Diarthroses")),
        (mpatches.Patch(color=C_AMP,   label="Amphiarthroses")),
    ]
    ax.legend(handles=leg_items, loc="lower left", fontsize=7.5,
              framealpha=0.85, edgecolor="#CFD8DC")

    ax.text(13.8, 0.1, "© THIEME Atlas of Anatomy", ha="right",
            va="bottom", fontsize=6.5, color="#9E9E9E")

    return save_fig(fig, "chart1_master_classification.png")


# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 2 – Structure of a True (Synovial) Joint
# ══════════════════════════════════════════════════════════════════════════════
def chart2():
    fig, ax = plt.subplots(figsize=(12, 9))
    ax.set_xlim(0, 12); ax.set_ylim(0, 9)
    ax.axis("off")
    fig.patch.set_facecolor(C_BG)
    ax.set_facecolor(C_BG)

    # title
    banner = FancyBboxPatch((0.3, 8.1), 11.4, 0.75,
                            boxstyle="round,pad=0.02",
                            linewidth=0, facecolor=C_TITLE, zorder=3)
    ax.add_patch(banner)
    ax.text(6, 8.48, "STRUCTURE OF A TRUE (SYNOVIAL) JOINT",
            ha="center", va="center", fontsize=13, color=C_WHITE, fontweight="bold")

    # central joint node
    text_box(ax, 6, 7.15, 3.6, 0.6,
             "TRUE JOINT (Diarthrosis / Synovial Joint)",
             C_TRUE, C_WHITE, fs=10, bold=True, edge=C_TRUE)

    # 8 components radiating out
    components = [
        # (x, y, title, detail, bg, edge)
        (1.7,  5.9,  "1. Articular Surfaces",
         "Covered by hyaline cartilage\n1-2 mm (phalanges)\n5-7 mm (femoropatellar)\nException: jaw & sternoclavicular\n→ fibrous cartilage",
         "#E3F2FD", C_TRUE),
        (4.2,  5.9,  "2. Joint Cavity",
         "Contains articular recesses\nFew millimetres wide\nFilled with synovial fluid",
         "#E8F5E9", C_MECH),
        (6.0,  5.9,  "3. Joint Capsule",
         "Closed capsule\nAlar folds, synovial folds,\nsynovial villi\nInner layer = synovial membrane\n(vascular; produces synovial fluid)",
         "#FFF3E0", "#E65100"),
        (7.8,  5.9,  "4. Synovial Fluid",
         "Highly viscous\nLubricates & nourishes\navascular cartilage\n(diffusion & convection)",
         "#F3E5F5", C_AMP),
        (10.3, 5.9,  "5. Intra-articular Structures",
         "Menisci – crescent-shaped;\nknee only; collagen/fibrocartilage\nArticular discs – divide into\n2 chambers (jaw, SC, wrist)\nLabra – wedge fibrocartilage;\nhip & shoulder sockets",
         "#E0F2F1", "#00695C"),
        (1.7,  3.6,  "6. Ligaments",
         "Intra- & extracapsular\nPrimary joint stabilizers\nLimit range of motion",
         "#FFF8E1", "#F57F17"),
        (4.2,  3.6,  "7. Muscles",
         "Agonist & antagonist pairs\ncross the joint\nMove it in opposite directions\nSecondary stabilizers",
         "#FCE4EC", "#C62828"),
        (7.5,  3.6,  "8. Synovial Bursae",
         "Often near the joint\nMay communicate with\nthe joint cavity\nReduce friction",
         "#E8EAF6", "#283593"),
    ]

    for (cx, cy, title, detail, bg, edge) in components:
        # title box
        text_box(ax, cx, cy+0.42, 2.2, 0.52, title, edge, C_WHITE,
                 fs=8, bold=True, edge=edge)
        # detail box
        text_box(ax, cx, cy-0.52, 2.2, 1.42, detail, bg, C_TEXT,
                 fs=7, edge=edge, lw=1.0)
        # connect title to detail
        v_line(ax, cx, cy+0.16, cy+0.15)

    # arrows from central node to each title box
    arrow_targets = [(1.7,6.69),(4.2,6.69),(6.0,6.69),(7.8,6.69),(10.3,6.69),
                     (1.7,4.44),(4.2,4.44),(7.5,4.44)]
    for (tx, ty) in arrow_targets:
        arrow(ax, 6, 6.83, tx, ty, lw=1.2)

    # important note box
    note = ("Key Note: Synovial membrane can regenerate via adjoining\n"
            "connective tissue — but hyaline cartilage CANNOT regenerate.\n"
            "(Avascular, lacks perichondrium; nourished only by synovial fluid)")
    note_box = FancyBboxPatch((1.5, 1.1), 9.0, 0.95,
                              boxstyle="round,pad=0.02",
                              linewidth=1.5, edgecolor="#F57F17", facecolor="#FFF8E1", zorder=3)
    ax.add_patch(note_box)
    ax.text(6, 1.58, note, ha="center", va="center", fontsize=8,
            color="#BF360C", fontweight="bold", zorder=4, multialignment="center")

    ax.text(11.8, 0.1, "© THIEME Atlas", ha="right",
            va="bottom", fontsize=6.5, color="#9E9E9E")

    return save_fig(fig, "chart2_true_joint_structure.png")


# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 3 – Types of True Joints (Axes, DoF, Examples)
# ══════════════════════════════════════════════════════════════════════════════
def chart3():
    fig, ax = plt.subplots(figsize=(13, 10))
    ax.set_xlim(0, 13); ax.set_ylim(0, 10)
    ax.axis("off")
    fig.patch.set_facecolor(C_BG)
    ax.set_facecolor(C_BG)

    banner = FancyBboxPatch((0.3, 9.1), 12.4, 0.75,
                            boxstyle="round,pad=0.02",
                            linewidth=0, facecolor=C_TITLE, zorder=3)
    ax.add_patch(banner)
    ax.text(6.5, 9.48, "TYPES OF TRUE JOINTS — Classification by Shape & Movement",
            ha="center", va="center", fontsize=12.5, color=C_WHITE, fontweight="bold")

    # root
    text_box(ax, 6.5, 8.35, 3.2, 0.55, "TRUE JOINTS (Diarthroses)",
             C_TRUE, C_WHITE, fs=10, bold=True)

    # two motion categories
    text_box(ax, 3.0, 7.15, 3.0, 0.55,
             "TRANSLATIONAL\n(Sliding / Plane)", "#0277BD", C_WHITE, fs=9, bold=True)
    text_box(ax, 10.0, 7.15, 3.0, 0.55,
             "ROTATIONAL\n(Angular)", "#1565C0", C_WHITE, fs=9, bold=True)

    arrow(ax, 6.5, 8.07, 3.0, 7.44)
    arrow(ax, 6.5, 8.07, 10.0, 7.44)

    # Translational sub-types
    trans = [
        (1.8, 5.6, "Femoropatellar\n(Plane) Joint",
         "1 axis  |  1 DoF\nSlide up/down\nin femoral groove\nEx: Patella in groove", "#E1F5FE"),
        (4.2, 5.6, "Vertebral\n(Plane) Joint",
         "1-2 axes  |  2-4 DoF\nGlide in multiple\ndirections\nEx: Facet joints (spine)", "#E1F5FE"),
    ]
    for (bx, by, title, detail, bg) in trans:
        text_box(ax, bx, by+0.5, 2.3, 0.55, title, "#0288D1", C_WHITE, fs=8, bold=True, edge="#0277BD")
        text_box(ax, bx, by-0.38, 2.3, 1.2, detail, bg, C_TEXT, fs=7.5, edge="#0277BD")
        arrow(ax, 3.0, 6.84, bx, by+0.78)

    # Rotational sub-types
    rot_joints = [
        (7.1,  5.6,  "Hinge Joint",
         "1 axis  |  2 DoF\nFlexion, Extension\n\nEx: Elbow, Knee",
         "#BBDEFB", "#1565C0"),
        (8.85, 5.6,  "Pivot Joint",
         "1 axis  |  2 DoF\nRotation (axial)\n\nEx: Atlanto-axial,\nProximal radioulnar",
         "#BBDEFB", "#1565C0"),
        (10.5, 5.6,  "Ellipsoidal\nJoint",
         "2 axes  |  4 DoF\nFlex/Ext,\nAbd/Add\nEx: Radiocarpal\n(wrist) joint",
         "#C5CAE9", "#283593"),
        (12.1, 5.6,  "Saddle Joint",
         "2 axes  |  4 DoF\nFlex/Ext,\nAbd/Add\nEx: CMC joint\nof thumb",
         "#C5CAE9", "#283593"),
    ]
    for (bx, by, title, detail, bg, edge) in rot_joints:
        text_box(ax, bx, by+0.5, 1.85, 0.55, title, edge, C_WHITE, fs=7.5, bold=True, edge=edge)
        text_box(ax, bx, by-0.38, 1.85, 1.2, detail, bg, C_TEXT, fs=7, edge=edge)
        arrow(ax, 10.0, 6.84, bx, by+0.78)

    # 3-axis spheroidal – special row
    text_box(ax, 9.0, 2.95, 2.5, 0.55, "Spheroidal Joint\n(Ball & Socket)",
             "#0D47A1", C_WHITE, fs=9, bold=True, edge="#0D47A1")
    arrow(ax, 10.0, 6.84, 9.0, 3.24)

    sph = [
        (7.4,  1.75, "Deep Socket\n(Hip Joint)",
         "3 axes  |  6 DoF\nFlex/Ext, Abd/Add\nInt/Ext rotation\nMore constrained",
         "#E8EAF6", "#283593"),
        (10.6, 1.75, "Shallow Socket\n(Shoulder Joint)",
         "3 axes  |  6 DoF\nAll 6 movements\n+ greater range\nLess constrained",
         "#E8EAF6", "#283593"),
    ]
    for (bx, by, title, detail, bg, edge) in sph:
        text_box(ax, bx, by+0.48, 2.6, 0.55, title, edge, C_WHITE, fs=8, bold=True, edge=edge)
        text_box(ax, bx, by-0.38, 2.6, 1.1, detail, bg, C_TEXT, fs=7.5, edge=edge)
        arrow(ax, 9.0, 2.68, bx, by+0.75)

    # key principle banner
    kp = FancyBboxPatch((0.5, 0.15), 12.0, 0.75,
                        boxstyle="round,pad=0.02",
                        linewidth=1.5, edgecolor="#F9A825", facecolor="#FFFDE7", zorder=3)
    ax.add_patch(kp)
    ax.text(6.5, 0.53,
            "Key Principle: More congruent surfaces (similar radius) → more sliding.  "
            "Less congruent → more rolling.  Most joints combine roll + slide.",
            ha="center", va="center", fontsize=8, color="#E65100",
            fontweight="bold", zorder=4)

    ax.text(12.8, 0.05, "© THIEME Atlas", ha="right",
            va="bottom", fontsize=6.5, color="#9E9E9E")

    return save_fig(fig, "chart3_true_joint_types.png")


# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 4 – Joint Mechanics: Stability & Constraints
# ══════════════════════════════════════════════════════════════════════════════
def chart4():
    fig, ax = plt.subplots(figsize=(12, 9))
    ax.set_xlim(0, 12); ax.set_ylim(0, 9)
    ax.axis("off")
    fig.patch.set_facecolor(C_BG)
    ax.set_facecolor(C_BG)

    banner = FancyBboxPatch((0.3, 8.1), 11.4, 0.75,
                            boxstyle="round,pad=0.02",
                            linewidth=0, facecolor=C_TITLE, zorder=3)
    ax.add_patch(banner)
    ax.text(6, 8.48, "JOINT MECHANICS — Stability, Constraints & Erect Posture",
            ha="center", va="center", fontsize=13, color=C_WHITE, fontweight="bold")

    text_box(ax, 6, 7.2, 4.0, 0.6,
             "JOINT STABILITY & FUNCTION",
             C_MECH, C_WHITE, fs=10.5, bold=True, edge=C_MECH)

    # 4 constraints
    text_box(ax, 3.0, 6.05, 3.2, 0.55,
             "4 CONSTRAINTS ON JOINT MOTION", "#1B5E20", C_WHITE, fs=9, bold=True)
    arrow(ax, 6, 6.88, 3.0, 6.34)

    constraints = [
        (1.3, 4.8,  "1. Bony\nConstraint",   "Shape of articular\nsurfaces", "#A5D6A7"),
        (2.7, 4.8,  "2. Muscular\nConstraint","Muscles crossing\nthe joint",  "#A5D6A7"),
        (4.1, 4.8,  "3. Ligamentous\nConstraint","Ligaments limiting\nrange of motion","#A5D6A7"),
        (5.5, 4.8,  "4. Soft-tissue\nConstraint","Joint capsule &\nsurrounding tissue","#A5D6A7"),
    ]
    for (cx, cy, title, detail, bg) in constraints:
        text_box(ax, cx, cy+0.42, 1.7, 0.58, title, C_MECH, C_WHITE,
                 fs=7.5, bold=True, edge=C_MECH)
        text_box(ax, cx, cy-0.3, 1.7, 0.82, detail, bg, C_TEXT, fs=7.5, edge=C_MECH)
        arrow(ax, 3.0, 5.77, cx, cy+0.72)

    # key muscles for erect posture
    text_box(ax, 9.0, 6.05, 3.2, 0.55,
             "ERECT POSTURE — Key Muscles", "#1A237E", C_WHITE, fs=9, bold=True)
    arrow(ax, 6, 6.88, 9.0, 6.34)

    posture_data = [
        (7.35,  4.65, "Triceps Surae\n+ Tibialis Anterior",
         "Stabilize ankle\nin sagittal plane"),
        (9.0,   4.65, "Quadriceps\nFemoris",
         "Stabilizes the knee"),
        (10.65, 4.65, "Gluteal Muscles",
         "Stabilize hip\n(sagittal plane)"),
        (7.35,  3.05, "Gluteus Medius\n& Minimus",
         "Stabilize hip\n(frontal plane)"),
        (9.0,   3.05, "Intrinsic Back\nMuscles",
         "Stabilize spinal\ncolumn"),
    ]
    for (cx, cy, title, detail) in posture_data:
        text_box(ax, cx, cy+0.42, 2.3, 0.6, title, "#283593", C_WHITE,
                 fs=7.5, bold=True, edge="#1A237E")
        text_box(ax, cx, cy-0.28, 2.3, 0.78, detail, "#E8EAF6", C_TEXT,
                 fs=7.5, edge="#283593")
        arrow(ax, 9.0, 5.77, cx, cy+0.72)

    # Roll-Slide note
    rs_box = FancyBboxPatch((0.5, 1.45), 11.0, 0.82,
                            boxstyle="round,pad=0.02",
                            linewidth=1.5, edgecolor=C_MECH, facecolor="#E8F5E9", zorder=3)
    ax.add_patch(rs_box)
    ax.text(6, 1.86,
            "Roll-Slide Principle:\n"
            "Congruent surfaces (e.g. shoulder) → surfaces slide onto each other\n"
            "Less congruent (e.g. knee) → surfaces roll off each other  |  Most joints = combination",
            ha="center", va="center", fontsize=7.5,
            color="#1B5E20", fontweight="bold", zorder=4, multialignment="center")

    # Amphiarthroses note
    amp_box = FancyBboxPatch((0.5, 0.25), 5.0, 0.9,
                             boxstyle="round,pad=0.02",
                             linewidth=1.5, edgecolor=C_AMP, facecolor=C_AMPL, zorder=3)
    ax.add_patch(amp_box)
    ax.text(3.0, 0.7,
            "Amphiarthroses — stiff true joints with very limited\n"
            "movement due to tight ligamentous constraints.\n"
            "Example: Sacroiliac (iliosacral) joint",
            ha="center", va="center", fontsize=7.5, color=C_AMP,
            fontweight="bold", zorder=4, multialignment="center")

    ax.text(11.8, 0.05, "© THIEME Atlas", ha="right",
            va="bottom", fontsize=6.5, color="#9E9E9E")

    return save_fig(fig, "chart4_joint_mechanics.png")


# ══════════════════════════════════════════════════════════════════════════════
# Assemble all 4 charts into a single PDF
# ══════════════════════════════════════════════════════════════════════════════
def build_pdf(img_paths):
    pdf_path = os.path.join(OUT_DIR, "Joints_Flowcharts.pdf")
    W, H = A4  # 595.27 x 841.89 pts

    c = canvas.Canvas(pdf_path, pagesize=A4)

    chart_titles = [
        "Flowchart 1: Master Classification of Joints",
        "Flowchart 2: Structure of a True Synovial Joint",
        "Flowchart 3: Types of True Joints — By Shape & Movement",
        "Flowchart 4: Joint Mechanics — Stability & Erect Posture",
    ]

    for i, (img_path, title) in enumerate(zip(img_paths, chart_titles)):
        # header
        c.setFillColorRGB(0.10, 0.23, 0.36)
        c.rect(0, H - 42, W, 42, fill=1, stroke=0)
        c.setFillColorRGB(1, 1, 1)
        c.setFont("Helvetica-Bold", 13)
        c.drawCentredString(W/2, H - 27, title)
        c.setFont("Helvetica", 8)
        c.setFillColorRGB(0.69, 0.74, 0.77)
        c.drawCentredString(W/2, H - 38,
            "THIEME Atlas of Anatomy — General Anatomy & Musculoskeletal System")

        # image
        img = ImageReader(img_path)
        iw, ih = img.getSize()
        scale = min((W - 40) / iw, (H - 90) / ih)
        draw_w = iw * scale
        draw_h = ih * scale
        x = (W - draw_w) / 2
        y = (H - 50) - draw_h
        c.drawImage(img_path, x, y, width=draw_w, height=draw_h,
                    preserveAspectRatio=True)

        # footer
        c.setFillColorRGB(0.62, 0.71, 0.76)
        c.setFont("Helvetica", 7)
        c.drawString(30, 15, f"Page {i+1} of {len(img_paths)}")
        c.drawRightString(W - 30, 15, "Source: THIEME Atlas — ISBN 9781626237186, pp. 60-71")

        if i < len(img_paths) - 1:
            c.showPage()

    c.save()
    return pdf_path


if __name__ == "__main__":
    print("Generating flowcharts...")
    p1 = chart1()
    print(f"  ✓ Chart 1: {p1}")
    p2 = chart2()
    print(f"  ✓ Chart 2: {p2}")
    p3 = chart3()
    print(f"  ✓ Chart 3: {p3}")
    p4 = chart4()
    print(f"  ✓ Chart 4: {p4}")
    pdf = build_pdf([p1, p2, p3, p4])
    print(f"  ✓ PDF assembled: {pdf}")
Running Command

python3 /home/daytona/workspace/joints-flowcharts/make_flowcharts.py 2>&1

All 4 flowcharts are generated and compiled into a single PDF. Here's your download link:

Joints Flowcharts

PDF Document · PDF


Here's what's inside the 4-page PDF:
Flowchart 1 - Master Classification Tree Full branching tree from JOINTS → False Joints (Syndesmoses, Synchondroses, Synostoses) and True Joints (Diarthroses, Amphiarthroses) with all leaf examples (interosseous membranes, gomphosis, costal cartilage, sacroiliac joint, etc.)
Flowchart 2 - Structure of a True Synovial Joint All 8 components radiating from the central node: articular surfaces, joint cavity, joint capsule, synovial fluid, intra-articular structures (menisci, discs, labra), ligaments, muscles, and synovial bursae - each with clinical details. Includes the key note about hyaline cartilage being unable to regenerate.
Flowchart 3 - Types of True Joints Organized by motion type (translational vs rotational) and number of axes: plane joints, hinge, pivot, ellipsoidal, saddle, and spheroidal (hip vs shoulder deep/shallow socket comparison) with axes, degrees of freedom, and examples for each.
Flowchart 4 - Joint Mechanics The 4 constraints on joint motion (bony, muscular, ligamentous, soft-tissue), the 5 key muscle groups for erect posture, and the roll-slide principle - all from the THIEME atlas pp. 60-71.
This is a shared conversation. Sign in to Orris to start your own chat.