Skip to content

Commit 358cd57

Browse files
authored
Merge pull request #49 from AdaWorldAPI/claude/osint-soa-rebake
cockpit/fma: nested "outer wall" rendering — the address IS the wall
2 parents f770e8e + 31b1def commit 358cd57

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

cockpit/src/FmaGraph.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,75 @@ function tierPos(
7474
return { x: ((lo + hi) / 2) * COL, y: soa.cls[i] * ROW };
7575
}
7676

77+
const isCeiling = (soa: Soa, i: number) => soa.ceiling[i] === 1 || soa.cls[i] === 5;
78+
79+
// A node n is INSIDE container c's wall iff its 8:8 tier instances match c's down
80+
// to c's depth — i.e. c's address is a prefix of n's. The address IS the wall.
81+
function inside(soa: Soa, c: number, n: number): boolean {
82+
const d = soa.cls[c];
83+
if (soa.cls[n] < d) return false;
84+
if (d >= 1 && inst(soa.hip[n]) !== inst(soa.hip[c])) return false;
85+
if (d >= 2 && inst(soa.twig[n]) !== inst(soa.twig[c])) return false;
86+
if (d >= 3 && inst(soa.leaf[n]) !== inst(soa.leaf[c])) return false;
87+
return true;
88+
}
89+
90+
interface Wall {
91+
x0: number;
92+
y0: number;
93+
x1: number;
94+
y1: number;
95+
color: string;
96+
depth: number;
97+
}
98+
99+
// One nested "outer wall" rectangle per container (Organ→Tissue; cells are
100+
// leaves). Each box bounds its tier-prefix descendants, so the walls nest exactly
101+
// like the partonomy — the Heart's box is the outermost wall (its epicardium).
102+
function containerWalls(soa: Soa): Wall[] {
103+
const center = new Map<number, { x: number; y: number }>();
104+
for (let i = 0; i < soa.nodeCount; i++) if (!isCeiling(soa, i)) center.set(i, tierPos(soa, i));
105+
const walls: Wall[] = [];
106+
for (let c = 0; c < soa.nodeCount; c++) {
107+
if (isCeiling(soa, c) || soa.cls[c] > 3) continue; // only Organ..Tissue contain
108+
let x0 = Infinity;
109+
let y0 = Infinity;
110+
let x1 = -Infinity;
111+
let y1 = -Infinity;
112+
let found = false;
113+
for (const [n, p] of center) {
114+
if (!inside(soa, c, n)) continue;
115+
found = true;
116+
x0 = Math.min(x0, p.x);
117+
y0 = Math.min(y0, p.y);
118+
x1 = Math.max(x1, p.x);
119+
y1 = Math.max(y1, p.y);
120+
}
121+
if (!found) continue;
122+
const pad = 42 - soa.cls[c] * 7; // coarser container → roomier wall
123+
walls.push({ x0: x0 - pad, y0: y0 - pad, x1: x1 + pad, y1: y1 + pad, color: classColor(soa.cls[c]), depth: soa.cls[c] });
124+
}
125+
return walls.sort((a, b) => a.depth - b.depth); // coarsest drawn first (behind)
126+
}
127+
128+
// Stroke a rounded rect in vis-network coordinates (the beforeDrawing ctx is
129+
// already in network space, so it aligns with node positions).
130+
function strokeWall(ctx: CanvasRenderingContext2D, w: Wall): void {
131+
const r = 16;
132+
ctx.beginPath();
133+
ctx.moveTo(w.x0 + r, w.y0);
134+
ctx.arcTo(w.x1, w.y0, w.x1, w.y1, r);
135+
ctx.arcTo(w.x1, w.y1, w.x0, w.y1, r);
136+
ctx.arcTo(w.x0, w.y1, w.x0, w.y0, r);
137+
ctx.arcTo(w.x0, w.y0, w.x1, w.y0, r);
138+
ctx.closePath();
139+
ctx.fillStyle = `${w.color}0f`; // very faint compartment fill
140+
ctx.fill();
141+
ctx.lineWidth = w.depth === 0 ? 2.6 : 1.4; // the Heart's outer wall is boldest
142+
ctx.strokeStyle = `${w.color}66`;
143+
ctx.stroke();
144+
}
145+
77146
const OPTIONS: Options = {
78147
nodes: { shape: 'dot', borderWidth: 2.5, font: { color: '#d9e9f9', size: 13, strokeWidth: 3, strokeColor: PAGE_BG } },
79148
edges: {
@@ -183,6 +252,12 @@ export function FmaGraph() {
183252
const visNodes = new DataSet<any>(Array.from({ length: soa.nodeCount }, (_, i) => baseNode(i)));
184253
const visEdges = new DataSet<any>(soa.edges.map((e, id) => baseEdge(e, id)));
185254
const net = new Network(hostRef.current, { nodes: visNodes, edges: visEdges }, OPTIONS);
255+
// nested "outer walls" — one rounded box per container, drawn behind the
256+
// nodes; they nest exactly like the partonomy (the address IS the wall).
257+
const walls = containerWalls(soa);
258+
net.on('beforeDrawing', (ctx: CanvasRenderingContext2D) => {
259+
for (const w of walls) strokeWall(ctx, w);
260+
});
186261
// fixed 8:8-tier slots, no simulation — just frame the nested cascade.
187262
net.once('afterDrawing', () => net.fit({ animation: false }));
188263
setStatus(`${soa.nodeCount} nodes · ${soa.edgeCount} edges — Z-order tile pyramid; click a tissue for its dual membership`);

0 commit comments

Comments
 (0)