@cardenelabs/cdl 0.47.0 → 0.49.0
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 +57 -1
- package/dist/index.cjs +230 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +230 -105
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +219 -101
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +219 -101
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/collisions.ts +293 -139
- package/src/layout/edges.ts +95 -23
- package/src/layout-with-validation.ts +4 -2
- package/src/visual-validate.ts +62 -6
package/dist/react.cjs
CHANGED
|
@@ -1841,7 +1841,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
1841
1841
|
{ axis: "y", sign: dy >= 0 ? 1 : -1 },
|
|
1842
1842
|
{ axis: "x", sign: dx >= 0 ? 1 : -1 }
|
|
1843
1843
|
];
|
|
1844
|
-
const \u52D5\u304B\u3057\u305F\
|
|
1844
|
+
const \u52D5\u304B\u3057\u305F\u7BB12 = (axis, sign) => ({
|
|
1845
1845
|
...label,
|
|
1846
1846
|
x: label.x + (axis === "x" ? sign * \u91CF : 0),
|
|
1847
1847
|
y: label.y + (axis === "y" ? sign * \u91CF : 0)
|
|
@@ -1866,7 +1866,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
1866
1866
|
return false;
|
|
1867
1867
|
};
|
|
1868
1868
|
for (const \u5019 of \u5019\u88DC) {
|
|
1869
|
-
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\
|
|
1869
|
+
if (\u60AA\u304F\u3059\u308B(\u52D5\u304B\u3057\u305F\u7BB12(\u5019.axis, \u5019.sign))) continue;
|
|
1870
1870
|
out.push({
|
|
1871
1871
|
target: { kind: "label", id: label.id },
|
|
1872
1872
|
axis: \u5019.axis,
|
|
@@ -2032,15 +2032,7 @@ function resolveEdgeLabelOverlapsWithChainAndPropagate(edges, obstacles, minClea
|
|
|
2032
2032
|
edgePathShifts: edgePathShiftMap
|
|
2033
2033
|
};
|
|
2034
2034
|
}
|
|
2035
|
-
function
|
|
2036
|
-
if (edges.length === 0) return edges;
|
|
2037
|
-
const labeledIds = new Set(
|
|
2038
|
-
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
2039
|
-
);
|
|
2040
|
-
if (labeledIds.size === 0) return edges;
|
|
2041
|
-
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
2042
|
-
const edgeById = /* @__PURE__ */ new Map();
|
|
2043
|
-
for (const e of edges) edgeById.set(e.id, e);
|
|
2035
|
+
function \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B() {
|
|
2044
2036
|
const \u533A\u9593cache = /* @__PURE__ */ new Map();
|
|
2045
2037
|
const \u533A\u9593 = (id, d) => {
|
|
2046
2038
|
const hit = \u533A\u9593cache.get(id);
|
|
@@ -2076,7 +2068,143 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
2076
2068
|
const dy = Math.max(0, Math.max(box.y - (b.y + b.h), b.y - (box.y + box.h)));
|
|
2077
2069
|
return Math.hypot(dx, dy) >= \u4F59\u88D5;
|
|
2078
2070
|
};
|
|
2079
|
-
|
|
2071
|
+
return { \u533A\u9593, \u9060\u3044 };
|
|
2072
|
+
}
|
|
2073
|
+
var \u672D\u306E\u6700\u5927\u79FB\u52D5 = CHAIN_SHIFT_MAX_ACCUMULATED * 2;
|
|
2074
|
+
var \u96E2\u308C\u91CF = (a, b) => ({
|
|
2075
|
+
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
2076
|
+
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
2077
|
+
});
|
|
2078
|
+
var \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
2079
|
+
...box,
|
|
2080
|
+
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
2081
|
+
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
2082
|
+
});
|
|
2083
|
+
function \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, label, \u884C\u304D\u5148) {
|
|
2084
|
+
const { \u7BB1\u305F\u3061, labels, edges, edgeById, \u6E2C\u308A\u65B9 } = \u5468\u308A;
|
|
2085
|
+
for (const obs of \u7BB1\u305F\u3061) {
|
|
2086
|
+
const \u524D = computeGap(label, obs);
|
|
2087
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
2088
|
+
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return { \u76F8\u624B: `${obs.kind}:${obs.id}`, \u524D, \u5F8C };
|
|
2089
|
+
}
|
|
2090
|
+
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
2091
|
+
for (const \u4ED6 of labels) {
|
|
2092
|
+
if (\u4ED6.id === label.id) continue;
|
|
2093
|
+
const \u524D = computeGap(label, \u4ED6);
|
|
2094
|
+
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
2095
|
+
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return { \u76F8\u624B: `edge-label:${\u4ED6.id}`, \u524D, \u5F8C };
|
|
2096
|
+
}
|
|
2097
|
+
const \u81EA\u5206 = edgeById.get(label.id);
|
|
2098
|
+
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
2099
|
+
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
2100
|
+
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
2101
|
+
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
2102
|
+
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return { \u76F8\u624B: "\u81EA\u5206\u306E\u7DDA", \u524D, \u5F8C };
|
|
2103
|
+
}
|
|
2104
|
+
for (const \u76F8\u624B of edges) {
|
|
2105
|
+
if (\u76F8\u624B.id === label.id) continue;
|
|
2106
|
+
if (\u6E2C\u308A\u65B9.\u9060\u3044(label, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE) && \u6E2C\u308A\u65B9.\u9060\u3044(\u884C\u304D\u5148, \u76F8\u624B.id, \u76F8\u624B.d, LABEL_TO_PATH_CLEARANCE)) continue;
|
|
2107
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
2108
|
+
if (segs.length === 0) continue;
|
|
2109
|
+
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
2110
|
+
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
2111
|
+
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return { \u76F8\u624B: `edge-path:${\u76F8\u624B.id}`, \u524D, \u5F8C };
|
|
2112
|
+
}
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
function \u77E9\u5F62\u306E\u5019\u88DC(label, static\u7BB1, required) {
|
|
2116
|
+
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
2117
|
+
const \u5411\u304D = (axis) => {
|
|
2118
|
+
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
2119
|
+
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
2120
|
+
return a >= b ? 1 : -1;
|
|
2121
|
+
};
|
|
2122
|
+
return [
|
|
2123
|
+
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
2124
|
+
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
2125
|
+
];
|
|
2126
|
+
}
|
|
2127
|
+
function \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign) {
|
|
2128
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
2129
|
+
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
2130
|
+
let lo = 0;
|
|
2131
|
+
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
2132
|
+
while (!\u6E80\u305F\u3059(hi)) {
|
|
2133
|
+
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
2134
|
+
lo = hi;
|
|
2135
|
+
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
2136
|
+
}
|
|
2137
|
+
for (let k = 0; k < 12; k += 1) {
|
|
2138
|
+
const mid = (lo + hi) / 2;
|
|
2139
|
+
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
2140
|
+
else lo = mid;
|
|
2141
|
+
}
|
|
2142
|
+
return hi + NEAR_MISS_MARGIN;
|
|
2143
|
+
}
|
|
2144
|
+
var \u5411\u304D\u306E\u9806 = [
|
|
2145
|
+
{ axis: "y", sign: -1, \u540D\u524D: "\u4E0A" },
|
|
2146
|
+
{ axis: "y", sign: 1, \u540D\u524D: "\u4E0B" },
|
|
2147
|
+
{ axis: "x", sign: -1, \u540D\u524D: "\u5DE6" },
|
|
2148
|
+
{ axis: "x", sign: 1, \u540D\u524D: "\u53F3" }
|
|
2149
|
+
];
|
|
2150
|
+
function \u672D\u304C\u52D5\u3051\u306A\u3044\u7406\u7531(labelId, \u76F8\u624B, edges, obstacles) {
|
|
2151
|
+
const edgeById = new Map(edges.map((e) => [e.id, e]));
|
|
2152
|
+
const \u81EA\u5206 = edgeById.get(labelId);
|
|
2153
|
+
if (!\u81EA\u5206) return null;
|
|
2154
|
+
const labels = edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => ({ kind: "edge-label", id: e.id, ...computeLabelBBoxWorld(e) }));
|
|
2155
|
+
const label = labels.find((l) => l.id === labelId);
|
|
2156
|
+
if (!label) return null;
|
|
2157
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
2158
|
+
const \u5468\u308A = {
|
|
2159
|
+
\u7BB1\u305F\u3061: obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label"),
|
|
2160
|
+
labels,
|
|
2161
|
+
edges,
|
|
2162
|
+
edgeById,
|
|
2163
|
+
\u6E2C\u308A\u65B9
|
|
2164
|
+
};
|
|
2165
|
+
const \u5019\u88DC = [];
|
|
2166
|
+
if (\u76F8\u624B.kind === "edge-path") {
|
|
2167
|
+
const \u7DDA = edgeById.get(\u76F8\u624B.id);
|
|
2168
|
+
if (!\u7DDA) return null;
|
|
2169
|
+
const segs = segmentsExcludingOwnPath(\u6E2C\u308A\u65B9.\u533A\u9593(\u7DDA.id, \u7DDA.d), \u6E2C\u308A\u65B9.\u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d));
|
|
2170
|
+
if (segs.length === 0) return null;
|
|
2171
|
+
const gap = measurePathLabelGapSegs(segs, label);
|
|
2172
|
+
for (const \u5411\u304D of \u5411\u304D\u306E\u9806) {
|
|
2173
|
+
\u5019\u88DC.push({ axis: \u5411\u304D.axis, sign: \u5411\u304D.sign, amount: \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, \u5411\u304D.axis, \u5411\u304D.sign) });
|
|
2174
|
+
}
|
|
2175
|
+
} else {
|
|
2176
|
+
const required = requiredNearClearance("edge-label", \u76F8\u624B.kind);
|
|
2177
|
+
for (const c of \u77E9\u5F62\u306E\u5019\u88DC(label, \u76F8\u624B, required)) {
|
|
2178
|
+
if (c.amount > 0) \u5019\u88DC.push(c);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
if (\u5019\u88DC.length === 0) return null;
|
|
2182
|
+
const \u7406\u7531 = [];
|
|
2183
|
+
for (const c of \u5019\u88DC) {
|
|
2184
|
+
const \u9806 = \u5411\u304D\u306E\u9806.findIndex((v) => v.axis === c.axis && v.sign === c.sign);
|
|
2185
|
+
const \u540D\u524D = \u5411\u304D\u306E\u9806[\u9806].\u540D\u524D;
|
|
2186
|
+
if (c.amount === null || c.amount > \u672D\u306E\u6700\u5927\u79FB\u52D5) {
|
|
2187
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=\u4E0A\u9650 ${\u672D\u306E\u6700\u5927\u79FB\u52D5} \u3092\u8D8A\u3048\u308B` });
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
const \u585E\u3050 = \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059(\u5468\u308A, label, \u52D5\u304B\u3057\u305F\u7BB1(label, c.axis, c.sign, c.amount));
|
|
2191
|
+
if (!\u585E\u3050) return null;
|
|
2192
|
+
\u7406\u7531.push({ \u9806, \u6587: `${\u540D\u524D}=${\u585E\u3050.\u76F8\u624B} (${\u585E\u3050.\u524D.toFixed(0)}\u2192${\u585E\u3050.\u5F8C.toFixed(0)})` });
|
|
2193
|
+
}
|
|
2194
|
+
return `\u9003\u3052\u5834\u306A\u3057: ${\u7406\u7531.sort((a, b) => a.\u9806 - b.\u9806).map((r) => r.\u6587).join(" / ")}`;
|
|
2195
|
+
}
|
|
2196
|
+
function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
2197
|
+
if (edges.length === 0) return edges;
|
|
2198
|
+
const labeledIds = new Set(
|
|
2199
|
+
edges.filter((e) => e.label?.trim() || e.sub?.trim()).map((e) => e.id)
|
|
2200
|
+
);
|
|
2201
|
+
if (labeledIds.size === 0) return edges;
|
|
2202
|
+
const \u7BB1\u305F\u3061 = obstacles.filter((o) => o.kind === "node" || o.kind === "lane-label");
|
|
2203
|
+
const edgeById = /* @__PURE__ */ new Map();
|
|
2204
|
+
for (const e of edges) edgeById.set(e.id, e);
|
|
2205
|
+
const \u6E2C\u308A\u65B9 = \u7DDA\u306E\u6E2C\u308A\u65B9\u3092\u4F5C\u308B();
|
|
2206
|
+
const { \u533A\u9593, \u9060\u3044 } = \u6E2C\u308A\u65B9;
|
|
2207
|
+
const \u6700\u5927\u79FB\u52D5 = \u672D\u306E\u6700\u5927\u79FB\u52D5;
|
|
2080
2208
|
const shiftMap = /* @__PURE__ */ new Map();
|
|
2081
2209
|
const getShift = (id) => shiftMap.get(id) ?? { dx: 0, dy: 0 };
|
|
2082
2210
|
const labelBBoxes = () => {
|
|
@@ -2089,50 +2217,12 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
2089
2217
|
}
|
|
2090
2218
|
return out;
|
|
2091
2219
|
};
|
|
2092
|
-
const \u96E2\u308C\u91CF = (a, b) => ({
|
|
2093
|
-
x: Math.max(a.x - (b.x + b.w), b.x - (a.x + a.w)),
|
|
2094
|
-
y: Math.max(a.y - (b.y + b.h), b.y - (a.y + a.h))
|
|
2095
|
-
});
|
|
2096
|
-
const \u52D5\u304B\u3057\u305F\u7BB1 = (box, axis, sign, amount) => ({
|
|
2097
|
-
...box,
|
|
2098
|
-
x: box.x + (axis === "x" ? sign * amount : 0),
|
|
2099
|
-
y: box.y + (axis === "y" ? sign * amount : 0)
|
|
2100
|
-
});
|
|
2101
2220
|
for (let iter = 0; iter < maxIter; iter += 1) {
|
|
2102
2221
|
const labels = labelBBoxes();
|
|
2103
2222
|
const \u672D\u306E\u8981\u6C42 = requiredNearClearance("edge-label", "edge-label");
|
|
2104
2223
|
let \u52D5\u3044\u305F = false;
|
|
2105
2224
|
const \u4ECA\u56DE\u52D5\u304B\u3057\u305F = /* @__PURE__ */ new Set();
|
|
2106
|
-
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => {
|
|
2107
|
-
for (const obs of \u7BB1\u305F\u3061) {
|
|
2108
|
-
const \u524D = computeGap(label, obs);
|
|
2109
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, obs);
|
|
2110
|
-
if (\u5F8C < Math.min(\u524D, requiredNearClearance("edge-label", obs.kind))) return false;
|
|
2111
|
-
}
|
|
2112
|
-
for (const \u4ED6 of labels) {
|
|
2113
|
-
if (\u4ED6.id === label.id) continue;
|
|
2114
|
-
const \u524D = computeGap(label, \u4ED6);
|
|
2115
|
-
const \u5F8C = computeGap(\u884C\u304D\u5148, \u4ED6);
|
|
2116
|
-
if (\u5F8C < Math.min(\u524D, \u672D\u306E\u8981\u6C42)) return false;
|
|
2117
|
-
}
|
|
2118
|
-
const \u81EA\u5206 = edgeById.get(label.id);
|
|
2119
|
-
const \u81EA\u5206\u306E\u533A\u9593 = \u81EA\u5206 ? \u533A\u9593(\u81EA\u5206.id, \u81EA\u5206.d) : [];
|
|
2120
|
-
if (\u81EA\u5206\u306E\u533A\u9593.length > 0) {
|
|
2121
|
-
const \u524D = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, label);
|
|
2122
|
-
const \u5F8C = measurePathLabelGapSegs(\u81EA\u5206\u306E\u533A\u9593, \u884C\u304D\u5148);
|
|
2123
|
-
if (\u5F8C > Math.max(\u524D, DIST_LABEL_PATH_MAX)) return false;
|
|
2124
|
-
}
|
|
2125
|
-
for (const \u76F8\u624B of edges) {
|
|
2126
|
-
if (\u76F8\u624B.id === label.id) continue;
|
|
2127
|
-
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;
|
|
2128
|
-
const segs = segmentsExcludingOwnPath(\u533A\u9593(\u76F8\u624B.id, \u76F8\u624B.d), \u81EA\u5206\u306E\u533A\u9593);
|
|
2129
|
-
if (segs.length === 0) continue;
|
|
2130
|
-
const \u524D = measurePathLabelGapSegs(segs, label);
|
|
2131
|
-
const \u5F8C = measurePathLabelGapSegs(segs, \u884C\u304D\u5148);
|
|
2132
|
-
if (\u5F8C < Math.min(\u524D, LABEL_TO_PATH_CLEARANCE)) return false;
|
|
2133
|
-
}
|
|
2134
|
-
return true;
|
|
2135
|
-
};
|
|
2225
|
+
const \u5B89\u5168\u304B = (label, \u884C\u304D\u5148) => \u672D\u306E\u79FB\u52D5\u3092\u585E\u3050\u76F8\u624B\u3092\u63A2\u3059({ \u7BB1\u305F\u3061, labels, edges, edgeById, \u6E2C\u308A\u65B9 }, label, \u884C\u304D\u5148) === null;
|
|
2136
2226
|
const \u9003\u304C\u3059 = (label, \u5019\u88DC) => {
|
|
2137
2227
|
for (const c of \u5019\u88DC.slice().sort((a, b) => a.amount - b.amount)) {
|
|
2138
2228
|
if (c.amount <= 0) continue;
|
|
@@ -2148,18 +2238,6 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
2148
2238
|
}
|
|
2149
2239
|
return false;
|
|
2150
2240
|
};
|
|
2151
|
-
const \u77E9\u5F62\u306E\u5019\u88DC = (label, static\u7BB1, required) => {
|
|
2152
|
-
const sep = \u96E2\u308C\u91CF(label, static\u7BB1);
|
|
2153
|
-
const \u5411\u304D = (axis) => {
|
|
2154
|
-
const a = axis === "x" ? label.x + label.w / 2 : label.y + label.h / 2;
|
|
2155
|
-
const b = axis === "x" ? static\u7BB1.x + static\u7BB1.w / 2 : static\u7BB1.y + static\u7BB1.h / 2;
|
|
2156
|
-
return a >= b ? 1 : -1;
|
|
2157
|
-
};
|
|
2158
|
-
return [
|
|
2159
|
-
{ axis: "x", sign: \u5411\u304D("x"), amount: required - sep.x + NEAR_MISS_MARGIN },
|
|
2160
|
-
{ axis: "y", sign: \u5411\u304D("y"), amount: required - sep.y + NEAR_MISS_MARGIN }
|
|
2161
|
-
];
|
|
2162
|
-
};
|
|
2163
2241
|
for (const label of labels) {
|
|
2164
2242
|
if (\u4ECA\u56DE\u52D5\u304B\u3057\u305F.has(label.id)) continue;
|
|
2165
2243
|
for (const obs of \u7BB1\u305F\u3061) {
|
|
@@ -2216,22 +2294,7 @@ function resolveLabelClearanceAfterPathPasses(edges, obstacles, maxIter = 8) {
|
|
|
2216
2294
|
}
|
|
2217
2295
|
}
|
|
2218
2296
|
if (!\u8FD1\u3044\u70B9) continue;
|
|
2219
|
-
const \u5FC5\u8981\u91CF = (axis, sign) =>
|
|
2220
|
-
const \u6E80\u305F\u3059 = (\u91CF) => measurePathLabelGapSegs(segs, \u52D5\u304B\u3057\u305F\u7BB1(label, axis, sign, \u91CF)) >= LABEL_TO_PATH_CLEARANCE;
|
|
2221
|
-
let lo = 0;
|
|
2222
|
-
let hi = Math.max(4, LABEL_TO_PATH_CLEARANCE - gap);
|
|
2223
|
-
while (!\u6E80\u305F\u3059(hi)) {
|
|
2224
|
-
if (hi >= \u6700\u5927\u79FB\u52D5) return null;
|
|
2225
|
-
lo = hi;
|
|
2226
|
-
hi = Math.min(hi * 2, \u6700\u5927\u79FB\u52D5);
|
|
2227
|
-
}
|
|
2228
|
-
for (let k = 0; k < 12; k += 1) {
|
|
2229
|
-
const mid = (lo + hi) / 2;
|
|
2230
|
-
if (\u6E80\u305F\u3059(mid)) hi = mid;
|
|
2231
|
-
else lo = mid;
|
|
2232
|
-
}
|
|
2233
|
-
return hi + NEAR_MISS_MARGIN;
|
|
2234
|
-
};
|
|
2297
|
+
const \u5FC5\u8981\u91CF = (axis, sign) => \u7DDA\u304B\u3089\u96E2\u308C\u308B\u91CF(segs, label, gap, axis, sign);
|
|
2235
2298
|
const dx = \u82AF.x - \u8FD1\u3044\u70B9.x;
|
|
2236
2299
|
const dy = \u82AF.y - \u8FD1\u3044\u70B9.y;
|
|
2237
2300
|
const \u5019\u88DC = [];
|
|
@@ -4967,9 +5030,15 @@ function visualValidateLaid(laid, diag, opts) {
|
|
|
4967
5030
|
const counts = emptyCounts();
|
|
4968
5031
|
const profile = "production";
|
|
4969
5032
|
const skipped = SKIPPED_AXES[profile];
|
|
4970
|
-
const push = (axis, detail, severity = "error") => {
|
|
5033
|
+
const push = (axis, detail, severity = "error", reason) => {
|
|
4971
5034
|
if (skipped.has(axis)) return;
|
|
4972
|
-
violations.push({
|
|
5035
|
+
violations.push({
|
|
5036
|
+
axis,
|
|
5037
|
+
diagramId: safeDiagramId(diag),
|
|
5038
|
+
detail,
|
|
5039
|
+
severity,
|
|
5040
|
+
...reason !== void 0 ? { reason } : {}
|
|
5041
|
+
});
|
|
4973
5042
|
counts[axis]++;
|
|
4974
5043
|
};
|
|
4975
5044
|
return visualValidateInner(
|
|
@@ -5281,10 +5350,19 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
|
|
|
5281
5350
|
}
|
|
5282
5351
|
}
|
|
5283
5352
|
const nears = detectNearCollisions(bboxes, laid.edges);
|
|
5353
|
+
const \u672D\u306E\u969C\u5BB3\u7269 = bboxes.filter((b) => b.kind === "node" || b.kind === "lane-label");
|
|
5354
|
+
const \u8FBA\u306E\u9806 = new Map(laid.edges.map((e, i) => [e.id, i]));
|
|
5284
5355
|
for (const n of nears) {
|
|
5356
|
+
const a\u306F\u672D = n.a.kind === "edge-label";
|
|
5357
|
+
const b\u306F\u672D = n.b.kind === "edge-label";
|
|
5358
|
+
const \u52D5\u304B\u3059 = a\u306F\u672D && b\u306F\u672D ? (\u8FBA\u306E\u9806.get(n.a.id) ?? 0) > (\u8FBA\u306E\u9806.get(n.b.id) ?? 0) ? n.a : n.b : a\u306F\u672D ? n.a : b\u306F\u672D ? n.b : null;
|
|
5359
|
+
const \u76F8\u624B = \u52D5\u304B\u3059 === n.a ? n.b : n.a;
|
|
5360
|
+
const reason = \u52D5\u304B\u3059 !== null ? \u672D\u304C\u52D5\u3051\u306A\u3044\u7406\u7531(\u52D5\u304B\u3059.id, \u76F8\u624B, laid.edges, \u672D\u306E\u969C\u5BB3\u7269) ?? void 0 : void 0;
|
|
5285
5361
|
push(
|
|
5286
5362
|
"clearance",
|
|
5287
|
-
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)
|
|
5363
|
+
`${n.a.kind}:${n.a.id} \u2194 ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)`,
|
|
5364
|
+
"error",
|
|
5365
|
+
reason
|
|
5288
5366
|
);
|
|
5289
5367
|
}
|
|
5290
5368
|
const PROXIMITY_WARN_THRESHOLD = DIST_LABEL_PATH_MAX;
|
|
@@ -8282,33 +8360,73 @@ function placeAvoidingSiblingPaths(args) {
|
|
|
8282
8360
|
}
|
|
8283
8361
|
};
|
|
8284
8362
|
consider([...wants]);
|
|
8363
|
+
const \u62BC\u3059\u91CF = (idx, side, pos) => {
|
|
8364
|
+
let \u8981\u308B = 0;
|
|
8365
|
+
for (const i of idx) {
|
|
8366
|
+
const box = boxes[i];
|
|
8367
|
+
const rect = { x: box.x, y: pos[i] - box.h / 2, w: box.w, h: box.h };
|
|
8368
|
+
for (const probe of probeSegs[i]) {
|
|
8369
|
+
if (measurePathLabelGapSegs(probe.segs, rect) >= CLEARANCE_PATH_LABEL) continue;
|
|
8370
|
+
let lo = Infinity;
|
|
8371
|
+
let hi = -Infinity;
|
|
8372
|
+
for (const seg of probe.segs) {
|
|
8373
|
+
const c = clipY(seg, box.x, box.x + box.w);
|
|
8374
|
+
if (!c) continue;
|
|
8375
|
+
lo = Math.min(lo, c.lo);
|
|
8376
|
+
hi = Math.max(hi, c.hi);
|
|
8377
|
+
}
|
|
8378
|
+
if (lo > hi) continue;
|
|
8379
|
+
const \u884C\u304D\u5148 = side === "above" ? lo - box.h / 2 - CLEARANCE_PATH_LABEL : hi + box.h / 2 + CLEARANCE_PATH_LABEL;
|
|
8380
|
+
\u8981\u308B = Math.max(\u8981\u308B, side === "above" ? pos[i] - \u884C\u304D\u5148 : \u884C\u304D\u5148 - pos[i]);
|
|
8381
|
+
}
|
|
8382
|
+
}
|
|
8383
|
+
return Math.max(0, \u8981\u308B);
|
|
8384
|
+
};
|
|
8285
8385
|
if (bands.some((b) => b !== null)) {
|
|
8286
8386
|
for (let split = 0; split <= order.length; split++) {
|
|
8287
8387
|
const above = order.slice(0, split);
|
|
8288
8388
|
const below = order.slice(split);
|
|
8289
|
-
const
|
|
8290
|
-
|
|
8291
|
-
[above, "above"],
|
|
8292
|
-
[below, "below"]
|
|
8293
|
-
]) {
|
|
8294
|
-
if (idx.length === 0) continue;
|
|
8295
|
-
const ys = spread(idx);
|
|
8296
|
-
let shift = 0;
|
|
8297
|
-
for (let k = 0; k < idx.length; k++) {
|
|
8298
|
-
const band = bands[idx[k]];
|
|
8299
|
-
if (!band) continue;
|
|
8300
|
-
shift = side === "above" ? Math.min(shift, band.lo - ys[k]) : Math.max(shift, band.hi - ys[k]);
|
|
8301
|
-
}
|
|
8302
|
-
for (let k = 0; k < idx.length; k++) out[idx[k]] = ys[k] + shift;
|
|
8303
|
-
}
|
|
8304
|
-
if (above.length > 0 && below.length > 0) {
|
|
8389
|
+
const \u9593\u3092\u7A7A\u3051\u308B = (out) => {
|
|
8390
|
+
if (above.length === 0 || below.length === 0) return;
|
|
8305
8391
|
const lastIdx = above[above.length - 1];
|
|
8306
8392
|
const firstIdx = below[0];
|
|
8307
8393
|
const need = (sizes[lastIdx] + sizes[firstIdx]) / 2 + CLEARANCE_LABEL_LABEL;
|
|
8308
8394
|
const deficit = need - (out[firstIdx] - out[lastIdx]);
|
|
8309
|
-
if (deficit > 0) for (const i of below) out[i]
|
|
8310
|
-
}
|
|
8311
|
-
|
|
8395
|
+
if (deficit > 0) for (const i of below) out[i] = out[i] + deficit;
|
|
8396
|
+
};
|
|
8397
|
+
const \u5E2F\u3092\u8D8A\u3048\u308B = () => {
|
|
8398
|
+
const out = new Array(members.length);
|
|
8399
|
+
for (const [idx, side] of [
|
|
8400
|
+
[above, "above"],
|
|
8401
|
+
[below, "below"]
|
|
8402
|
+
]) {
|
|
8403
|
+
if (idx.length === 0) continue;
|
|
8404
|
+
const ys = spread(idx);
|
|
8405
|
+
let shift = 0;
|
|
8406
|
+
for (let k = 0; k < idx.length; k++) {
|
|
8407
|
+
const band = bands[idx[k]];
|
|
8408
|
+
if (!band) continue;
|
|
8409
|
+
shift = side === "above" ? Math.min(shift, band.lo - ys[k]) : Math.max(shift, band.hi - ys[k]);
|
|
8410
|
+
}
|
|
8411
|
+
for (let k = 0; k < idx.length; k++) out[idx[k]] = ys[k] + shift;
|
|
8412
|
+
}
|
|
8413
|
+
\u9593\u3092\u7A7A\u3051\u308B(out);
|
|
8414
|
+
return out;
|
|
8415
|
+
};
|
|
8416
|
+
const \u62BC\u3057\u3066\u8D8A\u3048\u308B = () => {
|
|
8417
|
+
const out = \u5E2F\u3092\u8D8A\u3048\u308B();
|
|
8418
|
+
for (let round = 0; round < 4; round += 1) {
|
|
8419
|
+
const a = \u62BC\u3059\u91CF(above, "above", out);
|
|
8420
|
+
const b = \u62BC\u3059\u91CF(below, "below", out);
|
|
8421
|
+
if (a === 0 && b === 0) return out;
|
|
8422
|
+
for (const i of above) out[i] = out[i] - a;
|
|
8423
|
+
for (const i of below) out[i] = out[i] + b;
|
|
8424
|
+
}
|
|
8425
|
+
return \u62BC\u3059\u91CF(above, "above", out) === 0 && \u62BC\u3059\u91CF(below, "below", out) === 0 ? out : null;
|
|
8426
|
+
};
|
|
8427
|
+
consider(\u5E2F\u3092\u8D8A\u3048\u308B());
|
|
8428
|
+
const \u62BC\u3057\u305F = \u62BC\u3057\u3066\u8D8A\u3048\u308B();
|
|
8429
|
+
if (\u62BC\u3057\u305F) consider(\u62BC\u3057\u305F);
|
|
8312
8430
|
}
|
|
8313
8431
|
}
|
|
8314
8432
|
const plain = spread(all);
|