@tscircuit/schematic-trace-solver 0.0.96 → 0.0.98
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.d.ts +9 -0
- package/dist/index.js +986 -479
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +55 -14
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +31 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +45 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +55 -13
- package/lib/solvers/TraceCleanupSolver/alignSameNetRails.ts +66 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts +124 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts +108 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts +121 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts +114 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts +27 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts +30 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts +67 -0
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts +34 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +1 -60
- package/lib/utils/doesPathCoincideWithTraces.ts +61 -0
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +1 -1
- package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +1 -1
- package/tests/examples/__snapshots__/example02.snap.svg +1 -1
- package/tests/examples/__snapshots__/example14.snap.svg +1 -1
- package/tests/examples/__snapshots__/example18.snap.svg +1 -1
- package/tests/examples/__snapshots__/example46.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +1 -1
- package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +178 -0
- package/tests/repros/__snapshots__/repro-rp2040-zero-crystal-fallback-netlabels.snap.svg +54 -0
- package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +1 -1
- package/tests/repros/assets/repro-rp2040-gamepad-trace-alignment.input.json +237 -0
- package/tests/repros/assets/repro-rp2040-zero-crystal-fallback-netlabels.input.json +121 -0
- package/tests/repros/repro-rp2040-gamepad-trace-alignment.test.ts +38 -0
- package/tests/repros/repro-rp2040-zero-crystal-fallback-netlabels.test.ts +16 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/segment-intersects-rect-interior.test.ts +15 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/segment-overlaps-rect-boundary-boundary.test.ts +18 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/segment-overlaps-rect-boundary-interior.test.ts +18 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-component-scope.test.ts +38 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-eligible-traces.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-horizontal.test.ts +62 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-anchor.test.ts +36 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-label-junction.test.ts +42 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-obstacle.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-pipeline-label-connector.test.ts +52 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts +25 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-visible-length.test.ts +40 -0
- package/tests/solvers/TraceCleanupSolver/fixtures/alignSameNetRails.ts +89 -0
package/dist/index.js
CHANGED
|
@@ -585,54 +585,150 @@ import "graphics-debug";
|
|
|
585
585
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
586
586
|
import { calculateElbow as calculateElbow2 } from "calculate-elbow";
|
|
587
587
|
|
|
588
|
-
// lib/solvers/
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
589
|
+
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
590
|
+
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
591
|
+
function getDimsForOrientation(params) {
|
|
592
|
+
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
593
|
+
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
594
|
+
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
595
|
+
if (orientation === "y+" || orientation === "y-") {
|
|
596
|
+
return {
|
|
597
|
+
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
598
|
+
width: horizHeight,
|
|
599
|
+
height: horizWidth
|
|
600
|
+
};
|
|
601
|
+
}
|
|
592
602
|
return {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
minY: chip.center.y - halfHeight,
|
|
596
|
-
maxY: chip.center.y + halfHeight
|
|
603
|
+
width: horizWidth,
|
|
604
|
+
height: horizHeight
|
|
597
605
|
};
|
|
598
606
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
607
|
+
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
608
|
+
switch (orientation) {
|
|
609
|
+
case "x+":
|
|
610
|
+
return { x: anchor.x + width / 2, y: anchor.y };
|
|
611
|
+
case "x-":
|
|
612
|
+
return { x: anchor.x - width / 2, y: anchor.y };
|
|
613
|
+
case "y+":
|
|
614
|
+
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
615
|
+
case "y-":
|
|
616
|
+
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function getRectBounds(center, w, h) {
|
|
602
620
|
return {
|
|
603
|
-
minX:
|
|
604
|
-
minY:
|
|
605
|
-
maxX:
|
|
606
|
-
maxY:
|
|
621
|
+
minX: center.x - w / 2,
|
|
622
|
+
minY: center.y - h / 2,
|
|
623
|
+
maxX: center.x + w / 2,
|
|
624
|
+
maxY: center.y + h / 2
|
|
607
625
|
};
|
|
608
626
|
}
|
|
609
|
-
|
|
610
|
-
|
|
627
|
+
|
|
628
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/calculateDirectShortPath.ts
|
|
629
|
+
import { calculateElbow } from "calculate-elbow";
|
|
630
|
+
var MAX_SHORT_TRACE_DISTANCE = 0.15;
|
|
631
|
+
var SHORT_TRACE_OVERSHOOT = MAX_SHORT_TRACE_DISTANCE / 7.5;
|
|
632
|
+
var FALLBACK_ELBOW_MAX_OVERSHOOT = 0.2;
|
|
633
|
+
function segmentDirection(from, to) {
|
|
634
|
+
if (to.x > from.x) return "x+";
|
|
635
|
+
if (to.x < from.x) return "x-";
|
|
636
|
+
if (to.y > from.y) return "y+";
|
|
637
|
+
if (to.y < from.y) return "y-";
|
|
638
|
+
return null;
|
|
611
639
|
}
|
|
612
|
-
function
|
|
613
|
-
|
|
614
|
-
|
|
640
|
+
function pathMatchesPinDirections({
|
|
641
|
+
path,
|
|
642
|
+
pin1,
|
|
643
|
+
pin2
|
|
644
|
+
}) {
|
|
645
|
+
const firstDirection = segmentDirection(path[0], path[1]);
|
|
646
|
+
const lastDirection = segmentDirection(
|
|
647
|
+
path[path.length - 1],
|
|
648
|
+
path[path.length - 2]
|
|
649
|
+
);
|
|
650
|
+
return firstDirection === pin1._facingDirection && lastDirection === pin2._facingDirection;
|
|
651
|
+
}
|
|
652
|
+
function calculateShortOrthogonalRoute(pin1, pin2) {
|
|
653
|
+
if (pin1.x === pin2.x || pin1.y === pin2.y) return null;
|
|
654
|
+
const firstDir = pin1._facingDirection;
|
|
655
|
+
const lastDir = pin2._facingDirection;
|
|
656
|
+
const start = { x: pin1.x, y: pin1.y };
|
|
657
|
+
const end = { x: pin2.x, y: pin2.y };
|
|
658
|
+
let path = null;
|
|
659
|
+
if (firstDir?.startsWith("y") && lastDir?.startsWith("x")) {
|
|
660
|
+
let yOffset = -SHORT_TRACE_OVERSHOOT;
|
|
661
|
+
if (firstDir === "y+") {
|
|
662
|
+
yOffset = SHORT_TRACE_OVERSHOOT;
|
|
663
|
+
}
|
|
664
|
+
const routeY = start.y + yOffset;
|
|
665
|
+
const routeX = (start.x + end.x) / 2;
|
|
666
|
+
path = [
|
|
667
|
+
start,
|
|
668
|
+
{ x: start.x, y: routeY },
|
|
669
|
+
{ x: routeX, y: routeY },
|
|
670
|
+
{ x: routeX, y: end.y },
|
|
671
|
+
end
|
|
672
|
+
];
|
|
673
|
+
} else if (firstDir?.startsWith("x") && lastDir?.startsWith("y")) {
|
|
674
|
+
let xOffset = -SHORT_TRACE_OVERSHOOT;
|
|
675
|
+
if (firstDir === "x+") {
|
|
676
|
+
xOffset = SHORT_TRACE_OVERSHOOT;
|
|
677
|
+
}
|
|
678
|
+
const routeX = start.x + xOffset;
|
|
679
|
+
const routeY = (start.y + end.y) / 2;
|
|
680
|
+
path = [
|
|
681
|
+
start,
|
|
682
|
+
{ x: routeX, y: start.y },
|
|
683
|
+
{ x: routeX, y: routeY },
|
|
684
|
+
{ x: end.x, y: routeY },
|
|
685
|
+
end
|
|
686
|
+
];
|
|
615
687
|
}
|
|
616
|
-
return
|
|
688
|
+
if (!path) return null;
|
|
689
|
+
if (pathMatchesPinDirections({ path, pin1, pin2 })) {
|
|
690
|
+
return path;
|
|
691
|
+
}
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
function calculateDirectShortPath(pin1, pin2) {
|
|
695
|
+
const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y);
|
|
696
|
+
if (routingDistance > MAX_SHORT_TRACE_DISTANCE) return null;
|
|
697
|
+
const start = { x: pin1.x, y: pin1.y };
|
|
698
|
+
const end = { x: pin2.x, y: pin2.y };
|
|
699
|
+
const orthogonalRoute = calculateShortOrthogonalRoute(pin1, pin2);
|
|
700
|
+
if (orthogonalRoute) return orthogonalRoute;
|
|
701
|
+
let candidatePaths = [];
|
|
702
|
+
if (pin1.x !== pin2.x && pin1.y !== pin2.y) {
|
|
703
|
+
candidatePaths = [
|
|
704
|
+
[start, { x: pin2.x, y: pin1.y }, end],
|
|
705
|
+
[start, { x: pin1.x, y: pin2.y }, end]
|
|
706
|
+
];
|
|
707
|
+
} else {
|
|
708
|
+
candidatePaths = [[start, end]];
|
|
709
|
+
}
|
|
710
|
+
for (const path of candidatePaths) {
|
|
711
|
+
if (pathMatchesPinDirections({ path, pin1, pin2 })) return path;
|
|
712
|
+
}
|
|
713
|
+
return calculateElbow(
|
|
714
|
+
{
|
|
715
|
+
x: pin1.x,
|
|
716
|
+
y: pin1.y,
|
|
717
|
+
facingDirection: pin1._facingDirection
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
x: pin2.x,
|
|
721
|
+
y: pin2.y,
|
|
722
|
+
facingDirection: pin2._facingDirection
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
overshoot: Math.min(
|
|
726
|
+
FALLBACK_ELBOW_MAX_OVERSHOOT,
|
|
727
|
+
Math.max(SHORT_TRACE_OVERSHOOT, routingDistance / 4)
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
);
|
|
617
731
|
}
|
|
618
|
-
|
|
619
|
-
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
|
|
620
|
-
var chipToRect = (chip) => {
|
|
621
|
-
const b = getInputChipBounds(chip);
|
|
622
|
-
return { kind: "chip", chipId: chip.chipId, ...b };
|
|
623
|
-
};
|
|
624
|
-
var getObstacleRects = (problem, opts = {}) => {
|
|
625
|
-
const chipRects = problem.chips.map(chipToRect);
|
|
626
|
-
const textBoxRects = (problem.textBoxes ?? []).map((textBox) => {
|
|
627
|
-
const b = getTextBoxBounds(textBox, opts.textBoxPadding);
|
|
628
|
-
return {
|
|
629
|
-
kind: "text_box",
|
|
630
|
-
textBox,
|
|
631
|
-
...b
|
|
632
|
-
};
|
|
633
|
-
});
|
|
634
|
-
return [...chipRects, ...textBoxRects];
|
|
635
|
-
};
|
|
636
732
|
|
|
637
733
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts
|
|
638
734
|
var EPS = 1e-9;
|
|
@@ -658,6 +754,21 @@ var segmentIntersectsRect = (a, b, r, eps = EPS) => {
|
|
|
658
754
|
return overlap > eps;
|
|
659
755
|
}
|
|
660
756
|
};
|
|
757
|
+
var segmentOverlapsRectBoundary = (a, b, r, eps = EPS) => {
|
|
758
|
+
if (isVertical(a, b, eps)) {
|
|
759
|
+
const onVerticalBoundary = Math.abs(a.x - r.minX) <= eps || Math.abs(a.x - r.maxX) <= eps;
|
|
760
|
+
if (!onVerticalBoundary) return false;
|
|
761
|
+
const overlap = Math.min(Math.max(a.y, b.y), r.maxY) - Math.max(Math.min(a.y, b.y), r.minY);
|
|
762
|
+
return overlap > eps;
|
|
763
|
+
}
|
|
764
|
+
if (isHorizontal(a, b, eps)) {
|
|
765
|
+
const onHorizontalBoundary = Math.abs(a.y - r.minY) <= eps || Math.abs(a.y - r.maxY) <= eps;
|
|
766
|
+
if (!onHorizontalBoundary) return false;
|
|
767
|
+
const overlap = Math.min(Math.max(a.x, b.x), r.maxX) - Math.max(Math.min(a.x, b.x), r.minX);
|
|
768
|
+
return overlap > eps;
|
|
769
|
+
}
|
|
770
|
+
return false;
|
|
771
|
+
};
|
|
661
772
|
var findFirstCollision = (pts, rects, opts = {}) => {
|
|
662
773
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
663
774
|
const a = pts[i];
|
|
@@ -812,151 +923,55 @@ var pathKey = (pts, decimals = 6) => {
|
|
|
812
923
|
return key;
|
|
813
924
|
};
|
|
814
925
|
|
|
815
|
-
// lib/solvers/
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
820
|
-
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
821
|
-
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
822
|
-
if (orientation === "y+" || orientation === "y-") {
|
|
823
|
-
return {
|
|
824
|
-
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
825
|
-
width: horizHeight,
|
|
826
|
-
height: horizWidth
|
|
827
|
-
};
|
|
828
|
-
}
|
|
926
|
+
// lib/solvers/GuidelinesSolver/getInputChipBounds.ts
|
|
927
|
+
function getInputChipBounds(chip) {
|
|
928
|
+
const halfWidth = chip.width / 2;
|
|
929
|
+
const halfHeight = chip.height / 2;
|
|
829
930
|
return {
|
|
830
|
-
|
|
831
|
-
|
|
931
|
+
minX: chip.center.x - halfWidth,
|
|
932
|
+
maxX: chip.center.x + halfWidth,
|
|
933
|
+
minY: chip.center.y - halfHeight,
|
|
934
|
+
maxY: chip.center.y + halfHeight
|
|
832
935
|
};
|
|
833
936
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
return { x: anchor.x + width / 2, y: anchor.y };
|
|
838
|
-
case "x-":
|
|
839
|
-
return { x: anchor.x - width / 2, y: anchor.y };
|
|
840
|
-
case "y+":
|
|
841
|
-
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
842
|
-
case "y-":
|
|
843
|
-
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
function getRectBounds(center, w, h) {
|
|
937
|
+
|
|
938
|
+
// lib/utils/textBoxBounds.ts
|
|
939
|
+
function getTextBoxBounds(textBox, padding = {}) {
|
|
847
940
|
return {
|
|
848
|
-
minX: center.x -
|
|
849
|
-
minY: center.y -
|
|
850
|
-
maxX: center.x +
|
|
851
|
-
maxY: center.y +
|
|
941
|
+
minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
|
|
942
|
+
minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
|
|
943
|
+
maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
|
|
944
|
+
maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
|
|
852
945
|
};
|
|
853
946
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
import { calculateElbow } from "calculate-elbow";
|
|
857
|
-
var MAX_SHORT_TRACE_DISTANCE = 0.15;
|
|
858
|
-
var SHORT_TRACE_OVERSHOOT = MAX_SHORT_TRACE_DISTANCE / 7.5;
|
|
859
|
-
var FALLBACK_ELBOW_MAX_OVERSHOOT = 0.2;
|
|
860
|
-
function segmentDirection(from, to) {
|
|
861
|
-
if (to.x > from.x) return "x+";
|
|
862
|
-
if (to.x < from.x) return "x-";
|
|
863
|
-
if (to.y > from.y) return "y+";
|
|
864
|
-
if (to.y < from.y) return "y-";
|
|
865
|
-
return null;
|
|
866
|
-
}
|
|
867
|
-
function pathMatchesPinDirections({
|
|
868
|
-
path,
|
|
869
|
-
pin1,
|
|
870
|
-
pin2
|
|
871
|
-
}) {
|
|
872
|
-
const firstDirection = segmentDirection(path[0], path[1]);
|
|
873
|
-
const lastDirection = segmentDirection(
|
|
874
|
-
path[path.length - 1],
|
|
875
|
-
path[path.length - 2]
|
|
876
|
-
);
|
|
877
|
-
return firstDirection === pin1._facingDirection && lastDirection === pin2._facingDirection;
|
|
878
|
-
}
|
|
879
|
-
function calculateShortOrthogonalRoute(pin1, pin2) {
|
|
880
|
-
if (pin1.x === pin2.x || pin1.y === pin2.y) return null;
|
|
881
|
-
const firstDir = pin1._facingDirection;
|
|
882
|
-
const lastDir = pin2._facingDirection;
|
|
883
|
-
const start = { x: pin1.x, y: pin1.y };
|
|
884
|
-
const end = { x: pin2.x, y: pin2.y };
|
|
885
|
-
let path = null;
|
|
886
|
-
if (firstDir?.startsWith("y") && lastDir?.startsWith("x")) {
|
|
887
|
-
let yOffset = -SHORT_TRACE_OVERSHOOT;
|
|
888
|
-
if (firstDir === "y+") {
|
|
889
|
-
yOffset = SHORT_TRACE_OVERSHOOT;
|
|
890
|
-
}
|
|
891
|
-
const routeY = start.y + yOffset;
|
|
892
|
-
const routeX = (start.x + end.x) / 2;
|
|
893
|
-
path = [
|
|
894
|
-
start,
|
|
895
|
-
{ x: start.x, y: routeY },
|
|
896
|
-
{ x: routeX, y: routeY },
|
|
897
|
-
{ x: routeX, y: end.y },
|
|
898
|
-
end
|
|
899
|
-
];
|
|
900
|
-
} else if (firstDir?.startsWith("x") && lastDir?.startsWith("y")) {
|
|
901
|
-
let xOffset = -SHORT_TRACE_OVERSHOOT;
|
|
902
|
-
if (firstDir === "x+") {
|
|
903
|
-
xOffset = SHORT_TRACE_OVERSHOOT;
|
|
904
|
-
}
|
|
905
|
-
const routeX = start.x + xOffset;
|
|
906
|
-
const routeY = (start.y + end.y) / 2;
|
|
907
|
-
path = [
|
|
908
|
-
start,
|
|
909
|
-
{ x: routeX, y: start.y },
|
|
910
|
-
{ x: routeX, y: routeY },
|
|
911
|
-
{ x: end.x, y: routeY },
|
|
912
|
-
end
|
|
913
|
-
];
|
|
914
|
-
}
|
|
915
|
-
if (!path) return null;
|
|
916
|
-
if (pathMatchesPinDirections({ path, pin1, pin2 })) {
|
|
917
|
-
return path;
|
|
918
|
-
}
|
|
919
|
-
return null;
|
|
947
|
+
function boundsOverlap(a, b, eps = 1e-9) {
|
|
948
|
+
return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
|
|
920
949
|
}
|
|
921
|
-
function
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
const start = { x: pin1.x, y: pin1.y };
|
|
925
|
-
const end = { x: pin2.x, y: pin2.y };
|
|
926
|
-
const orthogonalRoute = calculateShortOrthogonalRoute(pin1, pin2);
|
|
927
|
-
if (orthogonalRoute) return orthogonalRoute;
|
|
928
|
-
let candidatePaths = [];
|
|
929
|
-
if (pin1.x !== pin2.x && pin1.y !== pin2.y) {
|
|
930
|
-
candidatePaths = [
|
|
931
|
-
[start, { x: pin2.x, y: pin1.y }, end],
|
|
932
|
-
[start, { x: pin1.x, y: pin2.y }, end]
|
|
933
|
-
];
|
|
934
|
-
} else {
|
|
935
|
-
candidatePaths = [[start, end]];
|
|
936
|
-
}
|
|
937
|
-
for (const path of candidatePaths) {
|
|
938
|
-
if (pathMatchesPinDirections({ path, pin1, pin2 })) return path;
|
|
950
|
+
function rectIntersectsAnyTextBox(bounds, inputProblem) {
|
|
951
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
952
|
+
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
939
953
|
}
|
|
940
|
-
return
|
|
941
|
-
{
|
|
942
|
-
x: pin1.x,
|
|
943
|
-
y: pin1.y,
|
|
944
|
-
facingDirection: pin1._facingDirection
|
|
945
|
-
},
|
|
946
|
-
{
|
|
947
|
-
x: pin2.x,
|
|
948
|
-
y: pin2.y,
|
|
949
|
-
facingDirection: pin2._facingDirection
|
|
950
|
-
},
|
|
951
|
-
{
|
|
952
|
-
overshoot: Math.min(
|
|
953
|
-
FALLBACK_ELBOW_MAX_OVERSHOOT,
|
|
954
|
-
Math.max(SHORT_TRACE_OVERSHOOT, routingDistance / 4)
|
|
955
|
-
)
|
|
956
|
-
}
|
|
957
|
-
);
|
|
954
|
+
return false;
|
|
958
955
|
}
|
|
959
956
|
|
|
957
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
|
|
958
|
+
var chipToRect = (chip) => {
|
|
959
|
+
const b = getInputChipBounds(chip);
|
|
960
|
+
return { kind: "chip", chipId: chip.chipId, ...b };
|
|
961
|
+
};
|
|
962
|
+
var getObstacleRects = (problem, opts = {}) => {
|
|
963
|
+
const chipRects = problem.chips.map(chipToRect);
|
|
964
|
+
const textBoxRects = (problem.textBoxes ?? []).map((textBox) => {
|
|
965
|
+
const b = getTextBoxBounds(textBox, opts.textBoxPadding);
|
|
966
|
+
return {
|
|
967
|
+
kind: "text_box",
|
|
968
|
+
textBox,
|
|
969
|
+
...b
|
|
970
|
+
};
|
|
971
|
+
});
|
|
972
|
+
return [...chipRects, ...textBoxRects];
|
|
973
|
+
};
|
|
974
|
+
|
|
960
975
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
961
976
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
962
977
|
pins;
|
|
@@ -1143,17 +1158,41 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
1143
1158
|
const collision = findFirstCollision(path, this.obstacles, {
|
|
1144
1159
|
excludeRectsForSegment: (segIndex2) => {
|
|
1145
1160
|
const lastSegIndex2 = path.length - 2;
|
|
1161
|
+
const excludedRects = /* @__PURE__ */ new Set();
|
|
1146
1162
|
if (segIndex2 === 0 || segIndex2 === lastSegIndex2) {
|
|
1147
|
-
|
|
1163
|
+
for (const textObstacle of this.textObstacles) {
|
|
1164
|
+
excludedRects.add(textObstacle);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
const segmentStart = path[segIndex2];
|
|
1168
|
+
const segmentEnd = path[segIndex2 + 1];
|
|
1169
|
+
const endpointEpsilon = 1e-9;
|
|
1170
|
+
const segmentTouchesPin = (pin) => [segmentStart, segmentEnd].some(
|
|
1171
|
+
(point) => Math.abs(point.x - pin.x) <= endpointEpsilon && Math.abs(point.y - pin.y) <= endpointEpsilon
|
|
1172
|
+
);
|
|
1173
|
+
for (const pin of this.pins) {
|
|
1174
|
+
if (!segmentTouchesPin(pin)) continue;
|
|
1175
|
+
const endpointChipObstacle = this.obstacles.find(
|
|
1176
|
+
(obstacle) => obstacle.kind === "chip" && obstacle.chipId === pin.chipId
|
|
1177
|
+
);
|
|
1178
|
+
if (endpointChipObstacle && segmentOverlapsRectBoundary(
|
|
1179
|
+
segmentStart,
|
|
1180
|
+
segmentEnd,
|
|
1181
|
+
endpointChipObstacle
|
|
1182
|
+
)) {
|
|
1183
|
+
excludedRects.add(endpointChipObstacle);
|
|
1184
|
+
}
|
|
1148
1185
|
}
|
|
1149
1186
|
const adjacentEndpointSegIndex = segIndex2 === 1 ? 2 : segIndex2 === lastSegIndex2 - 1 ? lastSegIndex2 - 2 : null;
|
|
1150
1187
|
if (adjacentEndpointSegIndex !== null && adjacentEndpointSegIndex >= 0 && adjacentEndpointSegIndex < lastSegIndex2 && this.isSegmentOutsidePinBand(
|
|
1151
1188
|
path[adjacentEndpointSegIndex],
|
|
1152
1189
|
path[adjacentEndpointSegIndex + 1]
|
|
1153
1190
|
)) {
|
|
1154
|
-
|
|
1191
|
+
for (const textObstacle of this.endpointTextObstacles) {
|
|
1192
|
+
excludedRects.add(textObstacle);
|
|
1193
|
+
}
|
|
1155
1194
|
}
|
|
1156
|
-
return
|
|
1195
|
+
return excludedRects;
|
|
1157
1196
|
}
|
|
1158
1197
|
});
|
|
1159
1198
|
if (!collision) {
|
|
@@ -1401,12 +1440,12 @@ var applyJogToTerminalSegment = ({
|
|
|
1401
1440
|
if (si !== 0 && si !== pts.length - 2) return;
|
|
1402
1441
|
const start = pts[si];
|
|
1403
1442
|
const end = pts[si + 1];
|
|
1404
|
-
const
|
|
1405
|
-
const
|
|
1406
|
-
if (!
|
|
1407
|
-
const segDir =
|
|
1443
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS11;
|
|
1444
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS11;
|
|
1445
|
+
if (!isVertical5 && !isHorizontal4) return;
|
|
1446
|
+
const segDir = isVertical5 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
|
|
1408
1447
|
if (pts.length === 2) {
|
|
1409
|
-
if (
|
|
1448
|
+
if (isVertical5) {
|
|
1410
1449
|
const jogYNearStart = start.y + segDir * JOG_SIZE;
|
|
1411
1450
|
const jogYNearEnd = end.y - segDir * JOG_SIZE;
|
|
1412
1451
|
pts.splice(
|
|
@@ -1432,7 +1471,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1432
1471
|
return;
|
|
1433
1472
|
}
|
|
1434
1473
|
if (si === 0) {
|
|
1435
|
-
if (
|
|
1474
|
+
if (isVertical5) {
|
|
1436
1475
|
const jogY = start.y + segDir * JOG_SIZE;
|
|
1437
1476
|
pts.splice(
|
|
1438
1477
|
1,
|
|
@@ -1452,7 +1491,7 @@ var applyJogToTerminalSegment = ({
|
|
|
1452
1491
|
);
|
|
1453
1492
|
}
|
|
1454
1493
|
} else {
|
|
1455
|
-
if (
|
|
1494
|
+
if (isVertical5) {
|
|
1456
1495
|
const jogY = end.y - segDir * JOG_SIZE;
|
|
1457
1496
|
pts.splice(
|
|
1458
1497
|
si,
|
|
@@ -1548,10 +1587,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1548
1587
|
} else {
|
|
1549
1588
|
const start = pts[si];
|
|
1550
1589
|
const end = pts[si + 1];
|
|
1551
|
-
const
|
|
1552
|
-
const
|
|
1553
|
-
if (!
|
|
1554
|
-
if (
|
|
1590
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS4;
|
|
1591
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
|
|
1592
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
1593
|
+
if (isVertical5) {
|
|
1555
1594
|
start.x += offset;
|
|
1556
1595
|
end.x += offset;
|
|
1557
1596
|
} else {
|
|
@@ -1580,11 +1619,11 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1580
1619
|
}) {
|
|
1581
1620
|
const blockingCollisions = this.getBlockingCollisions({ group, offset });
|
|
1582
1621
|
if (blockingCollisions.length === 0) return offset;
|
|
1583
|
-
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical:
|
|
1622
|
+
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical5, obstacle }) => {
|
|
1584
1623
|
let orig;
|
|
1585
1624
|
let min;
|
|
1586
1625
|
let max;
|
|
1587
|
-
if (
|
|
1626
|
+
if (isVertical5) {
|
|
1588
1627
|
orig = start.x;
|
|
1589
1628
|
min = obstacle.minX;
|
|
1590
1629
|
max = obstacle.maxX;
|
|
@@ -1596,8 +1635,8 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1596
1635
|
const next = orig + candidateOffset;
|
|
1597
1636
|
return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
|
|
1598
1637
|
});
|
|
1599
|
-
const edges = blockingCollisions.map(({ start, isVertical:
|
|
1600
|
-
if (
|
|
1638
|
+
const edges = blockingCollisions.map(({ start, isVertical: isVertical5, obstacle }) => {
|
|
1639
|
+
if (isVertical5) {
|
|
1601
1640
|
return {
|
|
1602
1641
|
coord: start.x,
|
|
1603
1642
|
lowEdge: obstacle.minX,
|
|
@@ -1631,10 +1670,10 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1631
1670
|
const start = trace.tracePath[traceSegmentIndex];
|
|
1632
1671
|
const end = trace.tracePath[traceSegmentIndex + 1];
|
|
1633
1672
|
if (!start || !end) continue;
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1636
|
-
if (!
|
|
1637
|
-
const shiftedSegment =
|
|
1673
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS4;
|
|
1674
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS4;
|
|
1675
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
1676
|
+
const shiftedSegment = isVertical5 ? [
|
|
1638
1677
|
{ ...start, x: start.x + offset },
|
|
1639
1678
|
{ ...end, x: end.x + offset }
|
|
1640
1679
|
] : [
|
|
@@ -1645,7 +1684,7 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1645
1684
|
if (collision) {
|
|
1646
1685
|
blockingCollisions.push({
|
|
1647
1686
|
start,
|
|
1648
|
-
isVertical:
|
|
1687
|
+
isVertical: isVertical5,
|
|
1649
1688
|
obstacle: collision.rect
|
|
1650
1689
|
});
|
|
1651
1690
|
}
|
|
@@ -1828,9 +1867,9 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
1828
1867
|
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
1829
1868
|
const p1 = tracePath[i];
|
|
1830
1869
|
const p2 = tracePath[i + 1];
|
|
1831
|
-
const
|
|
1832
|
-
const
|
|
1833
|
-
if (!
|
|
1870
|
+
const isHorizontal4 = Math.abs(p1.y - p2.y) < EPS11;
|
|
1871
|
+
const isVertical5 = Math.abs(p1.x - p2.x) < EPS11;
|
|
1872
|
+
if (!isHorizontal4 && !isVertical5) {
|
|
1834
1873
|
return { mspPairId, tracePath, i, p1, p2 };
|
|
1835
1874
|
}
|
|
1836
1875
|
}
|
|
@@ -3439,19 +3478,18 @@ var generateRerouteCandidates = ({
|
|
|
3439
3478
|
return [...fourPointCandidates, ...snipReconnectCandidates];
|
|
3440
3479
|
};
|
|
3441
3480
|
|
|
3442
|
-
// lib/
|
|
3443
|
-
var MAX_TRIES = 5;
|
|
3481
|
+
// lib/utils/doesPathCoincideWithTraces.ts
|
|
3444
3482
|
var COINCIDENT_EPS = 2e-3;
|
|
3445
3483
|
var doesPathCoincideWithTraces = (path, traces) => {
|
|
3446
3484
|
const rangesOverlap1D = (a1, a2, b1, b2) => Math.min(Math.max(a1, a2), Math.max(b1, b2)) - Math.max(Math.min(a1, a2), Math.min(b1, b2)) > COINCIDENT_EPS;
|
|
3447
3485
|
for (let i = 0; i < path.length - 1; i++) {
|
|
3448
3486
|
const pathSegStart = path[i];
|
|
3449
3487
|
const pathSegEnd = path[i + 1];
|
|
3450
|
-
const
|
|
3451
|
-
const
|
|
3452
|
-
if (!
|
|
3453
|
-
const crossAxis =
|
|
3454
|
-
const alongAxis =
|
|
3488
|
+
const isVertical5 = Math.abs(pathSegStart.x - pathSegEnd.x) < COINCIDENT_EPS;
|
|
3489
|
+
const isHorizontal4 = Math.abs(pathSegStart.y - pathSegEnd.y) < COINCIDENT_EPS;
|
|
3490
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
3491
|
+
const crossAxis = isVertical5 ? "x" : "y";
|
|
3492
|
+
const alongAxis = isVertical5 ? "y" : "x";
|
|
3455
3493
|
for (const trace of traces) {
|
|
3456
3494
|
for (let j = 0; j < trace.tracePath.length - 1; j++) {
|
|
3457
3495
|
const traceSegStart = trace.tracePath[j];
|
|
@@ -3473,6 +3511,9 @@ var doesPathCoincideWithTraces = (path, traces) => {
|
|
|
3473
3511
|
}
|
|
3474
3512
|
return false;
|
|
3475
3513
|
};
|
|
3514
|
+
|
|
3515
|
+
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
3516
|
+
var MAX_TRIES = 5;
|
|
3476
3517
|
var SingleOverlapSolver = class extends BaseSolver {
|
|
3477
3518
|
queuedCandidatePaths;
|
|
3478
3519
|
solvedTracePath = null;
|
|
@@ -4629,26 +4670,610 @@ var balanceZShapes = ({
|
|
|
4629
4670
|
if (Math.abs(len1Original - len2Original) < 1e-3) {
|
|
4630
4671
|
continue;
|
|
4631
4672
|
}
|
|
4632
|
-
if (isHVHZShape) {
|
|
4633
|
-
const idealX = (p1.x + p4.x) / 2;
|
|
4634
|
-
p2New = { x: idealX, y: p2.y };
|
|
4635
|
-
p3New = { x: idealX, y: p3.y };
|
|
4636
|
-
} else {
|
|
4637
|
-
const idealY = (p1.y + p4.y) / 2;
|
|
4638
|
-
p2New = { x: p2.x, y: idealY };
|
|
4639
|
-
p3New = { x: p3.x, y: idealY };
|
|
4640
|
-
}
|
|
4641
|
-
const collides = segmentIntersectsAnyRect(p1, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) || segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
|
|
4642
|
-
if (!collides) {
|
|
4643
|
-
newPath[i + 1] = p2New;
|
|
4644
|
-
newPath[i + 2] = p3New;
|
|
4645
|
-
i = 0;
|
|
4673
|
+
if (isHVHZShape) {
|
|
4674
|
+
const idealX = (p1.x + p4.x) / 2;
|
|
4675
|
+
p2New = { x: idealX, y: p2.y };
|
|
4676
|
+
p3New = { x: idealX, y: p3.y };
|
|
4677
|
+
} else {
|
|
4678
|
+
const idealY = (p1.y + p4.y) / 2;
|
|
4679
|
+
p2New = { x: p2.x, y: idealY };
|
|
4680
|
+
p3New = { x: p3.x, y: idealY };
|
|
4681
|
+
}
|
|
4682
|
+
const collides = segmentIntersectsAnyRect(p1, p2New, combinedObstacles) || segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) || segmentIntersectsAnyRect(p3New, p4, combinedObstacles) || segmentIntersectsAnyRect(p1, p2New, labelBounds) || segmentIntersectsAnyRect(p2New, p3New, labelBounds) || segmentIntersectsAnyRect(p3New, p4, labelBounds);
|
|
4683
|
+
if (!collides) {
|
|
4684
|
+
newPath[i + 1] = p2New;
|
|
4685
|
+
newPath[i + 2] = p3New;
|
|
4686
|
+
i = 0;
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
const finalSimplifiedPath = simplifyPath(newPath);
|
|
4690
|
+
return {
|
|
4691
|
+
...targetTrace,
|
|
4692
|
+
tracePath: finalSimplifiedPath
|
|
4693
|
+
};
|
|
4694
|
+
};
|
|
4695
|
+
|
|
4696
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry.ts
|
|
4697
|
+
var RAIL_ALIGNMENT_EPSILON = 2e-3;
|
|
4698
|
+
var nearlyEqual = (a, b) => Math.abs(a - b) < RAIL_ALIGNMENT_EPSILON;
|
|
4699
|
+
var isHorizontal2 = (a, b) => nearlyEqual(a.y, b.y);
|
|
4700
|
+
var isVertical2 = (a, b) => nearlyEqual(a.x, b.x);
|
|
4701
|
+
var isPositiveLength = (a, b) => Math.abs(a.x - b.x) >= RAIL_ALIGNMENT_EPSILON || Math.abs(a.y - b.y) >= RAIL_ALIGNMENT_EPSILON;
|
|
4702
|
+
var getRailOrientation = (a, b) => {
|
|
4703
|
+
if (!isPositiveLength(a, b)) return null;
|
|
4704
|
+
if (isVertical2(a, b)) return "vertical";
|
|
4705
|
+
if (isHorizontal2(a, b)) return "horizontal";
|
|
4706
|
+
return null;
|
|
4707
|
+
};
|
|
4708
|
+
var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
|
|
4709
|
+
var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
|
|
4710
|
+
var getDistinctCoordinates = (coordinates) => {
|
|
4711
|
+
const distinct = [];
|
|
4712
|
+
for (const coordinate of coordinates) {
|
|
4713
|
+
if (!distinct.some((item) => nearlyEqual(item, coordinate))) {
|
|
4714
|
+
distinct.push(coordinate);
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
return distinct;
|
|
4718
|
+
};
|
|
4719
|
+
var getMergedIntervalLength = (intervals) => {
|
|
4720
|
+
const groups = [];
|
|
4721
|
+
for (const interval of intervals) {
|
|
4722
|
+
const group = groups.find(
|
|
4723
|
+
(items) => nearlyEqual(items[0].coordinate, interval.coordinate)
|
|
4724
|
+
);
|
|
4725
|
+
if (group) group.push(interval);
|
|
4726
|
+
else groups.push([interval]);
|
|
4727
|
+
}
|
|
4728
|
+
return groups.reduce((total, group) => {
|
|
4729
|
+
const sorted = [...group].sort((a, b) => a.min - b.min);
|
|
4730
|
+
let groupLength = 0;
|
|
4731
|
+
let currentMin = sorted[0].min;
|
|
4732
|
+
let currentMax = sorted[0].max;
|
|
4733
|
+
for (const interval of sorted.slice(1)) {
|
|
4734
|
+
if (interval.min <= currentMax + RAIL_ALIGNMENT_EPSILON) {
|
|
4735
|
+
currentMax = Math.max(currentMax, interval.max);
|
|
4736
|
+
} else {
|
|
4737
|
+
groupLength += currentMax - currentMin;
|
|
4738
|
+
currentMin = interval.min;
|
|
4739
|
+
currentMax = interval.max;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
return total + groupLength + currentMax - currentMin;
|
|
4743
|
+
}, 0);
|
|
4744
|
+
};
|
|
4745
|
+
var getVisibleTraceLength = (traces) => {
|
|
4746
|
+
const horizontal = [];
|
|
4747
|
+
const vertical = [];
|
|
4748
|
+
for (const trace of traces) {
|
|
4749
|
+
for (let index = 0; index < trace.tracePath.length - 1; index++) {
|
|
4750
|
+
const start = trace.tracePath[index];
|
|
4751
|
+
const end = trace.tracePath[index + 1];
|
|
4752
|
+
if (isHorizontal2(start, end)) {
|
|
4753
|
+
horizontal.push({
|
|
4754
|
+
coordinate: start.y,
|
|
4755
|
+
min: Math.min(start.x, end.x),
|
|
4756
|
+
max: Math.max(start.x, end.x)
|
|
4757
|
+
});
|
|
4758
|
+
} else if (isVertical2(start, end)) {
|
|
4759
|
+
vertical.push({
|
|
4760
|
+
coordinate: start.x,
|
|
4761
|
+
min: Math.min(start.y, end.y),
|
|
4762
|
+
max: Math.max(start.y, end.y)
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
return getMergedIntervalLength(horizontal) + getMergedIntervalLength(vertical);
|
|
4768
|
+
};
|
|
4769
|
+
|
|
4770
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
|
|
4771
|
+
var moveRailSegments = (trace, segments, coordinate) => {
|
|
4772
|
+
const pointsToMove = /* @__PURE__ */ new Set();
|
|
4773
|
+
for (const segment of segments) {
|
|
4774
|
+
pointsToMove.add(segment.segmentIndex);
|
|
4775
|
+
pointsToMove.add(segment.segmentIndex + 1);
|
|
4776
|
+
}
|
|
4777
|
+
const orientation = segments[0].orientation;
|
|
4778
|
+
const tracePath = simplifyPath(
|
|
4779
|
+
trace.tracePath.map((point, index) => {
|
|
4780
|
+
if (!pointsToMove.has(index)) return point;
|
|
4781
|
+
return orientation === "vertical" ? { ...point, x: coordinate } : { ...point, y: coordinate };
|
|
4782
|
+
})
|
|
4783
|
+
);
|
|
4784
|
+
return { ...trace, tracePath };
|
|
4785
|
+
};
|
|
4786
|
+
|
|
4787
|
+
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
4788
|
+
var EPS5 = 1e-6;
|
|
4789
|
+
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
4790
|
+
var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS5 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS5;
|
|
4791
|
+
var getTraceCorners = (path) => {
|
|
4792
|
+
const corners = [];
|
|
4793
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
4794
|
+
const previousPoint = path[i - 1];
|
|
4795
|
+
const cornerPoint = path[i];
|
|
4796
|
+
const nextPoint = path[i + 1];
|
|
4797
|
+
if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
|
|
4798
|
+
corners.push(cornerPoint);
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
return corners;
|
|
4802
|
+
};
|
|
4803
|
+
var tracePathContainsPoint = (path, point) => {
|
|
4804
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4805
|
+
if (isPointOnSegment(point, path[i], path[i + 1])) return true;
|
|
4806
|
+
}
|
|
4807
|
+
return false;
|
|
4808
|
+
};
|
|
4809
|
+
var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
4810
|
+
var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
4811
|
+
var isTraceCorner = (a, b, c) => getSegmentOrientation(a, b) !== getSegmentOrientation(b, c);
|
|
4812
|
+
var isPointOnSegment = (point, start, end) => {
|
|
4813
|
+
if (getSegmentOrientation(start, end) === "horizontal") {
|
|
4814
|
+
return Math.abs(point.y - start.y) <= EPS5 && point.x >= Math.min(start.x, end.x) - EPS5 && point.x <= Math.max(start.x, end.x) + EPS5;
|
|
4815
|
+
}
|
|
4816
|
+
return Math.abs(point.x - start.x) <= EPS5 && point.y >= Math.min(start.y, end.y) - EPS5 && point.y <= Math.max(start.y, end.y) + EPS5;
|
|
4817
|
+
};
|
|
4818
|
+
var getSegmentOrientation = (a, b) => Math.abs(a.y - b.y) <= EPS5 ? "horizontal" : "vertical";
|
|
4819
|
+
|
|
4820
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors.ts
|
|
4821
|
+
var getAnchoredTraceIds = (label, traces) => new Set(
|
|
4822
|
+
traces.filter(
|
|
4823
|
+
(trace) => trace.globalConnNetId === label.globalConnNetId && tracePathContainsPoint(trace.tracePath, label.anchorPoint)
|
|
4824
|
+
).map((trace) => trace.mspPairId)
|
|
4825
|
+
);
|
|
4826
|
+
var preservesLabelAnchors = (labels, before, after) => labels.every((label) => {
|
|
4827
|
+
const anchoredBefore = getAnchoredTraceIds(label, before);
|
|
4828
|
+
if (anchoredBefore.size === 0) return true;
|
|
4829
|
+
const anchoredAfter = getAnchoredTraceIds(label, after);
|
|
4830
|
+
return [...anchoredBefore].every((traceId) => anchoredAfter.has(traceId));
|
|
4831
|
+
});
|
|
4832
|
+
|
|
4833
|
+
// lib/solvers/Example28Solver/types.ts
|
|
4834
|
+
var EPS6 = 1e-9;
|
|
4835
|
+
|
|
4836
|
+
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
4837
|
+
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
4838
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS6;
|
|
4839
|
+
const isHorizontal4 = Math.abs(start.y - end.y) < EPS6;
|
|
4840
|
+
if (isVertical5) {
|
|
4841
|
+
return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
|
|
4842
|
+
}
|
|
4843
|
+
if (isHorizontal4) {
|
|
4844
|
+
return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
|
|
4845
|
+
}
|
|
4846
|
+
return false;
|
|
4847
|
+
};
|
|
4848
|
+
|
|
4849
|
+
// lib/solvers/Example28Solver/geometry.ts
|
|
4850
|
+
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
4851
|
+
var getPathLength = (path) => {
|
|
4852
|
+
let length = 0;
|
|
4853
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4854
|
+
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
4855
|
+
}
|
|
4856
|
+
return length;
|
|
4857
|
+
};
|
|
4858
|
+
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
4859
|
+
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
|
|
4860
|
+
var getSegmentOrientation2 = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
|
|
4861
|
+
var projectPointToSegment = (point, start, end) => {
|
|
4862
|
+
if (Math.abs(start.x - end.x) < EPS6) {
|
|
4863
|
+
return {
|
|
4864
|
+
x: start.x,
|
|
4865
|
+
y: Math.min(
|
|
4866
|
+
Math.max(point.y, Math.min(start.y, end.y)),
|
|
4867
|
+
Math.max(start.y, end.y)
|
|
4868
|
+
)
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
if (Math.abs(start.y - end.y) < EPS6) {
|
|
4872
|
+
return {
|
|
4873
|
+
x: Math.min(
|
|
4874
|
+
Math.max(point.x, Math.min(start.x, end.x)),
|
|
4875
|
+
Math.max(start.x, end.x)
|
|
4876
|
+
),
|
|
4877
|
+
y: start.y
|
|
4878
|
+
};
|
|
4879
|
+
}
|
|
4880
|
+
return start;
|
|
4881
|
+
};
|
|
4882
|
+
var getPointToSegmentDistance = (point, start, end) => getDistance2(point, projectPointToSegment(point, start, end));
|
|
4883
|
+
var getSegments = (path) => {
|
|
4884
|
+
const segments = [];
|
|
4885
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4886
|
+
const start = path[i];
|
|
4887
|
+
const end = path[i + 1];
|
|
4888
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4889
|
+
segments.push({
|
|
4890
|
+
start,
|
|
4891
|
+
end,
|
|
4892
|
+
orientation: getSegmentOrientation2(start, end)
|
|
4893
|
+
});
|
|
4894
|
+
}
|
|
4895
|
+
return segments;
|
|
4896
|
+
};
|
|
4897
|
+
var findSegmentContainingPoint = (path, point) => {
|
|
4898
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4899
|
+
const start = path[i];
|
|
4900
|
+
const end = path[i + 1];
|
|
4901
|
+
if (!isAxisAlignedSegment(start, end)) continue;
|
|
4902
|
+
if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
|
|
4903
|
+
return {
|
|
4904
|
+
index: i,
|
|
4905
|
+
start,
|
|
4906
|
+
end,
|
|
4907
|
+
orientation: getSegmentOrientation2(start, end)
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4910
|
+
return null;
|
|
4911
|
+
};
|
|
4912
|
+
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
4913
|
+
if (segment.orientation === "horizontal") {
|
|
4914
|
+
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
|
|
4915
|
+
}
|
|
4916
|
+
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
|
|
4917
|
+
};
|
|
4918
|
+
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
4919
|
+
const matchingSegments = getSegments(path).filter(
|
|
4920
|
+
(segment) => segment.orientation === orientation
|
|
4921
|
+
);
|
|
4922
|
+
if (matchingSegments.length === 0) return null;
|
|
4923
|
+
const containingSegments = matchingSegments.filter(
|
|
4924
|
+
(segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
|
|
4925
|
+
);
|
|
4926
|
+
const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
|
|
4927
|
+
const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
|
|
4928
|
+
return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
|
|
4929
|
+
};
|
|
4930
|
+
var projectPointToPath = (point, path) => {
|
|
4931
|
+
let bestPoint = null;
|
|
4932
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
4933
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4934
|
+
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
4935
|
+
const distance3 = getDistance2(point, projectedPoint);
|
|
4936
|
+
if (distance3 < bestDistance) {
|
|
4937
|
+
bestPoint = projectedPoint;
|
|
4938
|
+
bestDistance = distance3;
|
|
4939
|
+
}
|
|
4940
|
+
}
|
|
4941
|
+
return bestPoint;
|
|
4942
|
+
};
|
|
4943
|
+
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
4944
|
+
const aVertical = Math.abs(a1.x - a2.x) < EPS6;
|
|
4945
|
+
const bVertical = Math.abs(b1.x - b2.x) < EPS6;
|
|
4946
|
+
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
|
|
4947
|
+
if (aVertical && bVertical) {
|
|
4948
|
+
if (Math.abs(a1.x - b1.x) > EPS6) return false;
|
|
4949
|
+
const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
|
|
4950
|
+
return overlap > EPS6;
|
|
4951
|
+
}
|
|
4952
|
+
if (!aVertical && !bVertical) {
|
|
4953
|
+
if (Math.abs(a1.y - b1.y) > EPS6) return false;
|
|
4954
|
+
const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
|
|
4955
|
+
return overlap > EPS6;
|
|
4956
|
+
}
|
|
4957
|
+
const verticalA = aVertical ? a1 : b1;
|
|
4958
|
+
const verticalB = aVertical ? a2 : b2;
|
|
4959
|
+
const horizontalA = aVertical ? b1 : a1;
|
|
4960
|
+
const horizontalB = aVertical ? b2 : a2;
|
|
4961
|
+
return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
|
|
4962
|
+
};
|
|
4963
|
+
var countPathIntersections = (pathA, pathB) => {
|
|
4964
|
+
let count = 0;
|
|
4965
|
+
for (let i = 0; i < pathA.length - 1; i++) {
|
|
4966
|
+
for (let j = 0; j < pathB.length - 1; j++) {
|
|
4967
|
+
if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
|
|
4968
|
+
count++;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
return count;
|
|
4973
|
+
};
|
|
4974
|
+
var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
4975
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
4976
|
+
const start = path[i];
|
|
4977
|
+
const end = path[i + 1];
|
|
4978
|
+
for (const obstacle of chipObstacles) {
|
|
4979
|
+
if (!segmentIntersectsRect(start, end, obstacle)) continue;
|
|
4980
|
+
if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
4981
|
+
return true;
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
return false;
|
|
4985
|
+
};
|
|
4986
|
+
|
|
4987
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/scoreRailAlignment.ts
|
|
4988
|
+
var countOtherNetCrossings = (traces, allTraces) => {
|
|
4989
|
+
let crossings = 0;
|
|
4990
|
+
for (const trace of traces) {
|
|
4991
|
+
for (const otherTrace of allTraces) {
|
|
4992
|
+
if (trace.globalConnNetId === otherTrace.globalConnNetId) continue;
|
|
4993
|
+
crossings += countPathIntersections(trace.tracePath, otherTrace.tracePath);
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
return crossings;
|
|
4997
|
+
};
|
|
4998
|
+
var getTraceGeometryMetrics = (traces, allTraces) => ({
|
|
4999
|
+
turnCount: traces.reduce(
|
|
5000
|
+
(total, trace) => total + countTurns(trace.tracePath),
|
|
5001
|
+
0
|
|
5002
|
+
),
|
|
5003
|
+
visibleLength: getVisibleTraceLength(traces),
|
|
5004
|
+
pathLength: traces.reduce(
|
|
5005
|
+
(sum, trace) => sum + getPathLength(trace.tracePath),
|
|
5006
|
+
0
|
|
5007
|
+
),
|
|
5008
|
+
otherNetCrossings: countOtherNetCrossings(traces, allTraces)
|
|
5009
|
+
});
|
|
5010
|
+
var scoreIsBetter = (candidate, best) => {
|
|
5011
|
+
if (candidate.turnCount !== best.turnCount)
|
|
5012
|
+
return candidate.turnCount < best.turnCount;
|
|
5013
|
+
if (!nearlyEqual(candidate.visibleLength, best.visibleLength))
|
|
5014
|
+
return candidate.visibleLength < best.visibleLength;
|
|
5015
|
+
if (!nearlyEqual(candidate.pathLength, best.pathLength))
|
|
5016
|
+
return candidate.pathLength < best.pathLength;
|
|
5017
|
+
if (candidate.otherNetCrossings !== best.otherNetCrossings)
|
|
5018
|
+
return candidate.otherNetCrossings < best.otherNetCrossings;
|
|
5019
|
+
if (!nearlyEqual(candidate.displacement, best.displacement))
|
|
5020
|
+
return candidate.displacement < best.displacement;
|
|
5021
|
+
return candidate.coordinate < best.coordinate;
|
|
5022
|
+
};
|
|
5023
|
+
var isReadabilityImprovement = (candidate, baseline) => candidate.turnCount <= baseline.turnCount && (candidate.turnCount < baseline.turnCount || candidate.visibleLength <= baseline.visibleLength + RAIL_ALIGNMENT_EPSILON);
|
|
5024
|
+
|
|
5025
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts
|
|
5026
|
+
var tracePathChanged = (original, candidate) => original.tracePath.length !== candidate.tracePath.length || candidate.tracePath.some(
|
|
5027
|
+
(point, index) => !pointsEqual(point, original.tracePath[index])
|
|
5028
|
+
);
|
|
5029
|
+
var evaluateRailGroup = ({
|
|
5030
|
+
group,
|
|
5031
|
+
traces,
|
|
5032
|
+
netLabelPlacements,
|
|
5033
|
+
obstacles,
|
|
5034
|
+
eligibleTraceIds
|
|
5035
|
+
}) => {
|
|
5036
|
+
const groupTraceIds = new Set(group.map((segment) => segment.traceId));
|
|
5037
|
+
const originalGroupTraces = traces.filter(
|
|
5038
|
+
(trace) => groupTraceIds.has(trace.mspPairId)
|
|
5039
|
+
);
|
|
5040
|
+
const baseline = getTraceGeometryMetrics(originalGroupTraces, traces);
|
|
5041
|
+
const coordinates = getDistinctCoordinates(
|
|
5042
|
+
group.map((segment) => segment.coordinate)
|
|
5043
|
+
);
|
|
5044
|
+
const otherNetTraces = traces.filter(
|
|
5045
|
+
(trace) => trace.globalConnNetId !== group[0].globalConnNetId
|
|
5046
|
+
);
|
|
5047
|
+
const immutableSameNetTraces = traces.filter(
|
|
5048
|
+
(trace) => trace.globalConnNetId === group[0].globalConnNetId && !eligibleTraceIds.has(trace.mspPairId)
|
|
5049
|
+
);
|
|
5050
|
+
let best = null;
|
|
5051
|
+
for (const coordinate of coordinates) {
|
|
5052
|
+
const candidateMap = /* @__PURE__ */ new Map();
|
|
5053
|
+
for (const trace of originalGroupTraces) {
|
|
5054
|
+
const candidateTrace = moveRailSegments(
|
|
5055
|
+
trace,
|
|
5056
|
+
group.filter((segment) => segment.traceId === trace.mspPairId),
|
|
5057
|
+
coordinate
|
|
5058
|
+
);
|
|
5059
|
+
candidateMap.set(trace.mspPairId, candidateTrace);
|
|
5060
|
+
}
|
|
5061
|
+
const candidateTraces = [...candidateMap.values()];
|
|
5062
|
+
const allCandidateTraces = traces.map(
|
|
5063
|
+
(trace) => candidateMap.get(trace.mspPairId) ?? trace
|
|
5064
|
+
);
|
|
5065
|
+
const candidatesAreClear = candidateTraces.every(
|
|
5066
|
+
(candidate2) => !isPathCollidingWithObstacles(candidate2.tracePath, obstacles) && detectTraceLabelOverlap({
|
|
5067
|
+
traces: [candidate2],
|
|
5068
|
+
netLabels: netLabelPlacements
|
|
5069
|
+
}).length === 0 && !doesPathCoincideWithTraces(candidate2.tracePath, otherNetTraces) && !doesPathCoincideWithTraces(
|
|
5070
|
+
candidate2.tracePath,
|
|
5071
|
+
immutableSameNetTraces.filter(
|
|
5072
|
+
(trace) => trace.mspPairId !== candidate2.mspPairId
|
|
5073
|
+
)
|
|
5074
|
+
)
|
|
5075
|
+
);
|
|
5076
|
+
if (!candidatesAreClear) continue;
|
|
5077
|
+
if (!preservesLabelAnchors(netLabelPlacements, traces, allCandidateTraces)) {
|
|
5078
|
+
continue;
|
|
5079
|
+
}
|
|
5080
|
+
const metrics = getTraceGeometryMetrics(candidateTraces, allCandidateTraces);
|
|
5081
|
+
if (metrics.otherNetCrossings > baseline.otherNetCrossings) continue;
|
|
5082
|
+
if (!isReadabilityImprovement(metrics, baseline)) continue;
|
|
5083
|
+
const score = {
|
|
5084
|
+
...metrics,
|
|
5085
|
+
displacement: group.reduce(
|
|
5086
|
+
(sum, segment) => sum + Math.abs(segment.coordinate - coordinate),
|
|
5087
|
+
0
|
|
5088
|
+
),
|
|
5089
|
+
coordinate
|
|
5090
|
+
};
|
|
5091
|
+
const changedTraceIds = candidateTraces.filter((candidate2) => {
|
|
5092
|
+
const original = traces.find(
|
|
5093
|
+
(trace) => trace.mspPairId === candidate2.mspPairId
|
|
5094
|
+
);
|
|
5095
|
+
return tracePathChanged(original, candidate2);
|
|
5096
|
+
}).map((trace) => trace.mspPairId);
|
|
5097
|
+
if (changedTraceIds.length === 0) continue;
|
|
5098
|
+
const candidate = { traces: allCandidateTraces, changedTraceIds, score };
|
|
5099
|
+
if (!best || scoreIsBetter(candidate.score, best.score)) best = candidate;
|
|
5100
|
+
}
|
|
5101
|
+
return best;
|
|
5102
|
+
};
|
|
5103
|
+
|
|
5104
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts
|
|
5105
|
+
var getMovableRailSegments = (trace) => {
|
|
5106
|
+
const segments = [];
|
|
5107
|
+
const path = trace.tracePath;
|
|
5108
|
+
for (let segmentIndex = 1; segmentIndex < path.length - 2; segmentIndex++) {
|
|
5109
|
+
const previous = path[segmentIndex - 1];
|
|
5110
|
+
const start = path[segmentIndex];
|
|
5111
|
+
const end = path[segmentIndex + 1];
|
|
5112
|
+
const next = path[segmentIndex + 2];
|
|
5113
|
+
const orientation = getRailOrientation(start, end);
|
|
5114
|
+
if (!orientation) continue;
|
|
5115
|
+
const previousOrientation = getRailOrientation(previous, start);
|
|
5116
|
+
const nextOrientation = getRailOrientation(end, next);
|
|
5117
|
+
if (!previousOrientation || !nextOrientation) continue;
|
|
5118
|
+
if (previousOrientation === orientation || nextOrientation === orientation)
|
|
5119
|
+
continue;
|
|
5120
|
+
const alongValues = orientation === "vertical" ? [start.y, end.y] : [start.x, end.x];
|
|
5121
|
+
segments.push({
|
|
5122
|
+
traceId: trace.mspPairId,
|
|
5123
|
+
segmentIndex,
|
|
5124
|
+
globalConnNetId: trace.globalConnNetId,
|
|
5125
|
+
orientation,
|
|
5126
|
+
coordinate: orientation === "vertical" ? start.x : start.y,
|
|
5127
|
+
minAlong: Math.min(...alongValues),
|
|
5128
|
+
maxAlong: Math.max(...alongValues)
|
|
5129
|
+
});
|
|
5130
|
+
}
|
|
5131
|
+
return segments;
|
|
5132
|
+
};
|
|
5133
|
+
var railIsOutsideComponent = (segment, chip, facingDirection) => {
|
|
5134
|
+
const minX = chip.center.x - chip.width / 2;
|
|
5135
|
+
const maxX = chip.center.x + chip.width / 2;
|
|
5136
|
+
const minY = chip.center.y - chip.height / 2;
|
|
5137
|
+
const maxY = chip.center.y + chip.height / 2;
|
|
5138
|
+
switch (facingDirection) {
|
|
5139
|
+
case "x-":
|
|
5140
|
+
return segment.orientation === "vertical" && segment.coordinate <= minX + RAIL_ALIGNMENT_EPSILON;
|
|
5141
|
+
case "x+":
|
|
5142
|
+
return segment.orientation === "vertical" && segment.coordinate >= maxX - RAIL_ALIGNMENT_EPSILON;
|
|
5143
|
+
case "y+":
|
|
5144
|
+
return segment.orientation === "horizontal" && segment.coordinate >= maxY - RAIL_ALIGNMENT_EPSILON;
|
|
5145
|
+
case "y-":
|
|
5146
|
+
return segment.orientation === "horizontal" && segment.coordinate <= minY + RAIL_ALIGNMENT_EPSILON;
|
|
5147
|
+
}
|
|
5148
|
+
};
|
|
5149
|
+
var getComponentSideRailSegments = (trace, chipMap) => {
|
|
5150
|
+
const segments = [];
|
|
5151
|
+
for (const segment of getMovableRailSegments(trace)) {
|
|
5152
|
+
const associations = trace.pins.flatMap((pin, pinIndex) => {
|
|
5153
|
+
const chip = chipMap.get(pin.chipId);
|
|
5154
|
+
if (!chip) return [];
|
|
5155
|
+
const componentFacingDirection = pin._facingDirection ?? getPinDirection(pin, chip);
|
|
5156
|
+
if (!railIsOutsideComponent(segment, chip, componentFacingDirection)) {
|
|
5157
|
+
return [];
|
|
5158
|
+
}
|
|
5159
|
+
return [
|
|
5160
|
+
{
|
|
5161
|
+
componentId: chip.chipId,
|
|
5162
|
+
componentFacingDirection,
|
|
5163
|
+
distanceFromEndpoint: pinIndex === 0 ? segment.segmentIndex : trace.tracePath.length - 2 - segment.segmentIndex
|
|
5164
|
+
}
|
|
5165
|
+
];
|
|
5166
|
+
});
|
|
5167
|
+
associations.sort((a, b) => a.distanceFromEndpoint - b.distanceFromEndpoint);
|
|
5168
|
+
const association = associations[0];
|
|
5169
|
+
if (!association) continue;
|
|
5170
|
+
segments.push({ ...segment, ...association });
|
|
5171
|
+
}
|
|
5172
|
+
return segments;
|
|
5173
|
+
};
|
|
5174
|
+
|
|
5175
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts
|
|
5176
|
+
var getCorridor = (a, b) => {
|
|
5177
|
+
const overlapMin = Math.max(a.minAlong, b.minAlong);
|
|
5178
|
+
const overlapMax = Math.min(a.maxAlong, b.maxAlong);
|
|
5179
|
+
const along = (overlapMin + overlapMax) / 2;
|
|
5180
|
+
return a.orientation === "vertical" ? [
|
|
5181
|
+
{ x: a.coordinate, y: along },
|
|
5182
|
+
{ x: b.coordinate, y: along }
|
|
5183
|
+
] : [
|
|
5184
|
+
{ x: along, y: a.coordinate },
|
|
5185
|
+
{ x: along, y: b.coordinate }
|
|
5186
|
+
];
|
|
5187
|
+
};
|
|
5188
|
+
var corridorIsClear = (a, b, obstacles) => {
|
|
5189
|
+
if (!rangesTouchOrOverlap(a, b)) return true;
|
|
5190
|
+
const [start, end] = getCorridor(a, b);
|
|
5191
|
+
return !obstacles.some(
|
|
5192
|
+
(obstacle) => segmentIntersectsRect(start, end, obstacle)
|
|
5193
|
+
);
|
|
5194
|
+
};
|
|
5195
|
+
var tracesSharePin = (a, b, traceMap) => {
|
|
5196
|
+
if (a.traceId === b.traceId) return true;
|
|
5197
|
+
const aPinIds = new Set(traceMap.get(a.traceId).pins.map((pin) => pin.pinId));
|
|
5198
|
+
return traceMap.get(b.traceId).pins.some((pin) => aPinIds.has(pin.pinId));
|
|
5199
|
+
};
|
|
5200
|
+
var canJoinRailGroup = (start, current, candidate, traceMap, obstacles) => candidate.globalConnNetId === start.globalConnNetId && candidate.orientation === start.orientation && candidate.componentId === start.componentId && candidate.componentFacingDirection === start.componentFacingDirection && (rangesTouchOrOverlap(current, candidate) || tracesSharePin(current, candidate, traceMap)) && corridorIsClear(current, candidate, obstacles);
|
|
5201
|
+
var getRailGroups = (traces, eligibleTraceIds, inputProblem, obstacles) => {
|
|
5202
|
+
const chipMap = new Map(inputProblem.chips.map((chip) => [chip.chipId, chip]));
|
|
5203
|
+
const segments = traces.filter((trace) => eligibleTraceIds.has(trace.mspPairId)).flatMap((trace) => getComponentSideRailSegments(trace, chipMap));
|
|
5204
|
+
const traceMap = new Map(traces.map((trace) => [trace.mspPairId, trace]));
|
|
5205
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5206
|
+
const groups = [];
|
|
5207
|
+
for (let startIndex = 0; startIndex < segments.length; startIndex++) {
|
|
5208
|
+
if (visited.has(startIndex)) continue;
|
|
5209
|
+
const start = segments[startIndex];
|
|
5210
|
+
const queue = [startIndex];
|
|
5211
|
+
const group = [];
|
|
5212
|
+
visited.add(startIndex);
|
|
5213
|
+
for (let queueIndex = 0; queueIndex < queue.length; queueIndex++) {
|
|
5214
|
+
const current = segments[queue[queueIndex]];
|
|
5215
|
+
group.push(current);
|
|
5216
|
+
for (let candidateIndex = 0; candidateIndex < segments.length; candidateIndex++) {
|
|
5217
|
+
if (visited.has(candidateIndex)) continue;
|
|
5218
|
+
const candidate = segments[candidateIndex];
|
|
5219
|
+
if (!canJoinRailGroup(start, current, candidate, traceMap, obstacles)) {
|
|
5220
|
+
continue;
|
|
5221
|
+
}
|
|
5222
|
+
visited.add(candidateIndex);
|
|
5223
|
+
queue.push(candidateIndex);
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
const traceCount = new Set(group.map((segment) => segment.traceId)).size;
|
|
5227
|
+
const hasDifferentCoordinates = group.some(
|
|
5228
|
+
(segment) => !nearlyEqual(segment.coordinate, group[0].coordinate)
|
|
5229
|
+
);
|
|
5230
|
+
if (traceCount >= 2 && hasDifferentCoordinates) groups.push(group);
|
|
5231
|
+
}
|
|
5232
|
+
return groups;
|
|
5233
|
+
};
|
|
5234
|
+
|
|
5235
|
+
// lib/solvers/TraceCleanupSolver/alignSameNetRails.ts
|
|
5236
|
+
var alignSameNetRails = ({
|
|
5237
|
+
inputProblem,
|
|
5238
|
+
traces,
|
|
5239
|
+
netLabelPlacements,
|
|
5240
|
+
eligibleTraceIds
|
|
5241
|
+
}) => {
|
|
5242
|
+
let outputTraces = [...traces];
|
|
5243
|
+
const obstacles = getObstacleRects(inputProblem);
|
|
5244
|
+
const alignedTraceIds = /* @__PURE__ */ new Set();
|
|
5245
|
+
let alignedRailGroupCount = 0;
|
|
5246
|
+
const maximumPasses = Math.max(
|
|
5247
|
+
1,
|
|
5248
|
+
traces.reduce((sum, trace) => sum + trace.tracePath.length, 0)
|
|
5249
|
+
);
|
|
5250
|
+
for (let pass = 0; pass < maximumPasses; pass++) {
|
|
5251
|
+
const groups = getRailGroups(
|
|
5252
|
+
outputTraces,
|
|
5253
|
+
eligibleTraceIds,
|
|
5254
|
+
inputProblem,
|
|
5255
|
+
obstacles
|
|
5256
|
+
);
|
|
5257
|
+
let applied = null;
|
|
5258
|
+
for (const group of groups) {
|
|
5259
|
+
applied = evaluateRailGroup({
|
|
5260
|
+
group,
|
|
5261
|
+
traces: outputTraces,
|
|
5262
|
+
netLabelPlacements,
|
|
5263
|
+
obstacles,
|
|
5264
|
+
eligibleTraceIds
|
|
5265
|
+
});
|
|
5266
|
+
if (applied) break;
|
|
4646
5267
|
}
|
|
5268
|
+
if (!applied) break;
|
|
5269
|
+
outputTraces = applied.traces;
|
|
5270
|
+
alignedRailGroupCount++;
|
|
5271
|
+
for (const traceId of applied.changedTraceIds) alignedTraceIds.add(traceId);
|
|
4647
5272
|
}
|
|
4648
|
-
const finalSimplifiedPath = simplifyPath(newPath);
|
|
4649
5273
|
return {
|
|
4650
|
-
|
|
4651
|
-
|
|
5274
|
+
traces: outputTraces,
|
|
5275
|
+
alignedRailGroupCount,
|
|
5276
|
+
alignedTraceCount: alignedTraceIds.size
|
|
4652
5277
|
};
|
|
4653
5278
|
};
|
|
4654
5279
|
|
|
@@ -4709,8 +5334,8 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
|
|
|
4709
5334
|
};
|
|
4710
5335
|
|
|
4711
5336
|
// lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
|
|
4712
|
-
var
|
|
4713
|
-
var
|
|
5337
|
+
var EPS7 = 1e-6;
|
|
5338
|
+
var isVertical3 = (a, b, eps = EPS7) => Math.abs(a.x - b.x) < eps;
|
|
4714
5339
|
var generateLShapeRerouteCandidates = ({
|
|
4715
5340
|
lShape,
|
|
4716
5341
|
rectangle,
|
|
@@ -4723,50 +5348,50 @@ var generateLShapeRerouteCandidates = ({
|
|
|
4723
5348
|
let c2;
|
|
4724
5349
|
let i1_padded = interactionPoint1;
|
|
4725
5350
|
let i2_padded = interactionPoint2;
|
|
4726
|
-
if (Math.abs(p2.x - x) <
|
|
5351
|
+
if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
|
|
4727
5352
|
c2 = { x: x + width + padding, y: y - padding };
|
|
4728
|
-
if (
|
|
5353
|
+
if (isVertical3(p1, p2)) {
|
|
4729
5354
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
4730
5355
|
} else {
|
|
4731
5356
|
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
4732
5357
|
}
|
|
4733
|
-
if (
|
|
5358
|
+
if (isVertical3(p2, p3)) {
|
|
4734
5359
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
4735
5360
|
} else {
|
|
4736
5361
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
4737
5362
|
}
|
|
4738
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5363
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - (y + height)) < EPS7) {
|
|
4739
5364
|
c2 = { x: x - padding, y: y - padding };
|
|
4740
|
-
if (
|
|
5365
|
+
if (isVertical3(p1, p2)) {
|
|
4741
5366
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
|
|
4742
5367
|
} else {
|
|
4743
5368
|
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
4744
5369
|
}
|
|
4745
|
-
if (
|
|
5370
|
+
if (isVertical3(p2, p3)) {
|
|
4746
5371
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y - padding };
|
|
4747
5372
|
} else {
|
|
4748
5373
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
4749
5374
|
}
|
|
4750
|
-
} else if (Math.abs(p2.x - x) <
|
|
5375
|
+
} else if (Math.abs(p2.x - x) < EPS7 && Math.abs(p2.y - y) < EPS7) {
|
|
4751
5376
|
c2 = { x: x + width + padding, y: y + height + padding };
|
|
4752
|
-
if (
|
|
5377
|
+
if (isVertical3(p1, p2)) {
|
|
4753
5378
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
4754
5379
|
} else {
|
|
4755
5380
|
i1_padded = { x: interactionPoint1.x + padding, y: interactionPoint1.y };
|
|
4756
5381
|
}
|
|
4757
|
-
if (
|
|
5382
|
+
if (isVertical3(p2, p3)) {
|
|
4758
5383
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
4759
5384
|
} else {
|
|
4760
5385
|
i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
|
|
4761
5386
|
}
|
|
4762
|
-
} else if (Math.abs(p2.x - (x + width)) <
|
|
5387
|
+
} else if (Math.abs(p2.x - (x + width)) < EPS7 && Math.abs(p2.y - y) < EPS7) {
|
|
4763
5388
|
c2 = { x: x - padding, y: y + height + padding };
|
|
4764
|
-
if (
|
|
5389
|
+
if (isVertical3(p1, p2)) {
|
|
4765
5390
|
i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
|
|
4766
5391
|
} else {
|
|
4767
5392
|
i1_padded = { x: interactionPoint1.x - padding, y: interactionPoint1.y };
|
|
4768
5393
|
}
|
|
4769
|
-
if (
|
|
5394
|
+
if (isVertical3(p2, p3)) {
|
|
4770
5395
|
i2_padded = { x: interactionPoint2.x, y: interactionPoint2.y + padding };
|
|
4771
5396
|
} else {
|
|
4772
5397
|
i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
|
|
@@ -5230,9 +5855,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
5230
5855
|
};
|
|
5231
5856
|
|
|
5232
5857
|
// lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
|
|
5233
|
-
var
|
|
5234
|
-
var sameX = (a, b) => Math.abs(a.x - b.x) <=
|
|
5235
|
-
var sameY = (a, b) => Math.abs(a.y - b.y) <=
|
|
5858
|
+
var EPS8 = 1e-6;
|
|
5859
|
+
var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS8;
|
|
5860
|
+
var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS8;
|
|
5236
5861
|
var is4PointRectangle = (path) => {
|
|
5237
5862
|
if (path.length !== 4) return false;
|
|
5238
5863
|
const [p0, p1, p2, p3] = path;
|
|
@@ -5242,12 +5867,19 @@ var is4PointRectangle = (path) => {
|
|
|
5242
5867
|
};
|
|
5243
5868
|
|
|
5244
5869
|
// lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
|
|
5870
|
+
var DEFAULT_OPERATIONS = [
|
|
5871
|
+
"untangling_traces",
|
|
5872
|
+
"minimizing_turns",
|
|
5873
|
+
"balancing_l_shapes"
|
|
5874
|
+
];
|
|
5245
5875
|
var TraceCleanupSolver = class extends BaseSolver {
|
|
5246
5876
|
input;
|
|
5247
5877
|
outputTraces;
|
|
5248
5878
|
traceIdQueue;
|
|
5249
5879
|
tracesMap;
|
|
5250
|
-
|
|
5880
|
+
operations;
|
|
5881
|
+
operationIndex = 0;
|
|
5882
|
+
pipelineStep;
|
|
5251
5883
|
activeTraceId = null;
|
|
5252
5884
|
// New property
|
|
5253
5885
|
activeSubSolver = null;
|
|
@@ -5256,6 +5888,8 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5256
5888
|
this.input = solverInput;
|
|
5257
5889
|
this.outputTraces = [...solverInput.allTraces];
|
|
5258
5890
|
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5891
|
+
this.operations = solverInput.operations ?? DEFAULT_OPERATIONS;
|
|
5892
|
+
this.pipelineStep = this.operations[0] ?? null;
|
|
5259
5893
|
this.traceIdQueue = Array.from(
|
|
5260
5894
|
solverInput.allTraces.map((e) => e.mspPairId)
|
|
5261
5895
|
);
|
|
@@ -5268,13 +5902,17 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5268
5902
|
this.outputTraces = output.traces;
|
|
5269
5903
|
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5270
5904
|
this.activeSubSolver = null;
|
|
5271
|
-
this.
|
|
5905
|
+
this._advancePipeline();
|
|
5272
5906
|
} else if (this.activeSubSolver.failed) {
|
|
5273
5907
|
this.activeSubSolver = null;
|
|
5274
|
-
this.
|
|
5908
|
+
this._advancePipeline();
|
|
5275
5909
|
}
|
|
5276
5910
|
return;
|
|
5277
5911
|
}
|
|
5912
|
+
if (!this.pipelineStep) {
|
|
5913
|
+
this.solved = true;
|
|
5914
|
+
return;
|
|
5915
|
+
}
|
|
5278
5916
|
switch (this.pipelineStep) {
|
|
5279
5917
|
case "untangling_traces":
|
|
5280
5918
|
this._runUntangleTracesStep();
|
|
@@ -5285,8 +5923,17 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5285
5923
|
case "balancing_l_shapes":
|
|
5286
5924
|
this._runBalanceLShapesStep();
|
|
5287
5925
|
break;
|
|
5926
|
+
case "aligning_same_net_rails":
|
|
5927
|
+
this._runAlignSameNetRailsStep();
|
|
5928
|
+
break;
|
|
5288
5929
|
}
|
|
5289
5930
|
}
|
|
5931
|
+
_advancePipeline() {
|
|
5932
|
+
this.operationIndex++;
|
|
5933
|
+
this.pipelineStep = this.operations[this.operationIndex] ?? null;
|
|
5934
|
+
this.traceIdQueue = this.outputTraces.map((trace) => trace.mspPairId);
|
|
5935
|
+
if (this.pipelineStep) this.activeTraceId = null;
|
|
5936
|
+
}
|
|
5290
5937
|
_runUntangleTracesStep() {
|
|
5291
5938
|
this.activeSubSolver = new UntangleTraceSubsolver({
|
|
5292
5939
|
...this.input,
|
|
@@ -5295,17 +5942,14 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5295
5942
|
}
|
|
5296
5943
|
_runMinimizeTurnsStep() {
|
|
5297
5944
|
if (this.traceIdQueue.length === 0) {
|
|
5298
|
-
this.
|
|
5299
|
-
this.traceIdQueue = Array.from(
|
|
5300
|
-
this.input.allTraces.map((e) => e.mspPairId)
|
|
5301
|
-
);
|
|
5945
|
+
this._advancePipeline();
|
|
5302
5946
|
return;
|
|
5303
5947
|
}
|
|
5304
5948
|
this._processTrace("minimizing_turns");
|
|
5305
5949
|
}
|
|
5306
5950
|
_runBalanceLShapesStep() {
|
|
5307
5951
|
if (this.traceIdQueue.length === 0) {
|
|
5308
|
-
this.
|
|
5952
|
+
this._advancePipeline();
|
|
5309
5953
|
return;
|
|
5310
5954
|
}
|
|
5311
5955
|
this._processTrace("balancing_l_shapes");
|
|
@@ -5335,6 +5979,19 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5335
5979
|
this.tracesMap.set(targetMspConnectionPairId, updatedTrace);
|
|
5336
5980
|
this.outputTraces = Array.from(this.tracesMap.values());
|
|
5337
5981
|
}
|
|
5982
|
+
_runAlignSameNetRailsStep() {
|
|
5983
|
+
const alignment = alignSameNetRails({
|
|
5984
|
+
inputProblem: this.input.inputProblem,
|
|
5985
|
+
traces: this.outputTraces,
|
|
5986
|
+
netLabelPlacements: this.input.allLabelPlacements,
|
|
5987
|
+
eligibleTraceIds: this.input.eligibleTraceIds ?? new Set(this.outputTraces.map((trace) => trace.mspPairId))
|
|
5988
|
+
});
|
|
5989
|
+
this.outputTraces = alignment.traces;
|
|
5990
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]));
|
|
5991
|
+
this.stats.alignedRailGroupCount = alignment.alignedRailGroupCount;
|
|
5992
|
+
this.stats.alignedTraceCount = alignment.alignedTraceCount;
|
|
5993
|
+
this._advancePipeline();
|
|
5994
|
+
}
|
|
5338
5995
|
getOutput() {
|
|
5339
5996
|
return {
|
|
5340
5997
|
traces: this.outputTraces
|
|
@@ -5413,160 +6070,6 @@ var dir = (facingDirection) => {
|
|
|
5413
6070
|
throw new Error(`Invalid facing direction: ${facingDirection}`);
|
|
5414
6071
|
};
|
|
5415
6072
|
|
|
5416
|
-
// lib/solvers/Example28Solver/types.ts
|
|
5417
|
-
var EPS7 = 1e-9;
|
|
5418
|
-
|
|
5419
|
-
// lib/solvers/Example28Solver/segmentRunsAlongRectBoundary.ts
|
|
5420
|
-
var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
5421
|
-
const isVertical4 = Math.abs(start.x - end.x) < EPS7;
|
|
5422
|
-
const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
|
|
5423
|
-
if (isVertical4) {
|
|
5424
|
-
return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
|
|
5425
|
-
}
|
|
5426
|
-
if (isHorizontal3) {
|
|
5427
|
-
return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
|
|
5428
|
-
}
|
|
5429
|
-
return false;
|
|
5430
|
-
};
|
|
5431
|
-
|
|
5432
|
-
// lib/solvers/Example28Solver/geometry.ts
|
|
5433
|
-
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
5434
|
-
var getPathLength = (path) => {
|
|
5435
|
-
let length = 0;
|
|
5436
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5437
|
-
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
5438
|
-
}
|
|
5439
|
-
return length;
|
|
5440
|
-
};
|
|
5441
|
-
var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
5442
|
-
var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
|
|
5443
|
-
var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
|
|
5444
|
-
var projectPointToSegment = (point, start, end) => {
|
|
5445
|
-
if (Math.abs(start.x - end.x) < EPS7) {
|
|
5446
|
-
return {
|
|
5447
|
-
x: start.x,
|
|
5448
|
-
y: Math.min(
|
|
5449
|
-
Math.max(point.y, Math.min(start.y, end.y)),
|
|
5450
|
-
Math.max(start.y, end.y)
|
|
5451
|
-
)
|
|
5452
|
-
};
|
|
5453
|
-
}
|
|
5454
|
-
if (Math.abs(start.y - end.y) < EPS7) {
|
|
5455
|
-
return {
|
|
5456
|
-
x: Math.min(
|
|
5457
|
-
Math.max(point.x, Math.min(start.x, end.x)),
|
|
5458
|
-
Math.max(start.x, end.x)
|
|
5459
|
-
),
|
|
5460
|
-
y: start.y
|
|
5461
|
-
};
|
|
5462
|
-
}
|
|
5463
|
-
return start;
|
|
5464
|
-
};
|
|
5465
|
-
var getPointToSegmentDistance = (point, start, end) => getDistance(point, projectPointToSegment(point, start, end));
|
|
5466
|
-
var getSegments = (path) => {
|
|
5467
|
-
const segments = [];
|
|
5468
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5469
|
-
const start = path[i];
|
|
5470
|
-
const end = path[i + 1];
|
|
5471
|
-
if (!isAxisAlignedSegment(start, end)) continue;
|
|
5472
|
-
segments.push({
|
|
5473
|
-
start,
|
|
5474
|
-
end,
|
|
5475
|
-
orientation: getSegmentOrientation(start, end)
|
|
5476
|
-
});
|
|
5477
|
-
}
|
|
5478
|
-
return segments;
|
|
5479
|
-
};
|
|
5480
|
-
var findSegmentContainingPoint = (path, point) => {
|
|
5481
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5482
|
-
const start = path[i];
|
|
5483
|
-
const end = path[i + 1];
|
|
5484
|
-
if (!isAxisAlignedSegment(start, end)) continue;
|
|
5485
|
-
if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
|
|
5486
|
-
return {
|
|
5487
|
-
index: i,
|
|
5488
|
-
start,
|
|
5489
|
-
end,
|
|
5490
|
-
orientation: getSegmentOrientation(start, end)
|
|
5491
|
-
};
|
|
5492
|
-
}
|
|
5493
|
-
return null;
|
|
5494
|
-
};
|
|
5495
|
-
var isPointWithinSegmentPrimaryRange = (point, segment) => {
|
|
5496
|
-
if (segment.orientation === "horizontal") {
|
|
5497
|
-
return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
|
|
5498
|
-
}
|
|
5499
|
-
return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
|
|
5500
|
-
};
|
|
5501
|
-
var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
|
|
5502
|
-
const matchingSegments = getSegments(path).filter(
|
|
5503
|
-
(segment) => segment.orientation === orientation
|
|
5504
|
-
);
|
|
5505
|
-
if (matchingSegments.length === 0) return null;
|
|
5506
|
-
const containingSegments = matchingSegments.filter(
|
|
5507
|
-
(segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
|
|
5508
|
-
);
|
|
5509
|
-
const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
|
|
5510
|
-
const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
|
|
5511
|
-
return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
|
|
5512
|
-
};
|
|
5513
|
-
var projectPointToPath = (point, path) => {
|
|
5514
|
-
let bestPoint = null;
|
|
5515
|
-
let bestDistance = Number.POSITIVE_INFINITY;
|
|
5516
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5517
|
-
const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
|
|
5518
|
-
const distance3 = getDistance(point, projectedPoint);
|
|
5519
|
-
if (distance3 < bestDistance) {
|
|
5520
|
-
bestPoint = projectedPoint;
|
|
5521
|
-
bestDistance = distance3;
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
return bestPoint;
|
|
5525
|
-
};
|
|
5526
|
-
var segmentsIntersect = (a1, a2, b1, b2) => {
|
|
5527
|
-
const aVertical = Math.abs(a1.x - a2.x) < EPS7;
|
|
5528
|
-
const bVertical = Math.abs(b1.x - b2.x) < EPS7;
|
|
5529
|
-
const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
|
|
5530
|
-
if (aVertical && bVertical) {
|
|
5531
|
-
if (Math.abs(a1.x - b1.x) > EPS7) return false;
|
|
5532
|
-
const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
|
|
5533
|
-
return overlap > EPS7;
|
|
5534
|
-
}
|
|
5535
|
-
if (!aVertical && !bVertical) {
|
|
5536
|
-
if (Math.abs(a1.y - b1.y) > EPS7) return false;
|
|
5537
|
-
const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
|
|
5538
|
-
return overlap > EPS7;
|
|
5539
|
-
}
|
|
5540
|
-
const verticalA = aVertical ? a1 : b1;
|
|
5541
|
-
const verticalB = aVertical ? a2 : b2;
|
|
5542
|
-
const horizontalA = aVertical ? b1 : a1;
|
|
5543
|
-
const horizontalB = aVertical ? b2 : a2;
|
|
5544
|
-
return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
|
|
5545
|
-
};
|
|
5546
|
-
var countPathIntersections = (pathA, pathB) => {
|
|
5547
|
-
let count = 0;
|
|
5548
|
-
for (let i = 0; i < pathA.length - 1; i++) {
|
|
5549
|
-
for (let j = 0; j < pathB.length - 1; j++) {
|
|
5550
|
-
if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
|
|
5551
|
-
count++;
|
|
5552
|
-
}
|
|
5553
|
-
}
|
|
5554
|
-
}
|
|
5555
|
-
return count;
|
|
5556
|
-
};
|
|
5557
|
-
var isPathCollidingWithChipInterior = (path, chipObstacles) => {
|
|
5558
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
5559
|
-
const start = path[i];
|
|
5560
|
-
const end = path[i + 1];
|
|
5561
|
-
for (const obstacle of chipObstacles) {
|
|
5562
|
-
if (!segmentIntersectsRect(start, end, obstacle)) continue;
|
|
5563
|
-
if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5564
|
-
return true;
|
|
5565
|
-
}
|
|
5566
|
-
}
|
|
5567
|
-
return false;
|
|
5568
|
-
};
|
|
5569
|
-
|
|
5570
6073
|
// lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
|
|
5571
6074
|
var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
|
|
5572
6075
|
const originalSegment = findSegmentContainingPoint(
|
|
@@ -5629,7 +6132,7 @@ var doesPathRunAlongChipBoundary = (path, chipObstacles) => {
|
|
|
5629
6132
|
const end = path[i + 1];
|
|
5630
6133
|
for (const obstacle of chipObstacles) {
|
|
5631
6134
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5632
|
-
if (getSegmentOverlapWithRectSpan(start, end, obstacle) >
|
|
6135
|
+
if (getSegmentOverlapWithRectSpan(start, end, obstacle) > EPS6) {
|
|
5633
6136
|
return true;
|
|
5634
6137
|
}
|
|
5635
6138
|
}
|
|
@@ -5644,14 +6147,14 @@ var getChipBoundaryOverlap = (path, chipObstacles) => {
|
|
|
5644
6147
|
for (const obstacle of chipObstacles) {
|
|
5645
6148
|
if (!segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
|
|
5646
6149
|
const overlap = getSegmentOverlapWithRectSpan(start, end, obstacle);
|
|
5647
|
-
if (overlap >
|
|
6150
|
+
if (overlap > EPS6) total += overlap;
|
|
5648
6151
|
}
|
|
5649
6152
|
}
|
|
5650
6153
|
return total;
|
|
5651
6154
|
};
|
|
5652
6155
|
var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
5653
|
-
const
|
|
5654
|
-
if (
|
|
6156
|
+
const isVertical5 = Math.abs(start.x - end.x) < EPS6;
|
|
6157
|
+
if (isVertical5) {
|
|
5655
6158
|
return Math.min(Math.max(start.y, end.y), rect.maxY) - Math.max(Math.min(start.y, end.y), rect.minY);
|
|
5656
6159
|
}
|
|
5657
6160
|
return Math.min(Math.max(start.x, end.x), rect.maxX) - Math.max(Math.min(start.x, end.x), rect.minX);
|
|
@@ -6414,16 +6917,16 @@ var createPortOnlyLabelConnectorTrace = ({
|
|
|
6414
6917
|
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
6415
6918
|
var LABEL_SEARCH_STEP = 0.05;
|
|
6416
6919
|
var WICK_CLEARANCE = 1e-3;
|
|
6417
|
-
var
|
|
6418
|
-
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE +
|
|
6920
|
+
var EPS9 = 1e-9;
|
|
6921
|
+
var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS9;
|
|
6419
6922
|
var CANDIDATE_SELECTED_COLOR2 = "blue";
|
|
6420
6923
|
var CANDIDATE_REJECTED_COLOR2 = "red";
|
|
6421
6924
|
|
|
6422
6925
|
// lib/solvers/AvailableNetOrientationSolver/geometry.ts
|
|
6423
6926
|
var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
|
|
6424
6927
|
var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
|
|
6425
|
-
var
|
|
6426
|
-
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) >
|
|
6928
|
+
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
|
|
6929
|
+
var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
|
|
6427
6930
|
var traceCrossesBoundsInterior = (bounds, traceMap) => {
|
|
6428
6931
|
for (const trace of Object.values(traceMap)) {
|
|
6429
6932
|
const points = trace.tracePath;
|
|
@@ -6446,7 +6949,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
6446
6949
|
return false;
|
|
6447
6950
|
}
|
|
6448
6951
|
if (sameX2(p1, p2)) {
|
|
6449
|
-
if (p1.x <= interiorBounds.minX +
|
|
6952
|
+
if (p1.x <= interiorBounds.minX + EPS9 || p1.x >= interiorBounds.maxX - EPS9) {
|
|
6450
6953
|
return false;
|
|
6451
6954
|
}
|
|
6452
6955
|
return rangesOverlap(
|
|
@@ -6457,7 +6960,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
|
|
|
6457
6960
|
);
|
|
6458
6961
|
}
|
|
6459
6962
|
if (sameY2(p1, p2)) {
|
|
6460
|
-
if (p1.y <= interiorBounds.minY +
|
|
6963
|
+
if (p1.y <= interiorBounds.minY + EPS9 || p1.y >= interiorBounds.maxY - EPS9) {
|
|
6461
6964
|
return false;
|
|
6462
6965
|
}
|
|
6463
6966
|
return rangesOverlap(
|
|
@@ -6496,7 +6999,7 @@ var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalP
|
|
|
6496
6999
|
);
|
|
6497
7000
|
var simplifyOrthogonalPath = (path) => {
|
|
6498
7001
|
const deduped = path.filter(
|
|
6499
|
-
(point, index) => index === 0 || !
|
|
7002
|
+
(point, index) => index === 0 || !pointsEqual2(point, path[index - 1])
|
|
6500
7003
|
);
|
|
6501
7004
|
if (deduped.length < 3) return deduped;
|
|
6502
7005
|
const simplified = [deduped[0]];
|
|
@@ -6512,9 +7015,9 @@ var simplifyOrthogonalPath = (path) => {
|
|
|
6512
7015
|
simplified.push(deduped[deduped.length - 1]);
|
|
6513
7016
|
return simplified;
|
|
6514
7017
|
};
|
|
6515
|
-
var
|
|
6516
|
-
var sameX2 = (a, b) => Math.abs(a.x - b.x) <=
|
|
6517
|
-
var sameY2 = (a, b) => Math.abs(a.y - b.y) <=
|
|
7018
|
+
var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
|
|
7019
|
+
var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS9;
|
|
7020
|
+
var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS9;
|
|
6518
7021
|
var getMaxSearchDistance = (inputProblem) => {
|
|
6519
7022
|
const maxChipWidth = Math.max(
|
|
6520
7023
|
...inputProblem.chips.map((chip) => chip.width),
|
|
@@ -6817,7 +7320,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6817
7320
|
);
|
|
6818
7321
|
const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
|
|
6819
7322
|
const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
|
|
6820
|
-
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance +
|
|
7323
|
+
for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS9; outwardDistance += LABEL_SEARCH_STEP) {
|
|
6821
7324
|
const baseAnchor = {
|
|
6822
7325
|
x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
|
|
6823
7326
|
y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
|
|
@@ -6890,7 +7393,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6890
7393
|
outwardDistance,
|
|
6891
7394
|
phase = "shift"
|
|
6892
7395
|
} = params;
|
|
6893
|
-
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance +
|
|
7396
|
+
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS9; distance3 += LABEL_SEARCH_STEP) {
|
|
6894
7397
|
const anchorPoint = {
|
|
6895
7398
|
x: baseAnchor.x + direction.x * distance3,
|
|
6896
7399
|
y: baseAnchor.y + direction.y * distance3
|
|
@@ -7127,9 +7630,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7127
7630
|
const { anchorPoint, orientation } = candidate;
|
|
7128
7631
|
const chipBounds = chip.bounds;
|
|
7129
7632
|
if (isYOrientation(orientation)) {
|
|
7130
|
-
return anchorPoint.x < chipBounds.minX -
|
|
7633
|
+
return anchorPoint.x < chipBounds.minX - EPS9 || anchorPoint.x > chipBounds.maxX + EPS9;
|
|
7131
7634
|
}
|
|
7132
|
-
return anchorPoint.y < chipBounds.minY -
|
|
7635
|
+
return anchorPoint.y < chipBounds.minY - EPS9 || anchorPoint.y > chipBounds.maxY + EPS9;
|
|
7133
7636
|
});
|
|
7134
7637
|
}
|
|
7135
7638
|
getBoundsStatus(candidateBoundsCheck) {
|
|
@@ -7196,15 +7699,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7196
7699
|
if (i === labelIndex) continue;
|
|
7197
7700
|
const label = this.outputNetLabelPlacements[i];
|
|
7198
7701
|
const otherBounds = getRectBounds(label.center, label.width, label.height);
|
|
7199
|
-
if (
|
|
7702
|
+
if (rectsOverlap2(bounds, otherBounds)) return true;
|
|
7200
7703
|
}
|
|
7201
7704
|
return false;
|
|
7202
7705
|
}
|
|
7203
7706
|
sharesChipBoundary(bounds) {
|
|
7204
7707
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7205
7708
|
const chipBounds = chip.bounds;
|
|
7206
|
-
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE +
|
|
7207
|
-
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE +
|
|
7709
|
+
const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS9;
|
|
7710
|
+
const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS9 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS9;
|
|
7208
7711
|
if (adjacentToVerticalSide && rangesOverlap(
|
|
7209
7712
|
bounds.minY,
|
|
7210
7713
|
bounds.maxY,
|
|
@@ -7252,7 +7755,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7252
7755
|
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
7253
7756
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7254
7757
|
const bounds = chip.bounds;
|
|
7255
|
-
if (point.x < bounds.minX -
|
|
7758
|
+
if (point.x < bounds.minX - EPS9 || point.x > bounds.maxX + EPS9 || point.y < bounds.minY - EPS9 || point.y > bounds.maxY + EPS9) {
|
|
7256
7759
|
continue;
|
|
7257
7760
|
}
|
|
7258
7761
|
for (const [side, distance3] of getSideDistances(point, bounds)) {
|
|
@@ -7269,8 +7772,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7269
7772
|
let nearestDistanceSq = Number.POSITIVE_INFINITY;
|
|
7270
7773
|
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
7271
7774
|
const bounds = chip.bounds;
|
|
7272
|
-
const dx = point.x < bounds.minX -
|
|
7273
|
-
const dy = point.y < bounds.minY -
|
|
7775
|
+
const dx = point.x < bounds.minX - EPS9 ? bounds.minX - point.x : point.x > bounds.maxX + EPS9 ? point.x - bounds.maxX : 0;
|
|
7776
|
+
const dy = point.y < bounds.minY - EPS9 ? bounds.minY - point.y : point.y > bounds.maxY + EPS9 ? point.y - bounds.maxY : 0;
|
|
7274
7777
|
if (dx === 0 && dy === 0) continue;
|
|
7275
7778
|
const distanceSq = dx ** 2 + dy ** 2;
|
|
7276
7779
|
if (distanceSq >= nearestDistanceSq) continue;
|
|
@@ -7308,39 +7811,6 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7308
7811
|
}
|
|
7309
7812
|
};
|
|
7310
7813
|
|
|
7311
|
-
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
7312
|
-
var EPS9 = 1e-6;
|
|
7313
|
-
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
7314
|
-
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
|
|
7315
|
-
var getTraceCorners = (path) => {
|
|
7316
|
-
const corners = [];
|
|
7317
|
-
for (let i = 1; i < path.length - 1; i++) {
|
|
7318
|
-
const previousPoint = path[i - 1];
|
|
7319
|
-
const cornerPoint = path[i];
|
|
7320
|
-
const nextPoint = path[i + 1];
|
|
7321
|
-
if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
|
|
7322
|
-
corners.push(cornerPoint);
|
|
7323
|
-
}
|
|
7324
|
-
}
|
|
7325
|
-
return corners;
|
|
7326
|
-
};
|
|
7327
|
-
var tracePathContainsPoint = (path, point) => {
|
|
7328
|
-
for (let i = 0; i < path.length - 1; i++) {
|
|
7329
|
-
if (isPointOnSegment(point, path[i], path[i + 1])) return true;
|
|
7330
|
-
}
|
|
7331
|
-
return false;
|
|
7332
|
-
};
|
|
7333
|
-
var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
7334
|
-
var getUniquePinCount = (pinIds) => new Set(pinIds).size;
|
|
7335
|
-
var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
|
|
7336
|
-
var isPointOnSegment = (point, start, end) => {
|
|
7337
|
-
if (getSegmentOrientation2(start, end) === "horizontal") {
|
|
7338
|
-
return Math.abs(point.y - start.y) <= EPS9 && point.x >= Math.min(start.x, end.x) - EPS9 && point.x <= Math.max(start.x, end.x) + EPS9;
|
|
7339
|
-
}
|
|
7340
|
-
return Math.abs(point.x - start.x) <= EPS9 && point.y >= Math.min(start.y, end.y) - EPS9 && point.y <= Math.max(start.y, end.y) + EPS9;
|
|
7341
|
-
};
|
|
7342
|
-
var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS9 ? "horizontal" : "vertical";
|
|
7343
|
-
|
|
7344
7814
|
// lib/solvers/RailNetLabelCornerPlacementSolver/visualize.ts
|
|
7345
7815
|
var CANDIDATE_SELECTED_COLOR3 = "blue";
|
|
7346
7816
|
var CANDIDATE_REJECTED_COLOR3 = "red";
|
|
@@ -7563,20 +8033,20 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7563
8033
|
}
|
|
7564
8034
|
intersectsAnyChip(bounds) {
|
|
7565
8035
|
return this.inputProblem.chips.some(
|
|
7566
|
-
(chip) =>
|
|
8036
|
+
(chip) => rectsOverlap(bounds, getRectBounds(chip.center, chip.width, chip.height))
|
|
7567
8037
|
);
|
|
7568
8038
|
}
|
|
7569
8039
|
intersectsAnyOtherNetLabel(bounds, labelIndex) {
|
|
7570
8040
|
return this.outputNetLabelPlacements.some((label, index) => {
|
|
7571
8041
|
if (index === labelIndex) return false;
|
|
7572
|
-
return
|
|
8042
|
+
return rectsOverlap(
|
|
7573
8043
|
bounds,
|
|
7574
8044
|
getRectBounds(label.center, label.width, label.height)
|
|
7575
8045
|
);
|
|
7576
8046
|
});
|
|
7577
8047
|
}
|
|
7578
8048
|
getCornerCandidatesForLabel(label) {
|
|
7579
|
-
const
|
|
8049
|
+
const isVertical5 = label.orientation === "y+" || label.orientation === "y-";
|
|
7580
8050
|
const candidates = [];
|
|
7581
8051
|
const seenCornerKeys = /* @__PURE__ */ new Set();
|
|
7582
8052
|
for (const trace of this.getTraceLinesForLabel(label)) {
|
|
@@ -7587,14 +8057,14 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7587
8057
|
if (seenCornerKeys.has(key)) continue;
|
|
7588
8058
|
seenCornerKeys.add(key);
|
|
7589
8059
|
const pinAligned = pins.some((pin) => {
|
|
7590
|
-
if (
|
|
7591
|
-
return Math.abs(pin.y - anchorPoint.y) <
|
|
8060
|
+
if (isVertical5) return Math.abs(pin.x - anchorPoint.x) < EPS5;
|
|
8061
|
+
return Math.abs(pin.y - anchorPoint.y) < EPS5;
|
|
7592
8062
|
});
|
|
7593
8063
|
if (!pinAligned) continue;
|
|
7594
8064
|
candidates.push({
|
|
7595
8065
|
anchorPoint,
|
|
7596
8066
|
traceId: trace.mspPairId,
|
|
7597
|
-
distance:
|
|
8067
|
+
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
7598
8068
|
pinAligned
|
|
7599
8069
|
});
|
|
7600
8070
|
}
|
|
@@ -7769,7 +8239,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7769
8239
|
if (interior.minX >= interior.maxX || interior.minY >= interior.maxY) {
|
|
7770
8240
|
return false;
|
|
7771
8241
|
}
|
|
7772
|
-
if (
|
|
8242
|
+
if (isVertical4(start, end)) {
|
|
7773
8243
|
if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
|
|
7774
8244
|
return false;
|
|
7775
8245
|
}
|
|
@@ -7780,7 +8250,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7780
8250
|
interior.maxY
|
|
7781
8251
|
);
|
|
7782
8252
|
}
|
|
7783
|
-
if (
|
|
8253
|
+
if (isHorizontal3(start, end)) {
|
|
7784
8254
|
if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
|
|
7785
8255
|
return false;
|
|
7786
8256
|
}
|
|
@@ -7794,20 +8264,20 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
|
|
|
7794
8264
|
return false;
|
|
7795
8265
|
};
|
|
7796
8266
|
var isPointOnSegment2 = (point, start, end) => {
|
|
7797
|
-
if (
|
|
8267
|
+
if (isHorizontal3(start, end)) {
|
|
7798
8268
|
return Math.abs(point.y - start.y) <= EPS10 && point.x >= Math.min(start.x, end.x) - EPS10 && point.x <= Math.max(start.x, end.x) + EPS10;
|
|
7799
8269
|
}
|
|
7800
|
-
if (
|
|
8270
|
+
if (isVertical4(start, end)) {
|
|
7801
8271
|
return Math.abs(point.x - start.x) <= EPS10 && point.y >= Math.min(start.y, end.y) - EPS10 && point.y <= Math.max(start.y, end.y) + EPS10;
|
|
7802
8272
|
}
|
|
7803
8273
|
return false;
|
|
7804
8274
|
};
|
|
7805
8275
|
var getSegmentDirection = (start, end) => ({
|
|
7806
|
-
x:
|
|
7807
|
-
y:
|
|
8276
|
+
x: isVertical4(start, end) ? 0 : Math.sign(end.x - start.x),
|
|
8277
|
+
y: isHorizontal3(start, end) ? 0 : Math.sign(end.y - start.y)
|
|
7808
8278
|
});
|
|
7809
|
-
var
|
|
7810
|
-
var
|
|
8279
|
+
var isHorizontal3 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
|
|
8280
|
+
var isVertical4 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
|
|
7811
8281
|
var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
|
|
7812
8282
|
var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
|
|
7813
8283
|
|
|
@@ -8816,11 +9286,11 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
8816
9286
|
for (let si = 0; si < pts.length - 1; si++) {
|
|
8817
9287
|
const segStart = pts[si];
|
|
8818
9288
|
const segEnd = pts[si + 1];
|
|
8819
|
-
const
|
|
8820
|
-
const
|
|
8821
|
-
if (!
|
|
9289
|
+
const isHorizontal4 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
|
|
9290
|
+
const isVertical5 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
|
|
9291
|
+
if (!isHorizontal4 && !isVertical5) continue;
|
|
8822
9292
|
let perpendicularOrientations;
|
|
8823
|
-
if (
|
|
9293
|
+
if (isHorizontal4) {
|
|
8824
9294
|
perpendicularOrientations = ["y+", "y-"];
|
|
8825
9295
|
} else {
|
|
8826
9296
|
perpendicularOrientations = ["x+", "x-"];
|
|
@@ -9033,7 +9503,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9033
9503
|
availableNetOrientationSolver;
|
|
9034
9504
|
railNetLabelCornerPlacementSolver;
|
|
9035
9505
|
traceAnchoredNetLabelOverlapSolver;
|
|
9506
|
+
preAlignmentNetLabelTraceCollisionSolver;
|
|
9036
9507
|
netLabelTraceCollisionSolver;
|
|
9508
|
+
traceCleanupSolver2;
|
|
9037
9509
|
netLabelNetLabelCollisionSolver;
|
|
9038
9510
|
startTimeOfPhase;
|
|
9039
9511
|
endTimeOfPhase;
|
|
@@ -9218,7 +9690,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9218
9690
|
]
|
|
9219
9691
|
),
|
|
9220
9692
|
definePipelineStep(
|
|
9221
|
-
"
|
|
9693
|
+
"preAlignmentNetLabelTraceCollisionSolver",
|
|
9222
9694
|
NetLabelTraceCollisionSolver,
|
|
9223
9695
|
(instance) => [
|
|
9224
9696
|
{
|
|
@@ -9228,6 +9700,41 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9228
9700
|
}
|
|
9229
9701
|
]
|
|
9230
9702
|
),
|
|
9703
|
+
definePipelineStep(
|
|
9704
|
+
"traceCleanupSolver2",
|
|
9705
|
+
TraceCleanupSolver,
|
|
9706
|
+
(instance) => {
|
|
9707
|
+
const collisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
9708
|
+
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
9709
|
+
return [
|
|
9710
|
+
{
|
|
9711
|
+
inputProblem: instance.inputProblem,
|
|
9712
|
+
allTraces: collisionOutput.traces,
|
|
9713
|
+
allLabelPlacements: collisionOutput.netLabelPlacements,
|
|
9714
|
+
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
9715
|
+
paddingBuffer: 0.1,
|
|
9716
|
+
operations: ["aligning_same_net_rails"],
|
|
9717
|
+
eligibleTraceIds: new Set(
|
|
9718
|
+
instance.traceCleanupSolver.getOutput().traces.map((trace) => trace.mspPairId)
|
|
9719
|
+
)
|
|
9720
|
+
}
|
|
9721
|
+
];
|
|
9722
|
+
}
|
|
9723
|
+
),
|
|
9724
|
+
definePipelineStep(
|
|
9725
|
+
"netLabelTraceCollisionSolver",
|
|
9726
|
+
NetLabelTraceCollisionSolver,
|
|
9727
|
+
(instance) => {
|
|
9728
|
+
const previousCollisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
9729
|
+
return [
|
|
9730
|
+
{
|
|
9731
|
+
inputProblem: instance.inputProblem,
|
|
9732
|
+
traces: instance.traceCleanupSolver2.getOutput().traces,
|
|
9733
|
+
netLabelPlacements: previousCollisionOutput.netLabelPlacements
|
|
9734
|
+
}
|
|
9735
|
+
];
|
|
9736
|
+
}
|
|
9737
|
+
),
|
|
9231
9738
|
definePipelineStep(
|
|
9232
9739
|
"netLabelNetLabelCollisionSolver",
|
|
9233
9740
|
NetLabelNetLabelCollisionSolver,
|