Create a super aesthetic, handwritten-style revision note page for the topic: Tissues • Make it look like neat exam notes written on pages(top view, clean layout, colorful headings, underlines, boxes). • Keep it crisp, high-yield, and exam-oriented. • Use clear headings and organize content like this (modify if needed for topic): ○ Definition ○ Etiology ○ Pathogenesis (flow/arrow style) ○ Clinical features ○ Radiographic features (if applicable) ○ Histopathology (if applicable) ○ Key features ○ Differential diagnosis ○ Management ○ Complications ○ Key points / last-minute revision box • Add simple diagrams or labeled sketches wherever helpful. • Highlight important keywords. • Include one memory trick / one-line summary • Keep handwriting-style fonts, pastel colors, and clean spacing

Checking Date & Time
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Browsing Directory

/home/daytona/workspace

Running Command

mkdir -p /home/daytona/workspace/tissues-notes && ls /home/daytona/workspace/tissues-notes

Writing File

~/tissues-notes/generate_notes.py

#!/usr/bin/env python3
"""
Generate aesthetic handwritten-style revision notes for TISSUES topic
Using PIL/Pillow to create a multi-page image
"""

from PIL import Image, ImageDraw, ImageFont
import os
import math

# ── Output path ──────────────────────────────────────────────────────────────
OUT_DIR = "/home/daytona/workspace/tissues-notes/output"
os.makedirs(OUT_DIR, exist_ok=True)

# ── Fonts ─────────────────────────────────────────────────────────────────────
FONT_PATHS = {
    "title":   "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
    "heading": "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
    "sub":     "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
    "body":    "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    "italic":  "/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf",
}

def load_font(key, size):
    try:
        return ImageFont.truetype(FONT_PATHS[key], size)
    except:
        return ImageFont.load_default()

# ── Palette ──────────────────────────────────────────────────────────────────
BG          = (255, 252, 245)       # warm cream paper
RULE_LINE   = (210, 200, 185)       # faint ruled lines
MARGIN_LINE = (255, 180, 160)       # pink margin line
BLACK       = (30,  25,  20)
DARK_BLUE   = (30,  60, 120)

# Section heading colours (pastel + dark text on light bg)
COLORS = {
    "pink":    {"bg": (255, 213, 220), "fg": (180, 30,  60),  "line": (240, 100, 130)},
    "blue":    {"bg": (210, 230, 255), "fg": (20,  60,  160), "line": (80,  130, 220)},
    "green":   {"bg": (200, 240, 210), "fg": (20,  110, 50),  "line": (60,  180, 90)},
    "purple":  {"bg": (230, 215, 255), "fg": (90,  30,  170), "line": (160, 80,  230)},
    "orange":  {"bg": (255, 230, 200), "fg": (180, 80,  10),  "line": (240, 150, 50)},
    "teal":    {"bg": (200, 240, 240), "fg": (10,  120, 130), "line": (30,  180, 190)},
    "yellow":  {"bg": (255, 245, 180), "fg": (140, 100, 10),  "line": (220, 185, 30)},
    "red":     {"bg": (255, 210, 205), "fg": (160, 20,  20),  "line": (220, 80,  70)},
}

# Section order → colour mapping
SECTION_COLORS = ["pink", "blue", "green", "purple", "orange", "teal", "yellow", "red"]

# ── Canvas ────────────────────────────────────────────────────────────────────
W, H        = 1600, 2200   # portrait A4-ish @ 200 dpi
MARGIN_L    = 100
MARGIN_R    = 80
RULE_STEP   = 38
TOP_OFFSET  = 180          # below title header

# ── Helpers ───────────────────────────────────────────────────────────────────
def draw_ruled_lines(draw, top, bottom):
    for y in range(top, bottom, RULE_STEP):
        draw.line([(MARGIN_L + 60, y), (W - MARGIN_R, y)], fill=RULE_LINE, width=1)

def draw_margin(draw):
    draw.line([(MARGIN_L + 55, 0), (MARGIN_L + 55, H)], fill=MARGIN_LINE, width=2)

def draw_page_header(draw, page_num, total):
    # pink top strip
    draw.rectangle([0, 0, W, 60], fill=(255, 182, 193))
    f = load_font("title", 32)
    draw.text((W//2, 30), "✦  TISSUES  –  Revision Notes  ✦",
              font=f, fill=(120, 20, 60), anchor="mm")
    # page number
    fp = load_font("body", 20)
    draw.text((W - MARGIN_R, 45), f"pg {page_num}/{total}",
              font=fp, fill=(160, 80, 100), anchor="rm")

def wrap_text(draw, text, font, max_width):
    """Return list of lines that fit within max_width."""
    words = text.split()
    lines, line = [], ""
    for w in words:
        test = (line + " " + w).strip()
        bb = draw.textbbox((0, 0), test, font=font)
        if bb[2] - bb[0] > max_width and line:
            lines.append(line)
            line = w
        else:
            line = test
    if line:
        lines.append(line)
    return lines

def draw_section_heading(draw, y, text, color_key, icon=""):
    c = COLORS[color_key]
    f = load_font("heading", 26)
    bb = draw.textbbox((0, 0), f"{icon} {text}", font=f)
    tw = bb[2] - bb[0]
    pad = 14
    rx0, ry0 = MARGIN_L + 60, y - 4
    rx1, ry1 = rx0 + tw + pad * 2, y + 36
    draw.rounded_rectangle([rx0, ry0, rx1, ry1], radius=8, fill=c["bg"])
    draw.line([rx0, ry1 + 2, rx1, ry1 + 2], fill=c["line"], width=3)
    draw.text((rx0 + pad, y + 2), f"{icon} {text}", font=f, fill=c["fg"])
    return ry1 + 10

def draw_bullet(draw, y, text, font, color=(50, 50, 50), indent=0, bullet="•"):
    x = MARGIN_L + 70 + indent
    max_w = W - x - MARGIN_R - 10
    lines = wrap_text(draw, text, font, max_w)
    draw.text((x - 20, y + 2), bullet, font=font, fill=color)
    for i, ln in enumerate(lines):
        draw.text((x, y + i * RULE_STEP), ln, font=font, fill=color)
    return y + len(lines) * RULE_STEP

def draw_highlight(draw, y, text, font, bg=(255, 245, 150), fg=(100, 60, 0)):
    x = MARGIN_L + 70
    bb = draw.textbbox((0, 0), text, font=font)
    tw = bb[2] - bb[0]
    draw.rounded_rectangle([x - 4, y - 2, x + tw + 8, y + 28], radius=5, fill=bg)
    draw.text((x, y), text, font=font, fill=fg)
    return y + RULE_STEP

def draw_box(draw, lines_text, y, color_key, title=""):
    c = COLORS[color_key]
    fb = load_font("body", 21)
    fh = load_font("sub", 22)
    x0 = MARGIN_L + 60
    x1 = W - MARGIN_R
    # measure height
    all_lines = []
    for ln in lines_text:
        wrapped = wrap_text(draw, ln, fb, x1 - x0 - 24)
        all_lines.extend(wrapped)
    box_h = len(all_lines) * 30 + (40 if title else 16) + 16
    draw.rounded_rectangle([x0, y, x1, y + box_h], radius=10,
                            fill=c["bg"], outline=c["line"], width=2)
    if title:
        draw.text((x0 + 12, y + 8), title, font=fh, fill=c["fg"])
        ty = y + 40
    else:
        ty = y + 12
    for ln in all_lines:
        draw.text((x0 + 20, ty), ln, font=fb, fill=BLACK)
        ty += 30
    return y + box_h + 12

def draw_arrow_flow(draw, items, y, color_key):
    """Draw a horizontal flow: item → item → item"""
    c = COLORS[color_key]
    fb = load_font("body", 20)
    x = MARGIN_L + 70
    box_w = 190
    box_h = 44
    gap = 30
    for i, item in enumerate(items):
        draw.rounded_rectangle([x, y, x + box_w, y + box_h],
                                radius=8, fill=c["bg"], outline=c["line"], width=2)
        # center text
        bb = draw.textbbox((0, 0), item, font=fb)
        tw = bb[2] - bb[0]
        tx = x + (box_w - tw) // 2
        draw.text((tx, y + 10), item, font=fb, fill=c["fg"])
        x += box_w
        if i < len(items) - 1:
            # draw arrow
            ay = y + box_h // 2
            draw.line([(x, ay), (x + gap - 4, ay)], fill=c["line"], width=2)
            draw.polygon([(x + gap, ay), (x + gap - 8, ay - 6), (x + gap - 8, ay + 6)],
                         fill=c["line"])
            x += gap
    return y + box_h + 16

def draw_table(draw, headers, rows, y, color_key):
    c = COLORS[color_key]
    fh = load_font("sub", 20)
    fb = load_font("body", 19)
    x0 = MARGIN_L + 60
    col_w = (W - MARGIN_R - x0) // len(headers)
    row_h = 36
    # header row
    draw.rectangle([x0, y, W - MARGIN_R, y + row_h], fill=c["bg"])
    for i, h in enumerate(headers):
        draw.text((x0 + i * col_w + 8, y + 8), h, font=fh, fill=c["fg"])
    y += row_h
    for ridx, row in enumerate(rows):
        bg2 = (248, 248, 248) if ridx % 2 == 0 else (238, 238, 248)
        draw.rectangle([x0, y, W - MARGIN_R, y + row_h], fill=bg2)
        for i, cell in enumerate(row):
            draw.text((x0 + i * col_w + 8, y + 8), cell, font=fb, fill=BLACK)
        y += row_h
    # border
    draw.rectangle([x0, y - row_h * (len(rows) + 1), W - MARGIN_R, y],
                   outline=c["line"], width=2)
    return y + 14

def draw_memory_trick(draw, y, trick, summary):
    c = COLORS["yellow"]
    fb = load_font("italic", 22)
    fh = load_font("sub", 22)
    x0 = MARGIN_L + 60
    x1 = W - MARGIN_R
    # star decoration
    draw.text((x0 - 10, y - 4), "★", font=load_font("heading", 28), fill=(220, 160, 10))
    draw.text((x1 - 20, y - 4), "★", font=load_font("heading", 28), fill=(220, 160, 10))
    lines_t = wrap_text(draw, trick, fb, x1 - x0 - 40)
    lines_s = wrap_text(draw, summary, fb, x1 - x0 - 40)
    total_h = 50 + (len(lines_t) + len(lines_s) + 1) * 30 + 20
    draw.rounded_rectangle([x0, y, x1, y + total_h], radius=12,
                            fill=c["bg"], outline=c["line"], width=3)
    draw.text((x0 + 16, y + 8), "🧠 Memory Trick", font=fh, fill=c["fg"])
    ty = y + 44
    for ln in lines_t:
        draw.text((x0 + 20, ty), ln, font=fb, fill=(100, 60, 0))
        ty += 30
    ty += 6
    draw.line([x0 + 16, ty, x1 - 16, ty], fill=c["line"], width=1)
    ty += 10
    draw.text((x0 + 16, ty), "📌 One-line summary:", font=fh, fill=c["fg"])
    ty += 30
    for ln in lines_s:
        draw.text((x0 + 20, ty), ln, font=fb, fill=BLACK)
        ty += 30
    return y + total_h + 16


# ══════════════════════════════════════════════════════════════════════════════
#  PAGE CONTENT DEFINITIONS
# ══════════════════════════════════════════════════════════════════════════════

def build_page1():
    img = Image.new("RGB", (W, H), BG)
    draw = ImageDraw.Draw(img)
    draw_ruled_lines(draw, TOP_OFFSET, H)
    draw_margin(draw)
    draw_page_header(draw, 1, 3)

    fb  = load_font("body", 22)
    fbb = load_font("body", 20)
    fi  = load_font("italic", 21)
    fh  = load_font("sub", 23)

    y = TOP_OFFSET + 10

    # ── TITLE ─────────────────────────────────────────────────────────────────
    ft = load_font("title", 42)
    draw.text((W // 2, y + 16), "TISSUES", font=ft, fill=DARK_BLUE, anchor="mm")
    y += 52
    draw.line([MARGIN_L + 60, y, W - MARGIN_R, y], fill=DARK_BLUE, width=3)
    y += 14

    # ── DEFINITION ────────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "DEFINITION", "pink", "📖")
    y = draw_bullet(draw, y, "A tissue is a group of cells with similar structure & function, working together with ECM to perform a specific role.", fb, color=BLACK)
    y = draw_bullet(draw, y, "4 Basic tissue types exist in the body:", fb, color=BLACK)
    y += 6

    # flow diagram
    y = draw_arrow_flow(draw, ["Epithelial", "Connective", "Muscle", "Nervous"], y, "pink")
    y += 4

    # ── OVERVIEW TABLE ────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "OVERVIEW TABLE", "blue", "📊")
    headers = ["Tissue", "Cells", "ECM", "Main Function"]
    rows = [
        ["Epithelial", "Polyhedral, tightly packed", "Small", "Lining, secretion, absorption"],
        ["Connective", "Fixed + wandering cells",   "Abundant", "Support, protection"],
        ["Muscle",     "Elongated, contractile",    "Moderate", "Movement, contraction"],
        ["Nervous",    "Neurons + glia",            "Very small", "Impulse transmission"],
    ]
    y = draw_table(draw, headers, rows, y, "blue")

    # ── EMBRYOLOGICAL ORIGIN ──────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "EMBRYOLOGICAL ORIGIN (Histogenesis)", "green", "🧬")
    y = draw_arrow_flow(draw, ["Ectoderm", "Mesoderm", "Endoderm"], y, "green")
    germ_bullets = [
        ("Ectoderm →", "Epidermis, CNS (neuroectoderm), cornea, enamel, adenohypophysis, sensory epithelia"),
        ("Mesoderm →", "Connective tissue, muscle, heart, blood vessels, kidneys, gonads, adrenal cortex"),
        ("Endoderm →", "GI epithelium, liver, pancreas, gallbladder, respiratory epithelium, thyroid, bladder"),
    ]
    fi2 = load_font("italic", 20)
    for label, desc in germ_bullets:
        draw.text((MARGIN_L + 70, y + 2), label, font=load_font("sub", 20), fill=COLORS["green"]["fg"])
        bb = draw.textbbox((0, 0), label + "  ", font=load_font("sub", 20))
        xoff = bb[2] - bb[0]
        lines = wrap_text(draw, desc, fbb, W - MARGIN_R - MARGIN_L - 80 - xoff)
        draw.text((MARGIN_L + 70 + xoff, y + 2), lines[0], font=fbb, fill=BLACK)
        for ln in lines[1:]:
            y += RULE_STEP
            draw.text((MARGIN_L + 90 + xoff, y + 2), ln, font=fbb, fill=BLACK)
        y += RULE_STEP

    # ── EPITHELIAL TISSUE ─────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "EPITHELIAL TISSUE", "purple", "🔬")
    y = draw_bullet(draw, y, "Closely packed cells with minimal ECM; always rests on a basement membrane", fb)
    y = draw_bullet(draw, y, "Avascular — nutrients supplied by diffusion from underlying connective tissue", fb)
    y = draw_bullet(draw, y, "Shows polarity: apical pole (free surface) ≠ basal pole (attached)", fb)
    y = draw_bullet(draw, y, "Functions: covering/lining • absorption • secretion • sensory reception", fb)
    y += 6

    # Classification sub-box
    y = draw_section_heading(draw, y, "Classification of Epithelium", "purple", "📐")
    headers2 = ["Layers", "Shape", "Example"]
    rows2 = [
        ["Simple (1 layer)",      "Squamous",  "Alveoli, Bowman's capsule"],
        ["Simple",                "Cuboidal",  "Thyroid follicle, kidney tubules"],
        ["Simple",                "Columnar",  "Small intestine (with villi)"],
        ["Pseudostratified",      "Columnar",  "Respiratory tract (ciliated)"],
        ["Stratified (≥2 layers)","Squamous",  "Skin (keratinised), oral mucosa"],
        ["Transitional",          "Variable",  "Urinary bladder (urothelium)"],
    ]
    y = draw_table(draw, headers2, rows2, y, "purple")

    return img

def build_page2():
    img = Image.new("RGB", (W, H), BG)
    draw = ImageDraw.Draw(img)
    draw_ruled_lines(draw, TOP_OFFSET, H)
    draw_margin(draw)
    draw_page_header(draw, 2, 3)

    fb  = load_font("body", 22)
    fbb = load_font("body", 20)
    fi  = load_font("italic", 21)

    y = TOP_OFFSET + 10

    # ── JUNCTIONS ─────────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "INTERCELLULAR JUNCTIONS (Epithelium)", "orange", "🔗")
    junctions = [
        ("Tight (Occluding)",    "Claudin & occludin → prevents paracellular leakage → BARRIER function"),
        ("Adherens junctions",   "Cadherins → Zonula adherens (actin) + Desmosomes / Maculae adherens (keratin IF)"),
        ("Hemidesmosomes",       "Integrins → attach cell to basal lamina"),
        ("Gap junctions",        "Connexins (connexons) → intercellular channels → cell communication & coupling"),
    ]
    for jname, jdesc in junctions:
        draw.text((MARGIN_L + 70, y + 2), f"◆ {jname}:", font=load_font("sub", 20), fill=COLORS["orange"]["fg"])
        y += RULE_STEP
        lines = wrap_text(draw, jdesc, fbb, W - MARGIN_R - MARGIN_L - 100)
        for ln in lines:
            draw.text((MARGIN_L + 90, y + 2), ln, font=fbb, fill=BLACK)
            y += RULE_STEP

    # ── APICAL SPECIALISATIONS ────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "APICAL SURFACE SPECIALISATIONS", "teal", "⬆️")
    apical = [
        ("Microvilli",  "Short membrane protrusions; actin core; ↑ absorption surface area (brush border in gut)"),
        ("Stereocilia", "Long microvilli (NOT true cilia); mechanosensory (inner ear); absorption (male repro tract)"),
        ("Cilia",       "9+2 microtubule axoneme; dynein-powered; propel mucus (respiratory tract) + oocytes (fallopian tube)"),
    ]
    for aname, adesc in apical:
        y = draw_bullet(draw, y, f"{aname}: {adesc}", fb, color=COLORS["teal"]["fg"], bullet="▸")

    # ── GLANDS ────────────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "GLANDS (Secretory Epithelium)", "yellow", "🧪")
    y = draw_bullet(draw, y, "Exocrine glands — have ducts; deliver secretion to surface", fb)
    y = draw_bullet(draw, y, "Endocrine glands — no ducts; secrete hormones directly into blood", fb)
    y += 4
    secretion_data = [
        ("Merocrine", "Exocytosis (no cell loss)", "Pancreatic acini, salivary glands"),
        ("Apocrine",  "Apical cytoplasm pinched off", "Mammary glands, axillary sweat glands"),
        ("Holocrine", "Whole cell disintegrates",   "Sebaceous glands"),
    ]
    y = draw_table(draw, ["Type", "Mechanism", "Example"], secretion_data, y, "yellow")
    y = draw_bullet(draw, y, "Mucous glands → mucin (stains with PAS); Serous glands → enzymes/proteins (stains dark H&E)", fbb)

    # ── CONNECTIVE TISSUE ─────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "CONNECTIVE TISSUE", "blue", "🕸️")
    y = draw_bullet(draw, y, "Characterised by its EXTRACELLULAR MATRIX (ECM) — most abundant component", fb)
    y = draw_bullet(draw, y, "Underlies & supports all other three tissue types", fb)
    y += 4

    ct_types = [
        ("Embryonic CT",         "Mesenchyme (most), Mucous CT (Wharton's jelly in umbilical cord)"),
        ("CT Proper – Loose",    "Areolar, Adipose, Reticular tissue"),
        ("CT Proper – Dense",    "Dense regular (tendons, ligaments) / Dense irregular (dermis)"),
        ("Specialised CT",       "Bone, Cartilage (hyaline/elastic/fibrocartilage), Blood, Lymph"),
    ]
    for tname, tdesc in ct_types:
        draw.text((MARGIN_L + 70, y + 2), f"• {tname}:", font=load_font("sub", 20), fill=COLORS["blue"]["fg"])
        y += RULE_STEP
        lines = wrap_text(draw, tdesc, fbb, W - MARGIN_R - MARGIN_L - 100)
        for ln in lines:
            draw.text((MARGIN_L + 90, y + 2), ln, font=fbb, fill=BLACK)
            y += RULE_STEP

    # ECM sub-section
    y = draw_section_heading(draw, y, "ECM Components", "blue", "🔩")
    y = draw_bullet(draw, y, "Fibres: Collagen (types I–IV+), Elastic fibres, Reticular fibres (type III collagen)", fb)
    y = draw_bullet(draw, y, "Ground substance: GAGs (hyaluronic acid, heparan sulphate), Proteoglycans, Adhesion proteins (fibronectin, laminin)", fb)
    y = draw_bullet(draw, y, "Cells: Fibroblasts, Macrophages, Mast cells, Plasma cells, Adipocytes, Leukocytes", fb)

    # ── MUSCLE TISSUE ─────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "MUSCLE TISSUE", "green", "💪")
    headers3 = ["Feature", "Skeletal", "Cardiac", "Smooth"]
    rows3 = [
        ["Striations",   "Yes",      "Yes",                 "No"],
        ["Nuclei",       "Multiple, peripheral", "1–2, central", "1, central"],
        ["Control",      "Voluntary", "Involuntary",        "Involuntary"],
        ["T-tubules",    "Wide (triad)", "Narrow (dyad)",   "Absent"],
        ["Intercalated", "Absent",    "Present",            "Absent"],
        ["Regeneration", "Satellite cells", "Very limited (fibrosis)", "Yes (mitosis)"],
    ]
    y = draw_table(draw, headers3, rows3, y, "green")
    y = draw_highlight(draw, y, "   KEY: All muscle cells contain ACTIN + MYOSIN (contractile proteins)   ",
                       load_font("sub", 21), bg=(200, 255, 200), fg=(10, 100, 30))

    return img

def build_page3():
    img = Image.new("RGB", (W, H), BG)
    draw = ImageDraw.Draw(img)
    draw_ruled_lines(draw, TOP_OFFSET, H)
    draw_margin(draw)
    draw_page_header(draw, 3, 3)

    fb  = load_font("body", 22)
    fbb = load_font("body", 20)
    fi  = load_font("italic", 21)

    y = TOP_OFFSET + 10

    # ── NERVOUS TISSUE ────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "NERVOUS TISSUE", "purple", "⚡")
    y = draw_bullet(draw, y, "Receives, integrates & transmits information — electrical impulses (action potentials)", fb)
    y = draw_bullet(draw, y, "Components: Neurons + Supporting (glial) cells", fb)
    y += 4

    neuron_rows = [
        ["Cell body (Soma)", "Contains nucleus, Nissl bodies (RER), organelles"],
        ["Axon",             "Carries impulses AWAY from cell body; 1 per neuron; may be myelinated"],
        ["Dendrites",        "Carry impulses TOWARD cell body; multiple per neuron; no myelin"],
    ]
    y = draw_table(draw, ["Part", "Description"], neuron_rows, y, "purple")

    y = draw_section_heading(draw, y, "Glial Cells", "purple", "🧠")
    glial = [
        ("Astrocytes",       "Blood-brain barrier; metabolic support; K+ buffering"),
        ("Oligodendrocytes", "Myelin sheath in CNS (one cell → many axons)"),
        ("Schwann cells",    "Myelin sheath in PNS (one cell → one axon)"),
        ("Microglia",        "CNS macrophages; immune surveillance"),
        ("Ependymal cells",  "Line ventricles; produce CSF"),
        ("Satellite cells",  "Support neurons in PNS ganglia"),
    ]
    for gname, gdesc in glial:
        y = draw_bullet(draw, y, f"{gname}: {gdesc}", fbb, color=COLORS["purple"]["fg"], bullet="▸")

    # ── BASEMENT MEMBRANE ─────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "BASEMENT MEMBRANE (Key Exam Point)", "red", "🧱")
    bm_items = [
        "Basal lamina (made by epithelial cells): Type IV collagen + Laminin",
        "Reticular lamina (made by CT cells): Type III collagen (reticulin) + Type VII anchoring fibrils",
        "Functions: Attachment • Filtration • Scaffold for regeneration • Compartmentalisation",
        "Stains: PAS-positive (basement membrane), Silver stain (reticular fibres)",
    ]
    for item in bm_items:
        y = draw_bullet(draw, y, item, fbb, bullet="✔")

    # ── KEY FACTS BOX ─────────────────────────────────────────────────────────
    y = draw_section_heading(draw, y, "HIGH-YIELD KEY FACTS", "orange", "⭐")
    kf = [
        "Only tissue type that is AVASCULAR → Epithelium",
        "Only epithelium found in urinary tract → Transitional (Urothelium)",
        "Respiratory epithelium → Pseudostratified ciliated columnar with goblet cells",
        "ECM most abundant in → Connective tissue",
        "Cardiac muscle unique feature → Intercalated discs (desmosomes + gap junctions)",
        "Repair: Skeletal muscle = satellite cells | Cardiac = fibrosis | Smooth = mitosis",
        "Myelination: CNS = Oligodendrocytes | PNS = Schwann cells",
        "Wharton's jelly (umbilical cord) = mucous connective tissue (embryonic)",
    ]
    for kf_item in kf:
        y = draw_bullet(draw, y, kf_item, fbb, color=COLORS["orange"]["fg"], bullet="★")
    y += 4

    # ── LAST-MINUTE REVISION BOX ──────────────────────────────────────────────
    y = draw_box(draw, [
        "4 Tissue types: Epithelial · Connective · Muscle · Nervous",
        "Epithelium: avascular, on BM, polar, no ECM",
        "CT: most ECM, supports all others",
        "Muscle: actin+myosin; Skeletal=voluntary; Cardiac=intercalated discs",
        "Nervous: neurons (axon away, dendrites toward) + glia",
        "Germ layers → Ecto (epidermis/CNS) | Meso (CT/muscle) | Endo (GI/respiratory)",
    ], y, "teal", title="📋  LAST-MINUTE REVISION")

    # ── MEMORY TRICK ─────────────────────────────────────────────────────────
    y = draw_memory_trick(
        draw, y,
        trick='Mnemonic: "Every Cat Meows Nicely" → Epithelial · Connective · Muscle · Nervous',
        summary="Tissues are organised assemblies of specialised cells + ECM performing a shared function; "
                "the 4 basic types arise from 3 embryonic germ layers and collectively form every organ in the body."
    )

    return img


# ── Build & save ──────────────────────────────────────────────────────────────
pages = [build_page1(), build_page2(), build_page3()]

# Save individual pages
for i, page in enumerate(pages):
    path = f"{OUT_DIR}/tissues_notes_page{i+1}.png"
    page.save(path, "PNG", dpi=(200, 200))
    print(f"Saved: {path}")

# Also save a combined vertical strip
total_h = sum(p.height for p in pages)
combined = Image.new("RGB", (W, total_h), BG)
cy = 0
for page in pages:
    combined.paste(page, (0, cy))
    cy += page.height
combined_path = f"{OUT_DIR}/tissues_notes_ALL_PAGES.png"
combined.save(combined_path, "PNG")
print(f"Combined saved: {combined_path}")

print("Done!")
You've run out of free credits. Sign up for a free account to continue using Orris.
This is a shared conversation. Sign in to Orris to start your own chat.