This commit is contained in:
33333-33333 2026-07-01 14:41:05 +09:00
commit 4d3fc2cd47
84 changed files with 3429 additions and 3480 deletions

View file

@ -21,15 +21,59 @@ function lineagePathFromPoints(points) {
return `M ${points[0][0].toFixed(1)} ${points[0][1].toFixed(1)} ` + points.slice(1).map(p => `L ${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
}
function lineageCrossGenerationPartnerPath(a, b, nodeW, nodeH, unionY) {
function lineageNodeRectsBetween(positions, nodeW, nodeH, y1, y2, excludeIds = new Set(), pad = 10) {
const top = Math.min(y1, y2) - pad;
const bottom = Math.max(y1, y2) + pad;
const rects = [];
for (const pos of positions?.values?.() || []) {
const id = pos?.node?.id;
if (!pos || (id && excludeIds.has(id))) continue;
const rectTop = pos.y - pad;
const rectBottom = pos.y + nodeH + pad;
if (rectBottom < top || rectTop > bottom) continue;
rects.push({ left: pos.x - pad, right: pos.x + nodeW + pad, top: rectTop, bottom: rectBottom });
}
return rects;
}
function lineageVerticalLaneClear(x, y1, y2, rects) {
const top = Math.min(y1, y2);
const bottom = Math.max(y1, y2);
return !(rects || []).some(r => x > r.left && x < r.right && Math.max(top, r.top) < Math.min(bottom, r.bottom));
}
function lineageCrossGenerationPartnerLane(a, b, nodeW, nodeH, context = {}) {
const ay = a.y + nodeH / 2;
const by = b.y + nodeH / 2;
const exclude = new Set([a.node?.id, b.node?.id].filter(Boolean));
const rects = lineageNodeRectsBetween(context.positions, nodeW, nodeH, ay, by, exclude, 12);
const ax = a.x + nodeW / 2;
const bx = b.x + nodeW / 2;
const ay = a.y + nodeH;
const by = b.y + nodeH;
const y = Math.max(unionY, ay + 12, by + 12);
const unionX = Math.abs(ax - bx) < 18 ? Math.max(ax, bx) + 30 : (ax + bx) / 2;
const points = [[ax, ay], [ax, y], [unionX, y], [bx, y], [bx, by]];
return { d: lineagePathFromPoints(points), segments: lineageSegmentsFromPoints(points), unionX, unionY: y };
const directX = Math.abs(ax - bx) < 18 ? Math.max(ax, bx) + 30 : (ax + bx) / 2;
if (lineageVerticalLaneClear(directX, ay, by, rects)) return directX;
const minLeft = Math.min(a.x, b.x, ...rects.map(r => r.left));
const maxRight = Math.max(a.x + nodeW, b.x + nodeW, ...rects.map(r => r.right));
const width = Number(context.width || 0) || Infinity;
const candidates = [
maxRight + 18,
minLeft - 18,
Math.max(a.x + nodeW, b.x + nodeW) + 18,
Math.min(a.x, b.x) - 18,
].filter(x => Number.isFinite(x) && x >= 12 && x <= width - 12);
const clear = candidates.filter(x => lineageVerticalLaneClear(x, ay, by, rects));
const pool = clear.length ? clear : candidates;
if (pool.length) return pool.sort((x, y) => Math.abs(x - directX) - Math.abs(y - directX))[0];
return directX;
}
function lineageCrossGenerationPartnerPath(a, b, nodeW, nodeH, unionY, context = {}) {
const railX = lineageCrossGenerationPartnerLane(a, b, nodeW, nodeH, context);
const aMidY = a.y + nodeH / 2;
const bMidY = b.y + nodeH / 2;
const aSideX = railX >= a.x + nodeW / 2 ? a.x + nodeW : a.x;
const bSideX = railX >= b.x + nodeW / 2 ? b.x + nodeW : b.x;
const points = [[aSideX, aMidY], [railX, aMidY], [railX, bMidY], [bSideX, bMidY]];
return { d: lineagePathFromPoints(points), segments: lineageSegmentsFromPoints(points), unionX: railX, unionY: Math.max(aMidY, bMidY) };
}
function lineageSameGenerationPartnerPath(a, b, nodeW, nodeH, unionY, attachOffsetA = 0, attachOffsetB = 0) {
@ -54,8 +98,8 @@ function lineageSameGenerationPartnerPath(a, b, nodeW, nodeH, unionY, attachOffs
return { d: lineagePathFromPoints(points), segments: lineageSegmentsFromPoints(points), unionX: (x1 + x2) / 2, unionY: y };
}
function lineagePartnerPathFromPositions(a, b, nodeW, nodeH, unionY, attachOffsetA = 0, attachOffsetB = 0) {
if (a.rowIndex !== b.rowIndex) return lineageCrossGenerationPartnerPath(a, b, nodeW, nodeH, unionY);
function lineagePartnerPathFromPositions(a, b, nodeW, nodeH, unionY, attachOffsetA = 0, attachOffsetB = 0, context = {}) {
if (a.rowIndex !== b.rowIndex) return lineageCrossGenerationPartnerPath(a, b, nodeW, nodeH, unionY, context);
return lineageSameGenerationPartnerPath(a, b, nodeW, nodeH, unionY, attachOffsetA, attachOffsetB);
}
@ -298,7 +342,7 @@ function lineageBuildLinks(nodes, idSet, layout) {
const attachA = 0;
const attachB = 0;
const partnerY = ((left.y + nodeH / 2) + (right.y + nodeH / 2)) / 2;
const partner = lineagePartnerPathFromPositions(left, right, nodeW, nodeH, partnerY, attachA, attachB);
const partner = lineagePartnerPathFromPositions(left, right, nodeW, nodeH, partnerY, attachA, attachB, layout);
if (Number.isFinite(partner.unionX)) group.joinX = partner.unionX;
const unionY = Number.isFinite(partner.unionY) ? partner.unionY : partnerY;
const clearY = clamp(group.parentBottom + 22 + i * 2, unionY + 10, group.childTop - 36);