@tscircuit/schematic-trace-solver 0.0.77 → 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 +216 -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/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 +12 -12
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
|
@@ -591,13 +591,41 @@ function getInputChipBounds(chip) {
|
|
|
591
591
|
};
|
|
592
592
|
}
|
|
593
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
|
+
|
|
594
613
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect.ts
|
|
595
614
|
var chipToRect = (chip) => {
|
|
596
615
|
const b = getInputChipBounds(chip);
|
|
597
|
-
return { chipId: chip.chipId, ...b };
|
|
616
|
+
return { kind: "chip", chipId: chip.chipId, ...b };
|
|
598
617
|
};
|
|
599
|
-
var getObstacleRects = (problem) => {
|
|
600
|
-
|
|
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];
|
|
601
629
|
};
|
|
602
630
|
|
|
603
631
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts
|
|
@@ -628,9 +656,9 @@ var findFirstCollision = (pts, rects, opts = {}) => {
|
|
|
628
656
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
629
657
|
const a = pts[i];
|
|
630
658
|
const b = pts[i + 1];
|
|
631
|
-
const excluded = opts.
|
|
659
|
+
const excluded = opts.excludeRectsForSegment?.(i) ?? /* @__PURE__ */ new Set();
|
|
632
660
|
for (const r of rects) {
|
|
633
|
-
if (excluded.has(r
|
|
661
|
+
if (excluded.has(r)) continue;
|
|
634
662
|
if (segmentIntersectsRect(a, b, r)) {
|
|
635
663
|
return { segIndex: i, rect: r };
|
|
636
664
|
}
|
|
@@ -676,8 +704,8 @@ var midBetweenPointAndRect = (axis, p, r, eps = EPS2) => {
|
|
|
676
704
|
return [r.minY - 0.2, r.maxY + 0.2];
|
|
677
705
|
}
|
|
678
706
|
};
|
|
679
|
-
var candidateMidsFromSet = (axis, colliding,
|
|
680
|
-
const setRects = [...
|
|
707
|
+
var candidateMidsFromSet = (axis, colliding, collisionRects, aabb, eps = EPS2) => {
|
|
708
|
+
const setRects = [...collisionRects];
|
|
681
709
|
if (axis === "x") {
|
|
682
710
|
const leftBoundaries = [aabb.minX, ...setRects.map((r) => r.maxX)].filter(
|
|
683
711
|
(v) => v < colliding.minX - eps
|
|
@@ -778,13 +806,54 @@ var pathKey = (pts, decimals = 6) => {
|
|
|
778
806
|
return key;
|
|
779
807
|
};
|
|
780
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
|
+
|
|
781
849
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
782
850
|
var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
783
851
|
pins;
|
|
852
|
+
connectionPair;
|
|
784
853
|
inputProblem;
|
|
785
854
|
chipMap;
|
|
786
855
|
obstacles;
|
|
787
|
-
|
|
856
|
+
textObstacles;
|
|
788
857
|
aabb;
|
|
789
858
|
baseElbow;
|
|
790
859
|
solvedTracePath = null;
|
|
@@ -793,6 +862,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
793
862
|
constructor(params) {
|
|
794
863
|
super();
|
|
795
864
|
this.pins = params.pins;
|
|
865
|
+
this.connectionPair = params.connectionPair;
|
|
796
866
|
this.inputProblem = params.inputProblem;
|
|
797
867
|
this.chipMap = params.chipMap;
|
|
798
868
|
for (const pin of this.pins) {
|
|
@@ -801,8 +871,12 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
801
871
|
pin._facingDirection = getPinDirection(pin, chip);
|
|
802
872
|
}
|
|
803
873
|
}
|
|
804
|
-
this.obstacles = getObstacleRects(this.inputProblem
|
|
805
|
-
|
|
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
|
+
);
|
|
806
880
|
const [pin1, pin2] = this.pins;
|
|
807
881
|
this.baseElbow = calculateElbow(
|
|
808
882
|
{
|
|
@@ -821,16 +895,84 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
821
895
|
{ x: pin1.x, y: pin1.y },
|
|
822
896
|
{ x: pin2.x, y: pin2.y }
|
|
823
897
|
);
|
|
824
|
-
this.queue.push({ path: this.baseElbow,
|
|
898
|
+
this.queue.push({ path: this.baseElbow, collisionRects: /* @__PURE__ */ new Set() });
|
|
825
899
|
this.visited.add(pathKey(this.baseElbow));
|
|
826
900
|
}
|
|
827
901
|
getConstructorParams() {
|
|
828
902
|
return {
|
|
829
903
|
chipMap: this.chipMap,
|
|
830
904
|
pins: this.pins,
|
|
905
|
+
connectionPair: this.connectionPair,
|
|
831
906
|
inputProblem: this.inputProblem
|
|
832
907
|
};
|
|
833
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
|
+
}
|
|
834
976
|
axisOfSegment(a, b) {
|
|
835
977
|
if (isVertical(a, b)) return "x";
|
|
836
978
|
if (isHorizontal(a, b)) return "y";
|
|
@@ -854,9 +996,17 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
854
996
|
this.error = "No collision-free path found";
|
|
855
997
|
return;
|
|
856
998
|
}
|
|
857
|
-
const { path,
|
|
999
|
+
const { path, collisionRects } = state;
|
|
858
1000
|
const [PA, PB] = this.pins;
|
|
859
|
-
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
|
+
});
|
|
860
1010
|
if (!collision) {
|
|
861
1011
|
const first = path[0];
|
|
862
1012
|
const last = path[path.length - 1];
|
|
@@ -889,20 +1039,14 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
889
1039
|
return;
|
|
890
1040
|
}
|
|
891
1041
|
const candidates = [];
|
|
892
|
-
if (
|
|
1042
|
+
if (collisionRects.size === 0) {
|
|
893
1043
|
const m1 = midBetweenPointAndRect(axis, { x: PA.x, y: PA.y }, rect);
|
|
894
1044
|
const m2 = midBetweenPointAndRect(axis, { x: PB.x, y: PB.y }, rect);
|
|
895
1045
|
const allCandidates = [...m1, ...m2];
|
|
896
1046
|
const uniqueCandidates = [...new Set(allCandidates)];
|
|
897
1047
|
candidates.push(...uniqueCandidates);
|
|
898
1048
|
} else {
|
|
899
|
-
const mids = candidateMidsFromSet(
|
|
900
|
-
axis,
|
|
901
|
-
rect,
|
|
902
|
-
this.rectById,
|
|
903
|
-
collisionChipIds,
|
|
904
|
-
this.aabb
|
|
905
|
-
);
|
|
1049
|
+
const mids = candidateMidsFromSet(axis, rect, collisionRects, this.aabb);
|
|
906
1050
|
candidates.push(...mids);
|
|
907
1051
|
}
|
|
908
1052
|
const newStates = [];
|
|
@@ -912,14 +1056,14 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
912
1056
|
const key = pathKey(newPath);
|
|
913
1057
|
if (this.visited.has(key)) continue;
|
|
914
1058
|
this.visited.add(key);
|
|
915
|
-
const nextSet = new Set(
|
|
916
|
-
nextSet.add(rect
|
|
1059
|
+
const nextSet = new Set(collisionRects);
|
|
1060
|
+
nextSet.add(rect);
|
|
917
1061
|
const len = this.pathLength(newPath);
|
|
918
|
-
newStates.push({ path: newPath,
|
|
1062
|
+
newStates.push({ path: newPath, collisionRects: nextSet, len });
|
|
919
1063
|
}
|
|
920
1064
|
newStates.sort((a2, b2) => a2.len - b2.len);
|
|
921
1065
|
for (const st of newStates) {
|
|
922
|
-
this.queue.push({ path: st.path,
|
|
1066
|
+
this.queue.push({ path: st.path, collisionRects: st.collisionRects });
|
|
923
1067
|
}
|
|
924
1068
|
}
|
|
925
1069
|
visualize() {
|
|
@@ -941,7 +1085,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
|
|
|
941
1085
|
strokeColor: "blue",
|
|
942
1086
|
strokeDash: "5 5"
|
|
943
1087
|
});
|
|
944
|
-
for (const { path
|
|
1088
|
+
for (const { path } of this.queue) {
|
|
945
1089
|
g.lines.push({ points: path, strokeColor: "teal", strokeDash: "2 2" });
|
|
946
1090
|
}
|
|
947
1091
|
if (this.solvedTracePath) {
|
|
@@ -1023,6 +1167,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
|
|
|
1023
1167
|
this.activeSubSolver = new SchematicTraceSingleLineSolver2({
|
|
1024
1168
|
inputProblem: this.inputProblem,
|
|
1025
1169
|
pins,
|
|
1170
|
+
connectionPair,
|
|
1026
1171
|
chipMap: this.chipMap
|
|
1027
1172
|
});
|
|
1028
1173
|
}
|
|
@@ -1638,46 +1783,6 @@ var ChipObstacleSpatialIndex = class {
|
|
|
1638
1783
|
}
|
|
1639
1784
|
};
|
|
1640
1785
|
|
|
1641
|
-
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
1642
|
-
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
1643
|
-
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
1644
|
-
function getDimsForOrientation(params) {
|
|
1645
|
-
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
1646
|
-
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
1647
|
-
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
1648
|
-
if (orientation === "y+" || orientation === "y-") {
|
|
1649
|
-
return {
|
|
1650
|
-
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
1651
|
-
width: horizHeight,
|
|
1652
|
-
height: horizWidth
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
return {
|
|
1656
|
-
width: horizWidth,
|
|
1657
|
-
height: horizHeight
|
|
1658
|
-
};
|
|
1659
|
-
}
|
|
1660
|
-
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
1661
|
-
switch (orientation) {
|
|
1662
|
-
case "x+":
|
|
1663
|
-
return { x: anchor.x + width / 2, y: anchor.y };
|
|
1664
|
-
case "x-":
|
|
1665
|
-
return { x: anchor.x - width / 2, y: anchor.y };
|
|
1666
|
-
case "y+":
|
|
1667
|
-
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
1668
|
-
case "y-":
|
|
1669
|
-
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
function getRectBounds(center, w, h) {
|
|
1673
|
-
return {
|
|
1674
|
-
minX: center.x - w / 2,
|
|
1675
|
-
minY: center.y - h / 2,
|
|
1676
|
-
maxX: center.x + w / 2,
|
|
1677
|
-
maxY: center.y + h / 2
|
|
1678
|
-
};
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
1786
|
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
1682
1787
|
function segmentIntersectsRect2(p1, p2, rect, EPS11 = 1e-9) {
|
|
1683
1788
|
const isVert = Math.abs(p1.x - p2.x) < EPS11;
|
|
@@ -1843,6 +1948,19 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
|
|
|
1843
1948
|
});
|
|
1844
1949
|
continue;
|
|
1845
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
|
+
}
|
|
1846
1964
|
const traceIntersectionResult = rectIntersectsAnyTrace(
|
|
1847
1965
|
bounds,
|
|
1848
1966
|
inputTraceMap,
|
|
@@ -1939,9 +2057,9 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
|
1939
2057
|
});
|
|
1940
2058
|
}
|
|
1941
2059
|
for (const c of solver.testedCandidates) {
|
|
1942
|
-
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)";
|
|
1943
|
-
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
|
|
1944
|
-
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";
|
|
1945
2063
|
graphics.rects.push({
|
|
1946
2064
|
center: {
|
|
1947
2065
|
x: (c.bounds.minX + c.bounds.maxX) / 2,
|
|
@@ -2129,6 +2247,19 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
2129
2247
|
});
|
|
2130
2248
|
continue;
|
|
2131
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
|
+
}
|
|
2132
2263
|
const traceIntersectionResult = rectIntersectsAnyTrace(
|
|
2133
2264
|
bounds,
|
|
2134
2265
|
this.inputTraceMap,
|
|
@@ -6476,6 +6607,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6476
6607
|
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
6477
6608
|
return "chip-collision";
|
|
6478
6609
|
}
|
|
6610
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
6611
|
+
return "text-collision";
|
|
6612
|
+
}
|
|
6479
6613
|
if (this.sharesChipBoundary(bounds)) {
|
|
6480
6614
|
return "chip-collision";
|
|
6481
6615
|
}
|
|
@@ -6836,6 +6970,8 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
6836
6970
|
}
|
|
6837
6971
|
getCandidateStatus(bounds, labelIndex) {
|
|
6838
6972
|
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
6973
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
6974
|
+
return "text-collision";
|
|
6839
6975
|
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
6840
6976
|
return "trace-collision";
|
|
6841
6977
|
}
|
|
@@ -7593,6 +7729,8 @@ var TraceAnchoredNetLabelOverlapSolver = class extends BaseSolver {
|
|
|
7593
7729
|
getCandidateStatus(candidate, labelIndex) {
|
|
7594
7730
|
const bounds = getLabelBounds(candidate);
|
|
7595
7731
|
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
7732
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
7733
|
+
return "text-collision";
|
|
7596
7734
|
if (traceCrossesBoundsInterior2(bounds, this.traces))
|
|
7597
7735
|
return "trace-collision";
|
|
7598
7736
|
if (this.intersectsAnyOtherLabel(bounds, labelIndex)) {
|
|
@@ -7896,15 +8034,17 @@ var CANDIDATE_STATUS_COLOR = {
|
|
|
7896
8034
|
ok: "green",
|
|
7897
8035
|
"label-collision": "orange",
|
|
7898
8036
|
"trace-collision": "darkorange",
|
|
7899
|
-
"chip-collision": "red"
|
|
8037
|
+
"chip-collision": "red",
|
|
8038
|
+
"text-collision": "purple"
|
|
7900
8039
|
};
|
|
7901
8040
|
var CANDIDATE_STATUS_FILL = {
|
|
7902
8041
|
ok: "rgba(0, 200, 0, 0.25)",
|
|
7903
8042
|
"label-collision": "rgba(255, 160, 0, 0.2)",
|
|
7904
8043
|
"trace-collision": "rgba(200, 80, 0, 0.2)",
|
|
7905
|
-
"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)"
|
|
7906
8046
|
};
|
|
7907
|
-
function
|
|
8047
|
+
function boundsOverlap2(a, b) {
|
|
7908
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;
|
|
7909
8049
|
}
|
|
7910
8050
|
function sampleAnchorsAlongSegment(a, b) {
|
|
@@ -7968,7 +8108,7 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
7968
8108
|
const b = labels[j];
|
|
7969
8109
|
if (a.globalConnNetId === b.globalConnNetId) continue;
|
|
7970
8110
|
if (this.skippedCollisionKeys.has(this.collisionKey(a, b))) continue;
|
|
7971
|
-
if (
|
|
8111
|
+
if (boundsOverlap2(this.labelBounds(a), this.labelBounds(b)))
|
|
7972
8112
|
return [a, b];
|
|
7973
8113
|
}
|
|
7974
8114
|
}
|
|
@@ -8060,12 +8200,14 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
|
8060
8200
|
const { bounds, hostPairId, hostSegIndex } = candidate;
|
|
8061
8201
|
if (this.chipIndex.getChipsInBounds(bounds).length > 0)
|
|
8062
8202
|
return "chip-collision";
|
|
8203
|
+
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
8204
|
+
return "text-collision";
|
|
8063
8205
|
if (rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex).hasIntersection) {
|
|
8064
8206
|
return "trace-collision";
|
|
8065
8207
|
}
|
|
8066
8208
|
for (const obstacle of obstacleLabels) {
|
|
8067
8209
|
if (obstacle.globalConnNetId === movingLabelNetId) continue;
|
|
8068
|
-
if (
|
|
8210
|
+
if (boundsOverlap2(bounds, this.labelBounds(obstacle)))
|
|
8069
8211
|
return "label-collision";
|
|
8070
8212
|
}
|
|
8071
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)
|