@tscircuit/schematic-trace-solver 0.0.76 → 0.0.78
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/.github/ISSUE_TEMPLATE/json-bug-report.yml +24 -0
- package/.github/scripts/import-json-bug-report.ts +483 -0
- package/.github/workflows/json-bug-report.yml +154 -0
- package/dist/index.d.ts +32 -10
- package/dist/index.js +226 -74
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +4 -0
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +1 -0
- package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +6 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +21 -1
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts +9 -3
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +27 -2
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts +1 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +121 -30
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +10 -10
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/mid.ts +5 -8
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts +31 -6
- package/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts +11 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver.ts +3 -0
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/types.ts +1 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver.ts +3 -0
- package/lib/solvers/VccNetLabelCornerPlacementSolver/types.ts +1 -0
- package/lib/utils/textBoxBounds.ts +40 -0
- package/package.json +1 -1
- package/tests/repros/__snapshots__/manufacturePartNumber-text-box.snap.svg +88 -0
- package/tests/repros/manufacturePartNumber-text-box.test.ts +57 -0
package/dist/index.d.ts
CHANGED
|
@@ -137,21 +137,39 @@ declare class MspConnectionPairSolver extends BaseSolver {
|
|
|
137
137
|
visualize(): GraphicsObject;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
type
|
|
141
|
-
|
|
140
|
+
type RectPadding = {
|
|
141
|
+
minX?: number;
|
|
142
|
+
minY?: number;
|
|
143
|
+
maxX?: number;
|
|
144
|
+
maxY?: number;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
type RectBounds = {
|
|
142
148
|
minX: number;
|
|
143
149
|
minY: number;
|
|
144
150
|
maxX: number;
|
|
145
151
|
maxY: number;
|
|
146
152
|
};
|
|
147
|
-
|
|
153
|
+
type ChipObstacleRect = RectBounds & {
|
|
154
|
+
kind: "chip";
|
|
155
|
+
chipId: string;
|
|
156
|
+
};
|
|
157
|
+
type TextBoxObstacleRect = RectBounds & {
|
|
158
|
+
kind: "text_box";
|
|
159
|
+
textBox: NonNullable<InputProblem["textBoxes"]>[number];
|
|
160
|
+
};
|
|
161
|
+
type ObstacleRect = ChipObstacleRect | TextBoxObstacleRect;
|
|
162
|
+
declare const getObstacleRects: (problem: InputProblem, opts?: {
|
|
163
|
+
textBoxPadding?: RectPadding;
|
|
164
|
+
}) => ObstacleRect[];
|
|
148
165
|
|
|
149
166
|
declare class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
150
167
|
pins: MspConnectionPair["pins"];
|
|
168
|
+
connectionPair?: MspConnectionPair;
|
|
151
169
|
inputProblem: InputProblem;
|
|
152
170
|
chipMap: Record<string, InputChip>;
|
|
153
|
-
obstacles:
|
|
154
|
-
|
|
171
|
+
obstacles: ObstacleRect[];
|
|
172
|
+
textObstacles: Set<ObstacleRect>;
|
|
155
173
|
aabb: {
|
|
156
174
|
minX: number;
|
|
157
175
|
maxX: number;
|
|
@@ -164,10 +182,14 @@ declare class SchematicTraceSingleLineSolver2 extends BaseSolver {
|
|
|
164
182
|
private visited;
|
|
165
183
|
constructor(params: {
|
|
166
184
|
pins: MspConnectionPair["pins"];
|
|
185
|
+
connectionPair?: MspConnectionPair;
|
|
167
186
|
inputProblem: InputProblem;
|
|
168
187
|
chipMap: Record<string, InputChip>;
|
|
169
188
|
});
|
|
170
189
|
getConstructorParams(): ConstructorParameters<typeof SchematicTraceSingleLineSolver2>[0];
|
|
190
|
+
private getTextBoxPaddingForConnectionPair;
|
|
191
|
+
private getNetLabelWidthForConnectionPair;
|
|
192
|
+
private getNetLabelHeightForConnectionPair;
|
|
171
193
|
private axisOfSegment;
|
|
172
194
|
private pathLength;
|
|
173
195
|
_step(): void;
|
|
@@ -324,7 +346,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
|
|
|
324
346
|
y: number;
|
|
325
347
|
};
|
|
326
348
|
orientation: FacingDirection;
|
|
327
|
-
status: "ok" | "chip-collision" | "trace-collision" | "parallel-to-segment";
|
|
349
|
+
status: "ok" | "chip-collision" | "trace-collision" | "text-collision" | "parallel-to-segment";
|
|
328
350
|
hostSegIndex: number;
|
|
329
351
|
}>;
|
|
330
352
|
constructor(params: {
|
|
@@ -672,7 +694,7 @@ type CandidateLabel = {
|
|
|
672
694
|
width: number;
|
|
673
695
|
height: number;
|
|
674
696
|
};
|
|
675
|
-
type CandidateStatus$2 = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
697
|
+
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
|
|
676
698
|
type CandidatePhase = "rotate" | "shift" | "lateral-shift";
|
|
677
699
|
type EvaluatedCandidate = CandidateLabel & {
|
|
678
700
|
status: CandidateStatus$2;
|
|
@@ -756,7 +778,7 @@ type TraceCornerCandidate = {
|
|
|
756
778
|
traceId: string;
|
|
757
779
|
distance: number;
|
|
758
780
|
};
|
|
759
|
-
type CornerCandidateStatus = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
781
|
+
type CornerCandidateStatus = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
|
|
760
782
|
type EvaluatedCornerCandidate = TraceCornerCandidate & {
|
|
761
783
|
center: Point;
|
|
762
784
|
width: number;
|
|
@@ -809,7 +831,7 @@ type LabelOverlap = {
|
|
|
809
831
|
firstLabelIndex: number;
|
|
810
832
|
secondLabelIndex: number;
|
|
811
833
|
};
|
|
812
|
-
type CandidateStatus$1 = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
834
|
+
type CandidateStatus$1 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
|
|
813
835
|
type LabelCandidate = {
|
|
814
836
|
anchorPoint: Point;
|
|
815
837
|
center: Point;
|
|
@@ -887,7 +909,7 @@ declare class NetLabelTraceCollisionSolver extends BaseSolver {
|
|
|
887
909
|
visualize(): GraphicsObject;
|
|
888
910
|
}
|
|
889
911
|
|
|
890
|
-
type CandidateStatus = "ok" | "chip-collision" | "trace-collision" | "label-collision";
|
|
912
|
+
type CandidateStatus = "ok" | "chip-collision" | "text-collision" | "trace-collision" | "label-collision";
|
|
891
913
|
type Candidate = {
|
|
892
914
|
orientation: FacingDirection;
|
|
893
915
|
anchor: {
|
package/dist/index.js
CHANGED
|
@@ -357,6 +357,16 @@ ${pin._facingDirection ?? getPinDirection(pin, chip)}`,
|
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
361
|
+
graphics.rects.push({
|
|
362
|
+
label: textBox.text ?? "schematic_text",
|
|
363
|
+
center: textBox.center,
|
|
364
|
+
width: textBox.width,
|
|
365
|
+
height: textBox.height,
|
|
366
|
+
fill: "rgba(160, 0, 220, 0.14)",
|
|
367
|
+
strokeColor: "rgba(160, 0, 220, 0.9)"
|
|
368
|
+
});
|
|
369
|
+
}
|
|
360
370
|
for (const directConn of inputProblem.directConnections) {
|
|
361
371
|
const [pinId1, pinId2] = directConn.pinIds;
|
|
362
372
|
const pin1 = pinIdMap.get(pinId1);
|
|
@@ -581,13 +591,41 @@ function getInputChipBounds(chip) {
|
|
|
581
591
|
};
|
|
582
592
|
}
|
|
583
593
|
|
|
594
|
+
// lib/utils/textBoxBounds.ts
|
|
595
|
+
function getTextBoxBounds(textBox, padding = {}) {
|
|
596
|
+
return {
|
|
597
|
+
minX: textBox.center.x - textBox.width / 2 - (padding.minX ?? 0),
|
|
598
|
+
minY: textBox.center.y - textBox.height / 2 - (padding.minY ?? 0),
|
|
599
|
+
maxX: textBox.center.x + textBox.width / 2 + (padding.maxX ?? 0),
|
|
600
|
+
maxY: textBox.center.y + textBox.height / 2 + (padding.maxY ?? 0)
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function boundsOverlap(a, b, eps = 1e-9) {
|
|
604
|
+
return a.minX < b.maxX - eps && a.maxX > b.minX + eps && a.minY < b.maxY - eps && a.maxY > b.minY + eps;
|
|
605
|
+
}
|
|
606
|
+
function rectIntersectsAnyTextBox(bounds, inputProblem) {
|
|
607
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
608
|
+
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
609
|
+
}
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
|
|
584
613
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
|
|
585
614
|
var chipToRect = (chip) => {
|
|
586
615
|
const b = getInputChipBounds(chip);
|
|
587
|
-
return { chipId: chip.chipId, ...b };
|
|
616
|
+
return { kind: "chip", chipId: chip.chipId, ...b };
|
|
588
617
|
};
|
|
589
|
-
var getObstacleRects = (problem) => {
|
|
590
|
-
|
|
618
|
+
var getObstacleRects = (problem, opts = {}) => {
|
|
619
|
+
const chipRects = problem.chips.map(chipToRect);
|
|
620
|
+
const textBoxRects = (problem.textBoxes ?? []).map((textBox) => {
|
|
621
|
+
const b = getTextBoxBounds(textBox, opts.textBoxPadding);
|
|
622
|
+
return {
|
|
623
|
+
kind: "text_box",
|
|
624
|
+
textBox,
|
|
625
|
+
...b
|
|
626
|
+
};
|
|
627
|
+
});
|
|
628
|
+
return [...chipRects, ...textBoxRects];
|
|
591
629
|
};
|
|
592
630
|
|
|
593
631
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts
|
|
@@ -618,9 +656,9 @@ var findFirstCollision = (pts, rects, opts = {}) => {
|
|
|
618
656
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
619
657
|
const a = pts[i];
|
|
620
658
|
const b = pts[i + 1];
|
|
621
|
-
const excluded = opts.
|
|
659
|
+
const excluded = opts.excludeRectsForSegment?.(i) ?? /* @__PURE__ */ new Set();
|
|
622
660
|
for (const r of rects) {
|
|
623
|
-
if (excluded.has(r
|
|
661
|
+
if (excluded.has(r)) continue;
|
|
624
662
|
if (segmentIntersectsRect(a, b, r)) {
|
|
625
663
|
return { segIndex: i, rect: r };
|
|
626
664
|
}
|
|
@@ -666,8 +704,8 @@ var midBetweenPointAndRect = (axis, p, r, eps = EPS2) => {
|
|
|
666
704
|
return [r.minY - 0.2, r.maxY + 0.2];
|
|
667
705
|
}
|
|
668
706
|
};
|
|
669
|
-
var candidateMidsFromSet = (axis, colliding,
|
|
670
|
-
const setRects = [...
|
|
707
|
+
var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, eps = EPS2) => {
|
|
708
|
+
const setRects = [...collisionRects];
|
|
671
709
|
if (axis === "x") {
|
|
672
710
|
const leftBoundaries = [aabb.minX, ...setRects.map((r) => r.maxX)].filter(
|
|
673
711
|
(v) => v < colliding.minX - eps
|
|
@@ -768,13 +806,54 @@ var pathKey = (pts, decimals = 6) => {
|
|
|
768
806
|
return key;
|
|
769
807
|
};
|
|
770
808
|
|
|
809
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
810
|
+
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
811
|
+
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
812
|
+
function getDimsForOrientation(params) {
|
|
813
|
+
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
814
|
+
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
815
|
+
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
816
|
+
if (orientation === "y+" || orientation === "y-") {
|
|
817
|
+
return {
|
|
818
|
+
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
819
|
+
width: horizHeight,
|
|
820
|
+
height: horizWidth
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
width: horizWidth,
|
|
825
|
+
height: horizHeight
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
829
|
+
switch (orientation) {
|
|
830
|
+
case "x+":
|
|
831
|
+
return { x: anchor.x + width / 2, y: anchor.y };
|
|
832
|
+
case "x-":
|
|
833
|
+
return { x: anchor.x - width / 2, y: anchor.y };
|
|
834
|
+
case "y+":
|
|
835
|
+
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
836
|
+
case "y-":
|
|
837
|
+
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function getRectBounds(center, w, h) {
|
|
841
|
+
return {
|
|
842
|
+
minX: center.x - w / 2,
|
|
843
|
+
minY: center.y - h / 2,
|
|
844
|
+
maxX: center.x + w / 2,
|
|
845
|
+
maxY: center.y + h / 2
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
771
849
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
772
850
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
773
851
|
pins;
|
|
852
|
+
connectionPair;
|
|
774
853
|
inputProblem;
|
|
775
854
|
chipMap;
|
|
776
855
|
obstacles;
|
|
777
|
-
|
|
856
|
+
textObstacles;
|
|
778
857
|
aabb;
|
|
779
858
|
baseElbow;
|
|
780
859
|
solvedTracePath = null;
|
|
@@ -783,6 +862,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
783
862
|
constructor(params) {
|
|
784
863
|
super();
|
|
785
864
|
this.pins = params.pins;
|
|
865
|
+
this.connectionPair = params.connectionPair;
|
|
786
866
|
this.inputProblem = params.inputProblem;
|
|
787
867
|
this.chipMap = params.chipMap;
|
|
788
868
|
for (const pin of this.pins) {
|
|
@@ -791,8 +871,12 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
791
871
|
pin._facingDirection = getPinDirection(pin, chip);
|
|
792
872
|
}
|
|
793
873
|
}
|
|
794
|
-
this.obstacles = getObstacleRects(this.inputProblem
|
|
795
|
-
|
|
874
|
+
this.obstacles = getObstacleRects(this.inputProblem, {
|
|
875
|
+
textBoxPadding: this.getTextBoxPaddingForConnectionPair()
|
|
876
|
+
});
|
|
877
|
+
this.textObstacles = new Set(
|
|
878
|
+
this.obstacles.filter((r) => r.kind === "text_box")
|
|
879
|
+
);
|
|
796
880
|
const [pin1, pin2] = this.pins;
|
|
797
881
|
this.baseElbow = calculateElbow(
|
|
798
882
|
{
|
|
@@ -811,16 +895,84 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
811
895
|
{ x: pin1.x, y: pin1.y },
|
|
812
896
|
{ x: pin2.x, y: pin2.y }
|
|
813
897
|
);
|
|
814
|
-
this.queue.push({ path: this.baseElbow,
|
|
898
|
+
this.queue.push({ path: this.baseElbow, collisionRects: /* @__PURE__ */ new Set() });
|
|
815
899
|
this.visited.add(pathKey(this.baseElbow));
|
|
816
900
|
}
|
|
817
901
|
getConstructorParams() {
|
|
818
902
|
return {
|
|
819
903
|
chipMap: this.chipMap,
|
|
820
904
|
pins: this.pins,
|
|
905
|
+
connectionPair: this.connectionPair,
|
|
821
906
|
inputProblem: this.inputProblem
|
|
822
907
|
};
|
|
823
908
|
}
|
|
909
|
+
getTextBoxPaddingForConnectionPair() {
|
|
910
|
+
if (!this.inputProblem.textBoxes?.length) return {};
|
|
911
|
+
const netId = this.connectionPair?.userNetId;
|
|
912
|
+
if (!netId) return {};
|
|
913
|
+
const orientations = this.inputProblem.availableNetLabelOrientations[netId] ?? ["x+", "x-", "y+", "y-"];
|
|
914
|
+
const netLabelWidth = this.getNetLabelWidthForConnectionPair(netId);
|
|
915
|
+
const netLabelHeight = this.getNetLabelHeightForConnectionPair(netId);
|
|
916
|
+
const padding = {
|
|
917
|
+
minX: 0,
|
|
918
|
+
minY: 0,
|
|
919
|
+
maxX: 0,
|
|
920
|
+
maxY: 0
|
|
921
|
+
};
|
|
922
|
+
for (const orientation of orientations) {
|
|
923
|
+
const { width, height } = getDimsForOrientation({
|
|
924
|
+
orientation,
|
|
925
|
+
netLabelWidth,
|
|
926
|
+
netLabelHeight
|
|
927
|
+
});
|
|
928
|
+
if (orientation === "y+" || orientation === "y-") {
|
|
929
|
+
padding.minX = Math.max(padding.minX, width / 2);
|
|
930
|
+
padding.maxX = Math.max(padding.maxX, width / 2);
|
|
931
|
+
if (orientation === "y+") {
|
|
932
|
+
padding.minY = Math.max(padding.minY, height);
|
|
933
|
+
} else {
|
|
934
|
+
padding.maxY = Math.max(padding.maxY, height);
|
|
935
|
+
}
|
|
936
|
+
} else {
|
|
937
|
+
padding.minY = Math.max(padding.minY, height / 2);
|
|
938
|
+
padding.maxY = Math.max(padding.maxY, height / 2);
|
|
939
|
+
if (orientation === "x+") {
|
|
940
|
+
padding.minX = Math.max(padding.minX, width);
|
|
941
|
+
} else {
|
|
942
|
+
padding.maxX = Math.max(padding.maxX, width);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return padding;
|
|
947
|
+
}
|
|
948
|
+
getNetLabelWidthForConnectionPair(netId) {
|
|
949
|
+
const ncWidth = this.inputProblem.netConnections.find(
|
|
950
|
+
(nc) => nc.netId === netId
|
|
951
|
+
)?.netLabelWidth;
|
|
952
|
+
if (ncWidth !== void 0) return ncWidth;
|
|
953
|
+
const dcWidthByNetId = this.inputProblem.directConnections.find(
|
|
954
|
+
(dc) => dc.netId === netId
|
|
955
|
+
)?.netLabelWidth;
|
|
956
|
+
if (dcWidthByNetId !== void 0) return dcWidthByNetId;
|
|
957
|
+
const pinIds = this.pins.map((p) => p.pinId);
|
|
958
|
+
const dcWidthByPinId = this.inputProblem.directConnections.find(
|
|
959
|
+
(dc) => dc.pinIds.some((pid) => pinIds.includes(pid))
|
|
960
|
+
)?.netLabelWidth;
|
|
961
|
+
if (dcWidthByPinId !== void 0) return dcWidthByPinId;
|
|
962
|
+
return this.inputProblem.netConnections.find(
|
|
963
|
+
(nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
|
|
964
|
+
)?.netLabelWidth;
|
|
965
|
+
}
|
|
966
|
+
getNetLabelHeightForConnectionPair(netId) {
|
|
967
|
+
const ncHeight = this.inputProblem.netConnections.find(
|
|
968
|
+
(nc) => nc.netId === netId
|
|
969
|
+
)?.netLabelHeight;
|
|
970
|
+
if (ncHeight !== void 0) return ncHeight;
|
|
971
|
+
const pinIds = this.pins.map((p) => p.pinId);
|
|
972
|
+
return this.inputProblem.netConnections.find(
|
|
973
|
+
(nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
|
|
974
|
+
)?.netLabelHeight;
|
|
975
|
+
}
|
|
824
976
|
axisOfSegment(a, b) {
|
|
825
977
|
if (isVertical(a, b)) return "x";
|
|
826
978
|
if (isHorizontal(a, b)) return "y";
|
|
@@ -844,9 +996,17 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
844
996
|
this.error = "No collision-free path found";
|
|
845
997
|
return;
|
|
846
998
|
}
|
|
847
|
-
const { path,
|
|
999
|
+
const { path, collisionRects } = state;
|
|
848
1000
|
const [PA, PB] = this.pins;
|
|
849
|
-
const collision = findFirstCollision(path, this.obstacles
|
|
1001
|
+
const collision = findFirstCollision(path, this.obstacles, {
|
|
1002
|
+
excludeRectsForSegment: (segIndex2) => {
|
|
1003
|
+
const lastSegIndex = path.length - 2;
|
|
1004
|
+
if (segIndex2 === 0 || segIndex2 === lastSegIndex) {
|
|
1005
|
+
return this.textObstacles;
|
|
1006
|
+
}
|
|
1007
|
+
return /* @__PURE__ */ new Set();
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
850
1010
|
if (!collision) {
|
|
851
1011
|
const first = path[0];
|
|
852
1012
|
const last = path[path.length - 1];
|
|
@@ -879,20 +1039,14 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
879
1039
|
return;
|
|
880
1040
|
}
|
|
881
1041
|
const candidates = [];
|
|
882
|
-
if (
|
|
1042
|
+
if (collisionRects.size === 0) {
|
|
883
1043
|
const m1 = midBetweenPointAndRect(axis, { x: PA.x, y: PA.y }, rect);
|
|
884
1044
|
const m2 = midBetweenPointAndRect(axis, { x: PB.x, y: PB.y }, rect);
|
|
885
1045
|
const allCandidates = [...m1, ...m2];
|
|
886
1046
|
const uniqueCandidates = [...new Set(allCandidates)];
|
|
887
1047
|
candidates.push(...uniqueCandidates);
|
|
888
1048
|
} else {
|
|
889
|
-
const mids = candidateMidsFromSet(
|
|
890
|
-
axis,
|
|
891
|
-
rect,
|
|
892
|
-
this.rectById,
|
|
893
|
-
collisionChipIds,
|
|
894
|
-
this.aabb
|
|
895
|
-
);
|
|
1049
|
+
const mids = candidateMidsFromSet(axis, rect, collisionRects, this.aabb);
|
|
896
1050
|
candidates.push(...mids);
|
|
897
1051
|
}
|
|
898
1052
|
const newStates = [];
|
|
@@ -902,14 +1056,14 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
902
1056
|
const key = pathKey(newPath);
|
|
903
1057
|
if (this.visited.has(key)) continue;
|
|
904
1058
|
this.visited.add(key);
|
|
905
|
-
const nextSet = new Set(
|
|
906
|
-
nextSet.add(rect
|
|
1059
|
+
const nextSet = new Set(collisionRects);
|
|
1060
|
+
nextSet.add(rect);
|
|
907
1061
|
const len = this.pathLength(newPath);
|
|
908
|
-
newStates.push({ path: newPath,
|
|
1062
|
+
newStates.push({ path: newPath, collisionRects: nextSet, len });
|
|
909
1063
|
}
|
|
910
1064
|
newStates.sort((a2, b2) => a2.len - b2.len);
|
|
911
1065
|
for (const st of newStates) {
|
|
912
|
-
this.queue.push({ path: st.path,
|
|
1066
|
+
this.queue.push({ path: st.path, collisionRects: st.collisionRects });
|
|
913
1067
|
}
|
|
914
1068
|
}
|
|
915
1069
|
visualize() {
|
|
@@ -931,7 +1085,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
931
1085
|
strokeColor: "blue",
|
|
932
1086
|
strokeDash: "5 5"
|
|
933
1087
|
});
|
|
934
|
-
for (const { path
|
|
1088
|
+
for (const { path } of this.queue) {
|
|
935
1089
|
g.lines.push({ points: path, strokeColor: "teal", strokeDash: "2 2" });
|
|
936
1090
|
}
|
|
937
1091
|
if (this.solvedTracePath) {
|
|
@@ -1013,6 +1167,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
|
|
|
1013
1167
|
this.activeSubSolver = new SchematicTraceSingleLineSolver2({
|
|
1014
1168
|
inputProblem: this.inputProblem,
|
|
1015
1169
|
pins,
|
|
1170
|
+
connectionPair,
|
|
1016
1171
|
chipMap: this.chipMap
|
|
1017
1172
|
});
|
|
1018
1173
|
}
|
|
@@ -1628,46 +1783,6 @@ var ChipObstacleSpatialIndex = class {
|
|
|
1628
1783
|
}
|
|
1629
1784
|
};
|
|
1630
1785
|
|
|
1631
|
-
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
1632
|
-
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
1633
|
-
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
1634
|
-
function getDimsForOrientation(params) {
|
|
1635
|
-
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
1636
|
-
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
1637
|
-
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
1638
|
-
if (orientation === "y+" || orientation === "y-") {
|
|
1639
|
-
return {
|
|
1640
|
-
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
1641
|
-
width: horizHeight,
|
|
1642
|
-
height: horizWidth
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
return {
|
|
1646
|
-
width: horizWidth,
|
|
1647
|
-
height: horizHeight
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
1651
|
-
switch (orientation) {
|
|
1652
|
-
case "x+":
|
|
1653
|
-
return { x: anchor.x + width / 2, y: anchor.y };
|
|
1654
|
-
case "x-":
|
|
1655
|
-
return { x: anchor.x - width / 2, y: anchor.y };
|
|
1656
|
-
case "y+":
|
|
1657
|
-
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
1658
|
-
case "y-":
|
|
1659
|
-
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
function getRectBounds(center, w, h) {
|
|
1663
|
-
return {
|
|
1664
|
-
minX: center.x - w / 2,
|
|
1665
|
-
minY: center.y - h / 2,
|
|
1666
|
-
maxX: center.x + w / 2,
|
|
1667
|
-
maxY: center.y + h / 2
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
1786
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
1672
1787
|
function segmentIntersectsRect2(p1, p2, rect, EPS11 = 1e-9) {
|
|
1673
1788
|
const isVert = Math.abs(p1.x - p2.x) < EPS11;
|
|
@@ -1833,6 +1948,19 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
|
|
|
1833
1948
|
});
|
|
1834
1949
|
continue;
|
|
1835
1950
|
}
|
|
1951
|
+
if (rectIntersectsAnyTextBox(bounds, inputProblem)) {
|
|
1952
|
+
testedCandidates.push({
|
|
1953
|
+
center,
|
|
1954
|
+
width: width2,
|
|
1955
|
+
height: height2,
|
|
1956
|
+
bounds,
|
|
1957
|
+
anchor,
|
|
1958
|
+
orientation,
|
|
1959
|
+
status: "text-collision",
|
|
1960
|
+
hostSegIndex: -1
|
|
1961
|
+
});
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1836
1964
|
const traceIntersectionResult = rectIntersectsAnyTrace(
|
|
1837
1965
|
bounds,
|
|
1838
1966
|
inputTraceMap,
|
|
@@ -1929,9 +2057,9 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
|
1929
2057
|
});
|
|
1930
2058
|
}
|
|
1931
2059
|
for (const c of solver.testedCandidates) {
|
|
1932
|
-
const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : "rgba(120, 120, 120, 0.15)";
|
|
1933
|
-
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
|
|
1934
|
-
const candidateLabel = c.status === "ok" ? "status: ok(valid net label candidate)" : c.status === "chip-collision" ? "status: chip-collision" : c.status === "trace-collision" ? "status: trace-collision" : "status: parallel-to-segment";
|
|
2060
|
+
const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : c.status === "text-collision" ? "rgba(160, 0, 220, 0.2)" : "rgba(120, 120, 120, 0.15)";
|
|
2061
|
+
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : c.status === "text-collision" ? "purple" : "gray";
|
|
2062
|
+
const candidateLabel = c.status === "ok" ? "status: ok(valid net label candidate)" : c.status === "chip-collision" ? "status: chip-collision" : c.status === "trace-collision" ? "status: trace-collision" : c.status === "text-collision" ? "status: text-collision" : "status: parallel-to-segment";
|
|
1935
2063
|
graphics.rects.push({
|
|
1936
2064
|
center: {
|
|
1937
2065
|
x: (c.bounds.minX + c.bounds.maxX) / 2,
|
|
@@ -2119,6 +2247,19 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2119
2247
|
});
|
|
2120
2248
|
continue;
|
|
2121
2249
|
}
|
|
2250
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
2251
|
+
this.testedCandidates.push({
|
|
2252
|
+
center: testCenter,
|
|
2253
|
+
width,
|
|
2254
|
+
height,
|
|
2255
|
+
bounds,
|
|
2256
|
+
anchor,
|
|
2257
|
+
orientation,
|
|
2258
|
+
status: "text-collision",
|
|
2259
|
+
hostSegIndex: si
|
|
2260
|
+
});
|
|
2261
|
+
continue;
|
|
2262
|
+
}
|
|
2122
2263
|
const traceIntersectionResult = rectIntersectsAnyTrace(
|
|
2123
2264
|
bounds,
|
|
2124
2265
|
this.inputTraceMap,
|
|
@@ -6466,6 +6607,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6466
6607
|
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
6467
6608
|
return "chip-collision";
|
|
6468
6609
|
}
|
|
6610
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
6611
|
+
return "text-collision";
|
|
6612
|
+
}
|
|
6469
6613
|
if (this.sharesChipBoundary(bounds)) {
|
|
6470
6614
|
return "chip-collision";
|
|
6471
6615
|
}
|
|
@@ -6826,6 +6970,8 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
6826
6970
|
}
|
|
6827
6971
|
getCandidateStatus(bounds, labelIndex) {
|
|
6828
6972
|
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
6973
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
6974
|
+
return "text-collision";
|
|
6829
6975
|
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
6830
6976
|
return "trace-collision";
|
|
6831
6977
|
}
|
|
@@ -7583,6 +7729,8 @@ var TraceAnchoredNetLabelOverlapSolver = class extends BaseSolver {
|
|
|
7583
7729
|
getCandidateStatus(candidate, labelIndex) {
|
|
7584
7730
|
const bounds = getLabelBounds(candidate);
|
|
7585
7731
|
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
7732
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
7733
|
+
return "text-collision";
|
|
7586
7734
|
if (traceCrossesBoundsInterior2(bounds, this.traces))
|
|
7587
7735
|
return "trace-collision";
|
|
7588
7736
|
if (this.intersectsAnyOtherLabel(bounds, labelIndex)) {
|
|
@@ -7886,15 +8034,17 @@ var CANDIDATE_STATUS_COLOR = {
|
|
|
7886
8034
|
ok: "green",
|
|
7887
8035
|
"label-collision": "orange",
|
|
7888
8036
|
"trace-collision": "darkorange",
|
|
7889
|
-
"chip-collision": "red"
|
|
8037
|
+
"chip-collision": "red",
|
|
8038
|
+
"text-collision": "purple"
|
|
7890
8039
|
};
|
|
7891
8040
|
var CANDIDATE_STATUS_FILL = {
|
|
7892
8041
|
ok: "rgba(0, 200, 0, 0.25)",
|
|
7893
8042
|
"label-collision": "rgba(255, 160, 0, 0.2)",
|
|
7894
8043
|
"trace-collision": "rgba(200, 80, 0, 0.2)",
|
|
7895
|
-
"chip-collision": "rgba(220, 0, 0, 0.15)"
|
|
8044
|
+
"chip-collision": "rgba(220, 0, 0, 0.15)",
|
|
8045
|
+
"text-collision": "rgba(128, 0, 128, 0.15)"
|
|
7896
8046
|
};
|
|
7897
|
-
function
|
|
8047
|
+
function boundsOverlap2(a, b) {
|
|
7898
8048
|
return a.minX < b.maxX - 1e-9 && a.maxX > b.minX + 1e-9 && a.minY < b.maxY - 1e-9 && a.maxY > b.minY + 1e-9;
|
|
7899
8049
|
}
|
|
7900
8050
|
function sampleAnchorsAlongSegment(a, b) {
|
|
@@ -7958,7 +8108,7 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
7958
8108
|
const b = labels[j];
|
|
7959
8109
|
if (a.globalConnNetId === b.globalConnNetId) continue;
|
|
7960
8110
|
if (this.skippedCollisionKeys.has(this.collisionKey(a, b))) continue;
|
|
7961
|
-
if (
|
|
8111
|
+
if (boundsOverlap2(this.labelBounds(a), this.labelBounds(b)))
|
|
7962
8112
|
return [a, b];
|
|
7963
8113
|
}
|
|
7964
8114
|
}
|
|
@@ -8050,12 +8200,14 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
8050
8200
|
const { bounds, hostPairId, hostSegIndex } = candidate;
|
|
8051
8201
|
if (this.chipIndex.getChipsInBounds(bounds).length > 0)
|
|
8052
8202
|
return "chip-collision";
|
|
8203
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
8204
|
+
return "text-collision";
|
|
8053
8205
|
if (rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex).hasIntersection) {
|
|
8054
8206
|
return "trace-collision";
|
|
8055
8207
|
}
|
|
8056
8208
|
for (const obstacle of obstacleLabels) {
|
|
8057
8209
|
if (obstacle.globalConnNetId === movingLabelNetId) continue;
|
|
8058
|
-
if (
|
|
8210
|
+
if (boundsOverlap2(bounds, this.labelBounds(obstacle)))
|
|
8059
8211
|
return "label-collision";
|
|
8060
8212
|
}
|
|
8061
8213
|
return "ok";
|
|
@@ -36,6 +36,7 @@ import type {
|
|
|
36
36
|
EvaluatedCandidate,
|
|
37
37
|
} from "./types"
|
|
38
38
|
import { visualizeAvailableNetOrientationSolver } from "./visualize"
|
|
39
|
+
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
39
40
|
|
|
40
41
|
export class AvailableNetOrientationSolver extends BaseSolver {
|
|
41
42
|
inputProblem: InputProblem
|
|
@@ -643,6 +644,9 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
643
644
|
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
644
645
|
return "chip-collision"
|
|
645
646
|
}
|
|
647
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
648
|
+
return "text-collision"
|
|
649
|
+
}
|
|
646
650
|
if (this.sharesChipBoundary(bounds)) {
|
|
647
651
|
return "chip-collision"
|
|
648
652
|
}
|
|
@@ -14,10 +14,12 @@ import { rectIntersectsAnyTrace } from "lib/solvers/NetLabelPlacementSolver/Sing
|
|
|
14
14
|
import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"
|
|
15
15
|
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
16
16
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
17
|
+
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
17
18
|
|
|
18
19
|
type CandidateStatus =
|
|
19
20
|
| "ok"
|
|
20
21
|
| "chip-collision"
|
|
22
|
+
| "text-collision"
|
|
21
23
|
| "trace-collision"
|
|
22
24
|
| "label-collision"
|
|
23
25
|
|
|
@@ -37,6 +39,7 @@ const CANDIDATE_STATUS_COLOR: Record<CandidateStatus, string> = {
|
|
|
37
39
|
"label-collision": "orange",
|
|
38
40
|
"trace-collision": "darkorange",
|
|
39
41
|
"chip-collision": "red",
|
|
42
|
+
"text-collision": "purple",
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const CANDIDATE_STATUS_FILL: Record<CandidateStatus, string> = {
|
|
@@ -44,6 +47,7 @@ const CANDIDATE_STATUS_FILL: Record<CandidateStatus, string> = {
|
|
|
44
47
|
"label-collision": "rgba(255, 160, 0, 0.2)",
|
|
45
48
|
"trace-collision": "rgba(200, 80, 0, 0.2)",
|
|
46
49
|
"chip-collision": "rgba(220, 0, 0, 0.15)",
|
|
50
|
+
"text-collision": "rgba(128, 0, 128, 0.15)",
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
type Candidate = {
|
|
@@ -269,6 +273,8 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
|
|
|
269
273
|
|
|
270
274
|
if (this.chipIndex.getChipsInBounds(bounds).length > 0)
|
|
271
275
|
return "chip-collision"
|
|
276
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
277
|
+
return "text-collision"
|
|
272
278
|
|
|
273
279
|
if (
|
|
274
280
|
rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex)
|