increased capillary permeability causes edema
Q = K × [(Pc - Pi) - σ(πc - πi)]


| Condition | Key Features |
|---|---|
| Local inflammation | Trauma, infection (cellulitis), insect stings, burns. Localized, nonpitting. |
| Preeclampsia | Endothelial dysfunction + hypoalbuminemia. Affects lower extremities, may extend to lungs and brain. Resolves after delivery. |
| Angioedema | Localized, nondependent, nonpitting; typically face/hands/genitalia. Mediated by histamine (allergic) or bradykinin (ACE inhibitor-induced). |
| Idiopathic edema | Affects menstruating women in 3rd-4th decade; worsens upright through day; lower limbs, face, hands. Not cycle-related. |
| Systemic capillary leak syndrome | Rare, life-threatening. Episodes of rapid generalized edema + hypovolemic shock + hemoconcentration (elevated hematocrit) + hypoalbuminemia. Most have monoclonal gammopathy. Pathogenesis unknown. |
give case scenario for edema for better understanding
Damaged left ventricle (post-MI) → poor forward output → blood backs up → left atrial pressure rises → pulmonary venous pressure rises → fluid pushed into lung interstitium (pulmonary edema) → right heart also stressed → systemic venous pressure rises → elevated hydrostatic pressure in peripheral capillaries → fluid forced into interstitial tissues of legs → bilateral pitting edema
Damaged glomerular filtration barrier → massive protein leak into urine → serum albumin falls dramatically → plasma oncotic pressure drops → the "suction force" that normally pulls fluid back into capillaries is lost → fluid seeps into interstitial tissues → generalized edema → periorbital in children (loose periorbital tissue fills first), then ankles and abdomen
Cirrhotic liver → poor albumin synthesis → low oncotic pressure (fails to retain fluid) PLUS → fibrotic liver → obstructs portal blood flow → portal hypertension (increased hydrostatic pressure in abdominal veins) → these two forces combine → fluid pours into the peritoneal cavity (ascites) and leg tissues (edema)
ACE inhibitor blocks ACE enzyme → bradykinin cannot be degraded (accumulates) → bradykinin binds B2 receptors on endothelial cells → endothelial cells contract, opening interendothelial gaps → capillary permeability increases in the face/tongue/throat → protein-rich fluid (exudate) floods into the submucosal tissue → rapid localized non-pitting swelling (angioedema)
Bacteria enter via skin wound → tissue infection → immune cells release histamine, bradykinin, prostaglandins, leukotrienes → endothelial cells contract → interendothelial gaps open → massive increase in local capillary permeability → protein-rich exudate floods the interstitium → localized inflammatory edema (the classic signs: rubor, calor, tumor, dolor = redness, heat, swelling, pain)
Surgery + radiotherapy destroys/obstructs lymphatic channels → lymphatic fluid cannot drain from tissues → protein-rich interstitial fluid accumulates (lymph is normally protein-rich) → proteins draw in more water → progressive tissue swelling → over time, chronic protein accumulation triggers fibroblast activation → tissue fibrosis and skin thickening (irreversible if untreated)
| Feature | Cardiac (↑Hydrostatic) | Nephrotic/Cirrhosis (↓Oncotic) | Inflammatory (↑Permeability) | Lymphatic |
|---|---|---|---|---|
| Distribution | Bilateral, dependent | Bilateral, generalized | Localized (or generalized) | Unilateral or regional |
| Pitting? | Yes | Yes | Variable (often no) | No |
| JVP | HIGH | Low/normal | Normal | Normal |
| Fluid type | Transudate (low protein) | Transudate (low protein) | Exudate (high protein) | High protein lymph |
| Key lab | ↑BNP, ↓EF | ↓Albumin, ↑Proteinuria | ↑CRP, ↑WBC | - |
| Response to diuretics | Good | Moderate | Poor | None |
| Classic cause | Heart failure | Nephrotic syndrome | Cellulitis, angioedema | Post-surgical, filariasis |
"Edema = fluid trapped in the interstitium because the balance of Starling forces is disrupted." Either too much pressure pushing fluid out (hydrostatic), too little force pulling it back in (oncotic), the wall becomes leaky (permeability), or the drainage system is blocked (lymphatics).
Create a diagnostic flowchart for approaching edema
mkdir -p /tmp/workspace/edema-flowchart && ls /tmp/workspace/
~/edema-flowchart/build_flowchart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
fig, ax = plt.subplots(figsize=(22, 28))
ax.set_xlim(0, 22)
ax.set_ylim(0, 28)
ax.axis('off')
fig.patch.set_facecolor('#F8F9FA')
# ── Color palette ──────────────────────────────────────────────
C_TITLE = '#1A237E' # deep navy
C_START = '#1565C0' # dark blue – start box
C_Q = '#0288D1' # mid blue – question diamond
C_HYDRO = '#1976D2' # blue – hydrostatic branch
C_ONCO = '#7B1FA2' # purple – oncotic branch
C_PERM = '#C62828' # red – permeability branch
C_LYMPH = '#2E7D32' # green – lymphatic branch
C_DX = '#E65100' # orange – diagnosis boxes
C_KEY = '#37474F' # slate – key points box
C_ARROW = '#455A64'
WHITE = '#FFFFFF'
LIGHT_GREY= '#ECEFF1'
def draw_box(ax, x, y, w, h, text, facecolor, textcolor=WHITE,
fontsize=9, bold=False, radius=0.3, alpha=1.0, valign='center'):
box = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=f"round,pad=0.05,rounding_size={radius}",
facecolor=facecolor, edgecolor='white',
linewidth=1.5, zorder=3, alpha=alpha)
ax.add_patch(box)
weight = 'bold' if bold else 'normal'
ax.text(x, y, text, ha='center', va=valign, fontsize=fontsize,
color=textcolor, weight=weight, zorder=4,
wrap=True, multialignment='center',
transform=ax.transData)
def draw_diamond(ax, x, y, w, h, text, facecolor, textcolor=WHITE, fontsize=9):
dx, dy = w/2, h/2
diamond = plt.Polygon([[x, y+dy],[x+dx, y],[x, y-dy],[x-dx, y]],
closed=True, facecolor=facecolor,
edgecolor='white', linewidth=1.5, zorder=3)
ax.add_patch(diamond)
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color=textcolor, weight='bold', zorder=4, multialignment='center')
def arrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.8):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color,
lw=lw, connectionstyle='arc3,rad=0.0'),
zorder=2)
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.15, my, label, fontsize=8, color=color,
weight='bold', va='center', zorder=5)
def darrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.8, rad=0.0):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=lw,
connectionstyle=f'arc3,rad={rad}'),
zorder=2)
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx, my+0.18, label, fontsize=8, color=color,
weight='bold', ha='center', zorder=5)
# ═══════════════════════════════════════════════════════════════
# TITLE
# ═══════════════════════════════════════════════════════════════
ax.text(11, 27.4, 'DIAGNOSTIC APPROACH TO EDEMA', ha='center', va='center',
fontsize=17, color=WHITE, weight='bold', zorder=5)
title_box = FancyBboxPatch((1.5, 26.9), 19, 1.0,
boxstyle="round,pad=0.1,rounding_size=0.4",
facecolor=C_TITLE, edgecolor='none', zorder=3)
ax.add_patch(title_box)
ax.text(11, 27.4, 'DIAGNOSTIC APPROACH TO EDEMA', ha='center', va='center',
fontsize=17, color=WHITE, weight='bold', zorder=5)
# ═══════════════════════════════════════════════════════════════
# STEP 1 – START
# ═══════════════════════════════════════════════════════════════
draw_box(ax, 11, 26.0, 7, 0.65,
'Patient presents with EDEMA\n(excess fluid in interstitial space)',
C_START, fontsize=10, bold=True)
arrow(ax, 11, 25.67, 11, 25.1)
# ═══════════════════════════════════════════════════════════════
# STEP 2 – Q1: Unilateral or Bilateral?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 24.55, 7.5, 0.95,
'STEP 1: Unilateral or Bilateral?', C_Q, fontsize=10)
# ── UNILATERAL → LEFT ──────────────────────────────────────────
arrow(ax, 7.25, 24.55, 4.8, 24.55, label='Unilateral', color=C_HYDRO)
draw_box(ax, 3.2, 24.55, 3.0, 0.65,
'UNILATERAL EDEMA\n(Local cause)', C_HYDRO, fontsize=9, bold=True)
arrow(ax, 3.2, 24.22, 3.2, 23.55)
draw_box(ax, 3.2, 23.2, 3.2, 0.65,
'Pitting? Pain? Erythema?\nWarmth?', '#0D47A1', fontsize=8.5)
# Sub-branches from unilateral
arrow(ax, 1.6, 23.2, 1.1, 22.45)
draw_box(ax, 1.1, 22.1, 2.0, 0.65,
'DVT\n(+Doppler US)', C_DX, fontsize=8.5, bold=True)
arrow(ax, 3.2, 22.87, 3.2, 22.45)
draw_box(ax, 3.2, 22.1, 2.0, 0.65,
'Cellulitis /\nInflammation', C_PERM, fontsize=8.5, bold=True)
arrow(ax, 4.8, 23.2, 5.3, 22.45)
draw_box(ax, 5.3, 22.1, 2.0, 0.65,
'Lymphedema /\nVenous Insuff.', C_LYMPH, fontsize=8.5, bold=True)
# ── BILATERAL → DOWN ───────────────────────────────────────────
arrow(ax, 11, 24.08, 11, 23.45, label='Bilateral', color=C_Q)
# ═══════════════════════════════════════════════════════════════
# STEP 3 – Q2: Pitting?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 22.95, 6.5, 0.9,
'STEP 2: Pitting or Non-Pitting?', C_Q, fontsize=10)
# ── NON-PITTING → RIGHT ────────────────────────────────────────
arrow(ax, 14.25, 22.95, 17.5, 22.95, label='Non-pitting', color=C_LYMPH)
draw_box(ax, 19.2, 22.95, 3.2, 0.65,
'NON-PITTING EDEMA', C_LYMPH, fontsize=9, bold=True)
arrow(ax, 19.2, 22.62, 19.2, 22.05)
draw_box(ax, 19.2, 21.7, 3.2, 0.65,
'Lymphedema\n(post-surgical, filariasis)', C_LYMPH, fontsize=8.5, bold=True)
arrow(ax, 19.2, 21.37, 19.2, 20.8)
draw_box(ax, 19.2, 20.45, 3.2, 0.65,
'Myxedema\n(hypothyroidism)', C_LYMPH, fontsize=8.5, bold=True)
arrow(ax, 19.2, 20.12, 19.2, 19.55)
draw_box(ax, 19.2, 19.2, 3.2, 0.65,
'Angioedema\n(face, lips, tongue)', C_PERM, fontsize=8.5, bold=True)
# ── PITTING → DOWN ─────────────────────────────────────────────
arrow(ax, 11, 22.5, 11, 21.85, label='Pitting', color=C_Q)
# ═══════════════════════════════════════════════════════════════
# STEP 4 – Q3: JVP?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 21.35, 7.0, 0.9,
'STEP 3: Jugular Venous Pressure (JVP)?', C_Q, fontsize=10)
# ── HIGH JVP → LEFT ────────────────────────────────────────────
arrow(ax, 7.5, 21.35, 5.0, 21.35, label='High', color=C_HYDRO)
draw_box(ax, 3.4, 21.35, 3.0, 0.65,
'↑ Hydrostatic Pressure\n(Venous congestion)', C_HYDRO, fontsize=9, bold=True)
arrow(ax, 3.4, 21.02, 3.4, 20.4)
draw_box(ax, 3.4, 20.05, 3.2, 0.65,
'Check: ECG, Echo,\nBNP, Chest X-ray', '#0D47A1', fontsize=8.5)
arrow(ax, 3.4, 19.72, 3.4, 19.15)
draw_box(ax, 1.9, 18.8, 1.9, 0.6,
'Heart Failure\n(↓EF, S3, crackles)', C_DX, fontsize=8, bold=True)
arrow(ax, 3.4, 19.72, 3.4, 19.15)
draw_box(ax, 3.9, 18.8, 1.9, 0.6,
'Renal Failure\n(↑Cr, ↓GFR)', C_DX, fontsize=8, bold=True)
draw_box(ax, 5.6, 18.8, 1.7, 0.6,
'Constrictive\nPericarditis', C_DX, fontsize=8, bold=True)
# ── LOW/NORMAL JVP → DOWN ──────────────────────────────────────
arrow(ax, 11, 20.9, 11, 20.3, label='Low/Normal', color=C_Q)
# ═══════════════════════════════════════════════════════════════
# STEP 5 – Q4: Albumin / Proteinuria
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 19.75, 8.0, 0.95,
'STEP 4: Check Serum Albumin + Urine Protein', C_Q, fontsize=10)
# ── LOW ALBUMIN → LEFT ─────────────────────────────────────────
arrow(ax, 7.0, 19.75, 5.2, 19.75, label='↓Albumin', color=C_ONCO)
draw_box(ax, 3.5, 19.75, 3.0, 0.65,
'↓ Oncotic Pressure\n(Hypoalbuminemia)', C_ONCO, fontsize=9, bold=True)
arrow(ax, 3.5, 19.42, 3.5, 18.75)
draw_box(ax, 3.5, 18.45, 3.2, 0.55,
'Proteinuria present?', '#4A148C', fontsize=8.5)
arrow(ax, 2.0, 18.45, 1.1, 17.9, label='YES', color=C_ONCO)
draw_box(ax, 1.1, 17.6, 1.9, 0.55,
'Nephrotic\nSyndrome', C_DX, fontsize=8, bold=True)
arrow(ax, 3.5, 18.17, 3.5, 17.6)
draw_box(ax, 3.5, 17.3, 1.9, 0.55,
'Liver Disease /\nMalnutrition', C_DX, fontsize=8, bold=True)
arrow(ax, 5.0, 18.45, 5.9, 17.9, label='No proteinuria', color=C_ONCO)
draw_box(ax, 5.9, 17.6, 2.2, 0.55,
'Protein-losing\nEnteropathy', C_DX, fontsize=8, bold=True)
# ── NORMAL ALBUMIN → DOWN ──────────────────────────────────────
arrow(ax, 11, 19.27, 11, 18.65, label='Normal albumin', color=C_Q)
# ═══════════════════════════════════════════════════════════════
# STEP 6 – Q5: Inflammatory signs?
# ═══════════════════════════════════════════════════════════════
draw_diamond(ax, 11, 18.15, 8.5, 0.9,
'STEP 5: Inflammatory signs?\n(Redness, Heat, Pain, Fever, Trigger?)', C_Q, fontsize=9.5)
# ── YES → RIGHT ────────────────────────────────────────────────
arrow(ax, 15.25, 18.15, 16.8, 18.15, label='YES', color=C_PERM)
draw_box(ax, 18.3, 18.15, 2.9, 0.65,
'↑ Capillary Permeability', C_PERM, fontsize=9, bold=True)
arrow(ax, 18.3, 17.82, 18.3, 17.25)
draw_box(ax, 18.3, 16.9, 3.0, 0.65,
'Local inflammation /\nCellulitis / Burn', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 16.57, 18.3, 16.0)
draw_box(ax, 18.3, 15.65, 3.0, 0.65,
'Angioedema\n(ACE-i / Allergy)', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 15.32, 18.3, 14.75)
draw_box(ax, 18.3, 14.4, 3.0, 0.65,
'Preeclampsia\n(Pregnant + HTN + Protein)', C_DX, fontsize=8.5, bold=True)
arrow(ax, 18.3, 14.07, 18.3, 13.5)
draw_box(ax, 18.3, 13.15, 3.0, 0.65,
'Capillary Leak Syndrome\n(Hemoconcentration)', C_DX, fontsize=8.5, bold=True)
# ── NO → DOWN ──────────────────────────────────────────────────
arrow(ax, 11, 17.7, 11, 17.1, label='NO', color=C_Q)
# ═══════════════════════════════════════════════════════════════
# STEP 7 – Consider other/medication causes
# ═══════════════════════════════════════════════════════════════
draw_box(ax, 11, 16.7, 8.0, 0.65,
'STEP 6: Review Medications & Special Contexts', '#455A64',
fontsize=9.5, bold=True)
arrow(ax, 11, 16.37, 11, 15.77)
# Three sub-boxes
draw_box(ax, 7.8, 15.45, 3.5, 0.55,
'CCBs, Steroids,\nNSAIDs, Thiazolidinediones', '#546E7A', fontsize=8.5)
draw_box(ax, 11.8, 15.45, 3.0, 0.55,
'Pregnancy\n(physiologic)', '#546E7A', fontsize=8.5)
draw_box(ax, 15.2, 15.45, 2.5, 0.55,
'Idiopathic\n(women 30–40s)', '#546E7A', fontsize=8.5)
# Horizontal connector at y=15.77
ax.plot([7.8, 15.2], [15.77, 15.77], color=C_ARROW, lw=1.5, zorder=2)
arrow(ax, 7.8, 15.77, 7.8, 15.72)
arrow(ax, 11.8, 15.77, 11.8, 15.72)
arrow(ax, 15.2, 15.77, 15.2, 15.72)
# ═══════════════════════════════════════════════════════════════
# KEY POINTS BOX
# ═══════════════════════════════════════════════════════════════
key_box = FancyBboxPatch((0.8, 12.3), 20.4, 2.7,
boxstyle="round,pad=0.1,rounding_size=0.35",
facecolor='#E3F2FD', edgecolor=C_TITLE,
linewidth=2.0, zorder=3)
ax.add_patch(key_box)
ax.text(11, 14.75, 'KEY DIFFERENTIATING FEATURES AT A GLANCE',
ha='center', va='center', fontsize=10, color=C_TITLE,
weight='bold', zorder=5)
# Table header
headers = ['Feature', '↑ Hydrostatic', '↓ Oncotic', '↑ Permeability', 'Lymphatic']
cols = [2.2, 5.5, 8.8, 12.1, 15.4]
hdr_y = 14.3
for i, (h, x) in enumerate(zip(headers, cols)):
fc = C_TITLE if i == 0 else [C_HYDRO, C_ONCO, C_PERM, C_LYMPH][i-1] if i > 0 else '#37474F'
ax.text(x, hdr_y, h, ha='center', va='center',
fontsize=8.5, color=fc, weight='bold', zorder=5)
rows = [
('Distribution', 'Bilateral dependent', 'Generalized', 'Local or general', 'Unilateral/regional'),
('Pitting', 'Yes', 'Yes', 'Variable (often No)', 'No'),
('JVP', 'HIGH ↑', 'Low/Normal', 'Normal', 'Normal'),
('Fluid type', 'Transudate (low prot)', 'Transudate', 'Exudate (high prot)', 'High-protein lymph'),
('Key lab', '↑BNP, ↓EF', '↓Albumin, ↑Prot-uria', '↑CRP, ↑WBC', 'Clinical / US'),
]
row_colors = ['#EEF2FF', '#F3E5F5', '#FFEBEE', '#E8F5E9']
for ri, row in enumerate(rows):
ry = hdr_y - 0.34*(ri+1)
for ci, (val, x) in enumerate(zip(row, cols)):
color = ['#263238', C_HYDRO, C_ONCO, C_PERM, C_LYMPH][ci]
ax.text(x, ry, val, ha='center', va='center',
fontsize=7.8, color=color, zorder=5)
# ═══════════════════════════════════════════════════════════════
# LEGEND
# ═══════════════════════════════════════════════════════════════
legend_items = [
(C_Q, 'Decision step'),
(C_HYDRO,'↑ Hydrostatic pressure'),
(C_ONCO, '↓ Oncotic pressure'),
(C_PERM, '↑ Capillary permeability'),
(C_LYMPH,'Lymphatic obstruction'),
(C_DX, 'Diagnosis'),
]
lx, ly = 1.0, 12.0
ax.text(lx, ly, 'LEGEND:', fontsize=8.5, color=C_KEY, weight='bold')
for i, (col, label) in enumerate(legend_items):
patch = mpatches.Patch(color=col, label=label)
rx = lx + i*3.5
rect = FancyBboxPatch((rx, ly-0.45), 0.55, 0.32,
boxstyle="round,pad=0.03",
facecolor=col, edgecolor='none', zorder=4)
ax.add_patch(rect)
ax.text(rx + 0.65, ly - 0.29, label, fontsize=7.8, color=C_KEY, va='center', zorder=5)
# ═══════════════════════════════════════════════════════════════
# SOURCE NOTE
# ═══════════════════════════════════════════════════════════════
ax.text(11, 11.6,
'Sources: Frameworks for Internal Medicine | Robbins & Kumar Basic Pathology | '
'Morgan & Mikhail\'s Clinical Anesthesiology',
ha='center', va='center', fontsize=7.5, color='#78909C', style='italic', zorder=5)
plt.tight_layout(pad=0.3)
plt.savefig('/tmp/workspace/edema-flowchart/edema_diagnostic_flowchart.png',
dpi=180, bbox_inches='tight', facecolor=fig.get_facecolor())
print("Saved successfully.")
cd /tmp/workspace/edema-flowchart && python build_flowchart.py 2>&1
edema diagnostic flowchart
Image · PNG
| Feature | ↑ Hydrostatic | ↓ Oncotic | ↑ Permeability | Lymphatic |
|---|---|---|---|---|
| Distribution | Bilateral dependent | Generalized | Local or general | Unilateral/regional |
| Pitting | Yes | Yes | Variable (often No) | No |
| JVP | HIGH ↑ | Low/Normal | Normal | Normal |
| Fluid type | Transudate | Transudate | Exudate | High-protein lymph |
| Key lab | ↑BNP, ↓EF | ↓Albumin, ↑Proteinuria | ↑CRP, ↑WBC | Clinical/US |
make a presentation on edema etiology/pathophysiology
ls /tmp/workspace/edema-flowchart/
~/edema-flowchart/build_pptx.py
"""
Edema Etiology & Pathophysiology — PowerPoint Presentation
Built with python-pptx | 16 slides
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE, MSO_CONNECTOR
from pptx.oxml.ns import qn
from lxml import etree
from io import BytesIO
import copy
# ─────────────────────────────────────────────
# PALETTE
# ─────────────────────────────────────────────
NAVY = RGBColor(0x0D, 0x2B, 0x5E)
BLUE = RGBColor(0x15, 0x65, 0xC0)
LIGHTBLUE = RGBColor(0xBB, 0xDE, 0xFB)
PURPLE = RGBColor(0x6A, 0x1B, 0x9A)
LPURPLE = RGBColor(0xE1, 0xBE, 0xE7)
RED = RGBColor(0xB7, 0x1C, 0x1C)
LRED = RGBColor(0xFF, 0xCD, 0xD2)
GREEN = RGBColor(0x1B, 0x5E, 0x20)
LGREEN = RGBColor(0xC8, 0xE6, 0xC9)
ORANGE = RGBColor(0xE6, 0x51, 0x00)
LORANGE = RGBColor(0xFF, 0xE0, 0xB2)
TEAL = RGBColor(0x00, 0x69, 0x64)
LTEAL = RGBColor(0xB2, 0xDF, 0xDB)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
OFFWHITE = RGBColor(0xF5, 0xF7, 0xFA)
DARKGREY = RGBColor(0x37, 0x47, 0x4F)
MIDGREY = RGBColor(0x78, 0x90, 0x9C)
LGREY = RGBColor(0xEC, 0xEF, 0xF1)
W = 13.333
H = 7.5
prs = Presentation()
prs.slide_width = Inches(W)
prs.slide_height = Inches(H)
blank_layout = prs.slide_layouts[6] # blank
content_layout = prs.slide_layouts[1] # title + content
# ─────────────────────────────────────────────
# HELPERS
# ─────────────────────────────────────────────
def bg(slide, color):
bg_shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0), Inches(0), Inches(W), Inches(H))
bg_shape.fill.solid()
bg_shape.fill.fore_color.rgb = color
bg_shape.line.fill.background()
return bg_shape
def rect(slide, x, y, w, h, fill, line_color=None, line_w=None, radius=False):
shape_type = MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE
shp = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
shp.fill.solid()
shp.fill.fore_color.rgb = fill
if line_color:
shp.line.color.rgb = line_color
if line_w:
shp.line.width = Pt(line_w)
else:
shp.line.fill.background()
shp.shadow.inherit = False
return shp
def textbox(slide, x, y, w, h, text, size, color, bold=False,
align=PP_ALIGN.LEFT, italic=False, font='Calibri',
v_anchor=MSO_ANCHOR.TOP, wrap=True):
tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
tf = tb.text_frame
tf.word_wrap = wrap
tf.vertical_anchor = v_anchor
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
p = tf.paragraphs[0]
p.alignment = align
r = p.add_run()
r.text = text
r.font.name = font
r.font.size = Pt(size)
r.font.bold = bold
r.font.italic = italic
r.font.color.rgb = color
return tb
def add_paragraph(tf, text, size, color, bold=False, italic=False,
align=PP_ALIGN.LEFT, level=0, font='Calibri', space_before=None):
p = tf.add_paragraph()
p.alignment = align
p.level = level
if space_before:
p.space_before = Pt(space_before)
r = p.add_run()
r.text = text
r.font.name = font
r.font.size = Pt(size)
r.font.bold = bold
r.font.italic = italic
r.font.color.rgb = color
return p
def shape_text(shp, text, size, color, bold=False, align=PP_ALIGN.CENTER,
v_anchor=MSO_ANCHOR.MIDDLE, font='Calibri'):
tf = shp.text_frame
tf.word_wrap = True
tf.vertical_anchor = v_anchor
tf.margin_left = Pt(4)
tf.margin_right = Pt(4)
tf.margin_top = Pt(2)
tf.margin_bottom = Pt(2)
p = tf.paragraphs[0]
p.alignment = align
r = p.add_run()
r.text = text
r.font.name = font
r.font.size = Pt(size)
r.font.bold = bold
r.font.color.rgb = color
def accent_bar(slide, color, y=0.82, h=0.07):
bar = rect(slide, 0, y, W, h, color)
return bar
def slide_header(slide, title, subtitle=None,
title_color=WHITE, bar_color=NAVY, bg_color=OFFWHITE):
bg(slide, bg_color)
rect(slide, 0, 0, W, 1.15, bar_color)
accent_bar(slide, BLUE, y=1.15, h=0.06)
textbox(slide, 0.35, 0.12, 12.5, 0.75,
title, 26, WHITE, bold=True, align=PP_ALIGN.LEFT, font='Calibri')
if subtitle:
textbox(slide, 0.35, 0.82, 12.5, 0.36,
subtitle, 13, LIGHTBLUE, bold=False, align=PP_ALIGN.LEFT, font='Calibri')
def bullet_card(slide, x, y, w, h, header, bullets, hdr_color, hdr_bg,
bullet_size=11, line_spacing=1.2):
card = rect(slide, x, y, w, h, WHITE, line_color=hdr_bg, line_w=1.2, radius=True)
hdr = rect(slide, x, y, w, 0.42, hdr_bg, radius=True)
textbox(slide, x+0.12, y+0.04, w-0.2, 0.38,
header, 12, hdr_color, bold=True, font='Calibri')
tb = slide.shapes.add_textbox(
Inches(x+0.12), Inches(y+0.5), Inches(w-0.24), Inches(h-0.58))
tf = tb.text_frame
tf.word_wrap = True
first = True
for b in bullets:
if first:
p = tf.paragraphs[0]; first = False
else:
p = tf.add_paragraph()
p.alignment = PP_ALIGN.LEFT
r = p.add_run()
r.text = b
r.font.name = 'Calibri'
r.font.size = Pt(bullet_size)
r.font.color.rgb = DARKGREY
return card
def connector(slide, x1, y1, x2, y2, color=MIDGREY, lw=1.5):
ln = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT,
Inches(x1), Inches(y1), Inches(x2), Inches(y2))
ln.line.color.rgb = color
ln.line.width = Pt(lw)
def pill(slide, x, y, w, h, text, fill, text_color=WHITE, size=10.5, bold=True):
shp = rect(slide, x, y, w, h, fill, radius=True)
shape_text(shp, text, size, text_color, bold=bold)
return shp
def divider(slide, y, color=LGREY):
connector(slide, 0.35, y, W-0.35, y, color=color, lw=0.8)
# ═════════════════════════════════════════════════════════════════
# SLIDE 1 – TITLE
# ═════════════════════════════════════════════════════════════════
s1 = prs.slides.add_slide(blank_layout)
bg(s1, NAVY)
# Gradient overlay strip
rect(s1, 0, 0, 5.5, H, BLUE)
rect(s1, 0, 5.8, W, 1.7, RGBColor(0x0A, 0x1F, 0x4A))
# Decorative circles
for cx, cy, cr, alpha_color in [
(1.8, 1.8, 1.6, RGBColor(0x1A, 0x4A, 0x8A)),
(3.5, 5.5, 0.9, RGBColor(0x1A, 0x4A, 0x8A)),
(4.8, 2.8, 0.55, RGBColor(0x0D, 0x3A, 0x7A)),
]:
circ = s1.shapes.add_shape(MSO_SHAPE.OVAL,
Inches(cx-cr/2), Inches(cy-cr/2), Inches(cr), Inches(cr))
circ.fill.solid(); circ.fill.fore_color.rgb = alpha_color
circ.line.fill.background(); circ.shadow.inherit = False
# Title text
textbox(s1, 5.8, 1.4, 7.1, 1.1,
'EDEMA', 54, WHITE, bold=True, align=PP_ALIGN.LEFT, font='Calibri')
textbox(s1, 5.8, 2.45, 7.1, 0.7,
'Etiology & Pathophysiology', 26, LIGHTBLUE, bold=False,
align=PP_ALIGN.LEFT, font='Calibri')
accent_bar(s1, ORANGE, y=3.2, h=0.055)
textbox(s1, 5.8, 3.35, 7.0, 0.4,
'Understanding the Starling Forces, Mechanisms & Clinical Causes',
13, MIDGREY, align=PP_ALIGN.LEFT, font='Calibri')
# Bottom tags
for i, tag in enumerate(['Pathophysiology', 'Internal Medicine', 'Clinical Sciences']):
pill(s1, 5.8 + i*2.42, 5.95, 2.2, 0.42, tag, BLUE, size=10)
textbox(s1, 5.8, 6.6, 7.0, 0.5,
'Sources: Frameworks for Internal Medicine | Robbins & Kumar | Morgan & Mikhail',
9, MIDGREY, align=PP_ALIGN.LEFT, font='Calibri', italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 2 – OVERVIEW / OUTLINE
# ═════════════════════════════════════════════════════════════════
s2 = prs.slides.add_slide(blank_layout)
slide_header(s2, 'Presentation Overview', 'What we will cover in this session')
topics = [
('01', 'Definition of Edema', BLUE),
('02', 'Normal Fluid Homeostasis', TEAL),
('03', 'Starling Equation Explained', PURPLE),
('04', 'Mechanism 1 — ↑ Hydrostatic Pressure', BLUE),
('05', 'Mechanism 2 — ↓ Oncotic Pressure', PURPLE),
('06', 'Mechanism 3 — ↑ Capillary Permeability', RED),
('07', 'Mechanism 4 — Lymphatic Obstruction', GREEN),
('08', 'Exudate vs. Transudate', ORANGE),
('09', 'Clinical Etiologies Summary', TEAL),
('10', 'Key Points & Take-Aways', NAVY),
]
cols = 2
rows_per_col = 5
for i, (num, text, color) in enumerate(topics):
col = i // rows_per_col
row = i % rows_per_col
x = 0.4 + col * 6.5
y = 1.45 + row * 1.0
pill(s2, x, y, 0.55, 0.45, num, color, size=11)
textbox(s2, x+0.68, y+0.03, 5.6, 0.42,
text, 13, DARKGREY, bold=False, font='Calibri')
if row < rows_per_col - 1 or col == 0:
divider(s2, y+0.54, color=LGREY)
# ═════════════════════════════════════════════════════════════════
# SLIDE 3 – DEFINITION
# ═════════════════════════════════════════════════════════════════
s3 = prs.slides.add_slide(blank_layout)
slide_header(s3, 'What is Edema?', 'Definition and basic concepts')
# Large definition box
def_box = rect(s3, 0.4, 1.38, 12.5, 1.45, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s3, 0.7, 1.42, 12.0, 1.38,
'"Edema is the abnormal accumulation of excess fluid within the '
'interstitial tissue spaces or body cavities, occurring when the '
'normal balance of Starling forces is disrupted."',
16, NAVY, bold=True, align=PP_ALIGN.CENTER, font='Calibri')
# Three sub-points
card_data = [
(BLUE, WHITE, 'Interstitial Edema',
['Fluid in tissue spaces', 'e.g. ankle swelling, pulmonary edema',
'Most clinically apparent form']),
(TEAL, WHITE, 'Intracavitary Fluid',
['Ascites (peritoneal cavity)', 'Pleural effusion (pleural space)',
'Pericardial effusion']),
(PURPLE, WHITE, 'Two Fluid Types',
['Transudate — low protein', 'Exudate — high protein, cells',
'Type guides the diagnosis']),
]
for i, (hc, tc, title, buls) in enumerate(card_data):
bullet_card(s3, 0.4 + i*4.32, 3.0, 4.0, 3.9,
title, tc, hc, buls, bullet_size=12)
textbox(s3, 0.4, 6.98, 12.5, 0.35,
'Edema itself is a sign, not a diagnosis — the underlying mechanism must always be identified.',
10, MIDGREY, italic=True, font='Calibri')
# ═════════════════════════════════════════════════════════════════
# SLIDE 4 – NORMAL FLUID HOMEOSTASIS
# ═════════════════════════════════════════════════════════════════
s4 = prs.slides.add_slide(blank_layout)
slide_header(s4, 'Normal Fluid Homeostasis', 'How the body keeps fluid in the right compartment')
# Capillary diagram (schematic)
# Arterial end
rect(s4, 0.4, 2.3, 3.2, 1.5, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s4, 0.5, 2.35, 3.0, 0.4, 'ARTERIAL END', 11, BLUE, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 0.5, 2.75, 3.0, 0.9,
'Hydrostatic P: ~32 mmHg\nOncotic P: ~25 mmHg\nNet: +7 mmHg OUT →',
11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')
# Capillary bed center
rect(s4, 3.8, 2.1, 5.7, 1.9, LGREY, line_color=MIDGREY, line_w=1, radius=True)
textbox(s4, 3.9, 2.2, 5.5, 0.4, 'CAPILLARY BED', 11, DARKGREY, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 3.9, 2.65, 5.5, 1.2,
'Fluid exchange governed by STARLING FORCES\n'
'Net outward flow (~small volume)\nis drained by lymphatics',
11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')
# Venous end
rect(s4, 9.7, 2.3, 3.2, 1.5, LGREEN, line_color=GREEN, line_w=1.5, radius=True)
textbox(s4, 9.8, 2.35, 3.0, 0.4, 'VENOUS END', 11, GREEN, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 9.8, 2.75, 3.0, 0.9,
'Hydrostatic P: ~12 mmHg\nOncotic P: ~25 mmHg\nNet: -13 mmHg ← IN',
11, DARKGREY, align=PP_ALIGN.CENTER, font='Calibri')
# Interstitium box
rect(s4, 3.8, 4.2, 5.7, 1.1, LORANGE, line_color=ORANGE, line_w=1.2, radius=True)
textbox(s4, 4.0, 4.28, 5.3, 0.45, 'INTERSTITIUM', 11, ORANGE, bold=True, align=PP_ALIGN.CENTER)
textbox(s4, 4.0, 4.68, 5.3, 0.55,
'Small amount of fluid + proteins → drained by lymphatics', 10.5, DARKGREY, align=PP_ALIGN.CENTER)
# Lymphatics
rect(s4, 4.6, 5.5, 4.1, 0.62, LTEAL, line_color=TEAL, line_w=1.2, radius=True)
textbox(s4, 4.7, 5.55, 3.9, 0.52, 'LYMPHATIC DRAINAGE → venous system', 11, TEAL, bold=True, align=PP_ALIGN.CENTER)
# Arrows
connector(s4, 3.6, 3.05, 4.0, 4.42, color=ORANGE, lw=1.8)
connector(s4, 9.5, 3.05, 9.3, 4.42, color=GREEN, lw=1.8)
connector(s4, 6.65, 5.3, 6.65, 5.5, color=TEAL, lw=1.8)
# Key insight box
rect(s4, 0.4, 5.55, 3.8, 1.65, NAVY, radius=True)
textbox(s4, 0.6, 5.65, 3.5, 1.5,
'In HEALTH:\n• Forces are nearly balanced\n• Net filtrate is minimal\n• Lymphatics clear excess',
11.5, WHITE, font='Calibri')
textbox(s4, 0.4, 7.15, 12.5, 0.28,
'Edema forms when net filtration EXCEEDS lymphatic drainage capacity.',
10, MIDGREY, italic=True, font='Calibri')
# ═════════════════════════════════════════════════════════════════
# SLIDE 5 – STARLING EQUATION
# ═════════════════════════════════════════════════════════════════
s5 = prs.slides.add_slide(blank_layout)
slide_header(s5, 'The Starling Equation', 'The mathematical basis of fluid movement across capillaries')
# Equation box
eq_box = rect(s5, 0.9, 1.38, 11.5, 1.1, NAVY, radius=True)
textbox(s5, 1.1, 1.42, 11.2, 1.0,
'Q = K × [ (Pc − Pi) − σ (πc − πi) ]',
26, WHITE, bold=True, align=PP_ALIGN.CENTER, font='Calibri')
# Variable explanations
vars_data = [
('Q', 'Net fluid flow across capillary wall\n(+ = out; − = reabsorption)', BLUE),
('K', 'Filtration coefficient\n(capillary surface area × hydraulic conductance)', TEAL),
('Pc − Pi', 'Hydrostatic pressure gradient\n(capillary minus interstitial)\nDrives fluid OUT', RED),
('σ', 'Reflection coefficient\n(0 = freely permeable; 1 = impermeable)\nMeasures permeability', ORANGE),
('πc − πi', 'Oncotic pressure gradient\n(capillary minus interstitial)\nDraws fluid IN', GREEN),
]
for i, (sym, desc, col) in enumerate(vars_data):
cx = 0.35 + i * 2.6
pill(s5, cx, 2.7, 0.9, 0.5, sym, col, size=13)
tb = s5.shapes.add_textbox(Inches(cx-0.15), Inches(3.28),
Inches(2.5), Inches(1.5))
tf = tb.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]
r = p.add_run(); r.text = desc
r.font.name = 'Calibri'; r.font.size = Pt(10); r.font.color.rgb = DARKGREY
# Divider line
connector(s5, 0.35, 4.85, W-0.35, 4.85, color=LGREY)
# Two ways to get edema
textbox(s5, 0.35, 5.0, 12.5, 0.38,
'Edema occurs when Q becomes persistently positive beyond lymphatic capacity, via:', 12.5, DARKGREY)
pill_data = [
(0.4, '↑ Hydrostatic gradient\n(Pc↑ or Pi↓)', BLUE),
(3.5, '↓ Oncotic gradient\n(πc↓ or πi↑)', PURPLE),
(6.6, '↑ Permeability\n(σ approaches 0)', RED),
(9.7, 'Lymphatic failure\n(drainage overwhelmed)', GREEN),
]
for x, txt, col in pill_data:
shp = rect(s5, x, 5.52, 2.8, 1.65, col, radius=True)
shape_text(shp, txt, 11.5, WHITE, bold=True)
textbox(s5, 0.35, 7.22, 12.5, 0.22,
'Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e; Frameworks for Internal Medicine',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 6 – MECHANISM 1: INCREASED HYDROSTATIC PRESSURE
# ═════════════════════════════════════════════════════════════════
s6 = prs.slides.add_slide(blank_layout)
slide_header(s6, 'Mechanism 1 — ↑ Hydrostatic Pressure',
'Increased capillary hydrostatic pressure pushes fluid out')
# Mechanism box
rect(s6, 0.4, 1.38, 8.2, 2.35, LIGHTBLUE, line_color=BLUE, line_w=1.5, radius=True)
textbox(s6, 0.6, 1.45, 7.8, 0.42,
'PATHOPHYSIOLOGY', 11, BLUE, bold=True)
textbox(s6, 0.6, 1.88, 7.8, 1.75,
'When venous or capillary pressure rises, the driving force for fluid '
'efflux (Pc − Pi) increases. This overwhelms the oncotic reabsorption force '
'and lymphatic drainage → net accumulation of fluid in the interstitium.\n\n'
'Systemic hypertension does NOT cause edema because precapillary '
'sphincters prevent arterial pressure from reaching the capillary bed.',
11.5, DARKGREY, wrap=True)
# Causes cards (right column)
causes = [
('Heart Failure', 'Venous congestion → ↑Pc\nBilateral, pitting, dependent\nHigh JVP, S3, ↑BNP', BLUE),
('Renal Failure', 'Na+ & H₂O retention → ↑volume\nSimilar to right heart failure\nTreated by dialysis', TEAL),
('Cirrhosis', 'Portal hypertension → ↑Pc\n+ Hypoalbuminemia (dual)\nAscites + leg edema', ORANGE),
('DVT / Venous Insuff.', 'Local venous obstruction → ↑Pc\nUnilateral, pitting, painful\nDoppler US confirms', PURPLE),
]
for i, (title, desc, col) in enumerate(causes):
cx = 8.85
cy = 1.38 + i * 1.53
shp = rect(s6, cx, cy, 4.1, 1.38, col, radius=True)
textbox(s6, cx+0.1, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
textbox(s6, cx+0.1, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)
# General features
rect(s6, 0.4, 3.9, 8.2, 2.8, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s6, 0.6, 3.98, 7.8, 0.38, 'GENERAL FEATURES OF HYDROSTATIC EDEMA', 11, BLUE, bold=True)
features = [
'✔ Bilateral (if systemic cause) or Unilateral (if local obstruction)',
'✔ Dependent — worsens on standing, shifts to sacrum when supine',
'✔ Pitting — finger pressure leaves a pit for several seconds',
'✔ Fluid is a TRANSUDATE (low protein) — no increase in permeability',
'✔ JVP is ELEVATED — key distinguishing feature from oncotic edema',
]
tb6 = s6.shapes.add_textbox(Inches(0.6), Inches(4.42), Inches(7.8), Inches(2.1))
tf6 = tb6.text_frame; tf6.word_wrap = True
first = True
for feat in features:
if first: p = tf6.paragraphs[0]; first = False
else: p = tf6.add_paragraph()
r = p.add_run(); r.text = feat
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
# ═════════════════════════════════════════════════════════════════
# SLIDE 7 – MECHANISM 2: DECREASED ONCOTIC PRESSURE
# ═════════════════════════════════════════════════════════════════
s7 = prs.slides.add_slide(blank_layout)
slide_header(s7, 'Mechanism 2 — ↓ Oncotic (Colloid Osmotic) Pressure',
'Loss of plasma proteins removes the "suction" force that retains fluid')
rect(s7, 0.4, 1.38, 8.2, 2.5, LPURPLE, line_color=PURPLE, line_w=1.5, radius=True)
textbox(s7, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, PURPLE, bold=True)
textbox(s7, 0.6, 1.88, 7.8, 1.85,
'Plasma albumin (made exclusively in the liver at ~15 g/day) generates '
'~25 mmHg of oncotic pressure. When albumin falls below 2 g/dL, '
'this reabsorptive force is lost. Fluid leaks out at ALL points '
'of the capillary bed.\n\n'
'Key: Both fluid AND proteins remain separated (no permeability change). '
'The fluid is protein-poor — a transudate.',
11.5, DARKGREY, wrap=True)
causes7 = [
('Nephrotic Syndrome', '≥3.5g/day proteinuria → ↓albumin\nPeriorbital + generalised\nHyperlipidaemia, lipiduria', PURPLE),
('Liver Disease', '↓Albumin synthesis (cirrhosis)\nAscites + leg oedema\nSpider naevi, jaundice', ORANGE),
('Malnutrition', 'Kwashiorkor — protein deficiency\nGeneralised oedema\n+ hair/skin changes', RED),
('Prot.-losing Enteropathy', 'GI protein loss\nNo proteinuria\nInflammatory bowel disease', TEAL),
]
for i, (title, desc, col) in enumerate(causes7):
cy = 1.38 + i * 1.53
shp = rect(s7, 8.85, cy, 4.1, 1.38, col, radius=True)
textbox(s7, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
textbox(s7, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)
rect(s7, 0.4, 4.05, 8.2, 2.65, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s7, 0.6, 4.13, 7.8, 0.38, 'GENERAL FEATURES OF ONCOTIC EDEMA', 11, PURPLE, bold=True)
feats7 = [
'✔ Bilateral, generalised — gravity-dependent but diffuse',
'✔ Pitting — fluid is mobile, no protein scaffold',
'✔ JVP is LOW/NORMAL — distinguish from cardiac oedema',
'✔ Fluid is a TRANSUDATE — low protein, low specific gravity',
'✔ Often accompanied by ascites and pleural effusions',
'✔ Serum albumin < 2 g/dL is diagnostic threshold',
]
tb7 = s7.shapes.add_textbox(Inches(0.6), Inches(4.55), Inches(7.8), Inches(2.0))
tf7 = tb7.text_frame; tf7.word_wrap = True
first = True
for feat in feats7:
if first: p = tf7.paragraphs[0]; first = False
else: p = tf7.add_paragraph()
r = p.add_run(); r.text = feat
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
# ═════════════════════════════════════════════════════════════════
# SLIDE 8 – MECHANISM 3: INCREASED CAPILLARY PERMEABILITY
# ═════════════════════════════════════════════════════════════════
s8 = prs.slides.add_slide(blank_layout)
slide_header(s8, 'Mechanism 3 — ↑ Capillary Permeability',
'Endothelial disruption allows both fluid and protein to escape')
rect(s8, 0.4, 1.38, 8.2, 2.8, LRED, line_color=RED, line_w=1.5, radius=True)
textbox(s8, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, RED, bold=True)
textbox(s8, 0.6, 1.88, 7.8, 2.2,
'Chemical mediators (histamine, bradykinin, leukotrienes, cytokines) cause '
'endothelial cell contraction, opening inter-endothelial gaps within '
'15-30 minutes. Both FLUID and PROTEINS escape into the interstitium.\n\n'
'Result: interstitial oncotic pressure RISES (because proteins accumulate '
'there), further drawing fluid out — a self-amplifying cycle.\n\n'
'The fluid formed is an EXUDATE (high protein, may contain cells).',
11.5, DARKGREY, wrap=True)
causes8 = [
('Local Inflammation', 'Trauma, cellulitis, burns, insect stings\nLocalized, non-pitting\nRubor, calor, dolor, tumor', RED),
('Angioedema', 'Histamine (allergy) or bradykinin (ACEi)\nFace/lips/tongue/larynx\nCan be life-threatening!', ORANGE),
('Preeclampsia', 'Endothelial dysfunction + hypoalbuminaemia\nLower limbs → pulmonary/cerebral\nResolves after delivery', PURPLE),
('Capillary Leak Synd.','Rare; rapid generalized oedema + shock\nHemoconcentration (↑Hct)\nMonoclonal gammopathy in most', BLUE),
]
for i, (title, desc, col) in enumerate(causes8):
cy = 1.38 + i * 1.53
shp = rect(s8, 8.85, cy, 4.1, 1.38, col, radius=True)
textbox(s8, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
textbox(s8, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)
rect(s8, 0.4, 4.35, 8.2, 2.35, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s8, 0.6, 4.43, 7.8, 0.38, 'GENERAL FEATURES OF PERMEABILITY EDEMA', 11, RED, bold=True)
feats8 = [
'✔ Can be localized or generalized depending on trigger',
'✔ Pitting OR non-pitting (proteins in fluid resist pitting)',
'✔ JVP is NORMAL (no venous congestion)',
'✔ Fluid is an EXUDATE — high protein, possible cellular debris',
'✔ Key mediators: histamine (fast, IgE-mediated) & bradykinin (ACEi)',
'✔ Direct endothelial injury (burns) causes sustained, immediate leakage',
]
tb8 = s8.shapes.add_textbox(Inches(0.6), Inches(4.85), Inches(7.8), Inches(1.75))
tf8 = tb8.text_frame; tf8.word_wrap = True
first = True
for feat in feats8:
if first: p = tf8.paragraphs[0]; first = False
else: p = tf8.add_paragraph()
r = p.add_run(); r.text = feat
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
# ═════════════════════════════════════════════════════════════════
# SLIDE 9 – MECHANISM 4: LYMPHATIC OBSTRUCTION
# ═════════════════════════════════════════════════════════════════
s9 = prs.slides.add_slide(blank_layout)
slide_header(s9, 'Mechanism 4 — Lymphatic Obstruction',
'Failure of lymphatic drainage leads to protein-rich fluid accumulation')
rect(s9, 0.4, 1.38, 8.2, 2.65, LGREEN, line_color=GREEN, line_w=1.5, radius=True)
textbox(s9, 0.6, 1.45, 7.8, 0.4, 'PATHOPHYSIOLOGY', 11, GREEN, bold=True)
textbox(s9, 0.6, 1.88, 7.8, 2.05,
'Under normal conditions, lymphatics drain the small amount of protein-rich '
'fluid that is filtered but not reabsorbed at the capillary bed. When '
'lymphatics are blocked or destroyed, this protein-rich fluid '
'accumulates in the interstitium.\n\n'
'Over time, stagnant proteins stimulate FIBROBLAST ACTIVATION → '
'tissue fibrosis and skin hardening. This is the reason lymphedema '
'is notoriously difficult to reverse once established.',
11.5, DARKGREY, wrap=True)
causes9 = [
('Filariasis', 'Wuchereria bancrofti (nematode)\nMost common worldwide cause\nChronic elephantiasis', GREEN),
('Post-Surgical', 'Axillary/inguinal node dissection\nBreast cancer, melanoma surgery\nUnilateral limb edema', TEAL),
('Malignant Infiltr.', 'Tumour invades lymph nodes\nMay be first sign of malignancy\nHard, non-tender nodes', RED),
('Myxedema', 'Hypothyroidism → GAG deposition\nNon-pitting (pretibial)\nTSH diagnostic; treated with T4', BLUE),
]
for i, (title, desc, col) in enumerate(causes9):
cy = 1.38 + i * 1.53
shp = rect(s9, 8.85, cy, 4.1, 1.38, col, radius=True)
textbox(s9, 8.95, cy+0.05, 3.8, 0.38, title, 11.5, WHITE, bold=True)
textbox(s9, 8.95, cy+0.44, 3.8, 0.9, desc, 9.8, WHITE)
rect(s9, 0.4, 4.2, 8.2, 2.5, LGREY, line_color=MIDGREY, line_w=0.8, radius=True)
textbox(s9, 0.6, 4.28, 7.8, 0.38, 'GENERAL FEATURES OF LYMPHATIC EDEMA', 11, GREEN, bold=True)
feats9 = [
'✔ NON-PITTING — fibrosis and protein scaffold prevent indentation',
'✔ Stemmer\'s sign positive (cannot pinch skin fold on dorsum of toe/finger)',
'✔ Unilateral or regional — does not become bilateral unless bilateral nodes affected',
'✔ Does NOT respond to diuretics (no excess hydrostatic pressure to offload)',
'✔ Fluid has HIGH protein content (unlike transudates)',
'✔ Treatment: compression garments, manual lymphatic drainage',
]
tb9 = s9.shapes.add_textbox(Inches(0.6), Inches(4.7), Inches(7.8), Inches(1.9))
tf9 = tb9.text_frame; tf9.word_wrap = True
first = True
for feat in feats9:
if first: p = tf9.paragraphs[0]; first = False
else: p = tf9.add_paragraph()
r = p.add_run(); r.text = feat
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
# ═════════════════════════════════════════════════════════════════
# SLIDE 10 – EXUDATE VS TRANSUDATE
# ═════════════════════════════════════════════════════════════════
s10 = prs.slides.add_slide(blank_layout)
slide_header(s10, 'Exudate vs. Transudate',
'Fluid characteristics help identify the underlying mechanism')
# Header row
rect(s10, 0.4, 1.38, 3.9, 0.52, MIDGREY, radius=True)
rect(s10, 4.42, 1.38, 4.2, 0.52, BLUE, radius=True)
rect(s10, 8.75, 1.38, 4.2, 0.52, RED, radius=True)
textbox(s10, 0.4, 1.42, 3.9, 0.45, 'CHARACTERISTIC', 12, WHITE, bold=True, align=PP_ALIGN.CENTER)
textbox(s10, 4.42, 1.42, 4.2, 0.45, 'TRANSUDATE', 14, WHITE, bold=True, align=PP_ALIGN.CENTER)
textbox(s10, 8.75, 1.42, 4.2, 0.45, 'EXUDATE', 14, WHITE, bold=True, align=PP_ALIGN.CENTER)
rows = [
('Protein content', '< 3 g/dL (low)', '> 3 g/dL (high)'),
('Specific gravity', '< 1.012', '> 1.020'),
('LDH', 'Low', 'High (> 2/3 serum)'),
('Cells', 'Few (mainly RBCs)', 'WBCs, debris, organisms'),
('Appearance', 'Clear, straw-coloured', 'Turbid, cloudy, bloody'),
('Pathophysiology', '↑Hydrostatic or ↓Oncotic', '↑Permeability or lymphatic'),
('Clinical causes', 'Heart failure, nephrotic,\ncirrhosis', 'Infection, inflammation,\nmalignancy, burns'),
('Response to Rx', 'Diuretics effective', 'Treat underlying cause'),
]
row_bg_a = RGBColor(0xF8, 0xF9, 0xFF)
row_bg_b = RGBColor(0xFF, 0xF3, 0xF3)
for ri, (char, trans, exud) in enumerate(rows):
ry = 2.02 + ri * 0.61
bg_color = row_bg_a if ri % 2 == 0 else OFFWHITE
rect(s10, 0.4, ry, 3.9, 0.58, bg_color)
rect(s10, 4.42, ry, 4.2, 0.58, LIGHTBLUE if ri % 2 == 0 else RGBColor(0xE3, 0xF2, 0xFD))
rect(s10, 8.75, ry, 4.2, 0.58, LRED if ri % 2 == 0 else RGBColor(0xFF, 0xEB, 0xEE))
textbox(s10, 0.5, ry+0.05, 3.7, 0.5, char, 10.5, DARKGREY, bold=True)
textbox(s10, 4.52, ry+0.05, 3.9, 0.5, trans, 10.5, NAVY)
textbox(s10, 8.85, ry+0.05, 3.9, 0.5, exud, 10.5, RED)
textbox(s10, 0.4, 7.18, 12.5, 0.25,
'Light\'s Criteria (for pleural fluid): exudate if protein ratio > 0.5 OR LDH ratio > 0.6 OR LDH > 2/3 upper normal.',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 11 – MEDIATORS OF INCREASED PERMEABILITY
# ═════════════════════════════════════════════════════════════════
s11 = prs.slides.add_slide(blank_layout)
slide_header(s11, 'Key Mediators of Vascular Permeability',
'Chemical signals that open inter-endothelial gaps')
mediators = [
('HISTAMINE', BLUE,
['Released from mast cells & basophils',
'Most important early mediator',
'IgE-mediated (allergic) release',
'Acts within 15–30 minutes',
'Short-lived effect',
'Target: H1 receptors on endothelium',
'Tx: antihistamines, epinephrine']),
('BRADYKININ', PURPLE,
['Kallikrein-kinin system',
'ACE inhibitors → bradykinin ↑↑↑',
'Slower onset than histamine',
'Angioedema: face, tongue, larynx',
'No urticaria (not histamine)',
'NOT treated with antihistamines',
'Tx: FFP, icatibant, C1-INH']),
('LEUKOTRIENES', RED,
['Arachidonic acid pathway',
'Released in inflammation & allergy',
'Increase vascular permeability',
'Also cause bronchoconstriction',
'Involved in asthma + anaphylaxis',
'Tx: montelukast (leukotriene blocker)',
'Slower but more sustained effect']),
('CYTOKINES\n(IL-1, TNF, VEGF)', ORANGE,
['Systemic inflammatory states',
'Sepsis, ARDS, burns',
'VEGF → angiogenesis + permeability',
'TNF-α → endothelial activation',
'Causes generalized edema',
'Capillary leak / "third spacing"',
'Tx: treat underlying cause']),
]
for i, (name, col, items) in enumerate(mediators):
cx = 0.4 + i * 3.22
shp = rect(s11, cx, 1.42, 3.0, 0.52, col, radius=True)
shape_text(shp, name, 12, WHITE, bold=True)
card = rect(s11, cx, 2.02, 3.0, 5.2, OFFWHITE, line_color=col, line_w=1.2, radius=True)
tb = s11.shapes.add_textbox(Inches(cx+0.12), Inches(2.1), Inches(2.76), Inches(5.0))
tf = tb.text_frame; tf.word_wrap = True
first = True
for item in items:
if first: p = tf.paragraphs[0]; first = False
else: p = tf.add_paragraph()
r = p.add_run(); r.text = '• ' + item
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
textbox(s11, 0.4, 7.22, 12.5, 0.22,
'Source: Robbins & Kumar Basic Pathology; Barash Clinical Anesthesia 9e',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 12 – PULMONARY EDEMA (special focus)
# ═════════════════════════════════════════════════════════════════
s12 = prs.slides.add_slide(blank_layout)
slide_header(s12, 'Special Focus — Pulmonary Edema',
'Excess fluid in the lung interstitium and alveoli')
# Two types side by side
rect(s12, 0.4, 1.38, 5.9, 5.75, LIGHTBLUE, line_color=BLUE, line_w=2.0, radius=True)
rect(s12, 6.9, 1.38, 5.9, 5.75, LRED, line_color=RED, line_w=2.0, radius=True)
pill(s12, 0.5, 1.42, 5.7, 0.55, 'TYPE 1 — Cardiogenic (Hydrostatic)', BLUE, size=12)
pill(s12, 7.0, 1.42, 5.7, 0.55, 'TYPE 2 — Non-Cardiogenic (Permeability)', RED, size=12)
card_items_L = [
('Mechanism', 'Left heart failure → ↑pulmonary capillary wedge pressure (PCWP > 18 mmHg) → fluid forced into alveoli'),
('Fluid type', 'Transudate — low protein'),
('Onset', 'Often gradual; may flash-onset in acute MI'),
('Signs', 'Orthopnea, PND, S3 gallop, crackles, frothy pink sputum'),
('CXR', 'Bilateral basal haziness, Kerley B lines, cardiomegaly'),
('Treatment', 'Diuretics (furosemide), oxygen, nitrates, treat underlying cause'),
]
card_items_R = [
('Mechanism', 'Microvascular injury (ARDS, sepsis, aspiration) → ↑permeability → protein-rich fluid floods alveoli'),
('Fluid type', 'Exudate — HIGH protein content'),
('Onset', 'Rapid (hours); PCWP normal (≤ 18 mmHg)'),
('Signs', 'Severe hypoxia (PaO₂/FiO₂ < 300), bilateral infiltrates, refractory to O₂'),
('CXR', 'Bilateral diffuse infiltrates without cardiomegaly'),
('Treatment', 'Treat underlying cause, lung-protective ventilation, prone positioning'),
]
for i, (label, val) in enumerate(card_items_L):
ty = 2.1 + i * 0.82
textbox(s12, 0.6, ty, 1.5, 0.38, label+':', 10, BLUE, bold=True)
textbox(s12, 2.1, ty, 3.9, 0.75, val, 10, DARKGREY, wrap=True)
for i, (label, val) in enumerate(card_items_R):
ty = 2.1 + i * 0.82
textbox(s12, 7.1, ty, 1.5, 0.38, label+':', 10, RED, bold=True)
textbox(s12, 8.6, ty, 3.9, 0.75, val, 10, DARKGREY, wrap=True)
textbox(s12, 0.4, 7.2, 12.5, 0.25,
'Key distinguishing test: Protein content of edema fluid — low = cardiogenic; high = permeability. '
'Source: Morgan & Mikhail Clinical Anesthesiology 7e',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 13 – CLINICAL ETIOLOGIES SUMMARY TABLE
# ═════════════════════════════════════════════════════════════════
s13 = prs.slides.add_slide(blank_layout)
slide_header(s13, 'Clinical Etiologies — Summary Table',
'Causes organised by mechanism with key clinical features')
# Table header
cols_w = [2.5, 1.6, 1.4, 1.4, 1.5, 1.5, 2.8]
col_x = [0.35]
for cw in cols_w[:-1]:
col_x.append(col_x[-1] + cw + 0.04)
headers = ['Condition', 'Mechanism', 'Laterality', 'Pitting?', 'JVP', 'Fluid', 'Key Clue']
hdr_y = 1.38
for ci, (hdr, cx, cw) in enumerate(zip(headers, col_x, cols_w)):
shp = rect(s13, cx, hdr_y, cw, 0.48, NAVY, radius=False)
textbox(s13, cx+0.06, hdr_y+0.06, cw-0.1, 0.38, hdr, 10, WHITE, bold=True, align=PP_ALIGN.CENTER)
rows13 = [
('Heart Failure', '↑Hydrostatic', 'Bilateral', 'Yes', 'HIGH', 'Transudate', '↑BNP, S3 gallop, crackles', BLUE),
('Renal Failure', '↑Hydrostatic', 'Bilateral', 'Yes', 'High', 'Transudate', '↑Creatinine, ↓GFR, oliguria', TEAL),
('DVT', '↑Hydrostatic', 'Unilateral', 'Yes', 'Nml', 'Transudate', 'Pain, erythema, + Doppler US', BLUE),
('Cirrhosis', 'Dual (↑Hp+↓Op)', 'Bilateral', 'Yes', 'Low', 'Transudate', 'Ascites, spider naevi, SAAG>1.1', ORANGE),
('Nephrotic Syndrome', '↓Oncotic', 'Bilateral', 'Yes', 'Low', 'Transudate', 'Periorbital, >3.5g/day proteinuria', PURPLE),
('Malnutrition', '↓Oncotic', 'Bilateral', 'Yes', 'Low', 'Transudate', 'Kwashiorkor, ↓albumin, ↓weight', PURPLE),
('Cellulitis/Inflam.', '↑Permeability','Unilateral','Var.','Nml', 'Exudate', 'Hot, red, tender skin, ↑CRP', RED),
('ACEi Angioedema', '↑Permeability','Localized', 'No', 'Nml', 'Exudate', 'Tongue/lips/throat, on ACEi', RED),
('Preeclampsia', '↑Permeability','Bilateral', 'Yes', 'Nml', 'Exudate', 'Pregnant + HTN + proteinuria', ORANGE),
('Lymphedema', 'Lymphatic obs.','Unilateral','No', 'Nml', 'High-prot.','Stemmer +ve, fibrosis, non-pitting', GREEN),
('Myxedema', 'Lymphatic/GAG','Bilateral', 'No', 'Nml', 'Mucin', 'Hypothyroid, pretibial, ↑TSH', GREEN),
]
for ri, row in enumerate(rows13):
ry = 1.98 + ri * 0.465
row_vals = list(row[:-1])
col = row[-1]
row_bg = OFFWHITE if ri % 2 == 0 else LGREY
for ci, (val, cx, cw) in enumerate(zip(row_vals, col_x, cols_w)):
c = col if ci == 1 else DARKGREY
bg_c = row_bg
shp2 = rect(s13, cx, ry, cw, 0.44, bg_c)
textbox(s13, cx+0.05, ry+0.03, cw-0.08, 0.4, val, 9.2, c,
bold=(ci==1), align=PP_ALIGN.CENTER)
textbox(s13, 0.35, 7.18, 12.5, 0.25,
'Var. = Variable | Nml = Normal | High-prot. = High-protein lymph fluid | Hp = hydrostatic | Op = oncotic',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 14 – DIAGNOSTIC APPROACH
# ═════════════════════════════════════════════════════════════════
s14 = prs.slides.add_slide(blank_layout)
slide_header(s14, 'Diagnostic Approach to Edema',
'A stepwise clinical framework')
steps = [
('STEP 1', 'Unilateral or Bilateral?',
'Unilateral → local cause (DVT, cellulitis, lymphedema)\nBilateral → systemic cause (heart, kidney, liver, nutrition)',
BLUE),
('STEP 2', 'Pitting or Non-Pitting?',
'Pitting → hydrostatic or oncotic\nNon-pitting → lymphatic, myxedema, angioedema',
TEAL),
('STEP 3', 'JVP: High or Low?',
'High JVP → ↑Hydrostatic (heart, renal failure)\nLow/Normal → ↓Oncotic or ↑Permeability',
PURPLE),
('STEP 4', 'Serum Albumin + Urine Protein',
'Low albumin + heavy proteinuria → Nephrotic syndrome\nLow albumin, no proteinuria → Liver disease/malnutrition',
ORANGE),
('STEP 5', 'Inflammatory signs?',
'Redness, heat, fever, recent trigger or drug → ↑Capillary permeability\nAngioedema? Check for ACEi use',
RED),
('STEP 6', 'Medications & Context',
'CCBs, NSAIDs, steroids, thiazolidinediones\nPregnancy (physiological) | Idiopathic (women 30–40s)',
GREEN),
]
for i, (step, title, desc, col) in enumerate(steps):
cy = 1.4 + i * 1.0
pill(s14, 0.35, cy, 1.0, 0.52, step, col, size=9.5)
rect(s14, 1.48, cy, 11.5, 0.88, OFFWHITE, line_color=col, line_w=1.2, radius=True)
textbox(s14, 1.62, cy+0.05, 3.2, 0.38, title, 12, col, bold=True)
textbox(s14, 1.62, cy+0.43, 11.1, 0.42, desc, 10, DARKGREY, wrap=True)
textbox(s14, 0.35, 7.42, 12.5, 0.22,
'Always investigate the CAUSE — edema is a sign, not a diagnosis. | Source: Frameworks for Internal Medicine',
9, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 15 – TREATMENT PRINCIPLES
# ═════════════════════════════════════════════════════════════════
s15 = prs.slides.add_slide(blank_layout)
slide_header(s15, 'Treatment Principles',
'Management is mechanism-specific — always treat the underlying cause')
treatments = [
('↑ HYDROSTATIC\nPRESSURE', BLUE,
['Loop diuretics (furosemide) — mainstay',
'Sodium and fluid restriction',
'Treat underlying cause (HF, renal failure)',
'ACEi/ARBs for cardiac remodelling',
'Compression stockings for venous edema',
'Anticoagulation if DVT is the cause']),
('↓ ONCOTIC\nPRESSURE', PURPLE,
['Treat underlying protein loss/deficiency',
'Nephrotic: steroids (minimal change disease)',
'Cirrhosis: spironolactone + furosemide',
'Nutritional: protein repletion',
'Salt restriction and fluid management',
'Albumin infusion in severe cases (selected)']),
('↑ CAPILLARY\nPERMEABILITY', RED,
['Histamine-mediated: antihistamines + epinephrine',
'Bradykinin (ACEi): STOP the ACEi immediately',
'ACEi angioedema: icatibant, FFP, C1-INH concentrate',
'Inflammatory edema: antibiotics, anti-inflammatories',
'Preeclampsia: MgSO₄ + antihypertensives; deliver',
'ARDS: lung-protective ventilation, treat sepsis']),
('LYMPHATIC\nOBSTRUCTION', GREEN,
['Compression garments (cornerstone of therapy)',
'Manual lymphatic drainage (physiotherapy)',
'Pneumatic compression devices',
'Skin care and infection prevention',
'Surgical: lymphovenous bypass (specialist)',
'Diuretics are NOT effective in lymphedema']),
]
for i, (name, col, items) in enumerate(treatments):
cx = 0.35 + i * 3.24
shp = rect(s15, cx, 1.38, 3.05, 0.72, col, radius=True)
shape_text(shp, name, 11, WHITE, bold=True)
card = rect(s15, cx, 2.18, 3.05, 5.0, OFFWHITE, line_color=col, line_w=1.2, radius=True)
tb = s15.shapes.add_textbox(Inches(cx+0.1), Inches(2.27), Inches(2.85), Inches(4.8))
tf = tb.text_frame; tf.word_wrap = True
first = True
for item in items:
if first: p = tf.paragraphs[0]; first = False
else: p = tf.add_paragraph()
r = p.add_run(); r.text = '• ' + item
r.font.name = 'Calibri'; r.font.size = Pt(10.5); r.font.color.rgb = DARKGREY
textbox(s15, 0.35, 7.25, 12.5, 0.22,
'Always address the root cause. Symptomatic diuresis without addressing the mechanism leads to recurrence.',
9.5, MIDGREY, italic=True)
# ═════════════════════════════════════════════════════════════════
# SLIDE 16 – KEY TAKE-AWAYS
# ═════════════════════════════════════════════════════════════════
s16 = prs.slides.add_slide(blank_layout)
bg(s16, NAVY)
rect(s16, 0, 0, W, 1.25, BLUE)
textbox(s16, 0.4, 0.22, 12.5, 0.75,
'Key Take-Away Points', 30, WHITE, bold=True, align=PP_ALIGN.LEFT)
takeaways = [
(BLUE, '1', 'Edema = excess interstitial fluid when net capillary filtration exceeds lymphatic drainage'),
(TEAL, '2', 'Four mechanisms: ↑Hydrostatic | ↓Oncotic | ↑Permeability | Lymphatic obstruction'),
(PURPLE, '3', 'The Starling equation (Q = K[(Pc−Pi) − σ(πc−πi)]) explains ALL four mechanisms mathematically'),
(RED, '4', 'Transudate (low protein) = hydrostatic or oncotic cause; Exudate (high protein) = permeability or lymphatic'),
(ORANGE, '5', 'JVP distinguishes cardiac (HIGH JVP) from hepatic/renal oncotic edema (LOW/NORMAL JVP)'),
(GREEN, '6', 'Non-pitting edema = lymphedema, myxedema, or angioedema; pitting = hydrostatic or oncotic'),
(BLUE, '7', 'Histamine mediates fast, IgE-driven angioedema; bradykinin mediates ACE inhibitor-induced angioedema'),
(TEAL, '8', 'Edema is a SIGN — always identify and treat the root cause, not just the fluid'),
]
for i, (col, num, text) in enumerate(takeaways):
cy = 1.42 + i * 0.74
pill(s16, 0.4, cy, 0.55, 0.5, num, col, size=13)
textbox(s16, 1.12, cy+0.04, 11.8, 0.55, text, 13, WHITE, font='Calibri', bold=False, wrap=True)
rect(s16, 0.4, 7.15, 12.5, 0.28, RGBColor(0x0A, 0x1F, 0x4A))
textbox(s16, 0.4, 7.17, 12.5, 0.25,
'References: Frameworks for Internal Medicine | Robbins & Kumar Basic Pathology | '
'Morgan & Mikhail\'s Clinical Anesthesiology 7e | Comprehensive Clinical Nephrology 7e',
8.5, MIDGREY, italic=True, align=PP_ALIGN.CENTER)
# ─────────────────────────────────────────────
# SAVE
# ─────────────────────────────────────────────
out = '/tmp/workspace/edema-flowchart/Edema_Etiology_Pathophysiology.pptx'
buf = BytesIO()
prs.save(buf)
with open(out, 'wb') as f:
f.write(buf.getvalue())
print(f"Saved: {out}")