@cardenelabs/cdl 0.30.0 → 0.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -1
- package/dist/index.cjs +242 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +242 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +241 -2
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +241 -2
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/layout/collisions.ts +345 -0
- package/src/layout/edges.ts +7 -2
- package/src/layout.ts +14 -0
package/dist/react.js
CHANGED
|
@@ -2013,6 +2013,234 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
2013
2013
|
edgePathShifts: edgePathShiftMap
|
|
2014
2014
|
};
|
|
2015
2015
|
}
|
|
2016
|
+
function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
2017
|
+
if (edges.length === 0) return edges;
|
|
2018
|
+
const labeledIds = new Set(
|
|
2019
|
+
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
2020
|
+
);
|
|
2021
|
+
if (labeledIds.size === 0) return edges;
|
|
2022
|
+
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
2023
|
+
const edgeById = /* @__PURE__ */ new Map();
|
|
2024
|
+
for (const e of edges) edgeById.set(e.id, e);
|
|
2025
|
+
const \u533A\u9593cache = /* @__PURE__ */ new Map();
|
|
2026
|
+
const \u533A\u9593 = (id, d) => {
|
|
2027
|
+
const hit = \u533A\u9593cache.get(id);
|
|
2028
|
+
if (hit) return hit;
|
|
2029
|
+
const segs = flattenPathSegments(extractPathSegments(d));
|
|
2030
|
+
\u533A\u9593cache.set(id, segs);
|
|
2031
|
+
return segs;
|
|
2032
|
+
};
|
|
2033
|
+
const \u5916\u63A5cache = /* @__PURE__ */ new Map();
|
|
2034
|
+
const \u5916\u63A5 = (id, d) => {
|
|
2035
|
+
const hit = \u5916\u63A5cache.get(id);
|
|
2036
|
+
if (hit !== void 0) return hit;
|
|
2037
|
+
const segs = \u533A\u9593(id, d);
|
|
2038
|
+
if (segs.length === 0) {
|
|
2039
|
+
\u5916\u63A5cache.set(id, null);
|
|
2040
|
+
return null;
|
|
2041
|
+
}
|
|
2042
|
+
let x1 = Infinity, y1 = Infinity, x2 = -Infinity, y2 = -Infinity;
|
|
2043
|
+
for (const g of segs) {
|
|
2044
|
+
x1 = Math.min(x1, g.x1, g.x2);
|
|
2045
|
+
y1 = Math.min(y1, g.y1, g.y2);
|
|
2046
|
+
x2 = Math.max(x2, g.x1, g.x2);
|
|
2047
|
+
y2 = Math.max(y2, g.y1, g.y2);
|
|
2048
|
+
}
|
|
2049
|
+
const box = { x: x1, y: y1, w: x2 - x1, h: y2 - y1 };
|
|
2050
|
+
\u5916\u63A5cache.set(id, box);
|
|
2051
|
+
return box;
|
|
2052
|
+
};
|
|
2053
|
+
const \u9060\u3044 = (box, edgeId, d, \u4F59\u88D5) => {
|
|
2054
|
+
const b = \u5916\u63A5(edgeId, d);
|
|
2055
|
+
if (!b) return true;
|
|
2056
|
+
const dx = Math.max(0, Math.max(box.x - (b.x + b.w), b.x - (box.x + box.w)));
|
|
2057
|
+
const dy = Math.max(0, Math.max(box.y - (b.y + b.h), b.y - (box.y + box.h)));
|
|
2058
|
+
return Math.hypot(dx, dy) >= \u4F59\u88D5;
|
|
2059
|
+
};
|
|
2060
|
+
const \u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
|
|
2061
|
+
const shiftMap = /* @__PURE__ */ new Map();
|
|
2062
|
+
const getShift = (id) => shiftMap.get(id) ?? { dx: 0, dy: 0 };
|
|
2063
|
+
const labelBBoxes = () => {
|
|
2064
|
+
const out = [];
|
|
2065
|
+
for (const e of edges) {
|
|
2066
|
+
if (!labeledIds.has(e.id)) continue;
|
|
2067
|
+
const base = computeLabelBBoxWorld(e);
|
|
2068
|
+
const s = getShift(e.id);
|
|
2069
|
+
out.push({ kind: "edge-label", id: e.id, x: base.x + s.dx, y: base.y + s.dy, w: base.w, h: base.h });
|
|
2070
|
+
}
|
|
2071
|
+
return out;
|
|
2072
|
+
};
|
|
2073
|
+
const \u96E2\u308C\u91CF = (a, b) => ({
|
|
2074
|
+
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
2075
|
+
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
2076
|
+
});
|
|
2077
|
+
const \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
2078
|
+
...box,
|
|
2079
|
+
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
2080
|
+
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
2081
|
+
});
|
|
2082
|
+
for (let iter = 0; iter < maxIter; iter += 1) {
|
|
2083
|
+
const labels = labelBBoxes();
|
|
2084
|
+
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
2085
|
+
let \u52D5\u3044\u305F = false;
|
|
2086
|
+
const \u4ECA\u56DE\u52D5\u304B\u3057\u305F = /* @__PURE__ */ new Set();
|
|
2087
|
+
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => {
|
|
2088
|
+
for (const obs of \u7BB1\u305F\u3061) {
|
|
2089
|
+
const \u524D = computeGap(label, obs);
|
|
2090
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
2091
|
+
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return false;
|
|
2092
|
+
}
|
|
2093
|
+
for (const \u4ED6 of labels) {
|
|
2094
|
+
if (\u4ED6.id === label.id) continue;
|
|
2095
|
+
const \u524D = computeGap(label, \u4ED6);
|
|
2096
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
2097
|
+
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return false;
|
|
2098
|
+
}
|
|
2099
|
+
const \u81EA\u5206 = edgeById.get(label.id);
|
|
2100
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
2101
|
+
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
2102
|
+
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
2103
|
+
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
2104
|
+
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return false;
|
|
2105
|
+
}
|
|
2106
|
+
for (const \u76F8\u624B of edges) {
|
|
2107
|
+
if (\u76F8\u624B.id === label.id) continue;
|
|
2108
|
+
if (\u9060\u3044(label, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE) && \u9060\u3044(\u884C\u304D\u5148, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE)) continue;
|
|
2109
|
+
const segs = segmentsExcludingOwnPath(\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
2110
|
+
if (segs.length === 0) continue;
|
|
2111
|
+
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
2112
|
+
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
2113
|
+
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return false;
|
|
2114
|
+
}
|
|
2115
|
+
return true;
|
|
2116
|
+
};
|
|
2117
|
+
const \u9003\u304C\u3059 = (label, \u5019\u88DC) => {
|
|
2118
|
+
for (const c of \u5019\u88DC.slice().sort((a, b) => a.amount - b.amount)) {
|
|
2119
|
+
if (c.amount <= 0) continue;
|
|
2120
|
+
const \u884C\u304D\u5148 = \u52D5\u304B\u3057\u305F\u7BB1(label, c.axis, c.sign, c.amount);
|
|
2121
|
+
if (!\u5B89\u5168\u304B(label, \u884C\u304D\u5148)) continue;
|
|
2122
|
+
const cur = getShift(label.id);
|
|
2123
|
+
const dx = c.axis === "x" ? cur.dx + c.sign * c.amount : cur.dx;
|
|
2124
|
+
const dy = c.axis === "y" ? cur.dy + c.sign * c.amount : cur.dy;
|
|
2125
|
+
if (Math.hypot(dx, dy) > \u6700\u5927\u79FB\u52D5) continue;
|
|
2126
|
+
shiftMap.set(label.id, { dx, dy });
|
|
2127
|
+
\u4ECA\u56DE\u52D5\u304B\u3057\u305F.add(label.id);
|
|
2128
|
+
return true;
|
|
2129
|
+
}
|
|
2130
|
+
return false;
|
|
2131
|
+
};
|
|
2132
|
+
const \u77E9\u5F62\u306E\u5019\u88DC = (label, static\u7BB1, required) => {
|
|
2133
|
+
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
2134
|
+
const \u5411\u304D = (axis) => {
|
|
2135
|
+
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
2136
|
+
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
2137
|
+
return a >= b ? 1 : -1;
|
|
2138
|
+
};
|
|
2139
|
+
return [
|
|
2140
|
+
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
2141
|
+
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
2142
|
+
];
|
|
2143
|
+
};
|
|
2144
|
+
for (const label of labels) {
|
|
2145
|
+
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
|
|
2146
|
+
for (const obs of \u7BB1\u305F\u3061) {
|
|
2147
|
+
const required = requiredNearClearance(label.kind, obs.kind);
|
|
2148
|
+
if (required <= 0) continue;
|
|
2149
|
+
if (computeGap(label, obs) >= required) continue;
|
|
2150
|
+
if (\u9003\u304C\u3059(label, \u77E9\u5F62\u306E\u5019\u88DC(label, obs, required))) {
|
|
2151
|
+
\u52D5\u3044\u305F = true;
|
|
2152
|
+
break;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
const \u672D\u9806 = /* @__PURE__ */ new Map();
|
|
2157
|
+
edges.forEach((e, i) => \u672D\u9806.set(e.id, i));
|
|
2158
|
+
for (let i = 0; i < labels.length; i += 1) {
|
|
2159
|
+
for (let j = i + 1; j < labels.length; j += 1) {
|
|
2160
|
+
const a = labels[i];
|
|
2161
|
+
const b = labels[j];
|
|
2162
|
+
if (computeGap(a, b) >= \u672D\u306E\u8981\u6C42) continue;
|
|
2163
|
+
const aIsLater = \u672D\u9806.get(a.id) > \u672D\u9806.get(b.id);
|
|
2164
|
+
const moveBox = aIsLater ? a : b;
|
|
2165
|
+
const staticBox = aIsLater ? b : a;
|
|
2166
|
+
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(moveBox.id)) continue;
|
|
2167
|
+
if (\u9003\u304C\u3059(moveBox, \u77E9\u5F62\u306E\u5019\u88DC(moveBox, staticBox, \u672D\u306E\u8981\u6C42))) \u52D5\u3044\u305F = true;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
for (const label of labels) {
|
|
2171
|
+
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
|
|
2172
|
+
const \u81EA\u5206 = edgeById.get(label.id);
|
|
2173
|
+
if (!\u81EA\u5206) continue;
|
|
2174
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d);
|
|
2175
|
+
for (const \u4ED6 of edges) {
|
|
2176
|
+
if (\u4ED6.id === label.id) continue;
|
|
2177
|
+
if (\u81EA\u5206.from === \u4ED6.from && \u81EA\u5206.to === \u4ED6.to) continue;
|
|
2178
|
+
if (\u9060\u3044(label, \u4ED6.id, \u4ED6.d, LABEL_TO_PATH_CLEARANCE)) continue;
|
|
2179
|
+
const segs = segmentsExcludingOwnPath(\u533A\u9593(\u4ED6.id, \u4ED6.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
2180
|
+
if (segs.length === 0) continue;
|
|
2181
|
+
const gap = measurePathLabelGapSegs(segs, label);
|
|
2182
|
+
if (gap >= LABEL_TO_PATH_CLEARANCE) continue;
|
|
2183
|
+
const \u82AF = { x: label.x + label.w / 2, y: label.y + label.h / 2 };
|
|
2184
|
+
let \u8FD1\u3044\u70B9 = null;
|
|
2185
|
+
let \u6700\u77ED = Infinity;
|
|
2186
|
+
for (const seg of segs) {
|
|
2187
|
+
for (const \u70B9 of [
|
|
2188
|
+
{ x: seg.x1, y: seg.y1 },
|
|
2189
|
+
{ x: seg.x2, y: seg.y2 },
|
|
2190
|
+
{ x: (seg.x1 + seg.x2) / 2, y: (seg.y1 + seg.y2) / 2 }
|
|
2191
|
+
]) {
|
|
2192
|
+
const d = Math.hypot(\u70B9.x - \u82AF.x, \u70B9.y - \u82AF.y);
|
|
2193
|
+
if (d < \u6700\u77ED) {
|
|
2194
|
+
\u6700\u77ED = d;
|
|
2195
|
+
\u8FD1\u3044\u70B9 = \u70B9;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
if (!\u8FD1\u3044\u70B9) continue;
|
|
2200
|
+
const \u5FC5\u8981\u91CF = (axis, sign) => {
|
|
2201
|
+
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
2202
|
+
let lo = 0;
|
|
2203
|
+
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
2204
|
+
while (!\u6E80\u305F\u3059(hi)) {
|
|
2205
|
+
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
2206
|
+
lo = hi;
|
|
2207
|
+
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
2208
|
+
}
|
|
2209
|
+
for (let k = 0; k < 12; k += 1) {
|
|
2210
|
+
const mid = (lo + hi) / 2;
|
|
2211
|
+
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
2212
|
+
else lo = mid;
|
|
2213
|
+
}
|
|
2214
|
+
return hi + NEAR_MISS_MARGIN;
|
|
2215
|
+
};
|
|
2216
|
+
const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
|
|
2217
|
+
const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
|
|
2218
|
+
const \u5019\u88DC = [];
|
|
2219
|
+
for (const c of [
|
|
2220
|
+
{ axis: "x", sign: dx >= 0 ? 1 : -1 },
|
|
2221
|
+
{ axis: "y", sign: dy >= 0 ? 1 : -1 },
|
|
2222
|
+
// 近い点から離れる向きが塞がれていることがある。 反対向きも候補に入れる。
|
|
2223
|
+
{ axis: "x", sign: dx >= 0 ? -1 : 1 },
|
|
2224
|
+
{ axis: "y", sign: dy >= 0 ? -1 : 1 }
|
|
2225
|
+
]) {
|
|
2226
|
+
const \u91CF = \u5FC5\u8981\u91CF(c.axis, c.sign);
|
|
2227
|
+
if (\u91CF !== null) \u5019\u88DC.push({ ...c, amount: \u91CF });
|
|
2228
|
+
}
|
|
2229
|
+
if (\u9003\u304C\u3059(label, \u5019\u88DC)) {
|
|
2230
|
+
\u52D5\u3044\u305F = true;
|
|
2231
|
+
break;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
if (!\u52D5\u3044\u305F) break;
|
|
2236
|
+
}
|
|
2237
|
+
if (shiftMap.size === 0) return edges;
|
|
2238
|
+
return edges.map((e) => {
|
|
2239
|
+
const s = shiftMap.get(e.id);
|
|
2240
|
+
if (!s || s.dx === 0 && s.dy === 0) return e;
|
|
2241
|
+
return { ...e, labelX: e.labelX + s.dx, labelY: e.labelY + s.dy };
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2016
2244
|
function applyEdgeLabelAndPathShifts(edges, labelShiftMap, edgePathShiftMap) {
|
|
2017
2245
|
if (labelShiftMap.size === 0 && edgePathShiftMap.size === 0) return edges;
|
|
2018
2246
|
return edges.map((e) => {
|
|
@@ -8571,7 +8799,11 @@ function \u672B\u5C3E\u3092\u8FBA\u306B\u6CBF\u3063\u3066\u305A\u3089\u3059(e, \
|
|
|
8571
8799
|
if (\u9577\u3055 < ENDPOINT_FAN_RUNUP + 1) return null;
|
|
8572
8800
|
const ux = (last.x1 - last.x2) / \u9577\u3055;
|
|
8573
8801
|
const uy = (last.y1 - last.y2) / \u9577\u3055;
|
|
8574
|
-
const
|
|
8802
|
+
const head = segs[0];
|
|
8803
|
+
const \u6700\u5F8C\u306E\u533A\u9593\u304C\u8D77\u70B9\u3092\u542B\u3080 = Math.abs(last.x1 - head.x1) <= 0.5 && Math.abs(last.y1 - head.y1) <= 0.5;
|
|
8804
|
+
const \u52A9\u8D70\u4E0A\u9650 = \u6700\u5F8C\u306E\u533A\u9593\u304C\u8D77\u70B9\u3092\u542B\u3080 ? \u9577\u3055 - EDGE_STUB_OUT : \u9577\u3055;
|
|
8805
|
+
const \u4E38\u3081\u305F\u52A9\u8D70 = Math.min(\u52A9\u8D70, \u52A9\u8D70\u4E0A\u9650);
|
|
8806
|
+
if (\u4E38\u3081\u305F\u52A9\u8D70 < ENDPOINT_FAN_RUNUP) return null;
|
|
8575
8807
|
const \u6298\u308C1 = { x: last.x2 + ux * \u4E38\u3081\u305F\u52A9\u8D70, y: last.y2 + uy * \u4E38\u3081\u305F\u52A9\u8D70 };
|
|
8576
8808
|
const \u6298\u308C2 = \u6A2A ? { x: \u6298\u308C1.x, y: \u6298\u308C1.y + d } : { x: \u6298\u308C1.x + d, y: \u6298\u308C1.y };
|
|
8577
8809
|
const \u7D42\u70B9 = \u6A2A ? { x: last.x2, y: last.y2 + d } : { x: last.x2 + d, y: last.y2 };
|
|
@@ -8583,7 +8815,6 @@ function \u672B\u5C3E\u3092\u8FBA\u306B\u6CBF\u3063\u3066\u305A\u3089\u3059(e, \
|
|
|
8583
8815
|
if (nodes.some((n) => n.id !== e.from && n.id !== e.to && segmentsCrossRect(\u65B0\u533A\u9593, nodeRect(n)))) {
|
|
8584
8816
|
return null;
|
|
8585
8817
|
}
|
|
8586
|
-
const head = segs[0];
|
|
8587
8818
|
const parts = [`M ${head.x1} ${head.y1}`];
|
|
8588
8819
|
for (let i = 0; i < segs.length - 1; i++) {
|
|
8589
8820
|
const s = segs[i];
|
|
@@ -9084,6 +9315,14 @@ function layout(diag) {
|
|
|
9084
9315
|
hopEdgeCrossings(edges);
|
|
9085
9316
|
repositionParallelLabels(edges, nodesFinal);
|
|
9086
9317
|
slideLabelsAlongOwnPath(edges, nodesFinal);
|
|
9318
|
+
if (!isChainEdgeLabelDisabled()) {
|
|
9319
|
+
edges = resolveLabelClearanceAfterPathPasses(
|
|
9320
|
+
edges,
|
|
9321
|
+
collectBBoxes(containedLanesFinal, nodesFinal, []).filter(
|
|
9322
|
+
(b) => b.kind === "node" || b.kind === "lane-label"
|
|
9323
|
+
)
|
|
9324
|
+
);
|
|
9325
|
+
}
|
|
9087
9326
|
const bboxes = collectBBoxes(containedLanesFinal, nodesFinal, edges);
|
|
9088
9327
|
const collisions = detectCollisions(bboxes, edges);
|
|
9089
9328
|
const nearCollisions = detectNearCollisions(bboxes, edges);
|