@tscircuit/schematic-trace-solver 0.0.29 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1494,10 +1494,11 @@ function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex)
1494
1494
  const pts = solved.tracePath;
1495
1495
  for (let i = 0; i < pts.length - 1; i++) {
1496
1496
  if (pairId === hostPathId && i === hostSegIndex) continue;
1497
- if (segmentIntersectsRect(pts[i], pts[i + 1], bounds)) return true;
1497
+ if (segmentIntersectsRect(pts[i], pts[i + 1], bounds))
1498
+ return { hasIntersection: true, mspPairId: pairId, segIndex: i };
1498
1499
  }
1499
1500
  }
1500
- return false;
1501
+ return { hasIntersection: false };
1501
1502
  }
1502
1503
 
1503
1504
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts
@@ -1582,14 +1583,16 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1582
1583
  };
1583
1584
  }
1584
1585
  let pin = null;
1586
+ let pinFacingDirection = null;
1585
1587
  for (const chip of inputProblem.chips) {
1586
1588
  const p = chip.pins.find((pp) => pp.pinId === pinId);
1587
1589
  if (p) {
1588
1590
  pin = { x: p.x, y: p.y };
1591
+ pinFacingDirection = p._facingDirection || getPinDirection(p, chip);
1589
1592
  break;
1590
1593
  }
1591
1594
  }
1592
- if (!pin) {
1595
+ if (!pin || !pinFacingDirection) {
1593
1596
  return {
1594
1597
  placement: null,
1595
1598
  testedCandidates: [],
@@ -1601,21 +1604,21 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1601
1604
  const outwardOf = (o) => o === "x+" ? { x: 1, y: 0 } : o === "x-" ? { x: -1, y: 0 } : o === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
1602
1605
  const testedCandidates = [];
1603
1606
  for (const orientation of orientations) {
1604
- const { width, height } = getDimsForOrientation(orientation);
1605
- const baseCenter = getCenterFromAnchor(anchor, orientation, width, height);
1606
- const outward = outwardOf(orientation);
1607
- const offset = 1e-3;
1607
+ const { width: width2, height: height2 } = getDimsForOrientation(orientation);
1608
+ const baseCenter2 = getCenterFromAnchor(anchor, orientation, width2, height2);
1609
+ const outward2 = outwardOf(orientation);
1610
+ const offset2 = 1e-3;
1608
1611
  const center = {
1609
- x: baseCenter.x + outward.x * offset,
1610
- y: baseCenter.y + outward.y * offset
1612
+ x: baseCenter2.x + outward2.x * offset2,
1613
+ y: baseCenter2.y + outward2.y * offset2
1611
1614
  };
1612
- const bounds = getRectBounds(center, width, height);
1615
+ const bounds = getRectBounds(center, width2, height2);
1613
1616
  const chips = chipObstacleSpatialIndex.getChipsInBounds(bounds);
1614
1617
  if (chips.length > 0) {
1615
1618
  testedCandidates.push({
1616
1619
  center,
1617
- width,
1618
- height,
1620
+ width: width2,
1621
+ height: height2,
1619
1622
  bounds,
1620
1623
  anchor,
1621
1624
  orientation,
@@ -1624,16 +1627,17 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1624
1627
  });
1625
1628
  continue;
1626
1629
  }
1627
- if (rectIntersectsAnyTrace(
1630
+ const traceIntersectionResult = rectIntersectsAnyTrace(
1628
1631
  bounds,
1629
1632
  inputTraceMap,
1630
1633
  "",
1631
1634
  -1
1632
- )) {
1635
+ );
1636
+ if (traceIntersectionResult.hasIntersection) {
1633
1637
  testedCandidates.push({
1634
1638
  center,
1635
- width,
1636
- height,
1639
+ width: width2,
1640
+ height: height2,
1637
1641
  bounds,
1638
1642
  anchor,
1639
1643
  orientation,
@@ -1644,8 +1648,8 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1644
1648
  }
1645
1649
  testedCandidates.push({
1646
1650
  center,
1647
- width,
1648
- height,
1651
+ width: width2,
1652
+ height: height2,
1649
1653
  bounds,
1650
1654
  anchor,
1651
1655
  orientation,
@@ -1660,17 +1664,39 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1660
1664
  pinIds: [pinId],
1661
1665
  orientation,
1662
1666
  anchorPoint: anchor,
1663
- width,
1664
- height,
1667
+ width: width2,
1668
+ height: height2,
1665
1669
  center
1666
1670
  };
1667
1671
  return { placement, testedCandidates };
1668
1672
  }
1669
- return {
1670
- placement: null,
1671
- testedCandidates,
1672
- error: "Could not place net label at port without collisions"
1673
+ const fallbackOrientation = pinFacingDirection;
1674
+ const { width, height } = getDimsForOrientation(fallbackOrientation);
1675
+ const baseCenter = getCenterFromAnchor(
1676
+ anchor,
1677
+ fallbackOrientation,
1678
+ width,
1679
+ height
1680
+ );
1681
+ const outward = outwardOf(fallbackOrientation);
1682
+ const offset = 1e-3;
1683
+ const fallbackCenter = {
1684
+ x: baseCenter.x + outward.x * offset,
1685
+ y: baseCenter.y + outward.y * offset
1686
+ };
1687
+ const fallbackPlacement = {
1688
+ globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
1689
+ dcConnNetId: void 0,
1690
+ netId: overlappingSameNetTraceGroup.netId,
1691
+ mspConnectionPairIds: [],
1692
+ pinIds: [pinId],
1693
+ orientation: fallbackOrientation,
1694
+ anchorPoint: anchor,
1695
+ width,
1696
+ height,
1697
+ center: fallbackCenter
1673
1698
  };
1699
+ return { placement: fallbackPlacement, testedCandidates };
1674
1700
  }
1675
1701
 
1676
1702
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts
@@ -1686,12 +1712,11 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
1686
1712
  const groupStroke = getColorFromString(groupId, 0.9);
1687
1713
  const groupFill = getColorFromString(groupId, 0.5);
1688
1714
  for (const trace of Object.values(solver.inputTraceMap)) {
1689
- if (trace.globalConnNetId !== groupId) continue;
1690
1715
  const isHost = host ? trace.mspPairId === host.mspPairId : false;
1691
1716
  graphics.lines.push({
1692
- points: trace.tracePath,
1693
- strokeColor: isHost ? groupStroke : groupFill,
1694
- strokeDash: isHost ? void 0 : "4 2"
1717
+ points: trace.tracePath
1718
+ // strokeColor: isHost ? groupStroke : groupFill,
1719
+ // strokeDash: isHost ? undefined : "4 2",
1695
1720
  });
1696
1721
  }
1697
1722
  for (const c of solver.testedCandidates) {
@@ -1863,12 +1888,13 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1863
1888
  });
1864
1889
  continue;
1865
1890
  }
1866
- if (rectIntersectsAnyTrace(
1891
+ const traceIntersectionResult = rectIntersectsAnyTrace(
1867
1892
  bounds,
1868
1893
  this.inputTraceMap,
1869
1894
  curr.mspPairId,
1870
1895
  si
1871
- )) {
1896
+ );
1897
+ if (traceIntersectionResult.hasIntersection) {
1872
1898
  this.testedCandidates.push({
1873
1899
  center: testCenter,
1874
1900
  width,
@@ -261,14 +261,13 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
261
261
  }
262
262
 
263
263
  // Trace collision check (ignore the host segment)
264
- if (
265
- rectIntersectsAnyTrace(
266
- bounds,
267
- this.inputTraceMap,
268
- curr.mspPairId,
269
- si,
270
- )
271
- ) {
264
+ const traceIntersectionResult = rectIntersectsAnyTrace(
265
+ bounds,
266
+ this.inputTraceMap,
267
+ curr.mspPairId,
268
+ si,
269
+ )
270
+ if (traceIntersectionResult.hasIntersection) {
272
271
  this.testedCandidates.push({
273
272
  center: testCenter,
274
273
  width,
@@ -21,12 +21,11 @@ export function visualizeSingleNetLabelPlacementSolver(
21
21
  const groupFill = getColorFromString(groupId, 0.5)
22
22
 
23
23
  for (const trace of Object.values(solver.inputTraceMap)) {
24
- if (trace.globalConnNetId !== groupId) continue
25
24
  const isHost = host ? trace.mspPairId === host.mspPairId : false
26
25
  graphics.lines!.push({
27
26
  points: trace.tracePath,
28
- strokeColor: isHost ? groupStroke : groupFill,
29
- strokeDash: isHost ? undefined : "4 2",
27
+ // strokeColor: isHost ? groupStroke : groupFill,
28
+ // strokeDash: isHost ? undefined : "4 2",
30
29
  } as any)
31
30
  }
32
31
 
@@ -33,13 +33,20 @@ export function rectIntersectsAnyTrace(
33
33
  inputTraceMap: Record<MspConnectionPairId, SolvedTracePath>,
34
34
  hostPathId?: MspConnectionPairId,
35
35
  hostSegIndex?: number,
36
- ): boolean {
36
+ ):
37
+ | {
38
+ hasIntersection: true
39
+ mspPairId: MspConnectionPairId
40
+ segIndex: number
41
+ }
42
+ | { hasIntersection: false } {
37
43
  for (const [pairId, solved] of Object.entries(inputTraceMap)) {
38
44
  const pts = solved.tracePath
39
45
  for (let i = 0; i < pts.length - 1; i++) {
40
46
  if (pairId === hostPathId && i === hostSegIndex) continue
41
- if (segmentIntersectsRect(pts[i]!, pts[i + 1]!, bounds)) return true
47
+ if (segmentIntersectsRect(pts[i]!, pts[i + 1]!, bounds))
48
+ return { hasIntersection: true, mspPairId: pairId, segIndex: i }
42
49
  }
43
50
  }
44
- return false
51
+ return { hasIntersection: false }
45
52
  }
@@ -3,6 +3,7 @@ import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/Ms
3
3
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
4
  import type { FacingDirection } from "lib/utils/dir"
5
5
  import { ChipObstacleSpatialIndex } from "lib/data-structures/ChipObstacleSpatialIndex"
6
+ import { getPinDirection } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
6
7
  import type {
7
8
  NetLabelPlacement,
8
9
  OverlappingSameNetTraceGroup,
@@ -51,16 +52,18 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
51
52
  }
52
53
  }
53
54
 
54
- // Find pin coordinates
55
+ // Find pin coordinates and facing direction
55
56
  let pin: { x: number; y: number } | null = null
57
+ let pinFacingDirection: FacingDirection | null = null
56
58
  for (const chip of inputProblem.chips) {
57
59
  const p = chip.pins.find((pp) => pp.pinId === pinId)
58
60
  if (p) {
59
61
  pin = { x: p.x, y: p.y }
62
+ pinFacingDirection = p._facingDirection || getPinDirection(p, chip)
60
63
  break
61
64
  }
62
65
  }
63
- if (!pin) {
66
+ if (!pin || !pinFacingDirection) {
64
67
  return {
65
68
  placement: null,
66
69
  testedCandidates: [],
@@ -123,14 +126,13 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
123
126
  }
124
127
 
125
128
  // Trace collision check
126
- if (
127
- rectIntersectsAnyTrace(
128
- bounds,
129
- inputTraceMap,
130
- "" as MspConnectionPairId,
131
- -1,
132
- )
133
- ) {
129
+ const traceIntersectionResult = rectIntersectsAnyTrace(
130
+ bounds,
131
+ inputTraceMap,
132
+ "" as MspConnectionPairId,
133
+ -1,
134
+ )
135
+ if (traceIntersectionResult.hasIntersection) {
134
136
  testedCandidates.push({
135
137
  center,
136
138
  width,
@@ -172,9 +174,34 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
172
174
  return { placement, testedCandidates }
173
175
  }
174
176
 
175
- return {
176
- placement: null,
177
- testedCandidates,
178
- error: "Could not place net label at port without collisions",
177
+ // If no valid placements found, return placement using pin's facing direction
178
+ const fallbackOrientation = pinFacingDirection
179
+ const { width, height } = getDimsForOrientation(fallbackOrientation)
180
+ const baseCenter = getCenterFromAnchor(
181
+ anchor,
182
+ fallbackOrientation,
183
+ width,
184
+ height,
185
+ )
186
+ const outward = outwardOf(fallbackOrientation)
187
+ const offset = 1e-3
188
+ const fallbackCenter = {
189
+ x: baseCenter.x + outward.x * offset,
190
+ y: baseCenter.y + outward.y * offset,
179
191
  }
192
+
193
+ const fallbackPlacement: NetLabelPlacement = {
194
+ globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
195
+ dcConnNetId: undefined,
196
+ netId: overlappingSameNetTraceGroup.netId,
197
+ mspConnectionPairIds: [],
198
+ pinIds: [pinId],
199
+ orientation: fallbackOrientation,
200
+ anchorPoint: anchor,
201
+ width,
202
+ height,
203
+ center: fallbackCenter,
204
+ }
205
+
206
+ return { placement: fallbackPlacement, testedCandidates }
180
207
  }
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.29",
4
+ "version": "0.0.30",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -1,7 +1,6 @@
1
1
  import { expect, test } from "bun:test"
2
2
  import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
3
  import { inputProblem } from "site/examples/example14.page"
4
- import "tests/fixtures/matcher"
5
4
 
6
5
  test.skip("example14 - should fail with net label placement collision error", () => {
7
6
  const solver = new SchematicTracePipelineSolver(inputProblem)
@@ -1,7 +1,6 @@
1
1
  import { expect, test } from "bun:test"
2
2
  import { SingleNetLabelPlacementSolver } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver"
3
3
  import { input } from "site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page"
4
- import "tests/fixtures/matcher"
5
4
 
6
5
  test("SingleNetLabelPlacementSolver01 issue reproduction (should pass)", () => {
7
6
  const solver = new SingleNetLabelPlacementSolver(input as any)
@@ -9,5 +8,5 @@ test("SingleNetLabelPlacementSolver01 issue reproduction (should pass)", () => {
9
8
  solver.solve()
10
9
 
11
10
  // TODO: Fix the test
12
- // expect(solver.solved).toBe(true)
11
+ expect(solver.solved).toBe(true)
13
12
  })