From c43fdbff4f1d825610dece97019075a98d84ffaa Mon Sep 17 00:00:00 2001 From: IliesAllali Date: Sun, 18 Oct 2026 21:16:05 +0200 Subject: [PATCH] =?UTF-8?q?fix(fil):=20enfant=20presque=20=C3=A0=20l'aplom?= =?UTF-8?q?b,=20un=20S=20doux=20depuis=20la=20barre=20au=20lieu=20d'une=20?= =?UTF-8?q?marche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sous deux rayons d'écart, deux coins de 24 px ne tenaient pas : le fil faisait une petite marche. Le tronc descend jusqu'à la barre et un S vertical au départ et à l'arrivée rejoint l'enfant. Co-Authored-By: Claude Opus 5.5 --- frontend/src/components/galaxy/renderers.js | 14 +++++++++++--- frontend/src/components/galaxy/renderers.test.js | 13 +++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/galaxy/renderers.js b/frontend/src/components/galaxy/renderers.js index e541346..7926e76 100644 --- a/frontend/src/components/galaxy/renderers.js +++ b/frontend/src/components/galaxy/renderers.js @@ -101,9 +101,10 @@ function computeCouplePoints(p1Pos, p2Pos, unionPos, p1R, p2R, nodeRandomData, s export const FIL = { color: '#E3A590', width: 1.1, dot: '#D2694A', dotR: 2.6 } // Ligne de la fratrie : sous les prénoms et années des parents, au-dessus des enfants const BUS_DY = 100 -// Accolade : coins arrondis de rayon fixe (forme A, choisie par Patch), écart minimal en dessous duquel l'enfant descend droit +// Accolade : coins arrondis de rayon fixe (forme A, choisie par Patch). En dessous de deux rayons d'écart, +// deux coins ne tiennent pas sans faire une marche : l'enfant est relié par un trait droit depuis l'union. const SIB_R = 24 -const SIB_STRAIGHT_DX = 12 +const SIB_STRAIGHT_DX = SIB_R * 2 function busY(sourceId, sy) { let h = 0 @@ -123,7 +124,14 @@ export function traceSibship(ctx, sourceId, sourcePos, items) { for (const { cx: ex, cy: ey } of items) { const dx = ex - sx ctx.moveTo(sx, sy) - if (Math.abs(dx) < SIB_STRAIGHT_DX) { ctx.lineTo(sx, by); ctx.lineTo(ex, by); ctx.lineTo(ex, ey); continue } + if (Math.abs(dx) < SIB_STRAIGHT_DX) { + // Le tronc descend jusqu'à la barre, puis un S doux, vertical au départ et à l'arrivée, rejoint l'enfant : + // il se sépare là où se séparent les autres enfants, pas sous l'union + const h = ey - by + ctx.lineTo(sx, by) + ctx.bezierCurveTo(sx, by + h * 0.55, ex, ey - h * 0.45, ex, ey) + continue + } const dir = Math.sign(dx) const k = Math.min(rr, Math.abs(dx) / 2) ctx.lineTo(sx, by - k) diff --git a/frontend/src/components/galaxy/renderers.test.js b/frontend/src/components/galaxy/renderers.test.js index 93ddbe0..a600ef0 100644 --- a/frontend/src/components/galaxy/renderers.test.js +++ b/frontend/src/components/galaxy/renderers.test.js @@ -35,4 +35,17 @@ describe('traceSibship (accolade franche)', () => { traceSibship(ctx, 'u-1', src, [{ cx: 505, cy: 480 }]) expect(count('arcTo')).toBe(0) }) + + it('un enfant à moins de deux rayons de l\'aplomb est relié par un seul S, sans marche', () => { + const { ctx, calls } = recorder() + traceSibship(ctx, 'u-1', src, [{ cx: 515, cy: 480 }]) + expect(calls.filter(c => c[0] === 'arcTo').length).toBe(0) + // Tronc vertical jusqu'à la barre, puis le S + expect(calls.filter(c => c[0] === 'lineTo').map(c => c[1])).toEqual([500]) + const [curve] = calls.filter(c => c[0] === 'bezierCurveTo') + // Départ vertical sous l'union, arrivée verticale sur l'enfant + expect(curve[1]).toBe(500) + expect(curve[3]).toBe(515) + expect(curve.slice(5)).toEqual([515, 480]) + }) })