From d58b7b779c58b8fbf9f5217d5e5b699dd03d93a5 Mon Sep 17 00:00:00 2001 From: IliesAllali Date: Sun, 18 Oct 2026 19:45:20 +0200 Subject: [PATCH] Filiations : une seule trajectoire par fratrie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chaque enfant avait son propre chemin depuis l'union, avec un rayon de virage propre : sous une union nombreuse, 3 ou 4 brins se séparaient juste sous le point d'union. Le tracé est maintenant un tronc unique, puis une barre de fratrie qui distribue vers chaque enfant ; le rayon des virages est commun à chaque côté, et tout part dans un seul stroke (pas de brins doublés en alpha). Un enfant presque sous l'union descend de la barre, sans second brin depuis l'union. Le chemin en surbrillance (path highlight) reste tracé enfant par enfant. Vérifié à l'écran sur un arbre de test à 6 enfants (avant/après dans _bmad-output/parenthese/renvoi/faisceau-*.png). Co-Authored-By: Claude Haiku 5.5 --- frontend/src/components/galaxy/renderers.js | 64 +++++++++++++++++---- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/galaxy/renderers.js b/frontend/src/components/galaxy/renderers.js index 25b52ce..a582d79 100644 --- a/frontend/src/components/galaxy/renderers.js +++ b/frontend/src/components/galaxy/renderers.js @@ -126,6 +126,39 @@ function traceBus(ctx, sx, sy, by, ex, ey) { ctx.stroke() } +// Une union, toute sa fratrie : une seule trajectoire (un tronc, une accolade, un trait par enfant). +// Un chemin par enfant aurait un rayon de virage propre, donc des brins qui se séparent sous l'union. +// Le rayon est commun à chaque côté (le plus petit écart du côté), et le tout part dans un seul stroke +// pour que les parties superposées ne s'additionnent pas en alpha. +function traceSibship(ctx, sourceId, sourcePos, items) { + const sx = sourcePos.cx, sy = sourcePos.cy + const by = busY(sourceId, sy) + const drop = Math.min(by - sy, 64) + const turnFor = (dir) => { + let t = BUS_TURN + for (const it of items) { + const dx = it.cx - sx + if (Math.sign(dx) === dir && Math.abs(dx) >= BUS_STRAIGHT_DX) t = Math.min(t, Math.abs(dx) / 2) + } + return t + } + const turns = { '-1': turnFor(-1), '1': turnFor(1) } + ctx.beginPath() + for (const { cx: ex, cy: ey } of items) { + const dx = ex - sx + ctx.moveTo(sx, sy) + ctx.lineTo(sx, by - drop) + // Enfant presque sous l'union : descente droite depuis la barre, pas de second brin depuis l'union + if (Math.abs(dx) < BUS_STRAIGHT_DX) { ctx.lineTo(ex, by); ctx.lineTo(ex, ey); continue } + const dir = Math.sign(dx) + const turn = turns[dir] + ctx.quadraticCurveTo(sx, by, sx + dir * turn, by) + ctx.lineTo(ex - dir * turn, by) + ctx.quadraticCurveTo(ex, by, ex, ey) + } + ctx.stroke() +} + // Trace une filiation union → enfant. progress < 1 : entrée en fondu function traceFiliation(ctx, sourceId, sourcePos, targetPos, childR, progress) { const a = ctx.globalAlpha @@ -251,6 +284,7 @@ export function drawFiliations(ctx, edges, posMap, nodeMap, nodeRandomData, entr renvoiHitAreas.length = 0 renvoiPills.length = 0 if (!edges) return + const sibships = new Map() edges.forEach(edge => { const sourceId = edge.sources[0] @@ -271,7 +305,22 @@ export function drawFiliations(ctx, edges, posMap, nodeMap, nodeRandomData, entr const targetNode = nodeMap.get(targetId) const pType = edge._parentageType || 'biologique' + const childR = nodeRadius(targetNode) + if (edge._renvoi) { + ctx.globalAlpha = Math.min(1, edgeProgress * 1.5) + drawRenvoi(ctx, edge, sourcePos, targetPos, targetNode, childR, view) + ctx.globalAlpha = 1 + return + } + // Les enfants d'une même union (même type de filiation) forment une seule fratrie, tracée d'un coup + const key = `${sourceId}|${pType}` + if (!sibships.has(key)) sibships.set(key, { sourceId, sourcePos, pType, items: [], progress: 0 }) + const sib = sibships.get(key) + sib.items.push({ cx: targetPos.cx, cy: targetPos.cy - childR }) + sib.progress += edgeProgress + }) + for (const { sourceId, sourcePos, pType, items, progress } of sibships.values()) { if (pType === 'adoption') { ctx.setLineDash([8, 5]) ctx.strokeStyle = FIL.color @@ -284,19 +333,12 @@ export function drawFiliations(ctx, edges, posMap, nodeMap, nodeRandomData, entr } ctx.lineWidth = FIL.width ctx.lineCap = 'round' - ctx.globalAlpha = Math.min(1, edgeProgress * 1.5) - - const childR = nodeRadius(targetNode) - if (edge._renvoi) { - drawRenvoi(ctx, edge, sourcePos, targetPos, targetNode, childR, view) - ctx.globalAlpha = 1 - ctx.setLineDash([]) - return - } - traceFiliation(ctx, sourceId, sourcePos, targetPos, childR, edgeProgress) + const p = progress / items.length + ctx.globalAlpha = Math.min(1, p * 1.5) * p + traceSibship(ctx, sourceId, sourcePos, items) ctx.globalAlpha = 1 ctx.setLineDash([]) - }) + } } // ============================================================