Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 109x 109x 109x 109x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 253x 277x 277x 277x 277x 277x 277x 277x 277x 277x 109x | type ExchangerLinesProps = {
streamYPositions: number[]; // For each stream, the pixel Y of its TOP edge. During drag, stream y is live-updated so lines follow it
streamIndexBySdeId: Map<string, number>; // map SDE id -> index in renderStreams. What is stream index in render order?
alignedHXByKey?: Map<string, Map<string, number[]>>; // Map<hxKey, Map<sdeId, number[]>> : X positions of HXs per participating row
alignedHXRowsByKey?: Map<string, Map<string, number[]>>; // Map<hxKey, Map<sdeId, number[]>> : row indices of rows with HXs.
streamBlockHeight: number;
};
export default function ExchangerLines({
streamYPositions,
streamIndexBySdeId,
alignedHXByKey,
alignedHXRowsByKey,
streamBlockHeight,
}: ExchangerLinesProps) {
const circlePad = 15; // so lines render from top/bot EDGE of circle (circle radius is 15).
Iif (!alignedHXByKey || !alignedHXRowsByKey) return null;
// push all <line> svg elemnts at the end for render.
const lines: JSX.Element[] = [];
// for each hx,
alignedHXByKey.forEach((sdeId, hxKey) => {
const rowsBySdeId = alignedHXRowsByKey.get(hxKey);
Iif (!rowsBySdeId) return;
// for each exchanger hxKey, grab all streams (sdeIds) where it appears.
// need exactly two sides of the exchanger
const sdeIds = Array.from(sdeId.keys());
Iif (sdeIds.length !== 2) return;
// look up stream's index to later get topY from streamYPositions
const [sdeId1, sdeId2] = sdeIds;
const idx1 = streamIndexBySdeId.get(sdeId1);
const idx2 = streamIndexBySdeId.get(sdeId2);
Iif (idx1 == null || idx2 == null) return;
// determine which stream is visually on top/bottom (y-pos) right now
// so we can draw lines from top to bottom consistently, even while dragging
const baseY1 = streamYPositions[idx1];
const baseY2 = streamYPositions[idx2];
const topSdeId = baseY1 <= baseY2 ? sdeId1 : sdeId2;
const bottomSdeId = baseY1 <= baseY2 ? sdeId2 : sdeId1;
const topIdx = baseY1 <= baseY2 ? idx1 : idx2;
const bottomIdx = baseY1 <= baseY2 ? idx2 : idx1;
const baseYTop = streamYPositions[topIdx];
const baseYBottom = streamYPositions[bottomIdx];
// get shared x pos, and participating row indices for each side
const topXs = sdeId.get(topSdeId) ?? [];
const botXs = sdeId.get(bottomSdeId) ?? [];
const topRows = rowsBySdeId.get(topSdeId) ?? [];
const botRows = rowsBySdeId.get(bottomSdeId) ?? [];
Iif (!topXs.length || !botXs.length || !topRows.length || !botRows.length) return;
// how many vertical lines to draw for this HX (some HX can connect multiple row pairs)
// e.g. if top has 2 participating rows and bottom has 3, we draw 3 lines
const multiplicity = Math.max(topXs.length, botXs.length);
// for each line to be drawn (for the same HX),
for (let k = 0; k < multiplicity; k++) {
// use modulo fan-out to pair rows if counts differ
// e.g. if top has 2 rows, but bottom has 3
// k=0 -> top0 with bot0; k=1 -> top1 with bot1; k=2 -> top0 (wraps) with bot2
const topIdxInList = k % topXs.length;
const botIdxInList = k % botXs.length;
// X should be identical for both sides (but be defensive)
// grab from top if valid, else fall back to bottom
const x = Number.isFinite(topXs[topIdxInList])
? topXs[topIdxInList]!
: botXs[botIdxInList];
Iif (x == null || !isFinite(x)) continue;
// which ROW in each block are we connecting? (segments had multiple rows.)
const topRowIndex = topRows[topIdxInList] ?? 0;
const botRowIndex = botRows[botIdxInList] ?? 0;
// the top and bottom Y for the exchanger lines.
const yTop = baseYTop + topRowIndex * streamBlockHeight + circlePad; // nudge down
const yBottom = baseYBottom + botRowIndex * streamBlockHeight - circlePad; // nudge up
lines.push(
<line
key={`${hxKey}-link-${k}`} // hxKey pair, as well as how many times it occurs in visualisation.
x1={x}
y1={yTop}
x2={x}
y2={yBottom}
stroke="hsl(var(--hen-diagram-foreground))"
strokeWidth={1}
/>
);
}
});
// return all lines as a fragmeent.
return <>{lines}</>;
} |