@tscircuit/core 0.0.2300 → 0.0.2301
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/index.js +799 -93
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -11356,6 +11356,42 @@ var underscorifyPortArrangement = (portArrangement, pinLabels) => {
|
|
|
11356
11356
|
return void 0;
|
|
11357
11357
|
};
|
|
11358
11358
|
|
|
11359
|
+
// lib/utils/join-imported-courtyard-outlines.ts
|
|
11360
|
+
function joinImportedCourtyardOutlines(courtyards, componentName) {
|
|
11361
|
+
const polygons = /* @__PURE__ */ new Map();
|
|
11362
|
+
const pending = courtyards.filter(
|
|
11363
|
+
(courtyard) => courtyard.outline.length === 2
|
|
11364
|
+
);
|
|
11365
|
+
const samePoint2 = (a2, b2) => Math.hypot(a2.x - b2.x, a2.y - b2.y) < 1e-6;
|
|
11366
|
+
while (pending.length) {
|
|
11367
|
+
const first = pending.shift();
|
|
11368
|
+
const outline = [...first.outline];
|
|
11369
|
+
while (!samePoint2(outline[0], outline.at(-1))) {
|
|
11370
|
+
const end = outline.at(-1);
|
|
11371
|
+
const nextIndex = pending.findIndex(
|
|
11372
|
+
(candidate) => candidate.pcb_component_id === first.pcb_component_id && candidate.layer === first.layer && candidate.outline.some((point8) => samePoint2(point8, end))
|
|
11373
|
+
);
|
|
11374
|
+
if (nextIndex === -1) {
|
|
11375
|
+
throw new Error(
|
|
11376
|
+
`Imported courtyard for "${componentName}" has disconnected line segments; provide a complete closed polygon.`
|
|
11377
|
+
);
|
|
11378
|
+
}
|
|
11379
|
+
const [next] = pending.splice(nextIndex, 1);
|
|
11380
|
+
outline.push(
|
|
11381
|
+
samePoint2(next.outline[0], end) ? next.outline[1] : next.outline[0]
|
|
11382
|
+
);
|
|
11383
|
+
}
|
|
11384
|
+
outline.pop();
|
|
11385
|
+
if (outline.length < 3) {
|
|
11386
|
+
throw new Error(
|
|
11387
|
+
`Imported courtyard for "${componentName}" needs at least three polygon vertices.`
|
|
11388
|
+
);
|
|
11389
|
+
}
|
|
11390
|
+
polygons.set(first, outline);
|
|
11391
|
+
}
|
|
11392
|
+
return polygons;
|
|
11393
|
+
}
|
|
11394
|
+
|
|
11359
11395
|
// lib/utils/createComponentsFromCircuitJson.ts
|
|
11360
11396
|
import { getUnitVectorFromDirection as getUnitVectorFromDirection2 } from "@tscircuit/math-utils";
|
|
11361
11397
|
import {
|
|
@@ -15829,6 +15865,10 @@ var createComponentsFromCircuitJson = ({
|
|
|
15829
15865
|
pcbPinLabels,
|
|
15830
15866
|
preserveSolderPaste = false
|
|
15831
15867
|
}, circuitJson) => {
|
|
15868
|
+
const joinedCourtyards = joinImportedCourtyardOutlines(
|
|
15869
|
+
circuitJson.filter((elm) => elm.type === "pcb_courtyard_outline"),
|
|
15870
|
+
componentName
|
|
15871
|
+
);
|
|
15832
15872
|
const importedSourcePorts = circuitJson.filter(
|
|
15833
15873
|
(elm) => elm.type === "source_port"
|
|
15834
15874
|
);
|
|
@@ -16540,9 +16580,11 @@ var createComponentsFromCircuitJson = ({
|
|
|
16540
16580
|
})
|
|
16541
16581
|
);
|
|
16542
16582
|
} else if (elm.type === "pcb_courtyard_outline") {
|
|
16583
|
+
const outline = elm.outline.length === 2 ? joinedCourtyards.get(elm) : elm.outline;
|
|
16584
|
+
if (!outline) continue;
|
|
16543
16585
|
components.push(
|
|
16544
16586
|
new CourtyardOutline({
|
|
16545
|
-
outline
|
|
16587
|
+
outline,
|
|
16546
16588
|
layer: elm.layer
|
|
16547
16589
|
})
|
|
16548
16590
|
);
|
|
@@ -22331,7 +22373,7 @@ function intersectsObstacle(points, obstacle, depth = 0) {
|
|
|
22331
22373
|
// lib/utils/system-blocks/round-system-block-route.ts
|
|
22332
22374
|
import { applyToPoint as applyToPoint33, compose as compose9, rotate as rotate4, translate as translate7 } from "transformation-matrix";
|
|
22333
22375
|
var EPSILON = 1e-8;
|
|
22334
|
-
function roundSystemBlockRoute(route, obstacles) {
|
|
22376
|
+
function roundSystemBlockRoute(route, obstacles, wireObstacles = []) {
|
|
22335
22377
|
const points = [];
|
|
22336
22378
|
for (const point8 of route) {
|
|
22337
22379
|
if (points.length && distance9(points.at(-1), point8) < EPSILON) continue;
|
|
@@ -22356,6 +22398,27 @@ function roundSystemBlockRoute(route, obstacles) {
|
|
|
22356
22398
|
const previous = points[index2 - 1];
|
|
22357
22399
|
const corner = points[index2];
|
|
22358
22400
|
const next = points[index2 + 1];
|
|
22401
|
+
const cornerObstacles = [
|
|
22402
|
+
...obstacles,
|
|
22403
|
+
...wireObstacles.map((wire) => {
|
|
22404
|
+
const clearance = Math.min(
|
|
22405
|
+
3,
|
|
22406
|
+
Math.max(
|
|
22407
|
+
0,
|
|
22408
|
+
wire.minX - corner.x,
|
|
22409
|
+
corner.x - wire.maxX,
|
|
22410
|
+
wire.minY - corner.y,
|
|
22411
|
+
corner.y - wire.maxY
|
|
22412
|
+
)
|
|
22413
|
+
);
|
|
22414
|
+
return {
|
|
22415
|
+
minX: wire.minX - clearance,
|
|
22416
|
+
maxX: wire.maxX + clearance,
|
|
22417
|
+
minY: wire.minY - clearance,
|
|
22418
|
+
maxY: wire.maxY + clearance
|
|
22419
|
+
};
|
|
22420
|
+
})
|
|
22421
|
+
];
|
|
22359
22422
|
const incoming = unitDirection(previous, corner);
|
|
22360
22423
|
const outgoing = unitDirection(corner, next);
|
|
22361
22424
|
const turn = Math.atan2(
|
|
@@ -22384,7 +22447,7 @@ function roundSystemBlockRoute(route, obstacles) {
|
|
|
22384
22447
|
const maxX = Math.max(start.x, corner.x, end.x);
|
|
22385
22448
|
const minY = Math.min(start.y, corner.y, end.y);
|
|
22386
22449
|
const maxY = Math.max(start.y, corner.y, end.y);
|
|
22387
|
-
const blocked =
|
|
22450
|
+
const blocked = cornerObstacles.some(
|
|
22388
22451
|
(obstacle) => minX < obstacle.maxX && maxX > obstacle.minX && minY < obstacle.maxY && maxY > obstacle.minY
|
|
22389
22452
|
);
|
|
22390
22453
|
if (!blocked || trim < 1e-3) break;
|
|
@@ -22439,103 +22502,744 @@ function unitDirection(a2, b2) {
|
|
|
22439
22502
|
return { x: (b2.x - a2.x) / length9, y: (b2.y - a2.y) / length9 };
|
|
22440
22503
|
}
|
|
22441
22504
|
|
|
22442
|
-
// lib/utils/system-blocks/
|
|
22443
|
-
function
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22505
|
+
// lib/utils/system-blocks/measure-system-route-quality.ts
|
|
22506
|
+
function measureSystemRouteQuality(routes) {
|
|
22507
|
+
let crossings = 0;
|
|
22508
|
+
let sharedLength = 0;
|
|
22509
|
+
let closeParallelLength = 0;
|
|
22510
|
+
let length9 = 0;
|
|
22511
|
+
let bends = 0;
|
|
22512
|
+
const segments = routes.map(
|
|
22513
|
+
(route) => route.slice(1).map((end, index2) => {
|
|
22514
|
+
const start = route[index2];
|
|
22515
|
+
length9 += Math.abs(end.x - start.x) + Math.abs(end.y - start.y);
|
|
22516
|
+
return { start, end, horizontal: start.y === end.y };
|
|
22517
|
+
})
|
|
22518
|
+
);
|
|
22519
|
+
for (const route of routes) bends += Math.max(0, route.length - 2);
|
|
22520
|
+
for (let i2 = 0; i2 < segments.length; i2++) {
|
|
22521
|
+
for (let j2 = i2 + 1; j2 < segments.length; j2++) {
|
|
22522
|
+
const intersections2 = /* @__PURE__ */ new Set();
|
|
22523
|
+
for (const a2 of segments[i2])
|
|
22524
|
+
for (const b2 of segments[j2]) {
|
|
22525
|
+
if (a2.horizontal === b2.horizontal) {
|
|
22526
|
+
const along2 = a2.horizontal ? "x" : "y";
|
|
22527
|
+
const across = a2.horizontal ? "y" : "x";
|
|
22528
|
+
const overlap = Math.min(
|
|
22529
|
+
Math.max(a2.start[along2], a2.end[along2]),
|
|
22530
|
+
Math.max(b2.start[along2], b2.end[along2])
|
|
22531
|
+
) - Math.max(
|
|
22532
|
+
Math.min(a2.start[along2], a2.end[along2]),
|
|
22533
|
+
Math.min(b2.start[along2], b2.end[along2])
|
|
22534
|
+
);
|
|
22535
|
+
if (overlap <= 1e-6) continue;
|
|
22536
|
+
const separation = Math.abs(a2.start[across] - b2.start[across]);
|
|
22537
|
+
if (separation < 1e-6) sharedLength += overlap;
|
|
22538
|
+
if (separation < 3 - 1e-6)
|
|
22539
|
+
closeParallelLength += overlap * (3 - separation) / 3;
|
|
22540
|
+
} else {
|
|
22541
|
+
const h2 = a2.horizontal ? a2 : b2, v2 = a2.horizontal ? b2 : a2;
|
|
22542
|
+
const x2 = v2.start.x, y2 = h2.start.y;
|
|
22543
|
+
if (x2 >= Math.min(h2.start.x, h2.end.x) - 1e-6 && x2 <= Math.max(h2.start.x, h2.end.x) + 1e-6 && y2 >= Math.min(v2.start.y, v2.end.y) - 1e-6 && y2 <= Math.max(v2.start.y, v2.end.y) + 1e-6) {
|
|
22544
|
+
intersections2.add(`${x2.toFixed(6)},${y2.toFixed(6)}`);
|
|
22545
|
+
}
|
|
22546
|
+
}
|
|
22547
|
+
}
|
|
22548
|
+
crossings += intersections2.size;
|
|
22549
|
+
}
|
|
22469
22550
|
}
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
22482
|
-
|
|
22483
|
-
|
|
22484
|
-
|
|
22485
|
-
|
|
22486
|
-
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
|
|
22495
|
-
|
|
22551
|
+
return {
|
|
22552
|
+
crossings,
|
|
22553
|
+
sharedLength,
|
|
22554
|
+
closeParallelLength,
|
|
22555
|
+
bends,
|
|
22556
|
+
length: length9,
|
|
22557
|
+
score: crossings * 200 + sharedLength * 100 + closeParallelLength * 10 + bends * 3 + length9 * 0.05
|
|
22558
|
+
};
|
|
22559
|
+
}
|
|
22560
|
+
|
|
22561
|
+
// lib/utils/system-blocks/find-system-orthogonal-path.ts
|
|
22562
|
+
function findSystemOrthogonalPath(start, end, obstacles, routedSegments) {
|
|
22563
|
+
const xs = [
|
|
22564
|
+
.../* @__PURE__ */ new Set([
|
|
22565
|
+
start.x,
|
|
22566
|
+
end.x,
|
|
22567
|
+
...obstacles.flatMap((r2) => [r2.left, r2.right]),
|
|
22568
|
+
...routedSegments.filter(([a2, b2]) => a2.x === b2.x).flatMap(([a2]) => [a2.x - 3, a2.x + 3])
|
|
22569
|
+
])
|
|
22570
|
+
].sort((a2, b2) => a2 - b2);
|
|
22571
|
+
const ys = [
|
|
22572
|
+
.../* @__PURE__ */ new Set([
|
|
22573
|
+
start.y,
|
|
22574
|
+
end.y,
|
|
22575
|
+
...obstacles.flatMap((r2) => [r2.bottom, r2.top]),
|
|
22576
|
+
...routedSegments.filter(([a2, b2]) => a2.y === b2.y).flatMap(([a2]) => [a2.y - 3, a2.y + 3])
|
|
22577
|
+
])
|
|
22578
|
+
].sort((a2, b2) => a2 - b2);
|
|
22579
|
+
xs.unshift(xs[0] - 5);
|
|
22580
|
+
xs.push(xs.at(-1) + 5);
|
|
22581
|
+
ys.unshift(ys[0] - 5);
|
|
22582
|
+
ys.push(ys.at(-1) + 5);
|
|
22583
|
+
const columns = xs.length;
|
|
22584
|
+
const pointAt2 = (node) => ({
|
|
22585
|
+
x: xs[node % columns],
|
|
22586
|
+
y: ys[Math.floor(node / columns)]
|
|
22587
|
+
});
|
|
22588
|
+
const startNode = ys.indexOf(start.y) * columns + xs.indexOf(start.x);
|
|
22589
|
+
const endNode = ys.indexOf(end.y) * columns + xs.indexOf(end.x);
|
|
22590
|
+
const costs = new Float64Array(xs.length * ys.length * 3).fill(Infinity);
|
|
22591
|
+
const previous = new Int32Array(costs.length).fill(-1);
|
|
22592
|
+
const horizontalCosts = new Float64Array(xs.length * ys.length).fill(NaN);
|
|
22593
|
+
const verticalCosts = new Float64Array(horizontalCosts.length).fill(NaN);
|
|
22594
|
+
const queue = [];
|
|
22595
|
+
const enqueue = (state, cost2, priority) => {
|
|
22596
|
+
const entry = { state, cost: cost2, priority };
|
|
22597
|
+
queue.push(entry);
|
|
22598
|
+
let i2 = queue.length - 1;
|
|
22599
|
+
while (i2 > 0) {
|
|
22600
|
+
const parent = i2 - 1 >> 1;
|
|
22601
|
+
if (queue[parent].priority <= priority) break;
|
|
22602
|
+
queue[i2] = queue[parent];
|
|
22603
|
+
i2 = parent;
|
|
22604
|
+
}
|
|
22605
|
+
queue[i2] = entry;
|
|
22606
|
+
};
|
|
22607
|
+
costs[startNode * 3 + 2] = 0;
|
|
22608
|
+
enqueue(startNode * 3 + 2, 0, 0);
|
|
22609
|
+
while (queue.length) {
|
|
22610
|
+
const current2 = queue[0];
|
|
22611
|
+
const last = queue.pop();
|
|
22612
|
+
if (queue.length) {
|
|
22613
|
+
let i2 = 0;
|
|
22614
|
+
while (2 * i2 + 1 < queue.length) {
|
|
22615
|
+
let child = 2 * i2 + 1;
|
|
22616
|
+
if (child + 1 < queue.length && queue[child + 1].priority < queue[child].priority)
|
|
22617
|
+
child++;
|
|
22618
|
+
if (last.priority <= queue[child].priority) break;
|
|
22619
|
+
queue[i2] = queue[child];
|
|
22620
|
+
i2 = child;
|
|
22621
|
+
}
|
|
22622
|
+
queue[i2] = last;
|
|
22623
|
+
}
|
|
22624
|
+
if (current2.cost !== costs[current2.state]) continue;
|
|
22625
|
+
const node = Math.floor(current2.state / 3);
|
|
22626
|
+
if (node === endNode) {
|
|
22627
|
+
const path = [];
|
|
22628
|
+
for (let state = current2.state; state !== -1; state = previous[state])
|
|
22629
|
+
path.push(pointAt2(Math.floor(state / 3)));
|
|
22630
|
+
return path.reverse();
|
|
22631
|
+
}
|
|
22632
|
+
const from = pointAt2(node);
|
|
22633
|
+
const col = node % columns;
|
|
22634
|
+
const row = Math.floor(node / columns);
|
|
22635
|
+
const neighbors = [
|
|
22636
|
+
...col > 0 ? [node - 1] : [],
|
|
22637
|
+
...col + 1 < columns ? [node + 1] : [],
|
|
22638
|
+
...row > 0 ? [node - columns] : [],
|
|
22639
|
+
...row + 1 < ys.length ? [node + columns] : []
|
|
22640
|
+
];
|
|
22641
|
+
for (const neighbor of neighbors) {
|
|
22642
|
+
const to = pointAt2(neighbor);
|
|
22643
|
+
const horizontal = from.y === to.y;
|
|
22644
|
+
const left = Math.min(from.x, to.x), right = Math.max(from.x, to.x);
|
|
22645
|
+
const bottom = Math.min(from.y, to.y), top = Math.max(from.y, to.y);
|
|
22646
|
+
const edgeCosts = horizontal ? horizontalCosts : verticalCosts;
|
|
22647
|
+
const edgeIndex = Math.min(node, neighbor);
|
|
22648
|
+
let travelCost = edgeCosts[edgeIndex];
|
|
22649
|
+
if (Number.isNaN(travelCost)) {
|
|
22650
|
+
if (obstacles.some(
|
|
22651
|
+
(r2) => horizontal ? from.y > r2.bottom + 1e-6 && from.y < r2.top - 1e-6 && right > r2.left + 1e-6 && left < r2.right - 1e-6 : from.x > r2.left + 1e-6 && from.x < r2.right - 1e-6 && top > r2.bottom + 1e-6 && bottom < r2.top - 1e-6
|
|
22652
|
+
)) {
|
|
22653
|
+
edgeCosts[edgeIndex] = Infinity;
|
|
22654
|
+
continue;
|
|
22496
22655
|
}
|
|
22497
|
-
|
|
22656
|
+
let congestion = 0;
|
|
22657
|
+
for (const [a2, b2] of routedSegments) {
|
|
22658
|
+
const sameAxis = a2.y === b2.y === horizontal;
|
|
22659
|
+
if (sameAxis) {
|
|
22660
|
+
const separation = horizontal ? Math.abs(a2.y - from.y) : Math.abs(a2.x - from.x);
|
|
22661
|
+
if (horizontal && separation < 3)
|
|
22662
|
+
congestion += Math.max(
|
|
22663
|
+
0,
|
|
22664
|
+
Math.min(right, Math.max(a2.x, b2.x)) - Math.max(left, Math.min(a2.x, b2.x))
|
|
22665
|
+
) * (separation < 1e-6 ? 40 : (3 - separation) * 8);
|
|
22666
|
+
if (!horizontal && separation < 3)
|
|
22667
|
+
congestion += Math.max(
|
|
22668
|
+
0,
|
|
22669
|
+
Math.min(top, Math.max(a2.y, b2.y)) - Math.max(bottom, Math.min(a2.y, b2.y))
|
|
22670
|
+
) * (separation < 1e-6 ? 40 : (3 - separation) * 8);
|
|
22671
|
+
} else if (horizontal ? a2.x >= left && a2.x <= right && from.y >= Math.min(a2.y, b2.y) && from.y <= Math.max(a2.y, b2.y) : a2.y >= bottom && a2.y <= top && from.x >= Math.min(a2.x, b2.x) && from.x <= Math.max(a2.x, b2.x))
|
|
22672
|
+
congestion += 80;
|
|
22673
|
+
}
|
|
22674
|
+
travelCost = right - left + top - bottom + congestion;
|
|
22675
|
+
edgeCosts[edgeIndex] = travelCost;
|
|
22676
|
+
}
|
|
22677
|
+
if (travelCost === Infinity) continue;
|
|
22678
|
+
const axis = horizontal ? 0 : 1;
|
|
22679
|
+
const state = neighbor * 3 + axis;
|
|
22680
|
+
const bend = current2.state % 3 === 2 || current2.state % 3 === axis ? 0 : 6;
|
|
22681
|
+
const cost2 = current2.cost + travelCost + bend;
|
|
22682
|
+
if (cost2 >= costs[state]) continue;
|
|
22683
|
+
costs[state] = cost2;
|
|
22684
|
+
previous[state] = current2.state;
|
|
22685
|
+
enqueue(
|
|
22686
|
+
state,
|
|
22687
|
+
cost2,
|
|
22688
|
+
cost2 + Math.abs(to.x - end.x) + Math.abs(to.y - end.y)
|
|
22689
|
+
);
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
}
|
|
22693
|
+
|
|
22694
|
+
// lib/utils/system-blocks/route-system-blocks.ts
|
|
22695
|
+
var SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING = 3;
|
|
22696
|
+
function getSystemConnectionLabelWidth(label) {
|
|
22697
|
+
let width = SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING;
|
|
22698
|
+
for (const character of label) {
|
|
22699
|
+
width += /[MW@%#&]|[^\x00-\x7f]/.test(character) ? 2.5 : /[A-Z0-9]/.test(character) ? 1.85 : /[il.,:;!| ']/.test(character) ? 0.85 : 1.5;
|
|
22700
|
+
}
|
|
22701
|
+
return width;
|
|
22702
|
+
}
|
|
22703
|
+
function routeSystemBlocks(tree, bounds, initialRoutes) {
|
|
22704
|
+
const obstacles = [...bounds].map(
|
|
22705
|
+
([block, box3]) => ({
|
|
22706
|
+
left: box3.center.x - box3.width / 2 - 2,
|
|
22707
|
+
right: tree.groups.includes(block) ? box3.center.x - box3.width / 2 + Math.min(
|
|
22708
|
+
box3.width,
|
|
22709
|
+
block._parsedProps.description ? box3.width : (block._parsedProps.displayName ?? block.name).length * 1.8 + 6
|
|
22710
|
+
) : box3.center.x + box3.width / 2 + 2,
|
|
22711
|
+
bottom: tree.groups.includes(block) ? box3.center.y + box3.height / 2 - (block._parsedProps.description ? 12 : 7) : box3.center.y - box3.height / 2 - 2,
|
|
22712
|
+
top: box3.center.y + box3.height / 2 + 2
|
|
22498
22713
|
})
|
|
22499
22714
|
);
|
|
22500
|
-
const
|
|
22501
|
-
|
|
22502
|
-
|
|
22715
|
+
const portsByBlock = /* @__PURE__ */ new Map();
|
|
22716
|
+
const portsByConnection = /* @__PURE__ */ new Map();
|
|
22717
|
+
for (const connection of tree.connections) {
|
|
22718
|
+
const endpoints = tree.endpoints.get(connection);
|
|
22719
|
+
const route = initialRoutes.get(connection).route;
|
|
22720
|
+
const ports = [endpoints.from, endpoints.to].map((block, end) => {
|
|
22721
|
+
const box3 = bounds.get(block);
|
|
22722
|
+
const target = bounds.get(
|
|
22723
|
+
end === 0 ? endpoints.to : endpoints.from
|
|
22724
|
+
).center;
|
|
22725
|
+
const guide = initialRoutes.get(connection).label_anchor_center ?? target;
|
|
22726
|
+
const point8 = endpoints.from === endpoints.to ? end === 0 ? route[0] : route.at(-1) : guide;
|
|
22727
|
+
const dx2 = (point8.x - box3.center.x) / box3.width;
|
|
22728
|
+
const dy2 = (point8.y - box3.center.y) / box3.height;
|
|
22729
|
+
const side = Math.abs(dx2) >= Math.abs(dy2) ? dx2 >= 0 ? 0 : 2 : dy2 >= 0 ? 1 : 3;
|
|
22730
|
+
const port = {
|
|
22731
|
+
point: { ...point8 },
|
|
22732
|
+
escape: { ...point8 },
|
|
22733
|
+
side,
|
|
22734
|
+
target: guide,
|
|
22735
|
+
connection
|
|
22736
|
+
};
|
|
22737
|
+
const ports2 = portsByBlock.get(block) ?? [];
|
|
22738
|
+
ports2.push(port);
|
|
22739
|
+
portsByBlock.set(block, ports2);
|
|
22740
|
+
return port;
|
|
22741
|
+
});
|
|
22742
|
+
portsByConnection.set(connection, ports);
|
|
22743
|
+
}
|
|
22744
|
+
for (const [block, ports] of portsByBlock) {
|
|
22745
|
+
const box3 = bounds.get(block);
|
|
22746
|
+
for (const side of [0, 1, 2, 3]) {
|
|
22747
|
+
const horizontal = side % 2 === 0;
|
|
22748
|
+
const facePorts = ports.filter((p2) => p2.side === side).sort(
|
|
22749
|
+
(a2, b2) => horizontal ? (a2.target.y - box3.center.y) / Math.max(1, Math.abs(a2.target.x - box3.center.x)) - (b2.target.y - box3.center.y) / Math.max(1, Math.abs(b2.target.x - box3.center.x)) : (a2.target.x - box3.center.x) / Math.max(1, Math.abs(a2.target.y - box3.center.y)) - (b2.target.x - box3.center.x) / Math.max(1, Math.abs(b2.target.y - box3.center.y))
|
|
22750
|
+
);
|
|
22751
|
+
const span = horizontal ? box3.height : box3.width;
|
|
22752
|
+
const spacing = Math.min(
|
|
22753
|
+
4,
|
|
22754
|
+
(span - 4) / Math.max(1, facePorts.length - 1)
|
|
22755
|
+
);
|
|
22756
|
+
facePorts.forEach((port, index2) => {
|
|
22757
|
+
const offset = (index2 - (facePorts.length - 1) / 2) * spacing;
|
|
22758
|
+
const sign = side < 2 ? 1 : -1;
|
|
22759
|
+
port.point = horizontal ? {
|
|
22760
|
+
x: box3.center.x + sign * box3.width / 2,
|
|
22761
|
+
y: box3.center.y + offset
|
|
22762
|
+
} : {
|
|
22763
|
+
x: box3.center.x + offset,
|
|
22764
|
+
y: box3.center.y + sign * box3.height / 2
|
|
22765
|
+
};
|
|
22766
|
+
port.escape = horizontal ? { x: port.point.x + sign * 4, y: port.point.y } : { x: port.point.x, y: port.point.y + sign * 4 };
|
|
22767
|
+
});
|
|
22768
|
+
}
|
|
22769
|
+
}
|
|
22770
|
+
const labels = /* @__PURE__ */ new Map();
|
|
22771
|
+
const labelCenters = /* @__PURE__ */ new Map();
|
|
22772
|
+
const labelAttachments = /* @__PURE__ */ new Map();
|
|
22773
|
+
const portObstacles = [...portsByConnection.values()].flat().map(({ escape }) => ({
|
|
22774
|
+
left: escape.x - 1,
|
|
22775
|
+
right: escape.x + 1,
|
|
22776
|
+
bottom: escape.y - 1,
|
|
22777
|
+
top: escape.y + 1
|
|
22778
|
+
}));
|
|
22779
|
+
for (const connection of tree.connections) {
|
|
22780
|
+
const label = connection._parsedProps.label;
|
|
22781
|
+
const center = initialRoutes.get(connection).label_anchor_center;
|
|
22782
|
+
if (label !== void 0 && center) {
|
|
22783
|
+
const halfWidth = getSystemConnectionLabelWidth(label) / 2;
|
|
22784
|
+
const occupied = [...obstacles, ...labels.values(), ...portObstacles];
|
|
22785
|
+
const fits = (rectangle) => !occupied.some(
|
|
22786
|
+
(r2) => rectangle.left < r2.right + 1 && rectangle.right > r2.left - 1 && rectangle.bottom < r2.top + 1 && rectangle.top > r2.bottom - 1
|
|
22787
|
+
);
|
|
22788
|
+
const [start, end] = portsByConnection.get(connection);
|
|
22789
|
+
const endpoints = tree.endpoints.get(connection);
|
|
22790
|
+
const selfLoop = endpoints.from === endpoints.to;
|
|
22791
|
+
const vertical = start.escape.x === end.escape.x;
|
|
22792
|
+
const horizontal = start.escape.y === end.escape.y;
|
|
22793
|
+
const oppositeFaces = (start.side + 2) % 4 === end.side;
|
|
22794
|
+
const straightAxis = vertical && start.side % 2 === 1 ? "y" : horizontal && start.side % 2 === 0 ? "x" : void 0;
|
|
22795
|
+
const straight = !selfLoop && oppositeFaces && straightAxis !== void 0 && (end.escape[straightAxis] - start.escape[straightAxis]) * (start.escape[straightAxis] - start.point[straightAxis]) >= 0;
|
|
22796
|
+
const sameFaceReturn = selfLoop && start.side === end.side;
|
|
22797
|
+
const textFitsHorizontalSpan = horizontal && !selfLoop && Math.abs(start.escape.x - end.escape.x) >= halfWidth * 2;
|
|
22798
|
+
if ((straight || sameFaceReturn) && !textFitsHorizontalSpan && !obstacles.some(
|
|
22799
|
+
(r2) => vertical ? start.escape.x > r2.left && start.escape.x < r2.right && Math.max(start.escape.y, end.escape.y) > r2.bottom && Math.min(start.escape.y, end.escape.y) < r2.top : start.escape.y > r2.bottom && start.escape.y < r2.top && Math.max(start.escape.x, end.escape.x) > r2.left && Math.min(start.escape.x, end.escape.x) < r2.right
|
|
22800
|
+
)) {
|
|
22801
|
+
const attachment = {
|
|
22802
|
+
x: (start.escape.x + end.escape.x) / 2,
|
|
22803
|
+
y: (start.escape.y + end.escape.y) / 2
|
|
22804
|
+
};
|
|
22805
|
+
const signs = selfLoop ? [start.side < 2 ? 1 : -1] : [1, -1];
|
|
22806
|
+
for (const sign of signs) {
|
|
22807
|
+
const candidate = vertical ? { x: attachment.x + sign * (halfWidth + 2), y: attachment.y } : { x: attachment.x, y: attachment.y + sign * 4.5 };
|
|
22808
|
+
const rectangle = {
|
|
22809
|
+
left: candidate.x - halfWidth,
|
|
22810
|
+
right: candidate.x + halfWidth,
|
|
22811
|
+
bottom: candidate.y - 2.5,
|
|
22812
|
+
top: candidate.y + 2.5
|
|
22813
|
+
};
|
|
22814
|
+
if (!fits(rectangle)) continue;
|
|
22815
|
+
labels.set(connection, rectangle);
|
|
22816
|
+
labelCenters.set(connection, candidate);
|
|
22817
|
+
labelAttachments.set(connection, attachment);
|
|
22818
|
+
break;
|
|
22819
|
+
}
|
|
22820
|
+
if (labelAttachments.has(connection)) continue;
|
|
22821
|
+
}
|
|
22822
|
+
let placed = false;
|
|
22823
|
+
for (let radius = 0; !placed; radius++) {
|
|
22824
|
+
const offsets = radius === 0 ? [[0, 0]] : [
|
|
22825
|
+
[0, radius * 6],
|
|
22826
|
+
[0, -radius * 6],
|
|
22827
|
+
[radius * 6, 0],
|
|
22828
|
+
[-radius * 6, 0]
|
|
22829
|
+
];
|
|
22830
|
+
for (const [dx2, dy2] of offsets) {
|
|
22831
|
+
const candidate = { x: center.x + dx2, y: center.y + dy2 };
|
|
22832
|
+
const rectangle = {
|
|
22833
|
+
left: candidate.x - halfWidth,
|
|
22834
|
+
right: candidate.x + halfWidth,
|
|
22835
|
+
bottom: candidate.y - 2.5,
|
|
22836
|
+
top: candidate.y + 2.5
|
|
22837
|
+
};
|
|
22838
|
+
if (!fits(rectangle)) continue;
|
|
22839
|
+
labels.set(connection, rectangle);
|
|
22840
|
+
labelCenters.set(connection, candidate);
|
|
22841
|
+
placed = true;
|
|
22842
|
+
break;
|
|
22843
|
+
}
|
|
22844
|
+
}
|
|
22845
|
+
}
|
|
22846
|
+
}
|
|
22847
|
+
const routeConnection = (connection, routedSegments) => {
|
|
22848
|
+
const [start, end] = portsByConnection.get(connection);
|
|
22849
|
+
const label = labels.get(connection);
|
|
22850
|
+
const attachment = labelAttachments.get(connection);
|
|
22851
|
+
const blocked = [
|
|
22852
|
+
...obstacles,
|
|
22853
|
+
...[...labels].filter(([c2]) => c2 !== connection).map(([, r2]) => r2)
|
|
22854
|
+
];
|
|
22855
|
+
const waypoints = [start.escape];
|
|
22856
|
+
if (attachment) {
|
|
22857
|
+
waypoints.push(attachment);
|
|
22858
|
+
} else if (label) {
|
|
22859
|
+
const y2 = labelCenters.get(connection).y;
|
|
22860
|
+
const labelPorts = [
|
|
22861
|
+
{ x: label.left, y: y2 },
|
|
22862
|
+
{ x: label.right, y: y2 }
|
|
22863
|
+
];
|
|
22864
|
+
if (start.escape.x > end.escape.x) labelPorts.reverse();
|
|
22865
|
+
waypoints.push(...labelPorts);
|
|
22866
|
+
}
|
|
22867
|
+
waypoints.push(end.escape);
|
|
22868
|
+
const route = [start.point];
|
|
22869
|
+
for (let i2 = 1; i2 < waypoints.length; i2++) {
|
|
22870
|
+
const legObstacles = label && (attachment || i2 !== 2) ? [...blocked, label] : blocked;
|
|
22871
|
+
const leg = findSystemOrthogonalPath(
|
|
22872
|
+
waypoints[i2 - 1],
|
|
22873
|
+
waypoints[i2],
|
|
22874
|
+
legObstacles,
|
|
22875
|
+
routedSegments
|
|
22876
|
+
);
|
|
22877
|
+
if (!leg) {
|
|
22878
|
+
throw new Error(
|
|
22879
|
+
`Cannot route system connection from "${connection._parsedProps.from}" to "${connection._parsedProps.to}" around blocks and labels`
|
|
22880
|
+
);
|
|
22881
|
+
}
|
|
22882
|
+
route.push(...leg);
|
|
22883
|
+
}
|
|
22884
|
+
route.push(end.point);
|
|
22885
|
+
const simplified = [];
|
|
22886
|
+
for (const point8 of route) {
|
|
22887
|
+
const previous = simplified.at(-1);
|
|
22888
|
+
if (previous?.x === point8.x && previous.y === point8.y) continue;
|
|
22889
|
+
const before = simplified.at(-2);
|
|
22890
|
+
if (before && previous && (before.x === previous.x && previous.x === point8.x || before.y === previous.y && previous.y === point8.y))
|
|
22891
|
+
simplified.pop();
|
|
22892
|
+
simplified.push(point8);
|
|
22893
|
+
}
|
|
22503
22894
|
return {
|
|
22504
|
-
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
22895
|
+
route: simplified,
|
|
22896
|
+
label_anchor_center: labelCenters.get(connection)
|
|
22897
|
+
};
|
|
22898
|
+
};
|
|
22899
|
+
const connections = [...tree.connections].sort((a2, b2) => {
|
|
22900
|
+
const distance29 = (connection) => {
|
|
22901
|
+
const [from, to] = portsByConnection.get(connection);
|
|
22902
|
+
return Math.abs(from.point.x - to.point.x) + Math.abs(from.point.y - to.point.y);
|
|
22508
22903
|
};
|
|
22904
|
+
return distance29(a2) - distance29(b2) || a2._parsedProps.from.localeCompare(b2._parsedProps.from) || a2._parsedProps.to.localeCompare(b2._parsedProps.to) || (a2._parsedProps.label ?? "").localeCompare(b2._parsedProps.label ?? "");
|
|
22509
22905
|
});
|
|
22510
|
-
|
|
22511
|
-
|
|
22512
|
-
|
|
22513
|
-
|
|
22906
|
+
const segmentsOf = (resolvedRoutes) => [...resolvedRoutes].flatMap(
|
|
22907
|
+
({ route }) => route.slice(1).map((point8, index2) => [route[index2], point8])
|
|
22908
|
+
);
|
|
22909
|
+
let routes = /* @__PURE__ */ new Map();
|
|
22910
|
+
for (const connection of connections)
|
|
22911
|
+
routes.set(
|
|
22912
|
+
connection,
|
|
22913
|
+
routeConnection(connection, segmentsOf(routes.values()))
|
|
22914
|
+
);
|
|
22915
|
+
let score = measureSystemRouteQuality(
|
|
22916
|
+
[...routes.values()].map((r2) => r2.route)
|
|
22917
|
+
).score;
|
|
22918
|
+
const reroute = (affected) => {
|
|
22919
|
+
const candidate = new Map(
|
|
22920
|
+
[...routes].filter(([connection]) => !affected.has(connection))
|
|
22921
|
+
);
|
|
22922
|
+
for (const connection of connections.filter((c2) => affected.has(c2)))
|
|
22923
|
+
candidate.set(
|
|
22924
|
+
connection,
|
|
22925
|
+
routeConnection(connection, segmentsOf(candidate.values()))
|
|
22926
|
+
);
|
|
22927
|
+
const candidateScore = measureSystemRouteQuality(
|
|
22928
|
+
[...candidate.values()].map((r2) => r2.route)
|
|
22929
|
+
).score;
|
|
22930
|
+
if (candidateScore >= score - 1e-6) return false;
|
|
22931
|
+
routes = candidate;
|
|
22932
|
+
score = candidateScore;
|
|
22933
|
+
return true;
|
|
22934
|
+
};
|
|
22935
|
+
let proposals = 0;
|
|
22936
|
+
const proposalLimit = Math.min(64, connections.length * 4);
|
|
22937
|
+
for (let pass = 0; pass < 4 && proposals < proposalLimit; pass++) {
|
|
22938
|
+
let improved = false;
|
|
22939
|
+
for (const [block, ports] of portsByBlock) {
|
|
22940
|
+
if (ports.length < 2) continue;
|
|
22941
|
+
const center = bounds.get(block).center;
|
|
22942
|
+
const perimeter = [...ports].sort(
|
|
22943
|
+
(a2, b2) => Math.atan2(a2.point.y - center.y, a2.point.x - center.x) - Math.atan2(b2.point.y - center.y, b2.point.x - center.x)
|
|
22944
|
+
);
|
|
22945
|
+
for (let i2 = 0; i2 < perimeter.length && proposals < proposalLimit; i2++) {
|
|
22946
|
+
proposals++;
|
|
22947
|
+
const a2 = perimeter[i2], b2 = perimeter[(i2 + 1) % perimeter.length];
|
|
22948
|
+
const swap2 = () => {
|
|
22949
|
+
;
|
|
22950
|
+
[a2.point, b2.point] = [b2.point, a2.point];
|
|
22951
|
+
[a2.escape, b2.escape] = [b2.escape, a2.escape];
|
|
22952
|
+
[a2.side, b2.side] = [b2.side, a2.side];
|
|
22953
|
+
};
|
|
22954
|
+
swap2();
|
|
22955
|
+
if (reroute(/* @__PURE__ */ new Set([a2.connection, b2.connection]))) {
|
|
22956
|
+
improved = true;
|
|
22957
|
+
perimeter[i2] = b2;
|
|
22958
|
+
perimeter[(i2 + 1) % perimeter.length] = a2;
|
|
22959
|
+
} else swap2();
|
|
22960
|
+
}
|
|
22961
|
+
}
|
|
22962
|
+
if (!improved) break;
|
|
22963
|
+
const quality = measureSystemRouteQuality(
|
|
22964
|
+
[...routes.values()].map((r2) => r2.route)
|
|
22965
|
+
);
|
|
22966
|
+
if (quality.crossings === 0 && quality.sharedLength === 0 && quality.closeParallelLength === 0)
|
|
22967
|
+
break;
|
|
22968
|
+
}
|
|
22969
|
+
for (const connection of connections) reroute(/* @__PURE__ */ new Set([connection]));
|
|
22970
|
+
return routes;
|
|
22971
|
+
}
|
|
22972
|
+
|
|
22973
|
+
// lib/utils/system-blocks/round-system-block-connections.ts
|
|
22974
|
+
function roundSystemBlockConnections(tree, bounds, routes) {
|
|
22975
|
+
const obstacles = [...bounds].map(
|
|
22976
|
+
([block, box3]) => ({
|
|
22977
|
+
minX: box3.center.x - box3.width / 2,
|
|
22978
|
+
maxX: box3.center.x + box3.width / 2,
|
|
22979
|
+
minY: tree.groups.includes(block) ? box3.center.y + box3.height / 2 - (block._parsedProps.description ? 12 : 7) : box3.center.y - box3.height / 2,
|
|
22980
|
+
maxY: box3.center.y + box3.height / 2
|
|
22981
|
+
})
|
|
22982
|
+
);
|
|
22983
|
+
for (const [connection, resolved] of routes) {
|
|
22984
|
+
const label = resolved.label_anchor_center;
|
|
22985
|
+
if (!label) continue;
|
|
22986
|
+
const halfWidth = (getSystemConnectionLabelWidth(connection._parsedProps.label) - SYSTEM_CONNECTION_LABEL_HORIZONTAL_PADDING) / 2;
|
|
22514
22987
|
obstacles.push({
|
|
22515
|
-
minX: label.x -
|
|
22516
|
-
maxX: label.x +
|
|
22517
|
-
minY: label.y -
|
|
22518
|
-
maxY: label.y +
|
|
22988
|
+
minX: label.x - halfWidth,
|
|
22989
|
+
maxX: label.x + halfWidth,
|
|
22990
|
+
minY: label.y - 2.5,
|
|
22991
|
+
maxY: label.y + 2.5
|
|
22992
|
+
});
|
|
22993
|
+
}
|
|
22994
|
+
const rounded = new Map(routes);
|
|
22995
|
+
for (const [connection, resolved] of routes) {
|
|
22996
|
+
const wireObstacles = [];
|
|
22997
|
+
for (const [other, { route }] of rounded) {
|
|
22998
|
+
if (other === connection) continue;
|
|
22999
|
+
for (let index2 = 1; index2 < route.length; index2++) {
|
|
23000
|
+
const end = route[index2];
|
|
23001
|
+
const points = [
|
|
23002
|
+
route[index2 - 1],
|
|
23003
|
+
...end.cubic_bezier_control_points ?? [],
|
|
23004
|
+
end
|
|
23005
|
+
];
|
|
23006
|
+
wireObstacles.push({
|
|
23007
|
+
minX: Math.min(...points.map((p2) => p2.x)),
|
|
23008
|
+
maxX: Math.max(...points.map((p2) => p2.x)),
|
|
23009
|
+
minY: Math.min(...points.map((p2) => p2.y)),
|
|
23010
|
+
maxY: Math.max(...points.map((p2) => p2.y))
|
|
23011
|
+
});
|
|
23012
|
+
}
|
|
23013
|
+
}
|
|
23014
|
+
const blocked = [
|
|
23015
|
+
...obstacles,
|
|
23016
|
+
...wireObstacles.map((wire) => ({
|
|
23017
|
+
minX: wire.minX - 3,
|
|
23018
|
+
maxX: wire.maxX + 3,
|
|
23019
|
+
minY: wire.minY - 3,
|
|
23020
|
+
maxY: wire.maxY + 3
|
|
23021
|
+
}))
|
|
23022
|
+
];
|
|
23023
|
+
const { from, to } = tree.endpoints.get(connection);
|
|
23024
|
+
const selfLoop = from === to ? createSystemBlockSelfLoop(resolved.route, bounds.get(from), blocked) : void 0;
|
|
23025
|
+
rounded.set(connection, {
|
|
23026
|
+
...resolved,
|
|
23027
|
+
route: selfLoop ?? roundSystemBlockRoute(resolved.route, obstacles, wireObstacles)
|
|
22519
23028
|
});
|
|
22520
23029
|
}
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
|
|
22524
|
-
|
|
22525
|
-
|
|
23030
|
+
return rounded;
|
|
23031
|
+
}
|
|
23032
|
+
|
|
23033
|
+
// lib/utils/system-blocks/layout-system-blocks.ts
|
|
23034
|
+
function layoutSystemBlocks(tree, direction) {
|
|
23035
|
+
const degrees2 = /* @__PURE__ */ new Map();
|
|
23036
|
+
for (const {
|
|
23037
|
+
_parsedProps: { from, to }
|
|
23038
|
+
} of tree.connections) {
|
|
23039
|
+
degrees2.set(from, (degrees2.get(from) ?? 0) + 1);
|
|
23040
|
+
degrees2.set(to, (degrees2.get(to) ?? 0) + 1);
|
|
23041
|
+
}
|
|
23042
|
+
const layout = layoutContainer(tree, void 0, direction ?? "right", degrees2);
|
|
23043
|
+
const toPage = compose10(
|
|
23044
|
+
translate8(-layout.width / 2, layout.height / 2),
|
|
23045
|
+
scale3(1, -1)
|
|
23046
|
+
);
|
|
23047
|
+
for (const box3 of layout.bounds.values())
|
|
23048
|
+
box3.center = applyToPoint34(toPage, box3.center);
|
|
23049
|
+
for (const [connection, resolved] of layout.routes) {
|
|
23050
|
+
resolved.route = resolved.route.map((point8) => applyToPoint34(toPage, point8));
|
|
23051
|
+
if (resolved.label_anchor_center)
|
|
23052
|
+
resolved.label_anchor_center = applyToPoint34(
|
|
23053
|
+
toPage,
|
|
23054
|
+
resolved.label_anchor_center
|
|
23055
|
+
);
|
|
23056
|
+
const endpoints = tree.endpoints.get(connection);
|
|
23057
|
+
for (const [block, index2] of [
|
|
23058
|
+
[endpoints.from, 0],
|
|
23059
|
+
[endpoints.to, resolved.route.length - 1]
|
|
23060
|
+
]) {
|
|
23061
|
+
const box3 = layout.bounds.get(block);
|
|
23062
|
+
const point8 = resolved.route[index2];
|
|
23063
|
+
let dx2 = point8.x - box3.center.x, dy2 = point8.y - box3.center.y;
|
|
23064
|
+
if (dx2 === 0 && dy2 === 0) {
|
|
23065
|
+
const neighbor = resolved.route[index2 === 0 ? 1 : index2 - 1];
|
|
23066
|
+
dx2 = neighbor.x - point8.x;
|
|
23067
|
+
dy2 = neighbor.y - point8.y;
|
|
23068
|
+
}
|
|
23069
|
+
const factor = Math.max(
|
|
23070
|
+
Math.abs(dx2) / (box3.width / 2),
|
|
23071
|
+
Math.abs(dy2) / (box3.height / 2)
|
|
22526
23072
|
);
|
|
22527
|
-
|
|
22528
|
-
|
|
22529
|
-
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22533
|
-
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
})
|
|
23073
|
+
resolved.route[index2] = {
|
|
23074
|
+
x: box3.center.x + dx2 / factor,
|
|
23075
|
+
y: box3.center.y + dy2 / factor
|
|
23076
|
+
};
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
const routes = roundSystemBlockConnections(
|
|
23080
|
+
tree,
|
|
23081
|
+
layout.bounds,
|
|
23082
|
+
routeSystemBlocks(tree, layout.bounds, layout.routes)
|
|
22538
23083
|
);
|
|
23084
|
+
let width = layout.width, height = layout.height;
|
|
23085
|
+
for (const [connection, resolved] of routes) {
|
|
23086
|
+
for (const point8 of resolved.route) {
|
|
23087
|
+
width = Math.max(width, 2 * (Math.abs(point8.x) + 6));
|
|
23088
|
+
height = Math.max(height, 2 * (Math.abs(point8.y) + 6));
|
|
23089
|
+
}
|
|
23090
|
+
if (resolved.label_anchor_center) {
|
|
23091
|
+
width = Math.max(
|
|
23092
|
+
width,
|
|
23093
|
+
2 * (Math.abs(resolved.label_anchor_center.x) + getSystemConnectionLabelWidth(connection._parsedProps.label) / 2 + 6)
|
|
23094
|
+
);
|
|
23095
|
+
height = Math.max(
|
|
23096
|
+
height,
|
|
23097
|
+
2 * (Math.abs(resolved.label_anchor_center.y) + 8)
|
|
23098
|
+
);
|
|
23099
|
+
}
|
|
23100
|
+
}
|
|
23101
|
+
return { width, height, bounds: layout.bounds, routes };
|
|
23102
|
+
}
|
|
23103
|
+
function layoutContainer(tree, parent, direction, degrees2) {
|
|
23104
|
+
const children = [
|
|
23105
|
+
...tree.groups,
|
|
23106
|
+
...tree.components
|
|
23107
|
+
].filter((block) => tree.parents.get(block) === parent);
|
|
23108
|
+
const groupLayouts = /* @__PURE__ */ new Map();
|
|
23109
|
+
const graph = new dagre.graphlib.Graph({ multigraph: true });
|
|
23110
|
+
graph.setGraph({
|
|
23111
|
+
rankdir: { right: "LR", left: "RL", down: "TB", up: "BT" }[direction],
|
|
23112
|
+
nodesep: parent ? 12 : 18,
|
|
23113
|
+
ranksep: 18,
|
|
23114
|
+
edgesep: 8,
|
|
23115
|
+
marginx: 8,
|
|
23116
|
+
marginy: 8
|
|
23117
|
+
});
|
|
23118
|
+
for (const block of children) {
|
|
23119
|
+
if (tree.groups.includes(block)) {
|
|
23120
|
+
const child = layoutContainer(
|
|
23121
|
+
tree,
|
|
23122
|
+
block,
|
|
23123
|
+
direction,
|
|
23124
|
+
degrees2
|
|
23125
|
+
);
|
|
23126
|
+
groupLayouts.set(block, child);
|
|
23127
|
+
graph.setNode(block.name, { width: child.width, height: child.height });
|
|
23128
|
+
} else {
|
|
23129
|
+
const { name, displayName, description } = block._parsedProps;
|
|
23130
|
+
const width2 = Math.max(
|
|
23131
|
+
32,
|
|
23132
|
+
Math.min(62, (displayName ?? name).length * 1.5 + 8)
|
|
23133
|
+
);
|
|
23134
|
+
const descriptionLines = description ? Math.ceil(description.length / ((width2 - 8) / 1.3)) : 0;
|
|
23135
|
+
graph.setNode(name, {
|
|
23136
|
+
width: width2,
|
|
23137
|
+
height: Math.max(18, 12 + descriptionLines * 3.5)
|
|
23138
|
+
});
|
|
23139
|
+
}
|
|
23140
|
+
}
|
|
23141
|
+
const childFor = (block) => {
|
|
23142
|
+
let current2 = block;
|
|
23143
|
+
while (current2 && tree.parents.get(current2) !== parent)
|
|
23144
|
+
current2 = tree.parents.get(current2);
|
|
23145
|
+
return current2;
|
|
23146
|
+
};
|
|
23147
|
+
const edges = /* @__PURE__ */ new Map();
|
|
23148
|
+
for (const [index2, connection] of tree.connections.entries()) {
|
|
23149
|
+
const endpoints = tree.endpoints.get(connection);
|
|
23150
|
+
const from = childFor(endpoints.from), to = childFor(endpoints.to);
|
|
23151
|
+
if (!from || !to || from === to && (from !== endpoints.from || to !== endpoints.to))
|
|
23152
|
+
continue;
|
|
23153
|
+
const props = connection._parsedProps;
|
|
23154
|
+
const reverse4 = props.direction === "both" && degrees2.get(props.to) > degrees2.get(props.from);
|
|
23155
|
+
const edge = {
|
|
23156
|
+
v: (reverse4 ? to : from).name,
|
|
23157
|
+
w: (reverse4 ? from : to).name,
|
|
23158
|
+
name: `connection_${index2}`
|
|
23159
|
+
};
|
|
23160
|
+
graph.setEdge(edge, {
|
|
23161
|
+
width: props.label === void 0 ? 0 : getSystemConnectionLabelWidth(props.label),
|
|
23162
|
+
height: props.label === void 0 ? 0 : 5,
|
|
23163
|
+
labelpos: "c",
|
|
23164
|
+
labeloffset: 0
|
|
23165
|
+
});
|
|
23166
|
+
edges.set(connection, edge);
|
|
23167
|
+
}
|
|
23168
|
+
if (children.length) dagre.layout(graph);
|
|
23169
|
+
const heading = parent ? parent._parsedProps.description ? 14 : 8 : 0;
|
|
23170
|
+
const minimumWidth = parent ? Math.min(
|
|
23171
|
+
90,
|
|
23172
|
+
(parent._parsedProps.displayName ?? parent.name).length * 1.8 + 8
|
|
23173
|
+
) : 32;
|
|
23174
|
+
const width = Math.max(minimumWidth, graph.graph().width ?? 0);
|
|
23175
|
+
const height = Math.max(24, (graph.graph().height ?? 0) + heading);
|
|
23176
|
+
const bounds = /* @__PURE__ */ new Map();
|
|
23177
|
+
const routes = /* @__PURE__ */ new Map();
|
|
23178
|
+
for (const block of children) {
|
|
23179
|
+
const node = graph.node(block.name);
|
|
23180
|
+
bounds.set(block, {
|
|
23181
|
+
center: { x: node.x, y: node.y + heading },
|
|
23182
|
+
width: node.width,
|
|
23183
|
+
height: node.height
|
|
23184
|
+
});
|
|
23185
|
+
const child = groupLayouts.get(block);
|
|
23186
|
+
if (!child) continue;
|
|
23187
|
+
const offset = {
|
|
23188
|
+
x: node.x - child.width / 2,
|
|
23189
|
+
y: node.y + heading - child.height / 2
|
|
23190
|
+
};
|
|
23191
|
+
const shift = (point8) => ({
|
|
23192
|
+
x: point8.x + offset.x,
|
|
23193
|
+
y: point8.y + offset.y
|
|
23194
|
+
});
|
|
23195
|
+
for (const [descendant, box3] of child.bounds)
|
|
23196
|
+
bounds.set(descendant, { ...box3, center: shift(box3.center) });
|
|
23197
|
+
for (const [connection, resolved] of child.routes)
|
|
23198
|
+
routes.set(connection, {
|
|
23199
|
+
route: resolved.route.map(shift),
|
|
23200
|
+
label_anchor_center: resolved.label_anchor_center ? shift(resolved.label_anchor_center) : void 0
|
|
23201
|
+
});
|
|
23202
|
+
}
|
|
23203
|
+
for (const [connection, edgeKey] of edges) {
|
|
23204
|
+
const edge = graph.edge(edgeKey);
|
|
23205
|
+
const route = edge.points.map((point8) => ({
|
|
23206
|
+
x: point8.x,
|
|
23207
|
+
y: point8.y + heading
|
|
23208
|
+
}));
|
|
23209
|
+
const from = childFor(tree.endpoints.get(connection).from);
|
|
23210
|
+
if (edgeKey.v !== from.name) route.reverse();
|
|
23211
|
+
routes.set(connection, {
|
|
23212
|
+
route,
|
|
23213
|
+
label_anchor_center: connection._parsedProps.label === void 0 ? void 0 : { x: edge.x, y: edge.y + heading }
|
|
23214
|
+
});
|
|
23215
|
+
}
|
|
23216
|
+
const bundles = /* @__PURE__ */ new Map();
|
|
23217
|
+
for (const [connection, edge] of edges) {
|
|
23218
|
+
if (connection._parsedProps.label === void 0) continue;
|
|
23219
|
+
const targets = bundles.get(edge.v) ?? /* @__PURE__ */ new Map();
|
|
23220
|
+
const connections = targets.get(edge.w) ?? [];
|
|
23221
|
+
connections.push(connection);
|
|
23222
|
+
targets.set(edge.w, connections);
|
|
23223
|
+
bundles.set(edge.v, targets);
|
|
23224
|
+
}
|
|
23225
|
+
const crossAxis = direction === "right" || direction === "left" ? "y" : "x";
|
|
23226
|
+
for (const targets of bundles.values()) {
|
|
23227
|
+
for (const connections of targets.values()) {
|
|
23228
|
+
const slots = connections.map((c2) => routes.get(c2).label_anchor_center).sort((a2, b2) => a2[crossAxis] - b2[crossAxis]);
|
|
23229
|
+
const destinationPosition = (connection) => {
|
|
23230
|
+
const endpoints = tree.endpoints.get(connection);
|
|
23231
|
+
const targetChild = edges.get(connection).w;
|
|
23232
|
+
const endpoint = childFor(endpoints.to).name === targetChild ? endpoints.to : endpoints.from;
|
|
23233
|
+
return bounds.get(endpoint).center[crossAxis];
|
|
23234
|
+
};
|
|
23235
|
+
connections.sort(
|
|
23236
|
+
(a2, b2) => destinationPosition(a2) - destinationPosition(b2)
|
|
23237
|
+
);
|
|
23238
|
+
connections.forEach((connection, index2) => {
|
|
23239
|
+
routes.get(connection).label_anchor_center = slots[index2];
|
|
23240
|
+
});
|
|
23241
|
+
}
|
|
23242
|
+
}
|
|
22539
23243
|
return { width, height, bounds, routes };
|
|
22540
23244
|
}
|
|
22541
23245
|
|
|
@@ -22634,7 +23338,7 @@ function SystemBlockPage_doInitialSystemBlockRender(page) {
|
|
|
22634
23338
|
}
|
|
22635
23339
|
for (const connection of tree.connections) {
|
|
22636
23340
|
const { from, to } = tree.endpoints.get(connection);
|
|
22637
|
-
const { name: name2, kind, direction: direction2, label } = connection._parsedProps;
|
|
23341
|
+
const { name: name2, kind, direction: direction2, label, color: color3, lineStyle } = connection._parsedProps;
|
|
22638
23342
|
db2.system_block_connection.insert({
|
|
22639
23343
|
system_block_page_id: page.system_block_page_id,
|
|
22640
23344
|
from_system_block_component_id: componentIds.get(from),
|
|
@@ -22642,6 +23346,8 @@ function SystemBlockPage_doInitialSystemBlockRender(page) {
|
|
|
22642
23346
|
name: name2,
|
|
22643
23347
|
label,
|
|
22644
23348
|
direction: direction2,
|
|
23349
|
+
color: color3,
|
|
23350
|
+
line_style: lineStyle,
|
|
22645
23351
|
is_signal: kind === "signal",
|
|
22646
23352
|
is_power: kind === "power",
|
|
22647
23353
|
...layout.routes.get(connection)
|
|
@@ -158599,7 +159305,7 @@ var RoutingPhasePreparationError = class extends Error {
|
|
|
158599
159305
|
var package_default = {
|
|
158600
159306
|
name: "@tscircuit/core",
|
|
158601
159307
|
type: "module",
|
|
158602
|
-
version: "0.0.
|
|
159308
|
+
version: "0.0.2300",
|
|
158603
159309
|
types: "dist/index.d.ts",
|
|
158604
159310
|
main: "dist/index.js",
|
|
158605
159311
|
module: "dist/index.js",
|
|
@@ -158677,12 +159383,12 @@ var package_default = {
|
|
|
158677
159383
|
"bun-match-svg": "0.0.12",
|
|
158678
159384
|
"calculate-elbow": "^0.0.12",
|
|
158679
159385
|
"chokidar-cli": "^3.0.0",
|
|
158680
|
-
"circuit-json": "^0.0.
|
|
159386
|
+
"circuit-json": "^0.0.548",
|
|
158681
159387
|
"circuit-json-to-bom-csv": "^0.0.28",
|
|
158682
159388
|
"circuit-json-to-bpc": "^0.0.13",
|
|
158683
159389
|
"circuit-json-to-connectivity-map": "^0.0.33",
|
|
158684
159390
|
"circuit-json-to-spice": "^0.0.45",
|
|
158685
|
-
"circuit-to-svg": "^0.0.
|
|
159391
|
+
"circuit-to-svg": "^0.0.457",
|
|
158686
159392
|
concurrently: "^9.1.2",
|
|
158687
159393
|
"connectivity-map": "^1.0.1",
|
|
158688
159394
|
debug: "^4.3.6",
|
|
@@ -158769,7 +159475,7 @@ var package_default = {
|
|
|
158769
159475
|
overrides: {
|
|
158770
159476
|
"@tscircuit/graphics-debug-3d": "https://jscdn.tscircuit.com/@tscircuit/graphics-debug-3d/0.0.1.tgz",
|
|
158771
159477
|
"@tscircuit/props": "^0.0.721",
|
|
158772
|
-
"circuit-json": "^0.0.
|
|
159478
|
+
"circuit-json": "^0.0.548",
|
|
158773
159479
|
"@tscircuit/cableprinter": "^0.0.6"
|
|
158774
159480
|
},
|
|
158775
159481
|
browser: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.2301",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -79,12 +79,12 @@
|
|
|
79
79
|
"bun-match-svg": "0.0.12",
|
|
80
80
|
"calculate-elbow": "^0.0.12",
|
|
81
81
|
"chokidar-cli": "^3.0.0",
|
|
82
|
-
"circuit-json": "^0.0.
|
|
82
|
+
"circuit-json": "^0.0.548",
|
|
83
83
|
"circuit-json-to-bom-csv": "^0.0.28",
|
|
84
84
|
"circuit-json-to-bpc": "^0.0.13",
|
|
85
85
|
"circuit-json-to-connectivity-map": "^0.0.33",
|
|
86
86
|
"circuit-json-to-spice": "^0.0.45",
|
|
87
|
-
"circuit-to-svg": "^0.0.
|
|
87
|
+
"circuit-to-svg": "^0.0.457",
|
|
88
88
|
"concurrently": "^9.1.2",
|
|
89
89
|
"connectivity-map": "^1.0.1",
|
|
90
90
|
"debug": "^4.3.6",
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"overrides": {
|
|
172
172
|
"@tscircuit/graphics-debug-3d": "https://jscdn.tscircuit.com/@tscircuit/graphics-debug-3d/0.0.1.tgz",
|
|
173
173
|
"@tscircuit/props": "^0.0.721",
|
|
174
|
-
"circuit-json": "^0.0.
|
|
174
|
+
"circuit-json": "^0.0.548",
|
|
175
175
|
"@tscircuit/cableprinter": "^0.0.6"
|
|
176
176
|
},
|
|
177
177
|
"browser": {
|