Please give relevant answers for the given questions
| Factor | Symbol |
|---|---|
| 1. Condylar guidance (incisal guidance) | C |
| 2. Incisal guidance | I |
| 3. Plane of occlusion | P |
| 4. Cusp height | C |
| 5. Compensating curve | C |
| Muscle | Origin | Insertion | Action |
|---|---|---|---|
| Masseter | Zygomatic arch | Ramus/angle of mandible | Elevation (closes jaw) |
| Temporalis | Temporal fossa | Coronoid process | Elevation; posterior fibers retract |
| Medial pterygoid | Medial pterygoid plate | Medial ramus/angle | Elevation, protrusion |
| Lateral pterygoid | Lateral pterygoid plate (superior/inferior heads) | Condyle neck/disc | Protrusion, lateral excursions, opening |
| Method | Agent | Duration |
|---|---|---|
| Soaking | 1% sodium hypochlorite | 5-10 min (max 15 min, longer causes bleaching) |
| Soaking | 2% glutaraldehyde (Cidex) | 10-30 min |
| Soaking | 0.2% chlorhexidine | 10-30 min |
| Microwave | Water in microwave at 650W | 3-5 min |
| UV light | UV chamber | 5-10 min |
| Chemical tablets | Alkaline peroxide tablets | 15-30 min overnight |
| Impression Material | Disinfectant | Method | Time |
|---|---|---|---|
| Alginate | 1% sodium hypochlorite | Spray (not soak - distortion) | 10 min |
| Polyvinyl siloxane | 2% glutaraldehyde or hypochlorite | Soak or spray | 10-30 min |
| Polyether | 1% sodium hypochlorite | Spray only | 10 min |
| ZOE paste | 2% glutaraldehyde | Soak | 10 min |
| Impression plaster | 1% hypochlorite | Spray | 10 min |
| Type | Examples | Mechanism |
|---|---|---|
| Mechanical | Toothbrush, denture brush | Physical removal |
| Chemical soaking | Alkaline peroxide tablets (Steradent, Polident) | Oxidative; effervescent |
| Chemical immersion | Dilute NaOCl (1%), dilute acids | Bleaching/cleaning |
| Ultrasonic | Ultrasonic baths with cleaning solution | Cavitation + chemical |
| Enzymatic | Proteolytic, glucoamylase | Break down organic deposits |
| Class | Description | Prognosis |
|---|---|---|
| I - Philosophical | Understands problem; accepts limitations; cooperative | Excellent |
| II - Exacting | Meticulous; hard to please but rational | Good with patience |
| III - Hysterical | Emotional; unrealistic expectations; blames dentist | Guarded |
| IV - Indifferent | Does not care; minimal cooperation; wears denture out of social pressure | Poor |
Create a study guide PDF with diagrams of key CD concepts
mkdir -p /home/daytona/workspace/cd-study-guide && ls /home/daytona/workspace/
~/cd-study-guide/generate_cd_guide.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.graphics.shapes import (
Drawing, Rect, Circle, Ellipse, Line, Polygon, String, Arrow,
Group, PolyLine
)
from reportlab.graphics import renderPDF
from reportlab.platypus.flowables import Flowable
import math
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor('#1B3A5C')
TEAL = colors.HexColor('#2E7D8C')
GOLD = colors.HexColor('#D4A843')
CREAM = colors.HexColor('#FFF8EE')
LTBLUE = colors.HexColor('#D6EAF8')
LTGREEN = colors.HexColor('#D5F5E3')
LTYELLOW= colors.HexColor('#FEFBD8')
LTRED = colors.HexColor('#FADBD8')
LTPURP = colors.HexColor('#E8DAEF')
GREY = colors.HexColor('#ECF0F1')
DKGREY = colors.HexColor('#5D6D7E')
WHITE = colors.white
BLACK = colors.black
RED = colors.HexColor('#C0392B')
GREEN = colors.HexColor('#1E8449')
PURPLE = colors.HexColor('#6C3483')
ORANGE = colors.HexColor('#D35400')
W, H = A4 # 595.27 x 841.89 pts
# ── Styles ───────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent='Normal', **kwargs):
return ParagraphStyle(name, parent=styles[parent], **kwargs)
TITLE_STYLE = make_style('MainTitle', 'Title',
fontSize=28, textColor=WHITE, alignment=TA_CENTER, spaceAfter=6,
fontName='Helvetica-Bold')
SUBTITLE_STYLE = make_style('SubTitle', 'Normal',
fontSize=13, textColor=GOLD, alignment=TA_CENTER, spaceAfter=4,
fontName='Helvetica-Oblique')
H1 = make_style('H1', fontSize=16, textColor=WHITE, fontName='Helvetica-Bold',
spaceAfter=6, spaceBefore=10, backColor=NAVY, leftIndent=-6, rightIndent=-6,
leading=22)
H2 = make_style('H2', fontSize=13, textColor=NAVY, fontName='Helvetica-Bold',
spaceAfter=4, spaceBefore=8, borderPad=2)
H3 = make_style('H3', fontSize=11, textColor=TEAL, fontName='Helvetica-Bold',
spaceAfter=3, spaceBefore=5)
BODY = make_style('Body', fontSize=9.5, leading=14, spaceAfter=3,
alignment=TA_JUSTIFY)
BULLET = make_style('Bullet', fontSize=9.5, leading=13, leftIndent=14,
bulletIndent=4, spaceAfter=2)
SMALL = make_style('Small', fontSize=8.5, leading=12, textColor=DKGREY)
CAPTION = make_style('Caption', fontSize=8, textColor=DKGREY,
alignment=TA_CENTER, fontName='Helvetica-Oblique')
# ── Helper: coloured banner paragraph ───────────────────────────────────────
def banner(text, style=H1, bg=NAVY, pad=6):
d = Drawing(W - 80, 28)
d.add(Rect(0, 0, W-80, 28, fillColor=bg, strokeColor=None, rx=4, ry=4))
return [d, Paragraph(f'<font color="white"><b>{text}</b></font>', H2)]
def section_heading(text, bg=NAVY):
d = Drawing(W - 80, 26)
d.add(Rect(0, 0, W-80, 26, fillColor=bg, strokeColor=None, rx=3, ry=3))
return d
# ── Cover Page Drawing ────────────────────────────────────────────────────────
def make_cover():
d = Drawing(W, H)
# background gradient-like blocks
d.add(Rect(0, 0, W, H, fillColor=NAVY, strokeColor=None))
d.add(Rect(0, H*0.55, W, H*0.45, fillColor=colors.HexColor('#0D2137'), strokeColor=None))
# decorative circles
for r, alpha in [(200,0.06),(140,0.09),(90,0.13)]:
d.add(Circle(W*0.82, H*0.22, r, fillColor=TEAL, strokeColor=None,
fillOpacity=alpha))
# gold accent bar
d.add(Rect(50, H*0.53, W-100, 4, fillColor=GOLD, strokeColor=None))
# tooth silhouette (simple molar outline)
pts = [280,620, 260,580, 265,555, 290,545, 310,548,
330,545, 355,555, 360,580, 340,620]
d.add(Polygon(pts, fillColor=WHITE, strokeColor=GOLD, strokeWidth=1.5, fillOpacity=0.12))
# denture arch (simplified half-arch)
cx, cy, rx2, ry2 = W/2, 480, 160, 80
arc_pts = []
for angle in range(0, 181, 10):
rad = math.radians(angle)
arc_pts.extend([cx + rx2*math.cos(rad), cy - ry2*math.sin(rad)])
d.add(PolyLine(arc_pts, strokeColor=GOLD, strokeWidth=2.5, strokeOpacity=0.5))
# title text
d.add(String(W/2, H*0.72, 'COMPLETE DENTURE',
fontSize=32, fillColor=WHITE, textAnchor='middle',
fontName='Helvetica-Bold'))
d.add(String(W/2, H*0.66, 'PROSTHODONTICS',
fontSize=32, fillColor=GOLD, textAnchor='middle',
fontName='Helvetica-Bold'))
d.add(String(W/2, H*0.60, 'STUDY GUIDE',
fontSize=20, fillColor=WHITE, textAnchor='middle',
fontName='Helvetica'))
d.add(String(W/2, H*0.54, 'Key Concepts, Definitions & Diagrams',
fontSize=13, fillColor=colors.HexColor('#A9CCE3'),
textAnchor='middle', fontName='Helvetica-Oblique'))
d.add(String(W/2, 60, 'BDS Prosthodontics | Complete Denture Series',
fontSize=10, fillColor=GOLD, textAnchor='middle',
fontName='Helvetica'))
return d
# ── Diagram helper functions ─────────────────────────────────────────────────
def labeled_arrow(d, x1,y1,x2,y2,label='',color=NAVY,lsize=8):
d.add(Line(x1,y1,x2,y2,strokeColor=color,strokeWidth=1.4))
# arrowhead
angle = math.atan2(y2-y1,x2-x1)
aw=8
for da in [0.4,-0.4]:
d.add(Line(x2,y2,
x2-aw*math.cos(angle+da),
y2-aw*math.sin(angle+da),
strokeColor=color, strokeWidth=1.4))
if label:
d.add(String(x2+5, y2, label, fontSize=lsize, fillColor=color,
fontName='Helvetica'))
def rounded_box(d, x, y, w, h, text, fill=LTBLUE, stroke=NAVY, tsize=8.5):
d.add(Rect(x, y, w, h, fillColor=fill, strokeColor=stroke,
strokeWidth=1.2, rx=5, ry=5))
# centre text (simple single line)
d.add(String(x+w/2, y+h/2-tsize*0.4, text, fontSize=tsize,
fillColor=BLACK, textAnchor='middle',
fontName='Helvetica-Bold'))
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 1 – Hanau's Quint
# ═══════════════════════════════════════════════════════════════════════════
def diag_hanau():
dw, dh = 440, 200
d = Drawing(dw, dh)
d.add(Rect(0, 0, dw, dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-18, "Hanau's Quint – Five Factors of Balanced Occlusion",
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
items = [
('C', 'Condylar\nGuidance', LTBLUE, NAVY),
('I', 'Incisal\nGuidance', LTGREEN, GREEN),
('P', 'Plane of\nOcclusion', LTYELLOW, ORANGE),
('C', 'Cusp\nHeight', LTRED, RED),
('C', 'Compensating\nCurve', LTPURP, PURPLE),
]
bw, bh, gap = 72, 70, 12
total = len(items)*bw + (len(items)-1)*gap
sx = (dw - total)/2
sy = 30
for i,(sym,label,fill,stroke) in enumerate(items):
bx = sx + i*(bw+gap)
d.add(Rect(bx, sy, bw, bh, fillColor=fill, strokeColor=stroke,
strokeWidth=2, rx=6, ry=6))
d.add(String(bx+bw/2, sy+bh-16, sym, fontSize=22, fillColor=stroke,
textAnchor='middle', fontName='Helvetica-Bold'))
# wrap label
lines = label.split('\n')
for li, ln in enumerate(lines):
d.add(String(bx+bw/2, sy+20-li*11, ln, fontSize=7.5,
fillColor=stroke, textAnchor='middle',
fontName='Helvetica'))
if i < len(items)-1:
mx = bx+bw+gap/2
d.add(String(mx, sy+bh/2-5, '⇄', fontSize=14,
fillColor=DKGREY, textAnchor='middle'))
d.add(String(dw/2, 12, 'Condylar guidance is patient-determined; all others are adjusted by the clinician.',
fontSize=7.5, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 2 – Muscles of Mastication
# ═══════════════════════════════════════════════════════════════════════════
def diag_muscles():
dw, dh = 440, 220
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Muscles of Mastication – Summary',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
muscles = [
('Masseter', 'Zygomatic arch', 'Ramus / Angle', 'Elevation', LTBLUE, NAVY),
('Temporalis', 'Temporal fossa', 'Coronoid process', 'Elevation + Retract', LTGREEN, GREEN),
('Medial Ptery.', 'Med. pterygoid plate', 'Med. ramus / Angle', 'Elevation + Protrude',LTYELLOW,ORANGE),
('Lateral Ptery.', 'Lat. pterygoid plate', 'Condyle neck / Disc', 'Protrusion / Opening',LTRED, RED),
]
headers = ['Muscle','Origin','Insertion','Action']
col_w = [90,110,115,105]
row_h = 28
sy = dh - 40
# header row
x0 = 20
for ci,(h,cw) in enumerate(zip(headers,col_w)):
cx2 = x0 + sum(col_w[:ci])
d.add(Rect(cx2, sy-row_h, cw, row_h, fillColor=NAVY,
strokeColor=WHITE, strokeWidth=0.5))
d.add(String(cx2+cw/2, sy-row_h+9, h, fontSize=8, fillColor=WHITE,
textAnchor='middle', fontName='Helvetica-Bold'))
for ri, (m, org, ins, act, fill, stroke) in enumerate(muscles):
ry2 = sy - row_h*(ri+2)
vals = [m, org, ins, act]
for ci,(val,cw) in enumerate(zip(vals,col_w)):
cx2 = x0 + sum(col_w[:ci])
bg = fill if ci==0 else WHITE
d.add(Rect(cx2, ry2, cw, row_h, fillColor=bg,
strokeColor=colors.lightgrey, strokeWidth=0.5))
d.add(String(cx2+cw/2, ry2+9, val, fontSize=7.5,
fillColor=stroke if ci==0 else BLACK,
textAnchor='middle',
fontName='Helvetica-Bold' if ci==0 else 'Helvetica'))
d.add(String(dw/2, 8, 'All supplied by Mandibular nerve (V3) – Motor branch of Trigeminal nerve',
fontSize=7.5, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 3 – Vertical Dimension (VDR / VDO / FWS)
# ═══════════════════════════════════════════════════════════════════════════
def diag_vd():
dw, dh = 440, 210
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Vertical Dimension – VDR, VDO & Free-Way Space',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# face outline (simplified)
fx, fy = 70, 30
# skull oval
d.add(Ellipse(fx, fy+80, 50, 90, fillColor=colors.HexColor('#FDEBD0'),
strokeColor=DKGREY, strokeWidth=1.2))
# chin
d.add(Ellipse(fx, fy+5, 28, 18, fillColor=colors.HexColor('#FDEBD0'),
strokeColor=DKGREY, strokeWidth=1.2))
# nose dot
d.add(Circle(fx, fy+108, 3, fillColor=DKGREY, strokeColor=None))
# eye
d.add(Ellipse(fx+18, fy+130, 10, 6, fillColor=WHITE, strokeColor=DKGREY,
strokeWidth=1))
# measurement bars on right side
# VDR bar (nose to chin at rest)
y_nose = fy + 108
y_chin_rest = fy + 10
y_chin_occ = fy + 20 # chin slightly higher at VDO
bx = fx + 70
# VDR
d.add(Line(bx, y_nose, bx, y_chin_rest, strokeColor=NAVY,
strokeWidth=2, strokeDashArray=[4,3]))
d.add(Line(bx-5, y_nose, bx+5, y_nose, strokeColor=NAVY, strokeWidth=1.5))
d.add(Line(bx-5, y_chin_rest, bx+5, y_chin_rest, strokeColor=NAVY, strokeWidth=1.5))
d.add(String(bx+8, (y_nose+y_chin_rest)/2, 'VDR', fontSize=8.5,
fillColor=NAVY, fontName='Helvetica-Bold'))
# FWS brace
fws_y1 = y_chin_rest
fws_y2 = y_chin_occ
d.add(Rect(bx+30, fws_y2, 46, fws_y1-fws_y2,
fillColor=LTGREEN, strokeColor=GREEN, strokeWidth=1, rx=2))
d.add(String(bx+53, fws_y2+(fws_y1-fws_y2)/2-4, 'FWS', fontSize=8,
fillColor=GREEN, textAnchor='middle',
fontName='Helvetica-Bold'))
d.add(String(bx+53, fws_y2+(fws_y1-fws_y2)/2-14, '2-4mm', fontSize=7,
fillColor=GREEN, textAnchor='middle'))
# VDO
d.add(Line(bx+85, y_nose, bx+85, y_chin_occ, strokeColor=RED,
strokeWidth=2))
d.add(Line(bx+80, y_nose, bx+90, y_nose, strokeColor=RED, strokeWidth=1.5))
d.add(Line(bx+80, y_chin_occ, bx+90, y_chin_occ, strokeColor=RED, strokeWidth=1.5))
d.add(String(bx+93, (y_nose+y_chin_occ)/2, 'VDO', fontSize=8.5,
fillColor=RED, fontName='Helvetica-Bold'))
# formula box
d.add(Rect(250, 20, 170, 55, fillColor=LTBLUE, strokeColor=NAVY,
strokeWidth=1.5, rx=6))
d.add(String(335, 62, 'Key Formula', fontSize=9, fillColor=NAVY,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(335, 49, 'VDR = VDO + FWS', fontSize=11, fillColor=NAVY,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(335, 34, 'FWS (Free-way space) = 2–4 mm', fontSize=8.5,
fillColor=DKGREY, textAnchor='middle'))
d.add(String(335, 24, '"S" sound = closest speaking space (0.5–1 mm)', fontSize=7.5,
fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
d.add(String(dw/2, 8, 'VDR = Vertical Dim. at Rest | VDO = Vertical Dim. of Occlusion | FWS = Freeway Space',
fontSize=7.5, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 4 – Compensating Curves (Curve of Spee & Wilson)
# ═══════════════════════════════════════════════════════════════════════════
def diag_curves():
dw, dh = 440, 210
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Compensating Curves in Complete Dentures',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# --- Curve of Spee (sagittal view) ---
d.add(String(110, dh-32, 'Curve of Spee (Sagittal)', fontSize=8.5,
fillColor=TEAL, textAnchor='middle',
fontName='Helvetica-Bold'))
# Spee arc
cx1, cy1, r1 = 110, 250, 140
pts1 = []
for ang in range(200, 340, 5):
rad = math.radians(ang)
pts1.extend([cx1+r1*math.cos(rad), cy1+r1*math.sin(rad)])
d.add(PolyLine(pts1, strokeColor=NAVY, strokeWidth=2.5))
# tooth bumps on curve
for ang in range(205, 338, 18):
rad = math.radians(ang)
tx = cx1+r1*math.cos(rad)
ty = cy1+r1*math.sin(rad)
d.add(Circle(tx, ty, 5, fillColor=GOLD, strokeColor=NAVY,
strokeWidth=1))
d.add(String(110, 22, 'Ant-Post curve, compensates\nfor condylar inclination (protrusion)',
fontSize=7, fillColor=DKGREY, textAnchor='middle'))
# divider
d.add(Line(220, 20, 220, dh-25, strokeColor=GREY, strokeWidth=1,
strokeDashArray=[4,3]))
# --- Curve of Wilson (frontal view) ---
d.add(String(330, dh-32, 'Curve of Wilson (Frontal)', fontSize=8.5,
fillColor=PURPLE, textAnchor='middle',
fontName='Helvetica-Bold'))
# Wilson arc (U-shape)
cx2, cy2, r2x, r2y = 330, 170, 80, 35
pts2 = []
for ang in range(180, 361, 8):
rad = math.radians(ang)
pts2.extend([cx2+r2x*math.cos(rad), cy2+r2y*math.sin(rad)])
d.add(PolyLine(pts2, strokeColor=PURPLE, strokeWidth=2.5))
# teeth on Wilson
for ang in range(185, 358, 30):
rad = math.radians(ang)
tx = cx2+r2x*math.cos(rad)
ty = cy2+r2y*math.sin(rad)
d.add(Circle(tx, ty, 5, fillColor=GOLD, strokeColor=PURPLE,
strokeWidth=1))
# lingual lower label
d.add(String(cx2-r2x-10, cy2, 'L', fontSize=9, fillColor=DKGREY,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(cx2+r2x+10, cy2, 'B', fontSize=9, fillColor=DKGREY,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(330, 22, 'Transverse curve, compensates\nfor Bennett movement (lateral excursion)',
fontSize=7, fillColor=DKGREY, textAnchor='middle'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 5 – Gothic Arch Tracing
# ═══════════════════════════════════════════════════════════════════════════
def diag_gothic():
dw, dh = 440, 200
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Gothic Arch Tracing (Needle Point / Arrow Point Tracing)',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
cx, cy = 160, 100
# Left lateral line
d.add(Line(cx, cy, cx-80, cy+55, strokeColor=RED, strokeWidth=2.5))
# Right lateral line
d.add(Line(cx, cy, cx+80, cy+55, strokeColor=RED, strokeWidth=2.5))
# Left protrusion
d.add(Line(cx, cy, cx-10, cy+65, strokeColor=NAVY, strokeWidth=1.5,
strokeDashArray=[4,2]))
# Right protrusion
d.add(Line(cx, cy, cx+10, cy+65, strokeColor=NAVY, strokeWidth=1.5,
strokeDashArray=[4,2]))
# APEX dot
d.add(Circle(cx, cy, 6, fillColor=GREEN, strokeColor=None))
d.add(String(cx+10, cy+2, 'APEX = Centric Relation', fontSize=8.5,
fillColor=GREEN, fontName='Helvetica-Bold'))
# labels
d.add(String(cx-90, cy+60, 'L Lateral', fontSize=8, fillColor=RED,
textAnchor='middle'))
d.add(String(cx+90, cy+60, 'R Lateral', fontSize=8, fillColor=RED,
textAnchor='middle'))
d.add(String(cx, cy+75, 'Protrusive', fontSize=8, fillColor=NAVY,
textAnchor='middle'))
# plate and stylus labels
d.add(String(cx, cy-20, '▲ Stylus (on mandibular rim)', fontSize=8,
fillColor=DKGREY, textAnchor='middle'))
d.add(String(cx, cy-32, 'Recording plate on maxillary rim (below)',
fontSize=7.5, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
# right info box
d.add(Rect(310, 30, 120, 130, fillColor=LTBLUE, strokeColor=NAVY,
strokeWidth=1, rx=5))
lines_txt = [
('Steps:', True),
('1. Attach stylus to lower', False),
('2. Plate on upper rim', False),
('3. Patient moves laterally', False),
(' & protrusively', False),
('4. Arrow shape traced', False),
('5. Apex = CR position', False),
('6. Lock at apex to record', False),
]
for i,(txt,bold) in enumerate(lines_txt):
d.add(String(316, 148-i*15, txt, fontSize=7.5,
fillColor=NAVY if bold else BLACK,
fontName='Helvetica-Bold' if bold else 'Helvetica'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 6 – Stress-Bearing Areas
# ═══════════════════════════════════════════════════════════════════════════
def diag_stress_areas():
dw, dh = 440, 210
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Primary & Secondary Stress-Bearing Areas',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# MAXILLA (palate view – horseshoe shape)
d.add(String(110, dh-32, 'MAXILLA (palatal view)', fontSize=8.5,
fillColor=TEAL, textAnchor='middle', fontName='Helvetica-Bold'))
# palate outline
for r2,fill2,alpha in [(70,LTBLUE,0.9),(55,CREAM,1)]:
d.add(Ellipse(110, 90, r2, r2*0.7, fillColor=fill2,
strokeColor=TEAL, strokeWidth=1.5,
fillOpacity=alpha))
# anterior arch gap
d.add(Rect(80, 120, 60, 25, fillColor=CREAM, strokeColor=None))
# primary = posterior palate (shaded green)
d.add(Rect(65, 63, 90, 30, fillColor=LTGREEN, strokeColor=GREEN,
strokeWidth=1.5, rx=4))
d.add(String(110, 73, 'PRIMARY', fontSize=7, fillColor=GREEN,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(110, 64, '(Post. hard palate, ridge slopes)', fontSize=6,
fillColor=GREEN, textAnchor='middle'))
# secondary = anterior
d.add(Rect(75, 95, 70, 22, fillColor=LTYELLOW, strokeColor=ORANGE,
strokeWidth=1, rx=3))
d.add(String(110, 102, 'SECONDARY', fontSize=7, fillColor=ORANGE,
textAnchor='middle', fontName='Helvetica-Bold'))
# relief areas
d.add(Circle(110, 132, 5, fillColor=LTRED, strokeColor=RED,
strokeWidth=1))
d.add(String(110, 120, 'Relief: Incisive papilla', fontSize=6.5,
fillColor=RED, textAnchor='middle'))
# divider
d.add(Line(220, 20, 220, dh-25, strokeColor=GREY, strokeWidth=1,
strokeDashArray=[4,3]))
# MANDIBLE (occlusal view)
d.add(String(330, dh-32, 'MANDIBLE (occlusal view)', fontSize=8.5,
fillColor=PURPLE, textAnchor='middle',
fontName='Helvetica-Bold'))
# U-shaped arch
for r2,fill2 in [(80, colors.HexColor('#F0F3FF')),(65,CREAM)]:
pts_arch=[]
for ang in range(0,181,10):
rad=math.radians(ang)
pts_arch.extend([330+r2*math.cos(rad),100+r2*0.6*math.sin(rad)])
d.add(PolyLine(pts_arch, strokeColor=PURPLE, strokeWidth=2))
# buccal shelf highlight
d.add(Rect(248, 65, 25, 55, fillColor=LTGREEN, strokeColor=GREEN,
strokeWidth=1.5, rx=3))
d.add(String(261, 90, 'Buccal\nShelf', fontSize=6.5, fillColor=GREEN,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(Rect(407, 65, 25, 55, fillColor=LTGREEN, strokeColor=GREEN,
strokeWidth=1.5, rx=3))
d.add(String(420, 90, 'Buccal\nShelf', fontSize=6.5, fillColor=GREEN,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(String(330, 40, 'PRIMARY = Buccal Shelf', fontSize=8,
fillColor=GREEN, textAnchor='middle',
fontName='Helvetica-Bold'))
d.add(String(330, 30, '(cortical bone, near-perpendicular to forces)', fontSize=7,
fillColor=DKGREY, textAnchor='middle'))
# retromolar pad
d.add(Circle(260, 50, 7, fillColor=GOLD, strokeColor=ORANGE, strokeWidth=1))
d.add(Circle(400, 50, 7, fillColor=GOLD, strokeColor=ORANGE, strokeWidth=1))
d.add(String(330, 17, 'Retromolar pad – cover anterior 2/3 with denture',
fontSize=7.5, fillColor=ORANGE, textAnchor='middle',
fontName='Helvetica-Oblique'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 7 – House Classification (Mental Attitude)
# ═══════════════════════════════════════════════════════════════════════════
def diag_house():
dw, dh = 440, 175
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, "House Classification – Mental Attitude of Edentulous Patients (1950)",
fontSize=9.5, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
classes = [
('Class I', 'Philosophical', 'Cooperative, realistic\naccepts limitations',
'Excellent', LTGREEN, GREEN),
('Class II', 'Exacting', 'Meticulous, rational\nbut hard to please',
'Good', LTBLUE, NAVY),
('Class III', 'Hysterical', 'Emotional, unrealistic\nblames dentist',
'Guarded', LTYELLOW, ORANGE),
('Class IV', 'Indifferent', 'Uncooperative,\nminimal motivation',
'Poor', LTRED, RED),
]
bw = 95; bh = 110; gap = 8
total = 4*bw + 3*gap
sx = (dw-total)/2
sy = 30
for i,(cls,name,desc,prog,fill,stroke) in enumerate(classes):
bx = sx + i*(bw+gap)
d.add(Rect(bx, sy, bw, bh, fillColor=fill, strokeColor=stroke,
strokeWidth=2, rx=6))
d.add(String(bx+bw/2, sy+bh-12, cls, fontSize=9, fillColor=stroke,
textAnchor='middle', fontName='Helvetica-Bold'))
d.add(Line(bx+8, sy+bh-18, bx+bw-8, sy+bh-18,
strokeColor=stroke, strokeWidth=0.8))
d.add(String(bx+bw/2, sy+bh-28, name, fontSize=8.5, fillColor=stroke,
textAnchor='middle', fontName='Helvetica-Bold'))
for li,ln in enumerate(desc.split('\n')):
d.add(String(bx+bw/2, sy+bh-42-li*11, ln, fontSize=7,
fillColor=BLACK, textAnchor='middle'))
d.add(Rect(bx+5, sy+4, bw-10, 20, fillColor=stroke,
strokeColor=None, rx=3))
d.add(String(bx+bw/2, sy+9, 'Prognosis: '+prog, fontSize=7.5,
fillColor=WHITE, textAnchor='middle',
fontName='Helvetica-Bold'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 8 – Bonwill's Equilateral Triangle
# ═══════════════════════════════════════════════════════════════════════════
def diag_bonwill():
dw, dh = 440, 210
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, "Bonwill's Equilateral Triangle Theory",
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# equilateral triangle
side = 160
cx, cy = 220, 100
h_tri = side * math.sqrt(3)/2
p1 = (cx, cy + h_tri*0.6) # top (mandibular incisor contact)
p2 = (cx - side/2, cy - h_tri*0.4) # bottom-left (L condyle)
p3 = (cx + side/2, cy - h_tri*0.4) # bottom-right (R condyle)
d.add(Polygon([p1[0],p1[1],p2[0],p2[1],p3[0],p3[1]],
fillColor=LTBLUE, strokeColor=NAVY, strokeWidth=2.5,
fillOpacity=0.5))
# side labels (101.6 mm = 4 inches)
def mid(a,b): return ((a[0]+b[0])/2, (a[1]+b[1])/2)
for pa,pb in [(p1,p2),(p2,p3),(p1,p3)]:
mx,my = mid(pa,pb)
d.add(String(mx, my-6, '4 in (101.6 mm)', fontSize=7.5,
fillColor=TEAL, textAnchor='middle',
fontName='Helvetica-Bold'))
# corner markers
d.add(Circle(p1[0],p1[1],7, fillColor=GOLD, strokeColor=NAVY,
strokeWidth=1.5))
d.add(Circle(p2[0],p2[1],7, fillColor=RED, strokeColor=NAVY,
strokeWidth=1.5))
d.add(Circle(p3[0],p3[1],7, fillColor=RED, strokeColor=NAVY,
strokeWidth=1.5))
d.add(String(p1[0], p1[1]+12, 'Mandibular incisor\ncontact point',
fontSize=7.5, fillColor=NAVY, textAnchor='middle'))
d.add(String(p2[0]-5, p2[1]-20, 'L Condyle', fontSize=7.5,
fillColor=RED, textAnchor='middle'))
d.add(String(p3[0]+5, p3[1]-20, 'R Condyle', fontSize=7.5,
fillColor=RED, textAnchor='middle'))
# info box
d.add(Rect(6, 8, 130, 55, fillColor=LTGREEN, strokeColor=GREEN,
strokeWidth=1, rx=5))
d.add(String(70, 52, 'Clinical Use', fontSize=8.5, fillColor=GREEN,
textAnchor='middle', fontName='Helvetica-Bold'))
for i,txt in enumerate(['- Sets intercondylar distance',
'- Basis for posterior tooth setting',
'- Combined with Balkwill angle (26°)',
'- Monson sphere radius = 4 in']):
d.add(String(10, 42-i*11, txt, fontSize=7, fillColor=BLACK))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 9 – Combination Syndrome
# ═══════════════════════════════════════════════════════════════════════════
def diag_combination_syndrome():
dw, dh = 440, 215
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Combination Syndrome (Kelly, 1972)',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# Setup label
d.add(String(dw/2, dh-32, 'Maxillary CD opposing mandibular RPD (only anterior teeth remaining)',
fontSize=8, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
features = [
('1', 'Bone loss – ant. maxilla', RED, LTRED),
('2', 'Tuberosity overgrowth', ORANGE, LTYELLOW),
('3', 'Papillary hyperplasia', PURPLE, LTPURP),
('4', 'Extrusion – mand. anteriors', NAVY, LTBLUE),
('5', 'Bone loss – mand. post. ridges', GREEN, LTGREEN),
]
bw2 = 74; bh2 = 50; gap2 = 8
total2 = 5*bw2 + 4*gap2
sx2 = (dw-total2)/2
for i,(num,feat,stroke2,fill2) in enumerate(features):
bx2 = sx2 + i*(bw2+gap2)
d.add(Rect(bx2, 80, bw2, bh2, fillColor=fill2, strokeColor=stroke2,
strokeWidth=2, rx=5))
d.add(String(bx2+bw2/2, 80+bh2-12, num, fontSize=16, fillColor=stroke2,
textAnchor='middle', fontName='Helvetica-Bold'))
lines_f = feat.split('–')
d.add(String(bx2+bw2/2, 88, lines_f[0].strip(), fontSize=7,
fillColor=stroke2, textAnchor='middle',
fontName='Helvetica-Bold'))
if len(lines_f) > 1:
d.add(String(bx2+bw2/2, 79, lines_f[1].strip(), fontSize=6.5,
fillColor=stroke2, textAnchor='middle'))
# arch illustration (simplified)
# maxilla
d.add(String(40, 68, 'MAXILLA', fontSize=7.5, fillColor=TEAL,
fontName='Helvetica-Bold'))
d.add(Ellipse(80, 50, 70, 22, fillColor=LTBLUE, strokeColor=TEAL,
strokeWidth=1.5, fillOpacity=0.7))
d.add(String(80, 46, 'Max CD', fontSize=7, fillColor=TEAL,
textAnchor='middle'))
# mandible
d.add(String(300, 68, 'MANDIBLE', fontSize=7.5, fillColor=PURPLE,
fontName='Helvetica-Bold'))
# anterior teeth only
for xi in range(3):
d.add(Rect(340+xi*12, 50, 9, 18, fillColor=LTYELLOW,
strokeColor=PURPLE, strokeWidth=1, rx=2))
d.add(String(350, 42, 'Ant. teeth only', fontSize=7, fillColor=PURPLE,
textAnchor='middle'))
# edentulous post
d.add(Line(370, 58, 415, 58, strokeColor=RED, strokeWidth=2,
strokeDashArray=[3,2]))
d.add(String(392, 47, 'Edentulous (RPD)', fontSize=7, fillColor=RED,
textAnchor='middle'))
d.add(String(dw/2, 15, 'Cause: Anterior force concentration on maxilla → unopposed occlusal load imbalance',
fontSize=7.5, fillColor=DKGREY, textAnchor='middle',
fontName='Helvetica-Oblique'))
d.add(String(dw/2, 6, 'Rx: Occlusal stop correction, relining, surgical correction if needed',
fontSize=7.5, fillColor=GREEN, textAnchor='middle',
fontName='Helvetica-Bold'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# DIAGRAM 10 – Posterior Palatal Seal Area
# ═══════════════════════════════════════════════════════════════════════════
def diag_pps():
dw, dh = 440, 210
d = Drawing(dw, dh)
d.add(Rect(0,0,dw,dh, fillColor=CREAM, strokeColor=None, rx=8, ry=8))
d.add(String(dw/2, dh-16, 'Posterior Palatal Seal (PPS) Area',
fontSize=10, fillColor=NAVY, textAnchor='middle',
fontName='Helvetica-Bold'))
# palate outline from below
cx2, cy2 = 160, 100
# hard palate oval
d.add(Ellipse(cx2, cy2, 110, 80, fillColor=colors.HexColor('#F0F8FF'),
strokeColor=NAVY, strokeWidth=1.5))
# soft palate
d.add(Ellipse(cx2, cy2-85, 90, 20, fillColor=colors.HexColor('#FDEDEC'),
strokeColor=RED, strokeWidth=1.5))
d.add(String(cx2, cy2-83, 'Soft Palate', fontSize=7.5, fillColor=RED,
textAnchor='middle'))
# vibrating line (AH line)
d.add(Line(cx2-100, cy2-65, cx2+100, cy2-65, strokeColor=RED,
strokeWidth=2.5, strokeDashArray=[6,3]))
d.add(String(cx2+85, cy2-57, 'AH line\n(Vibrating line)', fontSize=7,
fillColor=RED, fontName='Helvetica-Bold'))
# PPS zone (shaded area)
d.add(Rect(cx2-95, cy2-78, 190, 16, fillColor=LTGREEN,
strokeColor=GREEN, strokeWidth=1.5,
fillOpacity=0.7))
d.add(String(cx2, cy2-72, 'PPS Zone (1-2 mm posterior to AH line)',
fontSize=7.5, fillColor=GREEN, textAnchor='middle',
fontName='Helvetica-Bold'))
# hamular notch markers
d.add(Circle(cx2-95, cy2-65, 5, fillColor=GOLD, strokeColor=ORANGE,
strokeWidth=1.5))
d.add(Circle(cx2+95, cy2-65, 5, fillColor=GOLD, strokeColor=ORANGE,
strokeWidth=1.5))
d.add(String(cx2-95, cy2-52, 'Hamular\nNotch', fontSize=6.5,
fillColor=ORANGE, textAnchor='middle'))
d.add(String(cx2+95, cy2-52, 'Hamular\nNotch', fontSize=6.5,
fillColor=ORANGE, textAnchor='middle'))
# mid-palatal raphae
d.add(Line(cx2, cy2+75, cx2, cy2-65, strokeColor=DKGREY,
strokeWidth=1, strokeDashArray=[3,3]))
d.add(String(cx2+5, cy2+55, 'Mid-palatal\nraphae', fontSize=7,
fillColor=DKGREY))
# fovea palatinae
d.add(Circle(cx2-8, cy2-62, 3, fillColor=PURPLE, strokeColor=None))
d.add(Circle(cx2+8, cy2-62, 3, fillColor=PURPLE, strokeColor=None))
d.add(String(cx2, cy2-42, '● Fovea palatinae', fontSize=7,
fillColor=PURPLE, textAnchor='middle'))
# info box
d.add(Rect(295, 20, 140, 165, fillColor=LTBLUE, strokeColor=NAVY,
strokeWidth=1, rx=5))
d.add(String(365, 174, 'Advantages of PPS', fontSize=8.5, fillColor=NAVY,
textAnchor='middle', fontName='Helvetica-Bold'))
items2 = [
'• Improves retention (peripheral seal)',
'• Prevents food ingress',
'• Avoids sharp posterior edge',
'• Compensates for shrinkage',
'• Better adaptation at AH line',
]
for i,txt in enumerate(items2):
d.add(String(300, 160-i*18, txt, fontSize=7.5, fillColor=BLACK))
d.add(String(365, 30, 'Techniques:', fontSize=8, fillColor=NAVY,
textAnchor='middle', fontName='Helvetica-Bold'))
for i,txt in enumerate(['Scraping/Carving', 'Fluid wax', 'Compound',
'Beading & boxing', 'Thomas technique']):
d.add(String(365, 22-i*0, txt, fontSize=0, fillColor=BLACK))
# compact technique list
tech_txt = 'Scraping | Fluid wax | Compound | Beading-boxing | Thomas'
d.add(String(365, 22, tech_txt, fontSize=6.5, fillColor=DKGREY,
textAnchor='middle'))
return d
# ═══════════════════════════════════════════════════════════════════════════
# BUILD THE DOCUMENT
# ═══════════════════════════════════════════════════════════════════════════
class DrawingFlowable(Flowable):
"""Wraps a ReportLab Drawing so it can be placed in a Platypus story."""
def __init__(self, drawing, hAlign='CENTER'):
super().__init__()
self.drawing = drawing
self.hAlign = hAlign
self._width = drawing.width
self._height = drawing.height
def wrap(self, availWidth, availHeight):
scale = min(1.0, availWidth / self._width)
return self._width*scale, self._height*scale
def draw(self):
scale = min(1.0, self.canv._pagesize[0] / self._width)
self.canv.saveState()
self.canv.scale(scale, scale)
renderPDF.draw(self.drawing, self.canv, 0, 0)
self.canv.restoreState()
def build_pdf(outpath):
doc = SimpleDocTemplate(
outpath,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm,
title='Complete Denture Prosthodontics – Study Guide',
author='Orris Study Guide Generator',
)
story = []
# ── COVER PAGE ────────────────────────────────────────────────────────
story.append(DrawingFlowable(make_cover()))
story.append(PageBreak())
# ── TABLE OF CONTENTS ────────────────────────────────────────────────
story.append(Paragraph('<b>TABLE OF CONTENTS</b>', H1))
story.append(Spacer(1, 6))
toc_data = [
['1.', 'Posterior Palatal Seal (PPS) Area', '3'],
['2.', "Hanau's Quint", '3'],
['3.', 'Muscles of Mastication', '4'],
['4.', 'Vertical Dimension: VDR, VDO & Free-way Space','4'],
['5.', 'Compensating Curves', '5'],
['6.', 'Gothic Arch Tracing', '5'],
['7.', 'Stress-Bearing Areas', '6'],
['8.', 'House Classification (Mental Attitude)', '6'],
['9.', "Bonwill's Equilateral Triangle", '7'],
['10.', 'Combination Syndrome', '7'],
['11.', 'Key Definitions & Short Notes', '8'],
['12.', 'Border Molding', '9'],
['13.', 'Overdentures', '9'],
['14.', 'Jaw Relations & Centric Relation', '10'],
['15.', 'Selective Grinding (BULL Rule)', '10'],
]
toc_table = Table(toc_data, colWidths=[1.2*cm, 11.5*cm, 1.5*cm])
toc_table.setStyle(TableStyle([
('FONTNAME', (0,0),(-1,-1), 'Helvetica'),
('FONTSIZE', (0,0),(-1,-1), 10),
('FONTNAME', (0,0),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,0),(0,-1), NAVY),
('TEXTCOLOR', (2,0),(2,-1), TEAL),
('ROWBACKGROUNDS', (0,0),(-1,-1), [WHITE, GREY]),
('TOPPADDING', (0,0),(-1,-1), 5),
('BOTTOMPADDING', (0,0),(-1,-1), 5),
('LINEBELOW', (0,-1),(-1,-1), 1, NAVY),
]))
story.append(toc_table)
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 1 – PPS + Hanau
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('1. Posterior Palatal Seal (PPS) Area', H1))
story.append(DrawingFlowable(diag_pps()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'<b>Definition:</b> The soft tissue area at or just beyond the junction of hard and soft palate '
'on which pressure within physiologic limits can be applied by the denture to improve retention. '
'Lies along the <b>vibrating line (AH line)</b>, extending 1-2 mm posterior to it.',
BODY))
story.append(Spacer(1, 12))
story.append(Paragraph("2. Hanau's Quint", H1))
story.append(DrawingFlowable(diag_hanau()))
story.append(Spacer(1, 4))
story.append(Paragraph(
"<b>Hanau's formula:</b> Five factors must be balanced for harmonious complete denture occlusion. "
"Condylar guidance is fixed by anatomy; the dentist adjusts the remaining four. "
"Increasing condylar guidance requires increasing cusp height and/or steepening the compensating curve.",
BODY))
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 2 – Muscles + VD
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('3. Muscles of Mastication', H1))
story.append(DrawingFlowable(diag_muscles()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'All four muscles are supplied by the <b>mandibular nerve (V3)</b>, anterior division – '
'motor branch of the trigeminal nerve. The lateral pterygoid is the only muscle that opens '
'the mouth (protrusion + depression via inferior head). '
'Blood supply: branches of the <b>maxillary artery</b>.',
BODY))
story.append(Spacer(1, 12))
story.append(Paragraph('4. Vertical Dimension – VDR, VDO & Free-Way Space', H1))
story.append(DrawingFlowable(diag_vd()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'<b>VDR</b> (Rest) = <b>VDO</b> (Occlusion) + <b>FWS</b> (Free-way space 2-4 mm). '
'Rest position is recorded by phonetics ("M" sound, swallowing). '
'Closest speaking space for "S" = 0.5-1 mm. '
'If FWS = 0 during "S" sound: VDO is too high.',
BODY))
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 3 – Compensating curves + Gothic arch
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('5. Compensating Curves', H1))
story.append(DrawingFlowable(diag_curves()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'<b>Curve of Spee</b>: Sagittal plane; compensates condylar inclination during protrusion. '
'<b>Curve of Wilson</b>: Frontal plane; lingual cusps lower than buccal; compensates Bennett movement. '
'<b>Monson sphere</b>: All cusp tips on a sphere of 4-inch (10 cm) radius. '
'<b>Christensen phenomenon</b>: Posterior gap on protrusion – compensating curves prevent this.',
BODY))
story.append(Spacer(1, 12))
story.append(Paragraph('6. Gothic Arch Tracing (Arrow Point Tracing)', H1))
story.append(DrawingFlowable(diag_gothic()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'Records centric relation and mandibular movement paths. '
'<b>Apex of the arrow</b> = centric relation. '
'A stylus on the mandibular rim traces on a coated plate on the maxillary rim. '
'Useful when patient cannot reliably respond to verbal commands.',
BODY))
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 4 – Stress areas + House
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('7. Primary & Secondary Stress-Bearing Areas', H1))
story.append(DrawingFlowable(diag_stress_areas()))
story.append(Spacer(1, 4))
sb_data = [
['Arch', 'Primary', 'Secondary', 'Relief Areas'],
['Maxilla', 'Posterior hard palate, slopes of ridge',
'Anterior hard palate, rugae, ridge crest',
'Incisive papilla, mid-palatal raphae, torus'],
['Mandible', 'Buccal shelf (cortical bone)',
'Ridge crest, slopes, retromolar pad',
'Mental foramen, mylohyoid ridge, genial tubercles'],
]
sb_table = Table(sb_data, colWidths=[2.2*cm, 4.2*cm, 4.2*cm, 3.2*cm])
sb_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), NAVY),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0),(-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1),(-1,-1), [LTBLUE, LTGREEN]),
('TOPPADDING', (0,0),(-1,-1), 4),
('BOTTOMPADDING', (0,0),(-1,-1), 4),
('GRID', (0,0),(-1,-1), 0.5, colors.grey),
('VALIGN', (0,0),(-1,-1), 'MIDDLE'),
]))
story.append(sb_table)
story.append(Spacer(1, 12))
story.append(Paragraph('8. House Classification – Mental Attitude', H1))
story.append(DrawingFlowable(diag_house()))
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 5 – Bonwill + Combination syndrome
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph("9. Bonwill's Equilateral Triangle", H1))
story.append(DrawingFlowable(diag_bonwill()))
story.append(Spacer(1, 4))
story.append(Paragraph(
'W.G.A. Bonwill (1858): An equilateral triangle of side <b>4 inches (101.6 mm)</b> connects '
'the two condylar points and the mandibular central incisor contact point. '
'Used to set intercondylar distances on articulators and for posterior tooth arrangement.',
BODY))
story.append(Spacer(1, 12))
story.append(Paragraph('10. Combination Syndrome (Kelly, 1972)', H1))
story.append(DrawingFlowable(diag_combination_syndrome()))
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 6 – Key Definitions table
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('11. Key Definitions & Short Notes', H1))
story.append(Spacer(1, 4))
defs = [
['Term', 'Definition / Key Points'],
['Face Bow', 'Caliper device recording maxillary arch-to-TMJ relationship for articulator transfer. Types: Arbitrary (12 mm anterior to tragus) and Kinematic (true hinge axis).'],
['Free-Way Space', 'Gap between teeth at rest position = 2-4 mm. VDR = VDO + FWS. Phonetic "S" = closest speaking space (0.5-1 mm).'],
['Retromolar Pad', 'Pear-shaped soft tissue pad at distal end of mandibular ridge. Cover anterior 2/3. Landmark for occlusal plane and tooth placement.'],
['Border Molding', 'Shaping tray borders using tissue movements. Materials: green-stick compound, silicone. Sections: labial, buccal, lingual, posterior.'],
['Overdentures', 'Denture supported by retained roots/implants. Preserves bone, provides proprioception. Attachments: ball, bar-clip, magnets.'],
["Hanau's Quint", 'C-I-P-C-C: Condylar guidance, Incisal guidance, Plane of occlusion, Cusp height, Compensating curve. Condylar guidance is fixed.'],
['Gothic Arch Tracing', 'Arrow-shaped tracing. Apex = centric relation. Uses stylus (lower) on coated plate (upper).'],
['Centric Relation', 'Condyles in anterior-superior position on articular eminence, thinnest disc. Bone-to-bone, tooth-independent, reproducible.'],
['Bennett Shift', 'Lateral bodily translation of mandible in lateral excursion. Bennett angle = 15-17° (non-working condyle path vs sagittal plane).'],
['Epulis Fissuratum', 'Inflammatory fibrous hyperplasia from ill-fitting flange. Rx: adjust flange, tissue condition, surgical excision if persists.'],
['Snap-on Denture', '2-implant retained mandibular overdenture. Ball abutment + nylon insert in denture. McGill Consensus 2002: minimum standard of care.'],
['Christensen Phenom.', 'Posterior gap on protrusion when teeth occlude anteriorly. Basis for compensating curves in CD.'],
['Modiolus', 'Fibromuscular condensation at corner of mouth. Converging point for: orbicularis oris, buccinator, zygomaticus major, risorius, levator/depressor anguli oris.'],
['Tissue Conditioners', 'Viscoelastic soft liners (Visco-gel, Coe-Comfort). Treat abused mucosa before relining/new denture. Replace every 3-5 days.'],
]
def_table = Table(defs, colWidths=[4.5*cm, 11.3*cm])
def_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), NAVY),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTNAME', (0,1),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1),(0,-1), NAVY),
('FONTSIZE', (0,0),(-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1),(-1,-1), [WHITE, GREY]),
('TOPPADDING', (0,0),(-1,-1), 5),
('BOTTOMPADDING', (0,0),(-1,-1), 5),
('GRID', (0,0),(-1,-1), 0.4, colors.lightgrey),
('VALIGN', (0,0),(-1,-1), 'TOP'),
('WORDWRAP', (0,0),(-1,-1), True),
]))
story.append(def_table)
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 7 – Border Molding + Overdentures
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('12. Border Molding – Green Stick Compound Technique', H1))
story.append(Spacer(1, 4))
bm_steps = [
['Step', 'Action', 'Movement Performed'],
['1', 'Soften green stick (60-65°C)', 'N/A - preparation'],
['2', 'Apply to labial flange (upper)', 'Stretch lip up, down, side-to-side'],
['3', 'Buccal flange (upper + lower)', 'Cheeks distended, rotated inward'],
['4', 'Posterior palatal area (upper)', 'Say "Ah" – locates vibrating line'],
['5', 'Lingual flange (lower)', 'Tongue: protrusion, lateral, touch palate'],
['6', 'Distolingual extension', 'Swallowing motion'],
['7', 'Trim excess; repeat sections', 'Verify extension and seal'],
]
bm_table = Table(bm_steps, colWidths=[1.2*cm, 6.5*cm, 6.1*cm])
bm_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), TEAL),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0),(-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1),(-1,-1), [WHITE, LTBLUE]),
('TOPPADDING', (0,0),(-1,-1), 5),
('BOTTOMPADDING', (0,0),(-1,-1), 5),
('GRID', (0,0),(-1,-1), 0.4, colors.lightgrey),
('VALIGN', (0,0),(-1,-1), 'MIDDLE'),
('FONTNAME', (0,1),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1),(0,-1), TEAL),
]))
story.append(bm_table)
story.append(Spacer(1, 10))
story.append(Paragraph('13. Overdentures', H1))
story.append(Spacer(1, 4))
od_data = [
['Aspect', 'Details'],
['Definition', 'CD/RPD supported by retained roots, natural teeth, or implants'],
['Advantage', 'Bone preservation via PDL, proprioception, better stability'],
['Root prep', 'Decrown to 2-3 mm above gingiva, dome-shaped or coping'],
['Attachments', 'Ball & socket, Dolder bar-clip, stud (Locator), magnet'],
['Snap-on (implant)', '2 implants (canine region) + Locator/O-ring. McGill 2002 = standard of care'],
['Disadvantage', 'Root caries risk, higher cost, more maintenance visits'],
]
od_table = Table(od_data, colWidths=[3.5*cm, 12.3*cm])
od_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), PURPLE),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTNAME', (0,1),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1),(0,-1), PURPLE),
('FONTSIZE', (0,0),(-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1),(-1,-1), [WHITE, LTPURP]),
('TOPPADDING', (0,0),(-1,-1), 5),
('BOTTOMPADDING', (0,0),(-1,-1), 5),
('GRID', (0,0),(-1,-1), 0.4, colors.lightgrey),
]))
story.append(od_table)
story.append(PageBreak())
# ─────────────────────────────────────────────────────────────────────
# PAGE 8 – Jaw Relations + Selective Grinding
# ─────────────────────────────────────────────────────────────────────
story.append(Paragraph('14. Jaw Relations & Centric Relation', H1))
story.append(Spacer(1, 4))
jr_data = [
['Type', 'Definition', 'Clinical Use'],
['Centric Relation (CR)',
'Condyles in anterior-superior position on disc/eminence; bone-to-bone, tooth-independent',
'Reference for CD occlusal records; reproducible'],
['Vertical Dimension',
'Facial height at a given jaw position (VDO = with teeth; VDR = at rest)',
'Establishing correct VDO prevents over-/under-closure'],
['Centric Occlusion (CO)',
'Maximum intercuspation of teeth (may not coincide with CR)',
'In CD: CR = CO (both coincide)'],
['Eccentric Relations',
'All jaw positions other than CR: protrusive, lateral (working/balancing)',
'Used to set condylar inclination; achieve balanced occlusion'],
['Rest Position',
'Minimal muscle tone; no tooth contact; FWS = 2-4 mm',
'Baseline for VDO determination'],
]
jr_table = Table(jr_data, colWidths=[3.8*cm, 6.5*cm, 5.5*cm])
jr_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), ORANGE),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTNAME', (0,1),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1),(0,-1), ORANGE),
('FONTSIZE', (0,0),(-1,-1), 8),
('ROWBACKGROUNDS', (0,1),(-1,-1), [WHITE, LTYELLOW]),
('TOPPADDING', (0,0),(-1,-1), 5),
('BOTTOMPADDING', (0,0),(-1,-1), 5),
('GRID', (0,0),(-1,-1), 0.4, colors.lightgrey),
('VALIGN', (0,0),(-1,-1), 'TOP'),
]))
story.append(jr_table)
story.append(Spacer(1, 10))
story.append(Paragraph('15. Selective Grinding – BULL Rule', H1))
story.append(Spacer(1, 4))
sg_data = [
['Interference Type', 'Surfaces to Grind', 'Mnemonic'],
['Protrusive (P)',
'Buccal inclines of Upper posteriors\n+ Lingual inclines of Lower posteriors',
'BULL\n(Buccal Upper, Lingual Lower)'],
['Non-working / Balancing side',
'Mesial inclines of Upper\n+ Distal inclines of Lower',
'MUDL\n(Mesial Upper, Distal Lower)'],
['Working / Laterotrusive side',
'Mesial inclines of Lower buccal cusps\n+ Distal inclines of Upper buccal cusps',
'LUBL'],
['General rule',
'Never remove centric stops;\npolish after grinding;\nremount after adjustments',
'Preserve CR contacts'],
]
sg_table = Table(sg_data, colWidths=[4*cm, 7.2*cm, 4.6*cm])
sg_table.setStyle(TableStyle([
('BACKGROUND', (0,0),(-1,0), RED),
('TEXTCOLOR', (0,0),(-1,0), WHITE),
('FONTNAME', (0,0),(-1,0), 'Helvetica-Bold'),
('FONTNAME', (0,1),(0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1),(0,-1), RED),
('FONTNAME', (2,1),(2,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (2,1),(2,-1), NAVY),
('FONTSIZE', (0,0),(-1,-1), 8.5),
('ROWBACKGROUNDS', (0,1),(-1,-1), [WHITE, LTRED]),
('TOPPADDING', (0,0),(-1,-1), 6),
('BOTTOMPADDING', (0,0),(-1,-1), 6),
('GRID', (0,0),(-1,-1), 0.4, colors.lightgrey),
('VALIGN', (0,0),(-1,-1), 'TOP'),
]))
story.append(sg_table)
story.append(Spacer(1, 14))
story.append(HRFlowable(width='100%', thickness=2, color=GOLD))
story.append(Spacer(1, 6))
story.append(Paragraph(
'<b>Complete Denture Prosthodontics Study Guide</b> | '
'Covers: PPS, Hanau Quint, VD, Curves, Gothic Arch, Stress Areas, '
'House Classification, Bonwill Triangle, Combination Syndrome, '
'Border Molding, Overdentures, Jaw Relations, Selective Grinding.',
CAPTION))
doc.build(story)
print(f'PDF saved to: {outpath}')
if __name__ == '__main__':
out = '/home/daytona/workspace/cd-study-guide/CD_Prosthodontics_Study_Guide.pdf'
build_pdf(out)
cd /home/daytona/workspace/cd-study-guide && python generate_cd_guide.py 2>&1
~/cd-study-guide/generate_cd_guide.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/cd-study-guide/generate_cd_guide.py'
cd /home/daytona/workspace/cd-study-guide && python generate_cd_guide.py 2>&1
ls -lh /home/daytona/workspace/cd-study-guide/
CD Prosthodontics Study Guide
PDF Document · PDF
| Page | Content |
|---|---|
| Cover | Styled title page with denture arch illustration |
| TOC | Table of contents with 15 sections |
| P3 | Diagram: Posterior Palatal Seal Area (AH line, PPS zone, hamular notch, fovea palatinae) + Hanau's Quint (5 color-coded boxes) |
| P4 | Diagram: Muscles of Mastication (4-column table) + Vertical Dimension (VDR/VDO/FWS with face illustration and formula box) |
| P5 | Diagram: Compensating Curves - Curve of Spee (sagittal) and Curve of Wilson (frontal) + Gothic Arch Tracing (arrow-point tracing with apex = CR) |
| P6 | Diagram: Primary & Secondary Stress-Bearing Areas (maxilla palatal view + mandible occlusal view) + House Classification (4 colored class boxes) |
| P7 | Diagram: Bonwill's Equilateral Triangle (labeled geometric diagram) + Combination Syndrome (Kelly 1972, 5 features) |
| P8 | Key Definitions Table - 15 terms from the question list |
| P9 | Border Molding step-by-step table + Overdentures summary table |
| P10 | Jaw Relations & Centric Relation table + Selective Grinding BULL/MUDL rules table |