@tscircuit/schematic-trace-solver 0.0.79 → 0.0.81

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 CHANGED
@@ -694,7 +694,7 @@ type CandidateLabel = {
694
694
  width: number;
695
695
  height: number;
696
696
  };
697
- type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
697
+ type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
698
698
  type CandidatePhase = "rotate" | "shift" | "lateral-shift";
699
699
  type EvaluatedCandidate = CandidateLabel & {
700
700
  status: CandidateStatus$2;
@@ -758,6 +758,11 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
758
758
  private getNetLabelHeight;
759
759
  private getCandidateStatus;
760
760
  private getBoundsStatus;
761
+ private isTraceTooCloseToLabel;
762
+ private getLabelTraceClearanceBounds;
763
+ private shouldCheckTraceClearanceForLabel;
764
+ private hasRoutedLabelOnSameNet;
765
+ private isPortOnlyLabel;
761
766
  private intersectsAnyOtherNetLabel;
762
767
  private sharesChipBoundary;
763
768
  private getChipSideForPoint;
package/dist/index.js CHANGED
@@ -5314,6 +5314,33 @@ var segmentRunsAlongRectBoundary = (start, end, rect) => {
5314
5314
  return false;
5315
5315
  };
5316
5316
 
5317
+ // lib/solvers/Example28Solver/getMovedAnchorPointForReroute.ts
5318
+ var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
5319
+ const originalSegment = findSegmentContainingPoint(
5320
+ originalTracePath,
5321
+ anchorPoint
5322
+ );
5323
+ if (!originalSegment) return null;
5324
+ const preferredSegment = findPreferredReroutedSegment(
5325
+ reroutedTracePath,
5326
+ originalSegment.index,
5327
+ originalTracePath.length - 1,
5328
+ originalSegment.orientation,
5329
+ anchorPoint
5330
+ );
5331
+ if (!preferredSegment) {
5332
+ return projectPointToPath(anchorPoint, reroutedTracePath);
5333
+ }
5334
+ return projectPointToSegment(
5335
+ anchorPoint,
5336
+ preferredSegment.start,
5337
+ preferredSegment.end
5338
+ );
5339
+ };
5340
+
5341
+ // lib/solvers/Example28Solver/isLabelAttachedToTrace.ts
5342
+ var isLabelAttachedToTrace = (label, trace) => label.globalConnNetId === trace.globalConnNetId || label.mspConnectionPairIds.includes(trace.mspPairId);
5343
+
5317
5344
  // lib/solvers/Example28Solver/labelMovement.ts
5318
5345
  var moveAttachedLabelsToReroutedTrace = ({
5319
5346
  trace,
@@ -5341,29 +5368,6 @@ var moveAttachedLabelsToReroutedTrace = ({
5341
5368
  }
5342
5369
  };
5343
5370
  });
5344
- var isLabelAttachedToTrace = (label, trace) => label.globalConnNetId === trace.globalConnNetId || label.mspConnectionPairIds.includes(trace.mspPairId);
5345
- var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
5346
- const originalSegment = findSegmentContainingPoint(
5347
- originalTracePath,
5348
- anchorPoint
5349
- );
5350
- if (!originalSegment) return null;
5351
- const preferredSegment = findPreferredReroutedSegment(
5352
- reroutedTracePath,
5353
- originalSegment.index,
5354
- originalTracePath.length - 1,
5355
- originalSegment.orientation,
5356
- anchorPoint
5357
- );
5358
- if (!preferredSegment) {
5359
- return projectPointToPath(anchorPoint, reroutedTracePath);
5360
- }
5361
- return projectPointToSegment(
5362
- anchorPoint,
5363
- preferredSegment.start,
5364
- preferredSegment.end
5365
- );
5366
- };
5367
5371
 
5368
5372
  // lib/utils/dir.ts
5369
5373
  var dir = (facingDirection) => {
@@ -6163,6 +6167,7 @@ var ensureGraphicsArrays = (graphics) => {
6163
6167
  };
6164
6168
 
6165
6169
  // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
6170
+ var LABEL_TRACE_CLEARANCE = 0.1;
6166
6171
  var AvailableNetOrientationSolver = class extends BaseSolver {
6167
6172
  inputProblem;
6168
6173
  traces;
@@ -6576,10 +6581,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6576
6581
  }
6577
6582
  getCandidateStatus(params) {
6578
6583
  const { candidate, label, labelIndex, phase } = params;
6579
- const boundsStatus = this.getBoundsStatus(
6580
- getRectBounds(candidate.center, candidate.width, candidate.height),
6581
- labelIndex
6582
- );
6584
+ const boundsStatus = this.getBoundsStatus({
6585
+ bounds: getRectBounds(
6586
+ candidate.center,
6587
+ candidate.width,
6588
+ candidate.height
6589
+ ),
6590
+ labelIndex,
6591
+ label
6592
+ });
6583
6593
  if (boundsStatus !== "valid") return boundsStatus;
6584
6594
  const connectorTrace = this.getCandidateConnectorTrace(label, {
6585
6595
  anchorPoint: candidate.anchorPoint,
@@ -6603,7 +6613,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6603
6613
  }
6604
6614
  return "valid";
6605
6615
  }
6606
- getBoundsStatus(bounds, labelIndex) {
6616
+ getBoundsStatus(candidateBoundsCheck) {
6617
+ const { bounds, labelIndex, label } = candidateBoundsCheck;
6607
6618
  if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
6608
6619
  return "chip-collision";
6609
6620
  }
@@ -6616,11 +6627,49 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6616
6627
  if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
6617
6628
  return "trace-collision";
6618
6629
  }
6630
+ if (this.isTraceTooCloseToLabel(bounds, label)) {
6631
+ return "trace-clearance-violation";
6632
+ }
6619
6633
  if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
6620
6634
  return "netlabel-collision";
6621
6635
  }
6622
6636
  return "valid";
6623
6637
  }
6638
+ isTraceTooCloseToLabel(bounds, label) {
6639
+ if (!this.shouldCheckTraceClearanceForLabel(label)) return false;
6640
+ const clearanceBounds = this.getLabelTraceClearanceBounds(bounds);
6641
+ for (const trace of Object.values(this.traceMap)) {
6642
+ if (trace.globalConnNetId === label.globalConnNetId) continue;
6643
+ if (tracePathIntersectsBounds(trace.tracePath, clearanceBounds)) {
6644
+ return true;
6645
+ }
6646
+ }
6647
+ return false;
6648
+ }
6649
+ getLabelTraceClearanceBounds(bounds) {
6650
+ return {
6651
+ minX: bounds.minX - LABEL_TRACE_CLEARANCE,
6652
+ minY: bounds.minY - LABEL_TRACE_CLEARANCE,
6653
+ maxX: bounds.maxX + LABEL_TRACE_CLEARANCE,
6654
+ maxY: bounds.maxY + LABEL_TRACE_CLEARANCE
6655
+ };
6656
+ }
6657
+ shouldCheckTraceClearanceForLabel(label) {
6658
+ if (!this.isPortOnlyLabel(label)) return false;
6659
+ if (!this.hasRoutedLabelOnSameNet(label)) return false;
6660
+ const orientations = this.getAvailableOrientations(label);
6661
+ return orientations.length === 1;
6662
+ }
6663
+ hasRoutedLabelOnSameNet(label) {
6664
+ for (const otherLabel of this.outputNetLabelPlacements) {
6665
+ if (otherLabel.globalConnNetId !== label.globalConnNetId) continue;
6666
+ if (otherLabel.mspConnectionPairIds.length > 0) return true;
6667
+ }
6668
+ return false;
6669
+ }
6670
+ isPortOnlyLabel(label) {
6671
+ return label.mspConnectionPairIds.length === 0;
6672
+ }
6624
6673
  intersectsAnyOtherNetLabel(bounds, labelIndex) {
6625
6674
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
6626
6675
  if (i === labelIndex) continue;
@@ -38,6 +38,8 @@ import type {
38
38
  import { visualizeAvailableNetOrientationSolver } from "./visualize"
39
39
  import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
40
40
 
41
+ const LABEL_TRACE_CLEARANCE = 0.1
42
+
41
43
  export class AvailableNetOrientationSolver extends BaseSolver {
42
44
  inputProblem: InputProblem
43
45
  traces: SolvedTracePath[]
@@ -606,10 +608,15 @@ export class AvailableNetOrientationSolver extends BaseSolver {
606
608
  phase: CandidatePhase
607
609
  }) {
608
610
  const { candidate, label, labelIndex, phase } = params
609
- const boundsStatus = this.getBoundsStatus(
610
- getRectBounds(candidate.center, candidate.width, candidate.height),
611
+ const boundsStatus = this.getBoundsStatus({
612
+ bounds: getRectBounds(
613
+ candidate.center,
614
+ candidate.width,
615
+ candidate.height,
616
+ ),
611
617
  labelIndex,
612
- )
618
+ label,
619
+ })
613
620
  if (boundsStatus !== "valid") return boundsStatus
614
621
 
615
622
  const connectorTrace = this.getCandidateConnectorTrace(label, {
@@ -640,7 +647,13 @@ export class AvailableNetOrientationSolver extends BaseSolver {
640
647
  return "valid"
641
648
  }
642
649
 
643
- private getBoundsStatus(bounds: Bounds, labelIndex: number): CandidateStatus {
650
+ private getBoundsStatus(candidateBoundsCheck: {
651
+ bounds: Bounds
652
+ labelIndex: number
653
+ label: NetLabelPlacement
654
+ }): CandidateStatus {
655
+ const { bounds, labelIndex, label } = candidateBoundsCheck
656
+
644
657
  if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
645
658
  return "chip-collision"
646
659
  }
@@ -653,12 +666,59 @@ export class AvailableNetOrientationSolver extends BaseSolver {
653
666
  if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
654
667
  return "trace-collision"
655
668
  }
669
+ if (this.isTraceTooCloseToLabel(bounds, label)) {
670
+ return "trace-clearance-violation"
671
+ }
656
672
  if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
657
673
  return "netlabel-collision"
658
674
  }
659
675
  return "valid"
660
676
  }
661
677
 
678
+ private isTraceTooCloseToLabel(bounds: Bounds, label: NetLabelPlacement) {
679
+ if (!this.shouldCheckTraceClearanceForLabel(label)) return false
680
+
681
+ const clearanceBounds = this.getLabelTraceClearanceBounds(bounds)
682
+ for (const trace of Object.values(this.traceMap)) {
683
+ if (trace.globalConnNetId === label.globalConnNetId) continue
684
+ if (tracePathIntersectsBounds(trace.tracePath, clearanceBounds)) {
685
+ return true
686
+ }
687
+ }
688
+
689
+ return false
690
+ }
691
+
692
+ private getLabelTraceClearanceBounds(bounds: Bounds): Bounds {
693
+ return {
694
+ minX: bounds.minX - LABEL_TRACE_CLEARANCE,
695
+ minY: bounds.minY - LABEL_TRACE_CLEARANCE,
696
+ maxX: bounds.maxX + LABEL_TRACE_CLEARANCE,
697
+ maxY: bounds.maxY + LABEL_TRACE_CLEARANCE,
698
+ }
699
+ }
700
+
701
+ private shouldCheckTraceClearanceForLabel(label: NetLabelPlacement) {
702
+ if (!this.isPortOnlyLabel(label)) return false
703
+ if (!this.hasRoutedLabelOnSameNet(label)) return false
704
+
705
+ const orientations = this.getAvailableOrientations(label)
706
+ return orientations.length === 1
707
+ }
708
+
709
+ private hasRoutedLabelOnSameNet(label: NetLabelPlacement) {
710
+ for (const otherLabel of this.outputNetLabelPlacements) {
711
+ if (otherLabel.globalConnNetId !== label.globalConnNetId) continue
712
+ if (otherLabel.mspConnectionPairIds.length > 0) return true
713
+ }
714
+
715
+ return false
716
+ }
717
+
718
+ private isPortOnlyLabel(label: NetLabelPlacement) {
719
+ return label.mspConnectionPairIds.length === 0
720
+ }
721
+
662
722
  private intersectsAnyOtherNetLabel(bounds: Bounds, labelIndex: number) {
663
723
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
664
724
  if (i === labelIndex) continue
@@ -32,6 +32,7 @@ export type CandidateStatus =
32
32
  | "chip-collision"
33
33
  | "text-collision"
34
34
  | "trace-collision"
35
+ | "trace-clearance-violation"
35
36
  | "netlabel-collision"
36
37
 
37
38
  export type CandidatePhase = "rotate" | "shift" | "lateral-shift"
@@ -0,0 +1,37 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import {
3
+ findPreferredReroutedSegment,
4
+ findSegmentContainingPoint,
5
+ projectPointToPath,
6
+ projectPointToSegment,
7
+ } from "./geometry"
8
+
9
+ export const getMovedAnchorPointForReroute = (
10
+ anchorPoint: Point,
11
+ originalTracePath: Point[],
12
+ reroutedTracePath: Point[],
13
+ ) => {
14
+ const originalSegment = findSegmentContainingPoint(
15
+ originalTracePath,
16
+ anchorPoint,
17
+ )
18
+ if (!originalSegment) return null
19
+
20
+ const preferredSegment = findPreferredReroutedSegment(
21
+ reroutedTracePath,
22
+ originalSegment.index,
23
+ originalTracePath.length - 1,
24
+ originalSegment.orientation,
25
+ anchorPoint,
26
+ )
27
+
28
+ if (!preferredSegment) {
29
+ return projectPointToPath(anchorPoint, reroutedTracePath)
30
+ }
31
+
32
+ return projectPointToSegment(
33
+ anchorPoint,
34
+ preferredSegment.start,
35
+ preferredSegment.end,
36
+ )
37
+ }
@@ -0,0 +1,9 @@
1
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
+
4
+ export const isLabelAttachedToTrace = (
5
+ label: NetLabelPlacement,
6
+ trace: SolvedTracePath,
7
+ ) =>
8
+ label.globalConnNetId === trace.globalConnNetId ||
9
+ label.mspConnectionPairIds.includes(trace.mspPairId)
@@ -1,12 +1,8 @@
1
1
  import type { Point } from "@tscircuit/math-utils"
2
2
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
3
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
- import {
5
- findPreferredReroutedSegment,
6
- findSegmentContainingPoint,
7
- projectPointToPath,
8
- projectPointToSegment,
9
- } from "./geometry"
4
+ import { getMovedAnchorPointForReroute } from "./getMovedAnchorPointForReroute"
5
+ import { isLabelAttachedToTrace } from "./isLabelAttachedToTrace"
10
6
 
11
7
  export const moveAttachedLabelsToReroutedTrace = ({
12
8
  trace,
@@ -43,40 +39,3 @@ export const moveAttachedLabelsToReroutedTrace = ({
43
39
  },
44
40
  }
45
41
  })
46
-
47
- const isLabelAttachedToTrace = (
48
- label: NetLabelPlacement,
49
- trace: SolvedTracePath,
50
- ) =>
51
- label.globalConnNetId === trace.globalConnNetId ||
52
- label.mspConnectionPairIds.includes(trace.mspPairId)
53
-
54
- const getMovedAnchorPointForReroute = (
55
- anchorPoint: Point,
56
- originalTracePath: Point[],
57
- reroutedTracePath: Point[],
58
- ) => {
59
- const originalSegment = findSegmentContainingPoint(
60
- originalTracePath,
61
- anchorPoint,
62
- )
63
- if (!originalSegment) return null
64
-
65
- const preferredSegment = findPreferredReroutedSegment(
66
- reroutedTracePath,
67
- originalSegment.index,
68
- originalTracePath.length - 1,
69
- originalSegment.orientation,
70
- anchorPoint,
71
- )
72
-
73
- if (!preferredSegment) {
74
- return projectPointToPath(anchorPoint, reroutedTracePath)
75
- }
76
-
77
- return projectPointToSegment(
78
- anchorPoint,
79
- preferredSegment.start,
80
- preferredSegment.end,
81
- )
82
- }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.79",
4
+ "version": "0.0.81",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -86,7 +86,7 @@ orientation: x-" data-x="-1.485" data-y="-0.2" cx="256.11911623439005" cy="288.2
86
86
  </g>
87
87
  <g>
88
88
  <circle data-type="point" data-label="anchorPoint
89
- orientation: y-" data-x="-1.3860000000000001" data-y="-0.301" cx="261.4447646493756" cy="293.6945244956772" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
89
+ orientation: y-" data-x="-1.3860000000000001" data-y="-0.601" cx="261.4447646493756" cy="309.8328530259365" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
90
90
  </g>
91
91
  <g>
92
92
  <circle data-type="point" data-label="anchorPoint
@@ -218,10 +218,10 @@ orientation: x-" data-x="2.6749999999999994" data-y="-2.7362499999999996" cx="47
218
218
  <polyline data-points="-1.2850000000000001,0.20000000000000018 -1.866,0.20000000000000018 -1.866,-4.440892098500626e-16 -2.87,-4.440892098500626e-16 -2.87,0.19999999999999973" data-type="line" data-label="" points="266.8780019212296,266.74351585014404 235.62343900096062,266.74351585014404 235.62343900096062,277.5024015369836 181.61383285302594,277.5024015369836 181.61383285302594,266.74351585014404" fill="none" stroke="purple" stroke-width="1" />
219
219
  </g>
220
220
  <g>
221
- <polyline data-points="-1.2850000000000001,-0.2 -1.485,-0.2 -1.485,-0.20099999999999993 -2.166,-0.20099999999999993 -2.166,0.5 -1.485,0.5 -1.485,0.2 -1.2850000000000001,0.2" data-type="line" data-label="" points="266.8780019212296,288.26128722382316 256.11911623439005,288.26128722382316 256.11911623439005,288.3150816522574 219.48511047070127,288.3150816522574 219.48511047070127,250.60518731988466 256.11911623439005,250.60518731988466 256.11911623439005,266.74351585014404 266.8780019212296,266.74351585014404" fill="none" stroke="purple" stroke-width="1" />
221
+ <polyline data-points="-1.2850000000000001,-0.2 -1.485,-0.2 -1.485,-0.5 -2.166,-0.5 -2.166,0.5 -1.485,0.5 -1.485,0.2 -1.2850000000000001,0.2" data-type="line" data-label="" points="266.8780019212296,288.26128722382316 256.11911623439005,288.26128722382316 256.11911623439005,304.39961575408256 219.48511047070127,304.39961575408256 219.48511047070127,250.60518731988466 256.11911623439005,250.60518731988466 256.11911623439005,266.74351585014404 266.8780019212296,266.74351585014404" fill="none" stroke="purple" stroke-width="1" />
222
222
  </g>
223
223
  <g>
224
- <polyline data-points="-1.2850000000000001,0 -1.3860000000000001,0 -1.3860000000000001,-0.301" data-type="line" data-label="" points="266.8780019212296,277.5024015369836 261.4447646493756,277.5024015369836 261.4447646493756,293.6945244956772" fill="none" stroke="purple" stroke-width="1" />
224
+ <polyline data-points="-1.2850000000000001,0 -1.3860000000000001,0 -1.3860000000000001,-0.601" data-type="line" data-label="" points="266.8780019212296,277.5024015369836 261.4447646493756,277.5024015369836 261.4447646493756,309.8328530259365" fill="none" stroke="purple" stroke-width="1" />
225
225
  </g>
226
226
  <g>
227
227
  <rect data-type="rect" data-label="schematic_component_0" data-x="-2.71" data-y="0.7199999999999998" x="167.08933717579254" y="210.79731027857824" width="46.26320845341016" height="55.9462055715658" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018589285714285714" />
@@ -257,7 +257,7 @@ globalConnNetId: connectivity_net1" data-x="-1.725" data-y="-0.2" x="230.2977905
257
257
  </g>
258
258
  <g>
259
259
  <rect data-type="rect" data-label="netId: GND
260
- globalConnNetId: connectivity_net2" data-x="-1.3860000000000001" data-y="-0.5409999999999999" x="256.0653218059558" y="293.6945244956772" width="10.758885686839562" height="25.821325648414984" fill="#00000066" stroke="#000000" stroke-width="0.018589285714285714" />
260
+ globalConnNetId: connectivity_net2" data-x="-1.3860000000000001" data-y="-0.841" x="256.0653218059558" y="309.8328530259365" width="10.758885686839562" height="25.821325648414984" fill="#00000066" stroke="#000000" stroke-width="0.018589285714285714" />
261
261
  </g>
262
262
  <g>
263
263
  <rect data-type="rect" data-label="netId: GND
@@ -0,0 +1,219 @@
1
+ <svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="100%" height="100%" fill="white" />
3
+ <g>
4
+ <circle data-type="point" data-label="BT1.1
5
+ x+" data-x="-2.6" data-y="2.2" cx="84.13793103448276" cy="230.89655172413794" r="3" fill="hsl(114, 100%, 50%, 0.8)" />
6
+ </g>
7
+ <g>
8
+ <circle data-type="point" data-label="BT1.2
9
+ x+" data-x="-2.6" data-y="1.8" cx="84.13793103448276" cy="252.9655172413793" r="3" fill="hsl(115, 100%, 50%, 0.8)" />
10
+ </g>
11
+ <g>
12
+ <circle data-type="point" data-label="SW1.1
13
+ x-" data-x="-0.5" data-y="2" cx="200" cy="241.93103448275863" r="3" fill="hsl(104, 100%, 50%, 0.8)" />
14
+ </g>
15
+ <g>
16
+ <circle data-type="point" data-label="SW1.2
17
+ x+" data-x="0.5" data-y="2" cx="255.17241379310343" cy="241.93103448275863" r="3" fill="hsl(105, 100%, 50%, 0.8)" />
18
+ </g>
19
+ <g>
20
+ <circle data-type="point" data-label="R1.1
21
+ x-" data-x="2.45" data-y="1" cx="362.7586206896552" cy="297.1034482758621" r="3" fill="hsl(226, 100%, 50%, 0.8)" />
22
+ </g>
23
+ <g>
24
+ <circle data-type="point" data-label="R1.2
25
+ x+" data-x="3.55" data-y="1" cx="423.44827586206895" cy="297.1034482758621" r="3" fill="hsl(227, 100%, 50%, 0.8)" />
26
+ </g>
27
+ <g>
28
+ <circle data-type="point" data-label="R2.1
29
+ x-" data-x="2.45" data-y="-1" cx="362.7586206896552" cy="407.44827586206895" r="3" fill="hsl(107, 100%, 50%, 0.8)" />
30
+ </g>
31
+ <g>
32
+ <circle data-type="point" data-label="R2.2
33
+ x+" data-x="3.55" data-y="-1" cx="423.44827586206895" cy="407.44827586206895" r="3" fill="hsl(108, 100%, 50%, 0.8)" />
34
+ </g>
35
+ <g>
36
+ <circle data-type="point" data-label="D1.1
37
+ x-" data-x="5.45" data-y="1" cx="528.2758620689655" cy="297.1034482758621" r="3" fill="hsl(32, 100%, 50%, 0.8)" />
38
+ </g>
39
+ <g>
40
+ <circle data-type="point" data-label="D1.2
41
+ x+" data-x="6.55" data-y="1" cx="588.9655172413793" cy="297.1034482758621" r="3" fill="hsl(33, 100%, 50%, 0.8)" />
42
+ </g>
43
+ <g>
44
+ <circle data-type="point" data-label="D2.1
45
+ x-" data-x="5.45" data-y="-1" cx="528.2758620689655" cy="407.44827586206895" r="3" fill="hsl(273, 100%, 50%, 0.8)" />
46
+ </g>
47
+ <g>
48
+ <circle data-type="point" data-label="D2.2
49
+ x+" data-x="6.55" data-y="-1" cx="588.9655172413793" cy="407.44827586206895" r="3" fill="hsl(274, 100%, 50%, 0.8)" />
50
+ </g>
51
+ <g>
52
+ <circle data-type="point" data-label="anchorPoint
53
+ orientation: y+" data-x="-2.075" data-y="2.2" cx="113.10344827586206" cy="230.89655172413794" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
54
+ </g>
55
+ <g>
56
+ <circle data-type="point" data-label="anchorPoint
57
+ orientation: y-" data-x="-2.499" data-y="1.5990000000000002" cx="89.7103448275862" cy="264.0551724137931" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
58
+ </g>
59
+ <g>
60
+ <circle data-type="point" data-label="anchorPoint
61
+ orientation: y-" data-x="6.65" data-y="-1" cx="594.4827586206897" cy="407.44827586206895" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
62
+ </g>
63
+ <g>
64
+ <circle data-type="point" data-label="anchorPoint
65
+ orientation: y-" data-x="2.25" data-y="-1" cx="351.7241379310345" cy="407.44827586206895" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
66
+ </g>
67
+ <g>
68
+ <circle data-type="point" data-label="anchorPoint
69
+ orientation: y+" data-x="4.5" data-y="1" cx="475.8620689655172" cy="297.1034482758621" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
70
+ </g>
71
+ <g>
72
+ <circle data-type="point" data-label="anchorPoint
73
+ orientation: y+" data-x="4.5" data-y="-1" cx="475.8620689655172" cy="407.44827586206895" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
74
+ </g>
75
+ <g>
76
+ <polyline data-points="-2.6,2.2 -0.5,2" data-type="line" data-label="" points="84.13793103448276,230.89655172413794 200,241.93103448275863" fill="none" stroke="hsl(0, 100%, 50%, 0.8)" stroke-width="1" />
77
+ </g>
78
+ <g>
79
+ <polyline data-points="0.5,2 2.45,1" data-type="line" data-label="" points="255.17241379310343,241.93103448275863 362.7586206896552,297.1034482758621" fill="none" stroke="hsl(304, 100%, 50%, 0.8)" stroke-width="1" />
80
+ </g>
81
+ <g>
82
+ <polyline data-points="0.5,2 2.45,-1" data-type="line" data-label="" points="255.17241379310343,241.93103448275863 362.7586206896552,407.44827586206895" fill="none" stroke="hsl(304, 100%, 50%, 0.8)" stroke-width="1" />
83
+ </g>
84
+ <g>
85
+ <polyline data-points="3.55,1 5.45,1" data-type="line" data-label="" points="423.44827586206895,297.1034482758621 528.2758620689655,297.1034482758621" fill="none" stroke="hsl(312, 100%, 50%, 0.8)" stroke-width="1" />
86
+ </g>
87
+ <g>
88
+ <polyline data-points="3.55,-1 5.45,-1" data-type="line" data-label="" points="423.44827586206895,407.44827586206895 528.2758620689655,407.44827586206895" fill="none" stroke="hsl(320, 100%, 50%, 0.8)" stroke-width="1" />
89
+ </g>
90
+ <g>
91
+ <polyline data-points="-2.6,1.8 6.55,1" data-type="line" data-label="" points="84.13793103448276,252.9655172413793 588.9655172413793,297.1034482758621" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
92
+ </g>
93
+ <g>
94
+ <polyline data-points="-2.6,1.8 6.55,-1" data-type="line" data-label="" points="84.13793103448276,252.9655172413793 588.9655172413793,407.44827586206895" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
95
+ </g>
96
+ <g>
97
+ <polyline data-points="6.55,1 6.55,-1" data-type="line" data-label="" points="588.9655172413793,297.1034482758621 588.9655172413793,407.44827586206895" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
98
+ </g>
99
+ <g>
100
+ <polyline data-points="-2.6,2.2 -1.55,2.2 -1.55,2 -0.5,2" data-type="line" data-label="" points="84.13793103448276,230.89655172413794 142.06896551724137,230.89655172413794 142.06896551724137,241.93103448275863 200,241.93103448275863" fill="none" stroke="purple" stroke-width="1" />
101
+ </g>
102
+ <g>
103
+ <polyline data-points="2.45,-1 2.25,-1 2.25,1 2.45,1" data-type="line" data-label="" points="362.7586206896552,407.44827586206895 351.7241379310345,407.44827586206895 351.7241379310345,297.1034482758621 362.7586206896552,297.1034482758621" fill="none" stroke="purple" stroke-width="1" />
104
+ </g>
105
+ <g>
106
+ <polyline data-points="0.5,2 1.475,2 1.475,1 2.45,1" data-type="line" data-label="" points="255.17241379310343,241.93103448275863 308.9655172413793,241.93103448275863 308.9655172413793,297.1034482758621 362.7586206896552,297.1034482758621" fill="none" stroke="purple" stroke-width="1" />
107
+ </g>
108
+ <g>
109
+ <polyline data-points="3.55,1 5.45,1" data-type="line" data-label="" points="423.44827586206895,297.1034482758621 528.2758620689655,297.1034482758621" fill="none" stroke="purple" stroke-width="1" />
110
+ </g>
111
+ <g>
112
+ <polyline data-points="3.55,-1 5.45,-1" data-type="line" data-label="" points="423.44827586206895,407.44827586206895 528.2758620689655,407.44827586206895" fill="none" stroke="purple" stroke-width="1" />
113
+ </g>
114
+ <g>
115
+ <polyline data-points="6.55,-1 6.75,-1 6.75,1 6.55,1" data-type="line" data-label="" points="588.9655172413793,407.44827586206895 600,407.44827586206895 600,297.1034482758621 588.9655172413793,297.1034482758621" fill="none" stroke="purple" stroke-width="1" />
116
+ </g>
117
+ <g>
118
+ <polyline data-points="-2.6,1.8 -2.499,1.8 -2.499,1.5990000000000002" data-type="line" data-label="" points="84.13793103448276,252.9655172413793 89.7103448275862,252.9655172413793 89.7103448275862,264.0551724137931" fill="none" stroke="purple" stroke-width="1" />
119
+ </g>
120
+ <g>
121
+ <rect data-type="rect" data-label="BT1" data-x="-3" data-y="2" x="40" y="219.86206896551727" width="44.13793103448276" height="44.13793103448273" fill="hsl(199, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
122
+ </g>
123
+ <g>
124
+ <rect data-type="rect" data-label="SW1" data-x="0" data-y="2" x="200" y="219.86206896551727" width="55.17241379310343" height="44.13793103448273" fill="hsl(69, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
125
+ </g>
126
+ <g>
127
+ <rect data-type="rect" data-label="R1" data-x="3" data-y="1" x="362.7586206896552" y="286.3748772413793" width="60.68965517241378" height="21.45714206896554" fill="hsl(71, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
128
+ </g>
129
+ <g>
130
+ <rect data-type="rect" data-label="R2" data-x="3" data-y="-1" x="362.7586206896552" y="396.7197048275862" width="60.68965517241378" height="21.45714206896554" fill="hsl(72, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
131
+ </g>
132
+ <g>
133
+ <rect data-type="rect" data-label="D1" data-x="6" data-y="1" x="528.2758620689655" y="286.3748772413793" width="60.689655172413836" height="21.45714206896554" fill="hsl(357, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
134
+ </g>
135
+ <g>
136
+ <rect data-type="rect" data-label="D2" data-x="6" data-y="-1" x="528.2758620689655" y="396.7197048275862" width="60.689655172413836" height="21.45714206896554" fill="hsl(358, 100%, 50%, 0.8)" stroke="black" stroke-width="0.018125000000000002" />
137
+ </g>
138
+ <g>
139
+ <rect data-type="rect" data-label="netId: BT1.plus to SW1.in
140
+ globalConnNetId: connectivity_net0" data-x="-2.075" data-y="2.4250000000000003" x="107.5862068965517" y="206.06896551724137" width="11.034482758620712" height="24.82758620689657" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.018125000000000002" />
141
+ </g>
142
+ <g>
143
+ <rect data-type="rect" data-label="netId: GND
144
+ globalConnNetId: connectivity_net4" data-x="-2.499" data-y="1.3590000000000002" x="84.19310344827585" y="264.0551724137931" width="11.034482758620697" height="26.48275862068965" fill="#00000066" stroke="#000000" stroke-width="0.018125000000000002" />
145
+ </g>
146
+ <g>
147
+ <rect data-type="rect" data-label="netId: GND
148
+ globalConnNetId: connectivity_net4" data-x="6.65" data-y="-1.24" x="588.9655172413793" y="407.44827586206895" width="11.034482758620697" height="26.48275862068965" fill="#00000066" stroke="#000000" stroke-width="0.018125000000000002" />
149
+ </g>
150
+ <g>
151
+ <rect data-type="rect" data-label="netId: SW1.out to R2.left
152
+ globalConnNetId: connectivity_net1" data-x="2.25" data-y="-1.225" x="346.2068965517241" y="407.44827586206895" width="11.034482758620697" height="24.82758620689657" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.018125000000000002" />
153
+ </g>
154
+ <g>
155
+ <rect data-type="rect" data-label="netId: R1.right to D1.a
156
+ globalConnNetId: connectivity_net2" data-x="4.5" data-y="1.225" x="470.34482758620686" y="272.2758620689655" width="11.034482758620697" height="24.82758620689657" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.018125000000000002" />
157
+ </g>
158
+ <g>
159
+ <rect data-type="rect" data-label="netId: R2.right to D2.a
160
+ globalConnNetId: connectivity_net3" data-x="4.5" data-y="-0.775" x="470.34482758620686" y="382.62068965517244" width="11.034482758620697" height="24.827586206896513" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.018125000000000002" />
161
+ </g>
162
+ <g id="crosshair" style="display: none">
163
+ <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
164
+ <line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5" /><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text>
165
+ </g>
166
+ <script>
167
+ <![CDATA[
168
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
169
+ const svg = e.currentTarget;
170
+ const rect = svg.getBoundingClientRect();
171
+ const x = e.clientX - rect.left;
172
+ const y = e.clientY - rect.top;
173
+ const crosshair = svg.getElementById('crosshair');
174
+ const h = svg.getElementById('crosshair-h');
175
+ const v = svg.getElementById('crosshair-v');
176
+ const coords = svg.getElementById('coordinates');
177
+
178
+ crosshair.style.display = 'block';
179
+ h.setAttribute('x1', '0');
180
+ h.setAttribute('x2', '640');
181
+ h.setAttribute('y1', y);
182
+ h.setAttribute('y2', y);
183
+ v.setAttribute('x1', x);
184
+ v.setAttribute('x2', x);
185
+ v.setAttribute('y1', '0');
186
+ v.setAttribute('y2', '640');
187
+
188
+ // Calculate real coordinates using inverse transformation
189
+ const matrix = {
190
+ "a": 55.172413793103445,
191
+ "c": 0,
192
+ "e": 227.58620689655172,
193
+ "b": 0,
194
+ "d": -55.172413793103445,
195
+ "f": 352.2758620689655
196
+ };
197
+ // Manually invert and apply the affine transform
198
+ // Since we only use translate and scale, we can directly compute:
199
+ // x' = (x - tx) / sx
200
+ // y' = (y - ty) / sy
201
+ const sx = matrix.a;
202
+ const sy = matrix.d;
203
+ const tx = matrix.e;
204
+ const ty = matrix.f;
205
+ const realPoint = {
206
+ x: (x - tx) / sx,
207
+ y: (y - ty) / sy // Flip y back since we used negative scale
208
+ }
209
+
210
+ coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
211
+ coords.setAttribute('x', (x + 5).toString());
212
+ coords.setAttribute('y', (y - 5).toString());
213
+ });
214
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
215
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
216
+ });
217
+ ]]>
218
+ </script>
219
+ </svg>
@@ -0,0 +1,163 @@
1
+ {
2
+ "chips": [
3
+ {
4
+ "chipId": "BT1",
5
+ "center": {
6
+ "x": -3,
7
+ "y": 2
8
+ },
9
+ "width": 0.8,
10
+ "height": 0.8,
11
+ "pins": [
12
+ {
13
+ "pinId": "BT1.1",
14
+ "x": -2.6,
15
+ "y": 2.2
16
+ },
17
+ {
18
+ "pinId": "BT1.2",
19
+ "x": -2.6,
20
+ "y": 1.8
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "chipId": "SW1",
26
+ "center": {
27
+ "x": 0,
28
+ "y": 2
29
+ },
30
+ "width": 1,
31
+ "height": 0.8,
32
+ "pins": [
33
+ {
34
+ "pinId": "SW1.1",
35
+ "x": -0.5,
36
+ "y": 2
37
+ },
38
+ {
39
+ "pinId": "SW1.2",
40
+ "x": 0.5,
41
+ "y": 2
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "chipId": "R1",
47
+ "center": {
48
+ "x": 3,
49
+ "y": 1
50
+ },
51
+ "width": 1.06,
52
+ "height": 0.3889107,
53
+ "pins": [
54
+ {
55
+ "pinId": "R1.1",
56
+ "x": 2.45,
57
+ "y": 1
58
+ },
59
+ {
60
+ "pinId": "R1.2",
61
+ "x": 3.55,
62
+ "y": 1
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "chipId": "R2",
68
+ "center": {
69
+ "x": 3,
70
+ "y": -1
71
+ },
72
+ "width": 1.06,
73
+ "height": 0.3889107,
74
+ "pins": [
75
+ {
76
+ "pinId": "R2.1",
77
+ "x": 2.45,
78
+ "y": -1
79
+ },
80
+ {
81
+ "pinId": "R2.2",
82
+ "x": 3.55,
83
+ "y": -1
84
+ }
85
+ ]
86
+ },
87
+ {
88
+ "chipId": "D1",
89
+ "center": {
90
+ "x": 6,
91
+ "y": 1
92
+ },
93
+ "width": 1.06,
94
+ "height": 0.3889107,
95
+ "pins": [
96
+ {
97
+ "pinId": "D1.1",
98
+ "x": 5.45,
99
+ "y": 1
100
+ },
101
+ {
102
+ "pinId": "D1.2",
103
+ "x": 6.55,
104
+ "y": 1
105
+ }
106
+ ]
107
+ },
108
+ {
109
+ "chipId": "D2",
110
+ "center": {
111
+ "x": 6,
112
+ "y": -1
113
+ },
114
+ "width": 1.06,
115
+ "height": 0.3889107,
116
+ "pins": [
117
+ {
118
+ "pinId": "D2.1",
119
+ "x": 5.45,
120
+ "y": -1
121
+ },
122
+ {
123
+ "pinId": "D2.2",
124
+ "x": 6.55,
125
+ "y": -1
126
+ }
127
+ ]
128
+ }
129
+ ],
130
+ "directConnections": [
131
+ {
132
+ "pinIds": ["BT1.1", "SW1.1"],
133
+ "netId": "BT1.plus to SW1.in"
134
+ },
135
+ {
136
+ "pinIds": ["SW1.2", "R1.1"],
137
+ "netId": "SW1.out to R1.left"
138
+ },
139
+ {
140
+ "pinIds": ["SW1.2", "R2.1"],
141
+ "netId": "SW1.out to R2.left"
142
+ },
143
+ {
144
+ "pinIds": ["R1.2", "D1.1"],
145
+ "netId": "R1.right to D1.a"
146
+ },
147
+ {
148
+ "pinIds": ["R2.2", "D2.1"],
149
+ "netId": "R2.right to D2.a"
150
+ }
151
+ ],
152
+ "netConnections": [
153
+ {
154
+ "netId": "GND",
155
+ "pinIds": ["BT1.2", "D1.2", "D2.2"],
156
+ "netLabelWidth": 0.48
157
+ }
158
+ ],
159
+ "availableNetLabelOrientations": {
160
+ "GND": ["y-"]
161
+ },
162
+ "maxMspPairDistance": 8
163
+ }
@@ -0,0 +1,16 @@
1
+ import { expect, test } from "bun:test"
2
+ import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
+ import inputProblem from "./assets/bugreport-001-gnd-overlap.input.json"
4
+ import "tests/fixtures/matcher"
5
+
6
+ test("bugreport-001-gnd-overlap", () => {
7
+ const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
+ solver.solve()
9
+
10
+ expect(
11
+ solver.netLabelPlacementSolver!.netLabelPlacements.some(
12
+ (placement) => placement.netId === "GND",
13
+ ),
14
+ ).toBe(true)
15
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
16
+ })