@crazyhappyone/auto-graph 0.2.2 → 0.2.3
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/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +281 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -1
- package/dist/index.d.ts +85 -1
- package/dist/index.js +280 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4054,6 +4054,45 @@ function indentLines(values) {
|
|
|
4054
4054
|
return values.map(indent);
|
|
4055
4055
|
}
|
|
4056
4056
|
|
|
4057
|
+
// src/solver/pipeline/pipeline.ts
|
|
4058
|
+
var LayoutPipeline = class {
|
|
4059
|
+
phases = [];
|
|
4060
|
+
addPhase(phase) {
|
|
4061
|
+
this.phases.push(phase);
|
|
4062
|
+
return this;
|
|
4063
|
+
}
|
|
4064
|
+
addBefore(refName, phase) {
|
|
4065
|
+
const idx = this.phases.findIndex((p) => p.name === refName);
|
|
4066
|
+
if (idx === -1) throw new Error(`Phase "${refName}" not found`);
|
|
4067
|
+
this.phases.splice(idx, 0, phase);
|
|
4068
|
+
return this;
|
|
4069
|
+
}
|
|
4070
|
+
addAfter(refName, phase) {
|
|
4071
|
+
const idx = this.phases.findIndex((p) => p.name === refName);
|
|
4072
|
+
if (idx === -1) throw new Error(`Phase "${refName}" not found`);
|
|
4073
|
+
this.phases.splice(idx + 1, 0, phase);
|
|
4074
|
+
return this;
|
|
4075
|
+
}
|
|
4076
|
+
replacePhase(name, phase) {
|
|
4077
|
+
const idx = this.phases.findIndex((p) => p.name === name);
|
|
4078
|
+
if (idx === -1) throw new Error(`Phase "${name}" not found`);
|
|
4079
|
+
this.phases[idx] = phase;
|
|
4080
|
+
return this;
|
|
4081
|
+
}
|
|
4082
|
+
run(state) {
|
|
4083
|
+
for (const phase of this.phases) {
|
|
4084
|
+
const before = state.diagnostics.length;
|
|
4085
|
+
const start = performance.now();
|
|
4086
|
+
phase.run(state);
|
|
4087
|
+
state.phaseTrace.push({
|
|
4088
|
+
phase: phase.name,
|
|
4089
|
+
durationMs: performance.now() - start,
|
|
4090
|
+
diagnosticsAdded: state.diagnostics.length - before
|
|
4091
|
+
});
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
};
|
|
4095
|
+
|
|
4057
4096
|
// src/ir/diagnostics.ts
|
|
4058
4097
|
var DELIVERABILITY_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
|
|
4059
4098
|
"constraints.locked-target-not-moved",
|
|
@@ -5439,6 +5478,217 @@ function areCollinear2(a, b, c) {
|
|
|
5439
5478
|
return a.x === b.x && b.x === c.x || a.y === b.y && b.y === c.y;
|
|
5440
5479
|
}
|
|
5441
5480
|
|
|
5481
|
+
// src/solver/pipeline/quality.ts
|
|
5482
|
+
function scoreLayoutQuality(nodes, edges) {
|
|
5483
|
+
const diagnostics = [];
|
|
5484
|
+
const metrics = [];
|
|
5485
|
+
const overlapCount = countNodeOverlaps(nodes);
|
|
5486
|
+
const overlapScore = Math.max(0, 20 - overlapCount * 5);
|
|
5487
|
+
metrics.push({
|
|
5488
|
+
kind: "node-overlap",
|
|
5489
|
+
value: overlapCount,
|
|
5490
|
+
label: `${overlapCount} overlaps`
|
|
5491
|
+
});
|
|
5492
|
+
if (overlapCount > 0) {
|
|
5493
|
+
diagnostics.push({
|
|
5494
|
+
severity: "warning",
|
|
5495
|
+
code: "quality.node_overlap",
|
|
5496
|
+
message: `${overlapCount} node pair(s) overlap.`,
|
|
5497
|
+
detail: { overlapCount }
|
|
5498
|
+
});
|
|
5499
|
+
}
|
|
5500
|
+
const crossingCount = countEdgeCrossings(edges);
|
|
5501
|
+
const crossingScore = Math.max(0, 20 - crossingCount * 2);
|
|
5502
|
+
metrics.push({
|
|
5503
|
+
kind: "edge-crossing",
|
|
5504
|
+
value: crossingCount,
|
|
5505
|
+
label: `${crossingCount} crossings`
|
|
5506
|
+
});
|
|
5507
|
+
if (crossingCount > 0) {
|
|
5508
|
+
diagnostics.push({
|
|
5509
|
+
severity: "warning",
|
|
5510
|
+
code: "quality.edge_crossing",
|
|
5511
|
+
message: `${crossingCount} edge segment pair(s) cross.`,
|
|
5512
|
+
detail: { crossingCount }
|
|
5513
|
+
});
|
|
5514
|
+
}
|
|
5515
|
+
const totalBends = countTotalBends(edges);
|
|
5516
|
+
const bendScore = Math.max(0, 20 - totalBends * 0.5);
|
|
5517
|
+
metrics.push({
|
|
5518
|
+
kind: "bend-count",
|
|
5519
|
+
value: totalBends,
|
|
5520
|
+
label: `${totalBends} bends`
|
|
5521
|
+
});
|
|
5522
|
+
const backtrackCount = countBacktrackingEdges(edges);
|
|
5523
|
+
const backtrackScore = Math.max(0, 20 - backtrackCount * 5);
|
|
5524
|
+
metrics.push({
|
|
5525
|
+
kind: "route-backtrack",
|
|
5526
|
+
value: backtrackCount,
|
|
5527
|
+
label: `${backtrackCount} backtracking`
|
|
5528
|
+
});
|
|
5529
|
+
if (backtrackCount > 0) {
|
|
5530
|
+
diagnostics.push({
|
|
5531
|
+
severity: "warning",
|
|
5532
|
+
code: "quality.route_backtrack",
|
|
5533
|
+
message: `${backtrackCount} edge(s) are excessively long (>3\xD7 direct).`,
|
|
5534
|
+
detail: { backtrackCount }
|
|
5535
|
+
});
|
|
5536
|
+
}
|
|
5537
|
+
const labelCollisions = countLabelCollisions(nodes, edges);
|
|
5538
|
+
const labelScore = Math.max(0, 20 - labelCollisions * 3);
|
|
5539
|
+
metrics.push({
|
|
5540
|
+
kind: "label-collision",
|
|
5541
|
+
value: labelCollisions,
|
|
5542
|
+
label: `${labelCollisions} label collisions`
|
|
5543
|
+
});
|
|
5544
|
+
const score = Math.max(
|
|
5545
|
+
0,
|
|
5546
|
+
Math.min(
|
|
5547
|
+
100,
|
|
5548
|
+
overlapScore + crossingScore + bendScore + backtrackScore + labelScore
|
|
5549
|
+
)
|
|
5550
|
+
);
|
|
5551
|
+
return { metrics, score, diagnostics };
|
|
5552
|
+
}
|
|
5553
|
+
function countNodeOverlaps(nodes) {
|
|
5554
|
+
let count = 0;
|
|
5555
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
5556
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
5557
|
+
if (intersectsAabb(nodes[i].box, nodes[j].box)) {
|
|
5558
|
+
count++;
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
return count;
|
|
5563
|
+
}
|
|
5564
|
+
function countEdgeCrossings(edges) {
|
|
5565
|
+
let count = 0;
|
|
5566
|
+
for (let i = 0; i < edges.length; i++) {
|
|
5567
|
+
const aPts = edges[i].points;
|
|
5568
|
+
for (let j = i + 1; j < edges.length; j++) {
|
|
5569
|
+
const bPts = edges[j].points;
|
|
5570
|
+
for (let ai = 0; ai < aPts.length - 1; ai++) {
|
|
5571
|
+
for (let bi = 0; bi < bPts.length - 1; bi++) {
|
|
5572
|
+
if (segmentsIntersect(
|
|
5573
|
+
aPts[ai],
|
|
5574
|
+
aPts[ai + 1],
|
|
5575
|
+
bPts[bi],
|
|
5576
|
+
bPts[bi + 1]
|
|
5577
|
+
)) {
|
|
5578
|
+
count++;
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
return count;
|
|
5585
|
+
}
|
|
5586
|
+
function segmentsIntersect(a, b, c, d) {
|
|
5587
|
+
const d1 = cross(c, d, a);
|
|
5588
|
+
const d2 = cross(c, d, b);
|
|
5589
|
+
const d3 = cross(a, b, c);
|
|
5590
|
+
const d4 = cross(a, b, d);
|
|
5591
|
+
return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0);
|
|
5592
|
+
}
|
|
5593
|
+
function cross(o, a, b) {
|
|
5594
|
+
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
5595
|
+
}
|
|
5596
|
+
function countTotalBends(edges) {
|
|
5597
|
+
const sign = (n) => n > 0 ? 1 : n < 0 ? -1 : 0;
|
|
5598
|
+
let bends = 0;
|
|
5599
|
+
for (const e of edges) {
|
|
5600
|
+
if (e.points.length < 3) continue;
|
|
5601
|
+
for (let i = 1; i < e.points.length - 1; i++) {
|
|
5602
|
+
const prev = e.points[i - 1];
|
|
5603
|
+
const curr = e.points[i];
|
|
5604
|
+
const next = e.points[i + 1];
|
|
5605
|
+
const dx1 = curr.x - prev.x;
|
|
5606
|
+
const dy1 = curr.y - prev.y;
|
|
5607
|
+
const dx2 = next.x - curr.x;
|
|
5608
|
+
const dy2 = next.y - curr.y;
|
|
5609
|
+
if (sign(dx1) !== sign(dx2) || sign(dy1) !== sign(dy2)) {
|
|
5610
|
+
bends++;
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
return bends;
|
|
5615
|
+
}
|
|
5616
|
+
function countBacktrackingEdges(edges) {
|
|
5617
|
+
let count = 0;
|
|
5618
|
+
for (const e of edges) {
|
|
5619
|
+
if (e.points.length < 2) continue;
|
|
5620
|
+
const first = e.points[0];
|
|
5621
|
+
const last = e.points[e.points.length - 1];
|
|
5622
|
+
const direct = Math.hypot(last.x - first.x, last.y - first.y);
|
|
5623
|
+
if (direct <= 0) continue;
|
|
5624
|
+
let routeLen = 0;
|
|
5625
|
+
for (let i = 0; i < e.points.length - 1; i++) {
|
|
5626
|
+
routeLen += Math.hypot(
|
|
5627
|
+
e.points[i + 1].x - e.points[i].x,
|
|
5628
|
+
e.points[i + 1].y - e.points[i].y
|
|
5629
|
+
);
|
|
5630
|
+
}
|
|
5631
|
+
if (routeLen > direct * 3) count++;
|
|
5632
|
+
}
|
|
5633
|
+
return count;
|
|
5634
|
+
}
|
|
5635
|
+
function countLabelCollisions(nodes, edges) {
|
|
5636
|
+
let count = 0;
|
|
5637
|
+
const nodeBoxes = /* @__PURE__ */ new Map();
|
|
5638
|
+
for (const n of nodes) {
|
|
5639
|
+
nodeBoxes.set(n.id, n.box);
|
|
5640
|
+
if (n.label?.text !== void 0) {
|
|
5641
|
+
const lw = n.label.text.length * 8;
|
|
5642
|
+
const labelBox = {
|
|
5643
|
+
x: n.box.x + n.box.width / 2 - lw / 2,
|
|
5644
|
+
y: n.box.y - 8,
|
|
5645
|
+
width: lw,
|
|
5646
|
+
height: 14
|
|
5647
|
+
};
|
|
5648
|
+
for (const [id, box] of nodeBoxes) {
|
|
5649
|
+
if (id === n.id) continue;
|
|
5650
|
+
if (intersectsAabb(labelBox, box)) count++;
|
|
5651
|
+
}
|
|
5652
|
+
for (const e of edges) {
|
|
5653
|
+
for (let i = 0; i < e.points.length - 1; i++) {
|
|
5654
|
+
if (segmentIntersectsBox2(e.points[i], e.points[i + 1], labelBox)) {
|
|
5655
|
+
count++;
|
|
5656
|
+
break;
|
|
5657
|
+
}
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
return count;
|
|
5663
|
+
}
|
|
5664
|
+
function segmentIntersectsBox2(start, end, box) {
|
|
5665
|
+
const left = box.x;
|
|
5666
|
+
const right = box.x + box.width;
|
|
5667
|
+
const top = box.y;
|
|
5668
|
+
const bottom = box.y + box.height;
|
|
5669
|
+
if (start.x > left && start.x < right && start.y > top && start.y < bottom || end.x > left && end.x < right && end.y > top && end.y < bottom)
|
|
5670
|
+
return true;
|
|
5671
|
+
if (start.x === end.x) {
|
|
5672
|
+
return start.x > left && start.x < right && rangesOverlap3(start.y, end.y, top, bottom);
|
|
5673
|
+
}
|
|
5674
|
+
if (start.y === end.y) {
|
|
5675
|
+
return start.y > top && start.y < bottom && rangesOverlap3(start.x, end.x, left, right);
|
|
5676
|
+
}
|
|
5677
|
+
return edgeIntersect(start, end, left, top, right, top) || edgeIntersect(start, end, right, top, right, bottom) || edgeIntersect(start, end, right, bottom, left, bottom) || edgeIntersect(start, end, left, bottom, left, top);
|
|
5678
|
+
}
|
|
5679
|
+
function rangesOverlap3(a, b, min, max) {
|
|
5680
|
+
const lo = Math.min(a, b);
|
|
5681
|
+
const hi = Math.max(a, b);
|
|
5682
|
+
return hi > min && lo < max;
|
|
5683
|
+
}
|
|
5684
|
+
function edgeIntersect(start, end, x1, y1, x2, y2) {
|
|
5685
|
+
const denom = (end.x - start.x) * (y2 - y1) - (end.y - start.y) * (x2 - x1);
|
|
5686
|
+
if (denom === 0) return false;
|
|
5687
|
+
const t = ((start.x - x1) * (y2 - y1) - (start.y - y1) * (x2 - x1)) / denom;
|
|
5688
|
+
const u = ((start.x - x1) * (end.y - start.y) - (start.y - y1) * (end.x - start.x)) / denom;
|
|
5689
|
+
return t > 0 && t < 1 && u > 0 && u < 1;
|
|
5690
|
+
}
|
|
5691
|
+
|
|
5442
5692
|
// src/solver/solve.ts
|
|
5443
5693
|
var DEFAULT_MATRIX_CELL_SIZE2 = { width: 120, height: 36 };
|
|
5444
5694
|
var DEFAULT_TABLE_CELL_SIZE2 = { width: 128, height: 34 };
|
|
@@ -8126,13 +8376,13 @@ function routeIntersectsTextBox(points, box) {
|
|
|
8126
8376
|
if (start === void 0 || end === void 0) {
|
|
8127
8377
|
continue;
|
|
8128
8378
|
}
|
|
8129
|
-
if (
|
|
8379
|
+
if (segmentIntersectsBox3(start, end, box)) {
|
|
8130
8380
|
return true;
|
|
8131
8381
|
}
|
|
8132
8382
|
}
|
|
8133
8383
|
return false;
|
|
8134
8384
|
}
|
|
8135
|
-
function
|
|
8385
|
+
function segmentIntersectsBox3(start, end, box) {
|
|
8136
8386
|
const left = box.x;
|
|
8137
8387
|
const right = box.x + box.width;
|
|
8138
8388
|
const top = box.y;
|
|
@@ -8141,17 +8391,17 @@ function segmentIntersectsBox2(start, end, box) {
|
|
|
8141
8391
|
return true;
|
|
8142
8392
|
}
|
|
8143
8393
|
if (start.x === end.x) {
|
|
8144
|
-
return start.x > left && start.x < right &&
|
|
8394
|
+
return start.x > left && start.x < right && rangesOverlap4(start.y, end.y, top, bottom);
|
|
8145
8395
|
}
|
|
8146
8396
|
if (start.y === end.y) {
|
|
8147
|
-
return start.y > top && start.y < bottom &&
|
|
8397
|
+
return start.y > top && start.y < bottom && rangesOverlap4(start.x, end.x, left, right);
|
|
8148
8398
|
}
|
|
8149
8399
|
return segmentIntersectsBoxEdge2(start, end, left, top, right, top) || segmentIntersectsBoxEdge2(start, end, right, top, right, bottom) || segmentIntersectsBoxEdge2(start, end, right, bottom, left, bottom) || segmentIntersectsBoxEdge2(start, end, left, bottom, left, top);
|
|
8150
8400
|
}
|
|
8151
8401
|
function pointInsideBox2(point2, box) {
|
|
8152
8402
|
return point2.x > box.x && point2.x < box.x + box.width && point2.y > box.y && point2.y < box.y + box.height;
|
|
8153
8403
|
}
|
|
8154
|
-
function
|
|
8404
|
+
function rangesOverlap4(a, b, min, max) {
|
|
8155
8405
|
const low = Math.min(a, b);
|
|
8156
8406
|
const high = Math.max(a, b);
|
|
8157
8407
|
return high > min && low < max;
|
|
@@ -8514,6 +8764,30 @@ function groupReferenceMissing(groupId, referenceKind, id) {
|
|
|
8514
8764
|
detail: id === void 0 ? { groupId } : { groupId, id }
|
|
8515
8765
|
};
|
|
8516
8766
|
}
|
|
8767
|
+
function createDefaultPipeline() {
|
|
8768
|
+
return new LayoutPipeline().addPhase({
|
|
8769
|
+
name: "solve-diagram",
|
|
8770
|
+
run(state) {
|
|
8771
|
+
const result = solveDiagram(state.diagram, state.options);
|
|
8772
|
+
state.diagnostics.push(...result.diagnostics);
|
|
8773
|
+
state.bounds = result.bounds;
|
|
8774
|
+
state.degraded = result.degraded ?? false;
|
|
8775
|
+
state.coordinatedNodes = result.nodes;
|
|
8776
|
+
state.coordinatedEdges = result.edges;
|
|
8777
|
+
}
|
|
8778
|
+
}).addPhase({
|
|
8779
|
+
name: "quality-score",
|
|
8780
|
+
run(state) {
|
|
8781
|
+
if (!state.options.qualityScore) return;
|
|
8782
|
+
const report = scoreLayoutQuality(
|
|
8783
|
+
state.coordinatedNodes,
|
|
8784
|
+
state.coordinatedEdges
|
|
8785
|
+
);
|
|
8786
|
+
state.qualityReport = report;
|
|
8787
|
+
state.diagnostics.push(...report.diagnostics);
|
|
8788
|
+
}
|
|
8789
|
+
});
|
|
8790
|
+
}
|
|
8517
8791
|
|
|
8518
8792
|
// src/dsl/render.ts
|
|
8519
8793
|
function resolveOutputFormat(cliFormat, dslFormat) {
|
|
@@ -8756,6 +9030,7 @@ exports.DEFAULT_DSL_MAX_BYTES = DEFAULT_DSL_MAX_BYTES;
|
|
|
8756
9030
|
exports.DELIVERABILITY_DIAGNOSTIC_CODES = DELIVERABILITY_DIAGNOSTIC_CODES;
|
|
8757
9031
|
exports.DeterministicTextMeasurer = DeterministicTextMeasurer;
|
|
8758
9032
|
exports.LabelFitter = LabelFitter;
|
|
9033
|
+
exports.LayoutPipeline = LayoutPipeline;
|
|
8759
9034
|
exports.PretextTextMeasurer = PretextTextMeasurer;
|
|
8760
9035
|
exports.applyLayoutConstraints = applyLayoutConstraints;
|
|
8761
9036
|
exports.assertFiniteNonNegative = assertFiniteNonNegative;
|
|
@@ -8766,6 +9041,7 @@ exports.computeArrowhead = computeArrowhead;
|
|
|
8766
9041
|
exports.computeContainerGeometry = computeContainerGeometry;
|
|
8767
9042
|
exports.computeShapeGeometry = computeShapeGeometry;
|
|
8768
9043
|
exports.createBoxSpatialIndex = createBoxSpatialIndex;
|
|
9044
|
+
exports.createDefaultPipeline = createDefaultPipeline;
|
|
8769
9045
|
exports.createDefaultTextMeasurer = createDefaultTextMeasurer;
|
|
8770
9046
|
exports.expandBox = expandBox;
|
|
8771
9047
|
exports.expandBoxForQuery = expandBoxForQuery;
|