--- import { getMessages, type MessageKey } from '../../i18n/messages.ts'; interface Props { locale: string; /** Unique per diagram on the page; prefixes ids inside the SVG. */ uid: string; } const { locale, uid } = Astro.props; const t = getMessages(locale); const arrowId = `${uid}-fmd-arrow`; // Labels are wrapped at word boundaries so that no line is longer than `max` characters. function wrap(label: string, max: number): string[] { const lines: string[] = []; let line = ''; for (const word of label.split(' ')) { if (line !== '' && line.length + 1 + word.length > max) { lines.push(line); line = word; } else { line = line === '' ? word : `${line} ${word}`; } } if (line !== '') lines.push(line); return lines; } const lineHeight = 15; const lines = (key: MessageKey, max: number) => wrap(t[key], max); const beadsLines = lines('diagram.label.string-of-beads', 30); // One artery cut lengthwise, blood moving left to right: its initial part on the left, its middle // and far parts on the right. Normal: an even lumen all along. Disease (multifocal fibromuscular // dysplasia): in the middle and far parts narrowings alternate with widenings, a "string of // beads"; the initial part is unchanged. No plaque (it is not atherosclerosis), no stent, no // aneurysm and no numbers. const axis = 110; const x0 = 20; const x1 = 380; // Where the middle part begins. const xMid = 150; const lumenNormal = 18; // The wall is drawn at one thickness so the beaded lumen reads clearly; the captions make no claim about wall thickness. const wallNormal = 8; // Beads in the middle and far parts: narrowings at the ends of each period, widenings between. const period = 46; const narrow = 8; const wide = 24; // Over this distance before xMid the lumen eases from normal into the first narrowing. const ease = 24; function lumenAt(x: number): number { if (x <= xMid - ease) return lumenNormal; if (x < xMid) { const k = (1 - Math.cos((Math.PI * (x - (xMid - ease))) / ease)) / 2; return lumenNormal + (narrow - lumenNormal) * k; } const s = Math.sin((Math.PI * (x - xMid)) / period) ** 2; return narrow + (wide - narrow) * s; } const step = 2; const xs: number[] = []; for (let x = x0; x <= x1; x += step) xs.push(x); const round = (n: number) => Math.round(n * 10) / 10; // A closed outline symmetric about the axis for a radius function. function tube(radius: (x: number) => number): string { const top = xs.map((x) => `${x},${round(axis - radius(x))}`); const bottom = [...xs].reverse().map((x) => `${x},${round(axis + radius(x))}`); return `M${top.join(' L')} L${bottom.join(' L')} Z`; } const normalOuter = tube(() => lumenNormal + wallNormal); const normalLumen = tube(() => lumenNormal); const diseaseOuter = tube((x) => lumenAt(x) + wallNormal); const diseaseLumen = tube(lumenAt); // The upper and lower edges of a lumen, open at both ends. function edges(radius: (x: number) => number): string { const side = (sign: number) => xs.map((x) => `${x},${round(axis + sign * radius(x))}`).join(' L'); return `M${side(-1)} M${side(1)}`; } const normalEdges = edges(() => lumenNormal); const diseaseEdges = edges(lumenAt); // Label positions. const beadX = xMid + period * 2.5; const beadTop = axis - wide - wallNormal; const bracketY = 152; --- {t['diagram.label.initial-part']} {t['diagram.label.middle-and-far-parts']} {t['diagram.label.artery']} {t['diagram.label.even-lumen']} {beadsLines.map((line, index) => ( {line} ))}