Properties of skeleton muscle according to Sembulingm for knrush university exam for bpt
Sembulingam physiology properties of skeletal muscle BPT exam excitability contractility extensibility elasticity
https://pharmdguru.com/physiological-properties-of-skeletal-…
Because the absolute refractory period ends BEFORE the mechanical response begins, tetanization of skeletal muscle is possible.
| Type | Muscle Length | Tension | Example |
|---|---|---|---|
| Isometric | No change | Increases | Pushing a wall, postural muscles |
| Isotonic | Decreases (shortens) | Constant | Lifting a weight, walking |
| Property | Type | Definition |
|---|---|---|
| Excitability | Electrical | Ability to respond to a stimulus |
| Conductivity | Electrical | Ability to propagate action potentials |
| Contractility | Mechanical | Ability to shorten and develop tension |
| Extensibility | Mechanical | Ability to be stretched without damage |
| Elasticity | Mechanical | Ability to return to original length after stretch |
Create a detailed summary PDF with diagrams on skeletal muscle properties
mkdir -p /home/daytona/workspace/skeletal-muscle-pdf && ls /home/daytona/workspace/
~/skeletal-muscle-pdf/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Circle, Polygon, Path
from reportlab.graphics import renderPDF
from reportlab.platypus import Image as RLImage
import io
OUTPUT = "/home/daytona/workspace/skeletal-muscle-pdf/Skeletal_Muscle_Properties_BPT.pdf"
# ─── Colour palette ───────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
BLUE = colors.HexColor("#2563eb")
LBLUE = colors.HexColor("#dbeafe")
TEAL = colors.HexColor("#0d9488")
LTEAL = colors.HexColor("#ccfbf1")
ORANGE = colors.HexColor("#ea580c")
LORANGE = colors.HexColor("#ffedd5")
RED = colors.HexColor("#dc2626")
LRED = colors.HexColor("#fee2e2")
GREEN = colors.HexColor("#16a34a")
LGREEN = colors.HexColor("#dcfce7")
PURPLE = colors.HexColor("#7c3aed")
LPURPLE = colors.HexColor("#ede9fe")
YELLOW = colors.HexColor("#ca8a04")
LYELLOW = colors.HexColor("#fef9c3")
GRAY = colors.HexColor("#6b7280")
LGRAY = colors.HexColor("#f3f4f6")
WHITE = colors.white
BLACK = colors.black
W, H = A4 # 595.27 x 841.89 pts
# ─── Styles ───────────────────────────────────────────────────────
def make_styles():
base = getSampleStyleSheet()
s = {}
s['title'] = ParagraphStyle('title', fontSize=22, fontName='Helvetica-Bold',
textColor=WHITE, alignment=TA_CENTER, spaceAfter=4)
s['subtitle'] = ParagraphStyle('subtitle', fontSize=11, fontName='Helvetica',
textColor=LBLUE, alignment=TA_CENTER, spaceAfter=2)
s['h1'] = ParagraphStyle('h1', fontSize=14, fontName='Helvetica-Bold',
textColor=WHITE, spaceBefore=8, spaceAfter=4,
leftIndent=0, borderPad=4)
s['h2'] = ParagraphStyle('h2', fontSize=12, fontName='Helvetica-Bold',
textColor=NAVY, spaceBefore=6, spaceAfter=3)
s['h3'] = ParagraphStyle('h3', fontSize=10, fontName='Helvetica-Bold',
textColor=TEAL, spaceBefore=4, spaceAfter=2)
s['body'] = ParagraphStyle('body', fontSize=9, fontName='Helvetica',
textColor=colors.HexColor("#1f2937"),
leading=14, spaceAfter=4, alignment=TA_JUSTIFY)
s['bullet'] = ParagraphStyle('bullet', fontSize=9, fontName='Helvetica',
textColor=colors.HexColor("#1f2937"),
leading=13, spaceAfter=2,
leftIndent=14, firstLineIndent=-10)
s['kv'] = ParagraphStyle('kv', fontSize=9, fontName='Helvetica-Bold',
textColor=NAVY, leading=13, spaceAfter=2, leftIndent=14)
s['note'] = ParagraphStyle('note', fontSize=8.5, fontName='Helvetica-Oblique',
textColor=GRAY, leading=12, spaceAfter=3, leftIndent=10)
s['caption'] = ParagraphStyle('caption', fontSize=8, fontName='Helvetica-Oblique',
textColor=GRAY, alignment=TA_CENTER, spaceAfter=4)
s['table_hdr'] = ParagraphStyle('table_hdr', fontSize=9, fontName='Helvetica-Bold',
textColor=WHITE, alignment=TA_CENTER)
s['table_body'] = ParagraphStyle('table_body', fontSize=8.5, fontName='Helvetica',
textColor=BLACK, alignment=TA_LEFT, leading=12)
s['small'] = ParagraphStyle('small', fontSize=8, fontName='Helvetica',
textColor=GRAY, leading=11)
return s
S = make_styles()
# ─── Helper Flowables ─────────────────────────────────────────────
def section_header(text, bg=NAVY, width=None):
w = width or (W - 3.6*cm)
d = Drawing(w, 28)
d.add(Rect(0, 0, w, 28, fillColor=bg, strokeColor=None))
d.add(String(10, 8, text, fontName='Helvetica-Bold', fontSize=13, fillColor=WHITE))
return d
def subsection_bar(text, bg=TEAL, width=None):
w = width or (W - 3.6*cm)
d = Drawing(w, 22)
d.add(Rect(0, 0, w, 22, fillColor=bg, strokeColor=None, rx=3))
d.add(String(8, 6, text, fontName='Helvetica-Bold', fontSize=10.5, fillColor=WHITE))
return d
def info_box(text, bg=LBLUE, border=BLUE, width=None):
"""A simple info paragraph wrapped in a coloured table cell."""
w = width or (W - 3.6*cm)
data = [[Paragraph(text, ParagraphStyle('ib', fontSize=9, fontName='Helvetica',
textColor=NAVY, leading=13))]]
t = Table(data, colWidths=[w])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 0.8, border),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('ROWBACKGROUNDS', (0,0), (-1,-1), [bg]),
]))
return t
def bullet(text, color=BLUE):
return Paragraph(f'<font color="#{color.hexval()[1:]}">►</font> {text}', S['bullet'])
def sub_bullet(text):
return Paragraph(f' <font color="#6b7280">◦</font> {text}',
ParagraphStyle('sb', fontSize=8.5, fontName='Helvetica',
textColor=colors.HexColor("#374151"),
leading=12, spaceAfter=1, leftIndent=22, firstLineIndent=-10))
# ─── Diagram helpers ──────────────────────────────────────────────
def ap_diagram(width=460, height=180):
"""Action potential diagram for skeletal muscle."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2 - 80, height-14, "Action Potential of Skeletal Muscle",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
# axes
ox, oy, aw, ah = 55, 20, width-80, height-50
d.add(Line(ox, oy, ox, oy+ah, strokeColor=GRAY, strokeWidth=1))
d.add(Line(ox, oy+ah*0.35, ox+aw, oy+ah*0.35, strokeColor=GRAY, strokeWidth=1))
# Y labels
d.add(String(ox-52, oy+ah*0.35-4, "RMP = -90 mV", fontName='Helvetica', fontSize=7, fillColor=GRAY))
d.add(String(ox-52, oy+ah-6, "+30 mV", fontName='Helvetica', fontSize=7, fillColor=GRAY))
d.add(String(ox-52, oy+2, "-90 mV", fontName='Helvetica', fontSize=7, fillColor=GRAY))
d.add(String(ox-32, oy+ah*0.6, "mV", fontName='Helvetica-Bold', fontSize=7, fillColor=NAVY))
# X labels
d.add(String(ox, oy+ah*0.35-16, "0", fontName='Helvetica', fontSize=7, fillColor=GRAY))
d.add(String(ox+aw-10, oy+ah*0.35-16, "Time (ms)", fontName='Helvetica', fontSize=7, fillColor=GRAY))
# AP shape path
pts = [
(ox, oy+ah*0.35),
(ox+aw*0.10, oy+ah*0.35), # baseline
(ox+aw*0.18, oy+ah*0.85), # rapid depol
(ox+aw*0.25, oy+ah), # peak +30
(ox+aw*0.38, oy+ah*0.05), # rapid repol
(ox+aw*0.50, oy), # hyperpolarisation
(ox+aw*0.65, oy+ah*0.35), # return to RMP
(ox+aw, oy+ah*0.35),
]
path = Path(strokeColor=BLUE, strokeWidth=2, fillColor=None)
path.moveTo(pts[0][0], pts[0][1])
for px, py in pts[1:]:
path.lineTo(px, py)
d.add(path)
# annotations
d.add(Line(ox+aw*0.10, oy+ah*0.35, ox+aw*0.10, oy+ah*0.35+30,
strokeColor=colors.HexColor("#9ca3af"), strokeWidth=0.7, strokeDashArray=[2,2]))
d.add(String(ox+aw*0.05, oy+ah*0.35+32, "Threshold\n-50 mV", fontName='Helvetica', fontSize=6.5, fillColor=RED))
d.add(Line(ox+aw*0.25, oy+ah, ox+aw*0.25, oy+ah+10,
strokeColor=colors.HexColor("#9ca3af"), strokeWidth=0.7, strokeDashArray=[2,2]))
d.add(String(ox+aw*0.22, oy+ah+11, "Peak\n+30mV", fontName='Helvetica', fontSize=6.5, fillColor=RED))
# duration bar
d.add(Line(ox+aw*0.10, oy+ah*0.35-12, ox+aw*0.65, oy+ah*0.35-12, strokeColor=ORANGE, strokeWidth=1))
d.add(String(ox+aw*0.3, oy+ah*0.35-20, "Duration: 2-4 ms", fontName='Helvetica', fontSize=7, fillColor=ORANGE))
# phases
d.add(String(ox+aw*0.14, oy+ah*0.70, "Depol.", fontName='Helvetica', fontSize=6.5, fillColor=GREEN))
d.add(String(ox+aw*0.36, oy+ah*0.55, "Repol.", fontName='Helvetica', fontSize=6.5, fillColor=PURPLE))
d.add(String(ox+aw*0.44, oy+ah*0.20, "After-\nhyperpol.", fontName='Helvetica', fontSize=6.5, fillColor=TEAL))
# ARP marker
d.add(Rect(ox+aw*0.10, oy+3, aw*0.28, 8, fillColor=colors.HexColor("#fecaca"),
strokeColor=None, rx=2))
d.add(String(ox+aw*0.16, oy+5.5, "ARP (1-3 ms)", fontName='Helvetica', fontSize=6.5, fillColor=RED))
return d
def muscle_twitch_diagram(width=460, height=160):
"""Muscle twitch with latent, contraction, relaxation phases."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2 - 70, height-14, "Skeletal Muscle Twitch",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
ox, oy, aw, ah = 45, 15, width-65, height-48
d.add(Line(ox, oy, ox, oy+ah, strokeColor=GRAY, strokeWidth=1))
d.add(Line(ox, oy+12, ox+aw, oy+12, strokeColor=GRAY, strokeWidth=1))
d.add(String(ox-40, oy+ah-6, "Force", fontName='Helvetica-Bold', fontSize=7, fillColor=NAVY))
d.add(String(ox+aw-18, oy+5, "Time", fontName='Helvetica-Bold', fontSize=7, fillColor=NAVY))
# Twitch curve
pts = [
(ox, oy+12),
(ox+aw*0.08, oy+12), # latent
(ox+aw*0.35, oy+ah), # peak contraction
(ox+aw*0.75, oy+12), # relaxation
(ox+aw, oy+12),
]
path = Path(strokeColor=TEAL, strokeWidth=2.5, fillColor=None)
path.moveTo(pts[0][0], pts[0][1])
path.lineTo(pts[1][0], pts[1][1])
path.curveTo(pts[1][0]+aw*0.08, pts[1][1], pts[2][0]-aw*0.05, pts[2][1], pts[2][0], pts[2][1])
path.curveTo(pts[2][0]+aw*0.05, pts[2][1], pts[3][0]-aw*0.1, pts[3][1], pts[3][0], pts[3][1])
path.lineTo(pts[4][0], pts[4][1])
d.add(path)
# Phase separators
x_lat = ox+aw*0.08
x_peak = ox+aw*0.35
x_end = ox+aw*0.75
for x in [x_lat, x_peak, x_end]:
d.add(Line(x, oy+12, x, oy-5, strokeColor=GRAY, strokeWidth=0.7, strokeDashArray=[2,2]))
# Phase labels under X axis
def phase_label(x1, x2, label, clr):
mx = (x1+x2)/2
d.add(Line(x1+2, oy-2, x2-2, oy-2, strokeColor=clr, strokeWidth=1.2))
d.add(String(mx-len(label)*2.5, oy-14, label, fontName='Helvetica', fontSize=7, fillColor=clr))
phase_label(ox, x_lat, "Latent", ORANGE)
phase_label(x_lat, x_peak, "Contraction", GREEN)
phase_label(x_peak, x_end, "Relaxation", PURPLE)
# Stimulus arrow
d.add(String(ox+2, oy+12+3, "Stimulus", fontName='Helvetica', fontSize=6.5, fillColor=RED))
# Peak label
d.add(String(x_peak+3, oy+ah-5, "Peak tension", fontName='Helvetica', fontSize=6.5, fillColor=TEAL))
return d
def sarcomere_diagram(width=460, height=130):
"""Simplified sarcomere / sliding filament."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2 - 85, height-14, "Sarcomere Structure & Sliding Filament Theory",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
cy = height/2 + 5
# Z lines
for x in [50, width-50]:
d.add(Line(x, cy-28, x, cy+28, strokeColor=NAVY, strokeWidth=3))
label = "Z line"
d.add(String(x-12, cy+32, label, fontName='Helvetica', fontSize=7, fillColor=NAVY))
# Sarcomere brace
d.add(Line(50, cy-40, width-50, cy-40, strokeColor=GRAY, strokeWidth=0.7))
d.add(String(width/2 - 25, cy-50, "Sarcomere", fontName='Helvetica-Bold', fontSize=8, fillColor=GRAY))
# Thick filaments (myosin) - centre
for y_off in [-8, 8]:
d.add(Rect(120, cy+y_off-4, width-240, 8, fillColor=colors.HexColor("#dc2626"),
strokeColor=None, rx=4))
d.add(String(width/2-20, cy-22, "Myosin (thick)", fontName='Helvetica', fontSize=7, fillColor=RED))
# Thin filaments (actin) - full length
for y_off in [-18, 18]:
d.add(Rect(52, cy+y_off-3, width-106, 6, fillColor=colors.HexColor("#2563eb"),
strokeColor=None, rx=3))
d.add(String(60, cy+24, "Actin (thin)", fontName='Helvetica', fontSize=7, fillColor=BLUE))
# Cross bridges
for x in range(135, width-130, 35):
d.add(Line(x, cy-8, x+8, cy-18, strokeColor=ORANGE, strokeWidth=1.2))
d.add(Line(x, cy+8, x+8, cy+18, strokeColor=ORANGE, strokeWidth=1.2))
# M line
d.add(Line(width/2, cy-28, width/2, cy+28, strokeColor=ORANGE, strokeWidth=1.5, strokeDashArray=[3,2]))
d.add(String(width/2+3, cy+30, "M line", fontName='Helvetica', fontSize=7, fillColor=ORANGE))
# Legend
legend_x = width - 130
d.add(Rect(legend_x-5, 5, 130, 42, fillColor=WHITE, strokeColor=LGRAY, strokeWidth=0.5, rx=3))
d.add(Rect(legend_x, 35, 18, 7, fillColor=RED, strokeColor=None, rx=2))
d.add(String(legend_x+22, 35, "Myosin (thick)", fontName='Helvetica', fontSize=7, fillColor=BLACK))
d.add(Rect(legend_x, 22, 18, 7, fillColor=BLUE, strokeColor=None, rx=2))
d.add(String(legend_x+22, 22, "Actin (thin)", fontName='Helvetica', fontSize=7, fillColor=BLACK))
d.add(Line(legend_x, 12, legend_x+18, 12, strokeColor=ORANGE, strokeWidth=1.2))
d.add(String(legend_x+22, 8, "Cross bridges", fontName='Helvetica', fontSize=7, fillColor=BLACK))
return d
def contraction_types_diagram(width=460, height=155):
"""Isometric vs isotonic contraction."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2-80, height-14, "Types of Muscle Contraction",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
half = width/2 - 10
# Isometric box
d.add(Rect(8, 12, half-8, height-30, fillColor=LRED, strokeColor=RED, strokeWidth=1, rx=4))
d.add(String(half/2-30, height-26, "ISOMETRIC", fontName='Helvetica-Bold', fontSize=9, fillColor=RED))
# muscle bar - constant length
bar_y = height/2
for x in [25, half-40]:
d.add(Rect(x, bar_y-6, 15, 12, fillColor=RED, strokeColor=None, rx=3))
d.add(Rect(40, bar_y-4, half-75, 8, fillColor=colors.HexColor("#f87171"), strokeColor=None))
# wall
for y in range(int(bar_y)-20, int(bar_y)+22, 8):
d.add(Line(half-28, y, half-20, y+6, strokeColor=NAVY, strokeWidth=1.5))
d.add(Line(half-28, bar_y-20, half-28, bar_y+22, strokeColor=NAVY, strokeWidth=2))
d.add(String(25, bar_y-24, "No change in length", fontName='Helvetica', fontSize=7.5, fillColor=RED))
d.add(String(25, bar_y-34, "Tension increases", fontName='Helvetica', fontSize=7.5, fillColor=RED))
d.add(String(25, 18, "e.g. pushing a wall", fontName='Helvetica-Oblique', fontSize=7, fillColor=GRAY))
# Isotonic box
ox2 = half + 12
d.add(Rect(ox2, 12, half-8, height-30, fillColor=LGREEN, strokeColor=GREEN, strokeWidth=1, rx=4))
d.add(String(ox2 + (half-8)/2 - 32, height-26, "ISOTONIC", fontName='Helvetica-Bold', fontSize=9, fillColor=GREEN))
# muscle bar - shortening
cx = ox2 + 20
d.add(Rect(cx, bar_y-6, 12, 12, fillColor=GREEN, strokeColor=None, rx=3))
# shorter after contraction
d.add(Rect(cx+12, bar_y-4, half-80, 8, fillColor=colors.HexColor("#86efac"), strokeColor=None))
d.add(Rect(cx+half-70, bar_y-6, 12, 12, fillColor=GREEN, strokeColor=None, rx=3))
# weight
d.add(Rect(cx+half-64, bar_y-18, 20, 14, fillColor=YELLOW, strokeColor=None, rx=2))
d.add(String(cx+half-60, bar_y-14, "W", fontName='Helvetica-Bold', fontSize=8, fillColor=WHITE))
# arrow showing shortening
d.add(Line(cx+12, bar_y+14, cx+half-80, bar_y+14, strokeColor=GREEN, strokeWidth=1.2))
d.add(String(cx+20, bar_y+16, "shortening", fontName='Helvetica', fontSize=7, fillColor=GREEN))
d.add(String(cx, bar_y-24, "Length decreases", fontName='Helvetica', fontSize=7.5, fillColor=GREEN))
d.add(String(cx, bar_y-34, "Tension constant", fontName='Helvetica', fontSize=7.5, fillColor=GREEN))
d.add(String(cx, 18, "e.g. lifting a weight", fontName='Helvetica-Oblique', fontSize=7, fillColor=GRAY))
return d
def tetanus_diagram(width=460, height=155):
"""Single twitch, summation, incomplete & complete tetanus."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2-95, height-14, "Summation, Incomplete & Complete Tetanus",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
ox, oy, aw, ah = 45, 18, width-60, height-50
d.add(Line(ox, oy, ox, oy+ah, strokeColor=GRAY, strokeWidth=1))
d.add(Line(ox, oy+10, ox+aw, oy+10, strokeColor=GRAY, strokeWidth=1))
d.add(String(ox-40, oy+ah/2, "Tension", fontName='Helvetica-Bold', fontSize=7, fillColor=NAVY))
def twitch_curve(start_x, peak_h, clr, width_frac=0.12):
px = start_x + aw*width_frac*0.6
path = Path(strokeColor=clr, strokeWidth=1.8, fillColor=None)
path.moveTo(start_x, oy+10)
path.curveTo(start_x+aw*width_frac*0.3, oy+10,
px-aw*width_frac*0.1, oy+10+peak_h,
px, oy+10+peak_h)
end_x = start_x + aw*width_frac*1.2
path.curveTo(px+aw*width_frac*0.1, oy+10+peak_h,
end_x-aw*width_frac*0.1, oy+10,
end_x, oy+10)
d.add(path)
# Single twitch
twitch_curve(ox+aw*0.02, ah*0.4, TEAL)
d.add(String(ox+aw*0.01, oy+2, "Single\nTwitch", fontName='Helvetica', fontSize=6, fillColor=TEAL))
# Summation (2 close twitches, second higher)
base2 = ox+aw*0.22
twitch_curve(base2, ah*0.4, BLUE, 0.10)
twitch_curve(base2+aw*0.07, ah*0.55, BLUE, 0.10)
d.add(String(base2+aw*0.01, oy+2, "Summation", fontName='Helvetica', fontSize=6, fillColor=BLUE))
# Incomplete tetanus (wavy plateau)
base3 = ox+aw*0.44
# draw as a series of bumps
path3 = Path(strokeColor=ORANGE, strokeWidth=1.8, fillColor=None)
path3.moveTo(base3, oy+10)
# rising
path3.curveTo(base3+10, oy+10, base3+18, oy+10+ah*0.55, base3+22, oy+10+ah*0.55)
for bx in range(int(base3+22), int(base3+aw*0.18), 12):
path3.curveTo(bx+2, oy+10+ah*0.7, bx+6, oy+10+ah*0.45, bx+12, oy+10+ah*0.55)
path3.curveTo(base3+aw*0.20, oy+10+ah*0.55, base3+aw*0.22, oy+10, base3+aw*0.22, oy+10)
d.add(path3)
d.add(String(base3+aw*0.01, oy+2, "Incomplete\nTetanus", fontName='Helvetica', fontSize=6, fillColor=ORANGE))
# Complete tetanus (smooth plateau)
base4 = ox+aw*0.70
path4 = Path(strokeColor=RED, strokeWidth=2.2, fillColor=None)
path4.moveTo(base4, oy+10)
path4.curveTo(base4+8, oy+10, base4+14, oy+10+ah*0.78, base4+18, oy+10+ah*0.78)
path4.lineTo(base4+aw*0.22, oy+10+ah*0.78)
path4.curveTo(base4+aw*0.24, oy+10+ah*0.78, base4+aw*0.27, oy+10, base4+aw*0.27, oy+10)
d.add(path4)
d.add(String(base4+aw*0.01, oy+2, "Complete\nTetanus", fontName='Helvetica', fontSize=6, fillColor=RED))
# stimulus markers
for sx in [ox+aw*0.02, ox+aw*0.22, ox+aw*0.26, ox+aw*0.44,
ox+aw*0.47, ox+aw*0.50, ox+aw*0.53, ox+aw*0.56,
ox+aw*0.70, ox+aw*0.72, ox+aw*0.74, ox+aw*0.76, ox+aw*0.78]:
d.add(Line(sx, oy+10-4, sx, oy+10-9, strokeColor=NAVY, strokeWidth=1))
d.add(String(ox+aw*0.02, oy-10, "Stimuli", fontName='Helvetica', fontSize=6.5, fillColor=NAVY))
return d
def starling_law_diagram(width=460, height=145):
"""Length-tension relationship (Starling's law for skeletal muscle)."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
d.add(String(width/2-105, height-14, "Length-Tension Relationship (Starling's Law of Muscle)",
fontName='Helvetica-Bold', fontSize=9, fillColor=NAVY))
ox, oy, aw, ah = 55, 18, width-80, height-50
d.add(Line(ox, oy, ox, oy+ah, strokeColor=GRAY, strokeWidth=1))
d.add(Line(ox, oy, ox+aw, oy, strokeColor=GRAY, strokeWidth=1))
d.add(String(ox-50, oy+ah/2, "Tension", fontName='Helvetica-Bold', fontSize=7.5, fillColor=NAVY))
d.add(String(ox+aw/2-20, oy-14, "Initial Muscle Length", fontName='Helvetica-Bold', fontSize=7.5, fillColor=NAVY))
# Bell-shaped curve
import math
pts = []
for i in range(101):
x_frac = i/100
x = ox + aw*x_frac
# Gaussian-like curve peaking at 60%
y_frac = math.exp(-((x_frac - 0.60)**2) / (2*0.18**2))
y = oy + ah*0.92*y_frac
pts.append((x, y))
path = Path(strokeColor=PURPLE, strokeWidth=2.5, fillColor=None)
path.moveTo(pts[0][0], pts[0][1])
for px, py in pts[1:]:
path.lineTo(px, py)
d.add(path)
# Optimal length marker
opt_x = ox + aw*0.60
d.add(Line(opt_x, oy, opt_x, oy+ah*0.95, strokeColor=GREEN, strokeWidth=1, strokeDashArray=[3,2]))
d.add(String(opt_x-5, oy+ah*0.96+2, "Optimal\nLength", fontName='Helvetica', fontSize=7, fillColor=GREEN))
d.add(String(opt_x-18, oy+ah*0.93+12, "Max Tension", fontName='Helvetica-Bold', fontSize=7, fillColor=GREEN))
# Zones
d.add(String(ox+5, oy+5, "Too\nshort", fontName='Helvetica', fontSize=7, fillColor=ORANGE))
d.add(String(ox+aw*0.82, oy+5, "Too\nlong", fontName='Helvetica', fontSize=7, fillColor=ORANGE))
d.add(String(ox+aw*0.48, oy+ah*0.5, "Optimal zone", fontName='Helvetica', fontSize=7.5, fillColor=PURPLE))
return d
def properties_mind_map(width=460, height=200):
"""Central node with 5 property branches."""
d = Drawing(width, height)
d.add(Rect(0, 0, width, height, fillColor=colors.HexColor("#f8fafc"), strokeColor=LGRAY, strokeWidth=0.5))
cx, cy = width/2, height/2+8
# Central node
d.add(Circle(cx, cy, 38, fillColor=NAVY, strokeColor=None))
d.add(String(cx-28, cy+5, "SKELETAL", fontName='Helvetica-Bold', fontSize=8, fillColor=WHITE))
d.add(String(cx-22, cy-6, "MUSCLE", fontName='Helvetica-Bold', fontSize=8, fillColor=WHITE))
d.add(String(cx-24, cy-17, "PROPERTIES", fontName='Helvetica-Bold', fontSize=7, fillColor=LBLUE))
import math
props = [
("EXCITABILITY", BLUE, 90, -70),
("CONDUCTIVITY", TEAL, 60, 70),
("CONTRACTILITY", GREEN, -30, 90),
("EXTENSIBILITY", ORANGE,-80, -20),
("ELASTICITY", PURPLE, -20, -80),
]
for name, clr, dx, dy in props:
ex, ey = cx+dx, cy+dy
# spoke
d.add(Line(cx+28*(dx/abs(dx) if dx != 0 else 0)*0.8,
cy+28*(dy/abs(dy) if dy != 0 else 0)*0.8,
ex, ey, strokeColor=clr, strokeWidth=1.8))
# node
d.add(Rect(ex-38, ey-10, 76, 20, fillColor=clr, strokeColor=None, rx=8))
d.add(String(ex-len(name)*2.8, ey-4, name, fontName='Helvetica-Bold', fontSize=7, fillColor=WHITE))
d.add(String(5, 5, "Fig: Mind Map - Properties of Skeletal Muscle",
fontName='Helvetica-Oblique', fontSize=7, fillColor=GRAY))
return d
# ─── Cover page ───────────────────────────────────────────────────
def cover_page():
elems = []
# full-width header block
cover_bg = Drawing(W - 3.6*cm, 160)
cover_bg.add(Rect(0, 0, W-3.6*cm, 160, fillColor=NAVY, strokeColor=None, rx=6))
# decorative circles
for cx_, cy_, r_, alpha_ in [(320,130,60,0.12),(30,20,45,0.10),(W-120,40,80,0.08)]:
cover_bg.add(Circle(cx_, cy_, r_, fillColor=WHITE, strokeColor=None,
fillOpacity=alpha_))
cover_bg.add(String(20, 118, "KNRUSH UNIVERSITY", fontName='Helvetica-Bold', fontSize=11, fillColor=LBLUE))
cover_bg.add(String(20, 100, "Bachelor of Physiotherapy (BPT)", fontName='Helvetica', fontSize=9.5, fillColor=LBLUE))
cover_bg.add(String(20, 75, "PROPERTIES OF", fontName='Helvetica-Bold', fontSize=26, fillColor=WHITE))
cover_bg.add(String(20, 48, "SKELETAL MUSCLE", fontName='Helvetica-Bold', fontSize=26, fillColor=YELLOW))
cover_bg.add(String(20, 28, "Based on: Sembulingam's Essentials of Medical Physiology",
fontName='Helvetica', fontSize=9, fillColor=LBLUE))
cover_bg.add(String(20, 14, "Chapter 26 | Physiology Study Notes with Diagrams",
fontName='Helvetica', fontSize=8.5, fillColor=LGRAY))
elems.append(cover_bg)
elems.append(Spacer(1, 0.4*cm))
# Mind map
elems.append(properties_mind_map(W-3.6*cm, 200))
elems.append(Paragraph("<i>Fig 1: Overview - Properties of Skeletal Muscle</i>", S['caption']))
elems.append(Spacer(1, 0.3*cm))
# Quick overview table
data = [
[Paragraph("Property", S['table_hdr']),
Paragraph("Type", S['table_hdr']),
Paragraph("One-line definition", S['table_hdr'])],
[Paragraph("Excitability", S['table_body']), Paragraph("Electrical", S['table_body']),
Paragraph("Ability to receive and respond to a stimulus", S['table_body'])],
[Paragraph("Conductivity", S['table_body']), Paragraph("Electrical", S['table_body']),
Paragraph("Propagate action potentials along sarcolemma (3-5 m/s)", S['table_body'])],
[Paragraph("Contractility", S['table_body']), Paragraph("Mechanical", S['table_body']),
Paragraph("Shorten and develop tension when stimulated", S['table_body'])],
[Paragraph("Extensibility", S['table_body']), Paragraph("Mechanical", S['table_body']),
Paragraph("Stretch beyond resting length without damage", S['table_body'])],
[Paragraph("Elasticity", S['table_body']), Paragraph("Mechanical", S['table_body']),
Paragraph("Recoil to original resting length after stretch", S['table_body'])],
]
col_w = [(W-3.6*cm)*f for f in [0.22, 0.18, 0.60]]
t = Table(data, colWidths=col_w)
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), NAVY),
('BACKGROUND', (0,1), (-1,1), LBLUE),
('BACKGROUND', (0,2), (-1,2), LTEAL),
('BACKGROUND', (0,3), (-1,3), LGREEN),
('BACKGROUND', (0,4), (-1,4), LORANGE),
('BACKGROUND', (0,5), (-1,5), LPURPLE),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#cbd5e1")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t)
elems.append(Paragraph("<i>Table 1: Classification of Skeletal Muscle Properties</i>", S['caption']))
return elems
# ─── Section 1: Electrical Properties ───────────────────────────
def electrical_section():
elems = []
elems.append(PageBreak())
elems.append(section_header("A. ELECTRICAL PROPERTIES"))
elems.append(Spacer(1, 0.25*cm))
# --- 1. Excitability ---
elems.append(subsection_bar("1. EXCITABILITY (Irritability)"))
elems.append(Spacer(1, 0.1*cm))
elems.append(Paragraph(
"Excitability is the ability of the skeletal muscle to receive and respond to a stimulus. "
"A stimulus can be electrical, chemical, or mechanical in nature.", S['body']))
# AP values table
ap_data = [
[Paragraph("Parameter", S['table_hdr']), Paragraph("Value", S['table_hdr'])],
[Paragraph("Resting Membrane Potential (RMP)", S['table_body']), Paragraph("-90 mV", S['table_body'])],
[Paragraph("Threshold / Firing Level", S['table_body']), Paragraph("-50 mV", S['table_body'])],
[Paragraph("Amplitude of Action Potential", S['table_body']), Paragraph("120 mV", S['table_body'])],
[Paragraph("Duration of Action Potential", S['table_body']), Paragraph("2 - 4 ms", S['table_body'])],
[Paragraph("Absolute Refractory Period (ARP)", S['table_body']), Paragraph("1 - 3 ms", S['table_body'])],
[Paragraph("Conduction Velocity", S['table_body']), Paragraph("3 - 5 m/s", S['table_body'])],
]
col_w2 = [(W-3.6*cm)*f for f in [0.65, 0.35]]
t2 = Table(ap_data, colWidths=col_w2)
t2.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), BLUE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LBLUE]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#bfdbfe")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t2)
elems.append(Paragraph("<i>Table 2: Action Potential Parameters - Skeletal Muscle</i>", S['caption']))
elems.append(Spacer(1, 0.2*cm))
# AP diagram
elems.append(ap_diagram(W-3.6*cm, 185))
elems.append(Paragraph("<i>Fig 2: Action Potential of Skeletal Muscle</i>", S['caption']))
elems.append(Spacer(1, 0.2*cm))
elems.append(bullet("Single muscle fiber obeys the <b>All-or-None Law</b> - fires maximally or not at all."))
elems.append(bullet("The whole skeletal muscle does <b>NOT</b> obey all-or-none law (different fibers have different thresholds)."))
elems.append(bullet("Muscle can be stimulated <b>directly</b> (electrode on muscle) or <b>indirectly</b> (via motor nerve)."))
elems.append(bullet("Mechanical activity begins only when the electrical activity is <b>about to end</b>."))
elems.append(bullet("Because ARP ends before the mechanical response begins, <b>tetanization is possible</b> in skeletal muscle."))
elems.append(Spacer(1, 0.15*cm))
elems.append(info_box(
"<b>EXAM TIP:</b> Cardiac muscle obeys all-or-none law for the WHOLE muscle (syncytium) and cannot be tetanized "
"because its ARP (200-300 ms) lasts as long as the mechanical response. Skeletal muscle CAN be tetanized.",
bg=LYELLOW, border=YELLOW))
elems.append(Spacer(1, 0.3*cm))
# --- 2. Conductivity ---
elems.append(subsection_bar("2. CONDUCTIVITY"))
elems.append(Spacer(1, 0.1*cm))
elems.append(Paragraph(
"Conductivity is the ability of the muscle to propagate action potentials along the sarcolemma "
"in both directions from the point of stimulation.", S['body']))
elems.append(bullet("Conduction velocity: <b>3 - 5 m/s</b> (similar to unmyelinated nerve fibers)"))
elems.append(bullet("Action potential travels along the sarcolemma and enters the muscle via <b>T-tubules</b>"))
elems.append(bullet("T-tubules trigger Ca²⁺ release from sarcoplasmic reticulum (SR) - the basis of <b>excitation-contraction coupling</b>"))
elems.append(bullet("Ca²⁺ binds troponin C → tropomyosin shifts → myosin cross-bridge formation proceeds"))
return elems
# ─── Section 2: Mechanical Properties ────────────────────────────
def mechanical_section():
elems = []
elems.append(PageBreak())
elems.append(section_header("B. MECHANICAL PROPERTIES", bg=TEAL))
elems.append(Spacer(1, 0.25*cm))
# --- 3. Contractility ---
elems.append(subsection_bar("3. CONTRACTILITY", bg=GREEN))
elems.append(Spacer(1, 0.1*cm))
elems.append(Paragraph(
"Contractility is the ability of the skeletal muscle to shorten and develop tension (force) "
"when stimulated by an action potential. It is the fundamental property underlying all movement.",
S['body']))
# Sarcomere diagram
elems.append(sarcomere_diagram(W-3.6*cm, 135))
elems.append(Paragraph("<i>Fig 3: Sarcomere Structure and Sliding Filament Theory</i>", S['caption']))
elems.append(Spacer(1, 0.15*cm))
elems.append(Paragraph("<b>Sliding Filament Theory:</b>", S['h3']))
elems.append(bullet("Actin (thin) filaments slide over myosin (thick) filaments → sarcomere shortens"))
elems.append(bullet("Filament length does NOT change - only degree of overlap changes"))
elems.append(bullet("Requires ATP hydrolysis by myosin ATPase for cross-bridge cycling"))
elems.append(Spacer(1, 0.2*cm))
# Muscle twitch diagram
elems.append(muscle_twitch_diagram(W-3.6*cm, 160))
elems.append(Paragraph("<i>Fig 4: Skeletal Muscle Twitch - Latent, Contraction and Relaxation Phases</i>", S['caption']))
elems.append(Spacer(1, 0.15*cm))
elems.append(Paragraph("<b>Phases of a Single Muscle Twitch:</b>", S['h3']))
twitch_data = [
[Paragraph("Phase", S['table_hdr']), Paragraph("Duration", S['table_hdr']),
Paragraph("Description", S['table_hdr'])],
[Paragraph("Latent period", S['table_body']), Paragraph("~2 ms", S['table_body']),
Paragraph("Delay between stimulus and onset of contraction; E-C coupling occurs", S['table_body'])],
[Paragraph("Contraction phase", S['table_body']), Paragraph("~40 ms", S['table_body']),
Paragraph("Tension rises to peak; cross-bridge cycling; Ca²⁺ released", S['table_body'])],
[Paragraph("Relaxation phase", S['table_body']), Paragraph("~50 ms", S['table_body']),
Paragraph("Tension falls; Ca²⁺ pumped back to SR; cross-bridges detach", S['table_body'])],
]
col_w3 = [(W-3.6*cm)*f for f in [0.22, 0.15, 0.63]]
t3 = Table(twitch_data, colWidths=col_w3)
t3.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TEAL),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LTEAL]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#99f6e4")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t3)
elems.append(Paragraph("<i>Table 3: Phases of a Single Muscle Twitch</i>", S['caption']))
elems.append(Spacer(1, 0.2*cm))
# Types of contraction
elems.append(contraction_types_diagram(W-3.6*cm, 160))
elems.append(Paragraph("<i>Fig 5: Types of Muscle Contraction - Isometric vs Isotonic</i>", S['caption']))
elems.append(Spacer(1, 0.15*cm))
contr_data = [
[Paragraph("Feature", S['table_hdr']), Paragraph("Isometric", S['table_hdr']),
Paragraph("Isotonic", S['table_hdr'])],
[Paragraph("Muscle length", S['table_body']), Paragraph("No change", S['table_body']),
Paragraph("Decreases (shortens)", S['table_body'])],
[Paragraph("Tension", S['table_body']), Paragraph("Increases", S['table_body']),
Paragraph("Remains constant", S['table_body'])],
[Paragraph("External work", S['table_body']), Paragraph("No (energy → heat)", S['table_body']),
Paragraph("Yes (moves a load)", S['table_body'])],
[Paragraph("Examples", S['table_body']), Paragraph("Pushing a wall, postural muscles", S['table_body']),
Paragraph("Lifting weight, walking", S['table_body'])],
]
col_w4 = [(W-3.6*cm)*f for f in [0.25, 0.38, 0.37]]
t4 = Table(contr_data, colWidths=col_w4)
t4.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), GREEN),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LGREEN]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#bbf7d0")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t4)
elems.append(Paragraph("<i>Table 4: Isometric vs Isotonic Contraction</i>", S['caption']))
return elems
# ─── Section 3: Summation & Tetanus + Starling's Law ─────────────
def advanced_contractility():
elems = []
elems.append(PageBreak())
elems.append(section_header("SUMMATION, TETANUS & FACTORS AFFECTING CONTRACTION", bg=PURPLE))
elems.append(Spacer(1, 0.25*cm))
elems.append(tetanus_diagram(W-3.6*cm, 158))
elems.append(Paragraph("<i>Fig 6: Summation and Tetanus in Skeletal Muscle</i>", S['caption']))
elems.append(Spacer(1, 0.2*cm))
tetanus_data = [
[Paragraph("Type", S['table_hdr']), Paragraph("Mechanism", S['table_hdr']),
Paragraph("Result", S['table_hdr'])],
[Paragraph("Single Twitch", S['table_body']), Paragraph("One stimulus", S['table_body']),
Paragraph("Brief contraction and relaxation", S['table_body'])],
[Paragraph("Summation", S['table_body']), Paragraph("Second stimulus before complete relaxation", S['table_body']),
Paragraph("Greater tension than single twitch", S['table_body'])],
[Paragraph("Incomplete Tetanus", S['table_body']), Paragraph("Rapid stimuli with partial relaxation", S['table_body']),
Paragraph("Wavy sustained contraction", S['table_body'])],
[Paragraph("Complete Tetanus", S['table_body']), Paragraph("Very rapid stimuli, no relaxation", S['table_body']),
Paragraph("Smooth maximal sustained contraction", S['table_body'])],
[Paragraph("Staircase (Treppe)", S['table_body']),
Paragraph("Low freq successive stimuli; more Ca²⁺ available", S['table_body']),
Paragraph("Progressively stronger twitches", S['table_body'])],
]
col_w5 = [(W-3.6*cm)*f for f in [0.24, 0.38, 0.38]]
t5 = Table(tetanus_data, colWidths=col_w5)
t5.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), PURPLE),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LPURPLE]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#ddd6fe")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t5)
elems.append(Paragraph("<i>Table 5: Types of Muscle Response</i>", S['caption']))
elems.append(Spacer(1, 0.25*cm))
# Starling's law
elems.append(subsection_bar("Starling's Law of Skeletal Muscle (Length-Tension Relationship)", bg=PURPLE))
elems.append(Spacer(1, 0.1*cm))
elems.append(starling_law_diagram(W-3.6*cm, 148))
elems.append(Paragraph("<i>Fig 7: Length-Tension Relationship in Skeletal Muscle</i>", S['caption']))
elems.append(Spacer(1, 0.12*cm))
elems.append(bullet("Force of contraction is maximum when muscle is at its <b>optimal resting length</b>"))
elems.append(bullet("Too short: overlap of actin filaments from both sides → reduced cross-bridges"))
elems.append(bullet("Too long: reduced overlap between actin and myosin → fewer cross-bridges"))
elems.append(Spacer(1, 0.2*cm))
elems.append(subsection_bar("Factors Affecting Force of Contraction", bg=colors.HexColor("#7c3aed")))
elems.append(Spacer(1, 0.1*cm))
factors_data = [
[Paragraph("Factor", S['table_hdr']),
Paragraph("Effect", S['table_hdr'])],
[Paragraph("Stimulus strength", S['table_body']),
Paragraph("More motor units recruited → greater force (up to maximal)", S['table_body'])],
[Paragraph("Stimulus frequency", S['table_body']),
Paragraph("Higher frequency → summation → tetanus → max force", S['table_body'])],
[Paragraph("Initial muscle length", S['table_body']),
Paragraph("Optimal length → maximal cross-bridge formation → max force", S['table_body'])],
[Paragraph("Temperature", S['table_body']),
Paragraph("Warm muscle → faster enzyme activity → more force (to a limit)", S['table_body'])],
[Paragraph("Blood supply", S['table_body']),
Paragraph("O₂ and glucose delivery → sustained contraction", S['table_body'])],
[Paragraph("Fatigue", S['table_body']),
Paragraph("ATP depletion, lactic acid accumulation → reduced force", S['table_body'])],
]
col_w6 = [(W-3.6*cm)*f for f in [0.32, 0.68]]
t6 = Table(factors_data, colWidths=col_w6)
t6.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor("#7c3aed")),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LPURPLE]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#ddd6fe")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t6)
elems.append(Paragraph("<i>Table 6: Factors Affecting Force of Skeletal Muscle Contraction</i>", S['caption']))
return elems
# ─── Section 4: Extensibility & Elasticity ───────────────────────
def ext_elastic_section():
elems = []
elems.append(PageBreak())
elems.append(section_header("EXTENSIBILITY & ELASTICITY", bg=ORANGE))
elems.append(Spacer(1, 0.25*cm))
# --- 4. Extensibility ---
elems.append(subsection_bar("4. EXTENSIBILITY", bg=ORANGE))
elems.append(Spacer(1, 0.1*cm))
elems.append(Paragraph(
"Extensibility is the capacity of the muscle to be stretched beyond its resting length "
"without being damaged. This property is essential for smooth, coordinated movement.",
S['body']))
elems.append(bullet("Antagonist muscles must lengthen when agonists contract"))
elems.append(bullet("During elbow flexion: biceps contracts → triceps must extend"))
elems.append(bullet("Loss of extensibility leads to <b>spasticity</b> (seen in UMN lesions)"))
elems.append(bullet("Extensibility is limited by the connective tissue sheaths (epimysium, perimysium, endomysium)"))
elems.append(bullet("Important for physiotherapy stretching exercises and range-of-motion assessment"))
elems.append(Spacer(1, 0.2*cm))
# Extensibility diagram
ext_d = Drawing(W-3.6*cm, 120)
ext_d.add(Rect(0, 0, W-3.6*cm, 120, fillColor=LORANGE, strokeColor=ORANGE, strokeWidth=0.5, rx=4))
ext_d.add(String(10, 105, "Extensibility - Stretch Beyond Resting Length",
fontName='Helvetica-Bold', fontSize=9, fillColor=ORANGE))
mid_y = 65
# Resting muscle
rx0 = 30
ext_d.add(Rect(rx0, mid_y-8, 100, 16, fillColor=colors.HexColor("#fed7aa"), strokeColor=ORANGE, rx=5))
ext_d.add(String(rx0+20, mid_y-4, "REST (100%)", fontName='Helvetica', fontSize=7.5, fillColor=ORANGE))
# Arrow
ext_d.add(Line(rx0+115, mid_y, rx0+155, mid_y, strokeColor=NAVY, strokeWidth=1.5))
ext_d.add(String(rx0+118, mid_y+4, "Stretch", fontName='Helvetica', fontSize=7, fillColor=NAVY))
# Stretched muscle
ext_d.add(Rect(rx0+160, mid_y-8, 155, 16, fillColor=colors.HexColor("#fdba74"), strokeColor=ORANGE, rx=5))
ext_d.add(String(rx0+195, mid_y-4, "STRETCHED (>100%)", fontName='Helvetica', fontSize=7.5, fillColor=ORANGE))
# Labels
ext_d.add(String(rx0+20, mid_y-22, "Resting Length", fontName='Helvetica', fontSize=7, fillColor=GRAY))
ext_d.add(String(rx0+195, mid_y-22, "No damage within physiological limits",
fontName='Helvetica', fontSize=7, fillColor=GRAY))
# Connective tissue label
ext_d.add(String(10, 15, "Limited by: Titin protein | Connective tissue (epimysium, perimysium, endomysium)",
fontName='Helvetica-Oblique', fontSize=7.5, fillColor=colors.HexColor("#9a3412")))
elems.append(ext_d)
elems.append(Paragraph("<i>Fig 8: Extensibility - Muscle Stretch Without Damage</i>", S['caption']))
elems.append(Spacer(1, 0.3*cm))
# --- 5. Elasticity ---
elems.append(subsection_bar("5. ELASTICITY", bg=PURPLE))
elems.append(Spacer(1, 0.1*cm))
elems.append(Paragraph(
"Elasticity is the ability of the muscle to return to its original resting length after "
"being stretched or after contraction. It is a passive property dependent on the elastic "
"components within the muscle fiber.",
S['body']))
elems.append(bullet("Due to <b>titin</b> (connectin) - the giant elastic protein spanning the sarcomere"))
elems.append(bullet("Also contributed by surrounding connective tissue (endomysium, perimysium)"))
elems.append(bullet("Helps maintain <b>muscle tone</b> and passive tension at rest"))
elems.append(bullet("Prevents overstretching injury by generating passive restoring force"))
elems.append(bullet("In physiotherapy: elastic recoil assists return to neutral position after stretch"))
elems.append(Spacer(1, 0.2*cm))
# Elasticity diagram
elas_d = Drawing(W-3.6*cm, 120)
elas_d.add(Rect(0, 0, W-3.6*cm, 120, fillColor=LPURPLE, strokeColor=PURPLE, strokeWidth=0.5, rx=4))
elas_d.add(String(10, 105, "Elasticity - Recoil to Original Length After Stretch",
fontName='Helvetica-Bold', fontSize=9, fillColor=PURPLE))
mid_y2 = 65
rx1 = 20
# States: rest → stretched → recoil
for i, (label, w, clr) in enumerate([
("REST", 90, colors.HexColor("#c4b5fd")),
("STRETCHED", 155, colors.HexColor("#a78bfa")),
("RECOIL (back)", 90, colors.HexColor("#7c3aed")),
]):
x = rx1 + i*160
elas_d.add(Rect(x, mid_y2-8, w, 16, fillColor=clr, strokeColor=PURPLE, rx=5))
elas_d.add(String(x+5, mid_y2-4, label, fontName='Helvetica-Bold', fontSize=7, fillColor=WHITE))
if i < 2:
arw = "→" if i == 0 else "→"
lbl = "stretch" if i == 0 else "release"
ax = x + w + 5
elas_d.add(Line(ax, mid_y2, ax+22, mid_y2, strokeColor=GRAY, strokeWidth=1.3))
elas_d.add(String(ax+1, mid_y2+4, lbl, fontName='Helvetica', fontSize=7, fillColor=GRAY))
elas_d.add(String(10, 15, "Structural basis: Titin protein (sarcomere) + Connective tissue sheaths",
fontName='Helvetica-Oblique', fontSize=7.5, fillColor=colors.HexColor("#5b21b6")))
elems.append(elas_d)
elems.append(Paragraph("<i>Fig 9: Elasticity - Muscle Recoil After Stretch</i>", S['caption']))
return elems
# ─── Section 5: Comparison & Quick Revision ──────────────────────
def comparison_section():
elems = []
elems.append(PageBreak())
elems.append(section_header("COMPARISON & QUICK REVISION", bg=colors.HexColor("#0f766e")))
elems.append(Spacer(1, 0.25*cm))
# Skeletal vs Cardiac vs Smooth comparison
elems.append(subsection_bar("Comparison: Skeletal vs Cardiac vs Smooth Muscle Properties", bg=colors.HexColor("#0f766e")))
elems.append(Spacer(1, 0.1*cm))
comp_data = [
[Paragraph("Feature", S['table_hdr']),
Paragraph("Skeletal Muscle", S['table_hdr']),
Paragraph("Cardiac Muscle", S['table_hdr']),
Paragraph("Smooth Muscle", S['table_hdr'])],
[Paragraph("Control", S['table_body']),
Paragraph("Voluntary", S['table_body']),
Paragraph("Involuntary", S['table_body']),
Paragraph("Involuntary", S['table_body'])],
[Paragraph("RMP", S['table_body']),
Paragraph("-90 mV", S['table_body']),
Paragraph("-90 mV", S['table_body']),
Paragraph("-50 to -60 mV", S['table_body'])],
[Paragraph("AP duration", S['table_body']),
Paragraph("2-4 ms", S['table_body']),
Paragraph("200-300 ms", S['table_body']),
Paragraph("10-50 ms", S['table_body'])],
[Paragraph("All-or-None", S['table_body']),
Paragraph("Single fiber only", S['table_body']),
Paragraph("Whole muscle (syncytium)", S['table_body']),
Paragraph("Variable", S['table_body'])],
[Paragraph("Tetanus possible?", S['table_body']),
Paragraph("YES", S['table_body']),
Paragraph("NO (ARP = mechanical)", S['table_body']),
Paragraph("YES (in some)", S['table_body'])],
[Paragraph("Striations", S['table_body']),
Paragraph("Present", S['table_body']),
Paragraph("Present", S['table_body']),
Paragraph("Absent", S['table_body'])],
[Paragraph("Regeneration", S['table_body']),
Paragraph("Limited (satellite cells)", S['table_body']),
Paragraph("Poor", S['table_body']),
Paragraph("Good", S['table_body'])],
]
col_w7 = [(W-3.6*cm)*f for f in [0.22, 0.26, 0.26, 0.26]]
t7 = Table(comp_data, colWidths=col_w7)
t7.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), colors.HexColor("#0f766e")),
('BACKGROUND', (1,1), (1,-1), colors.HexColor("#e0f2fe")),
('BACKGROUND', (2,1), (2,-1), colors.HexColor("#fce7f3")),
('BACKGROUND', (3,1), (3,-1), colors.HexColor("#fef3c7")),
('BACKGROUND', (0,1), (0,-1), LGRAY),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#cbd5e1")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
]))
elems.append(t7)
elems.append(Paragraph("<i>Table 7: Comparison of Muscle Types</i>", S['caption']))
elems.append(Spacer(1, 0.3*cm))
# One-line rapid revision
elems.append(subsection_bar("RAPID REVISION - KEY NUMBERS TO REMEMBER", bg=RED))
elems.append(Spacer(1, 0.1*cm))
rev_data = [
[Paragraph("Parameter", S['table_hdr']), Paragraph("Value", S['table_hdr']),
Paragraph("Significance", S['table_hdr'])],
[Paragraph("RMP", S['table_body']), Paragraph("-90 mV", S['table_body']),
Paragraph("Resting state; K⁺ and Cl⁻ dominant", S['table_body'])],
[Paragraph("Threshold", S['table_body']), Paragraph("-50 mV", S['table_body']),
Paragraph("Level at which AP fires", S['table_body'])],
[Paragraph("AP amplitude", S['table_body']), Paragraph("120 mV", S['table_body']),
Paragraph("From RMP (-90) to peak (+30 mV)", S['table_body'])],
[Paragraph("AP duration", S['table_body']), Paragraph("2-4 ms", S['table_body']),
Paragraph("Very short - allows tetanization", S['table_body'])],
[Paragraph("ARP", S['table_body']), Paragraph("1-3 ms", S['table_body']),
Paragraph("Shorter than mechanical response - tetanus possible", S['table_body'])],
[Paragraph("Conduction velocity", S['table_body']), Paragraph("3-5 m/s", S['table_body']),
Paragraph("Along sarcolemma; similar to unmyelinated nerve", S['table_body'])],
]
col_w8 = [(W-3.6*cm)*f for f in [0.24, 0.16, 0.60]]
t8 = Table(rev_data, colWidths=col_w8)
t8.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), RED),
('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LRED]),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#fca5a5")),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 6),
]))
elems.append(t8)
elems.append(Paragraph("<i>Table 8: Key Numbers - Skeletal Muscle Properties</i>", S['caption']))
elems.append(Spacer(1, 0.3*cm))
# Exam questions box
elems.append(subsection_bar("PROBABLE EXAM QUESTIONS (KNRush BPT)", bg=colors.HexColor("#1d4ed8")))
elems.append(Spacer(1, 0.1*cm))
qs = [
"1. Enumerate the properties of skeletal muscle and describe excitability in detail with the action potential.",
"2. What is the All-or-None law? How does it apply to skeletal muscle vs cardiac muscle?",
"3. Describe the types of muscle contraction with examples.",
"4. Explain Starling's law as applied to skeletal muscle.",
"5. What is tetanus? Differentiate complete and incomplete tetanus.",
"6. Write a short note on: (a) Muscle twitch (b) Treppe (c) Extensibility (d) Elasticity",
"7. Compare skeletal, cardiac, and smooth muscle properties.",
"8. Why can skeletal muscle be tetanized but not cardiac muscle?",
]
for q in qs:
elems.append(Paragraph(q, S['bullet']))
return elems
# ─── Page numbering ───────────────────────────────────────────────
def add_page_num(canvas, doc):
canvas.saveState()
canvas.setFont('Helvetica', 7.5)
canvas.setFillColor(GRAY)
page_num = canvas.getPageNumber()
canvas.drawString(2*cm, 1.2*cm, "KNRush University BPT | Skeletal Muscle Properties | Sembulingam Ch.26")
canvas.drawRightString(W - 2*cm, 1.2*cm, f"Page {page_num}")
canvas.restoreState()
# ─── Build PDF ────────────────────────────────────────────────────
def build():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=1.8*cm, bottomMargin=2*cm,
title="Properties of Skeletal Muscle - BPT",
author="KNRush University",
subject="Sembulingam Medical Physiology"
)
story = []
story += cover_page()
story += electrical_section()
story += mechanical_section()
story += advanced_contractility()
story += ext_elastic_section()
story += comparison_section()
doc.build(story, onFirstPage=add_page_num, onLaterPages=add_page_num)
print(f"PDF saved: {OUTPUT}")
if __name__ == "__main__":
build()
Chemical changes during muscle contraction according to Sembulingm for knrush university exam for bpt
Create a detailed PDF summary on chemical changes during muscle contraction