@tscircuit/schematic-trace-solver 0.0.171 → 0.0.173

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
@@ -13307,6 +13307,33 @@ var placeGroundRailLabelsAtOuterEnd = ({
13307
13307
  }
13308
13308
  }
13309
13309
  }
13310
+ for (let index = 0; index < output.length; index++) {
13311
+ const label = output[index];
13312
+ if (label.orientation !== "y-") continue;
13313
+ const connection = inputProblem.netConnections.find(
13314
+ (candidate) => candidate.isGround && candidate.netId === label.netId && candidate.pinIds.length > 2
13315
+ );
13316
+ if (!connection) continue;
13317
+ const anchorPoint = traces.filter((trace) => trace.globalConnNetId === label.globalConnNetId).flatMap((trace) => trace.tracePath).filter(
13318
+ (point) => nearlyEqual(point.x, label.anchorPoint.x) && point.y < label.anchorPoint.y
13319
+ ).sort((a, b) => a.y - b.y)[0];
13320
+ if (!anchorPoint) continue;
13321
+ const center = getCenterFromAnchor(
13322
+ anchorPoint,
13323
+ label.orientation,
13324
+ label.width,
13325
+ label.height
13326
+ );
13327
+ const bounds = getRectBounds(center, label.width, label.height);
13328
+ if (obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) || traceCrossesBoundsInterior(bounds, traceMap) || output.some(
13329
+ (other, otherIndex) => otherIndex !== index && rectsOverlap(
13330
+ bounds,
13331
+ getRectBounds(other.center, other.width, other.height)
13332
+ )
13333
+ ))
13334
+ continue;
13335
+ output[index] = { ...label, anchorPoint, center };
13336
+ }
13310
13337
  return output;
13311
13338
  };
13312
13339
 
@@ -15625,24 +15652,42 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15625
15652
  }
15626
15653
  }
15627
15654
  }
15628
- candidates.push(...this.buildRoutedComponentCandidates());
15655
+ candidates.push(
15656
+ ...this.buildRoutedComponentCandidates(groundGlobalConnNetIds)
15657
+ );
15629
15658
  candidates.sort(
15630
15659
  (first, second) => first.perpendicularOffset - second.perpendicularOffset || first.routeDistance - second.routeDistance || first.key.localeCompare(second.key)
15631
15660
  );
15632
15661
  return candidates;
15633
15662
  }
15634
- buildRoutedComponentCandidates() {
15663
+ buildRoutedComponentCandidates(groundGlobalConnNetIds) {
15635
15664
  const { netConnMap } = getConnectivityMapsFromInputProblem(
15636
15665
  this.inputProblem
15637
15666
  );
15638
15667
  const candidates = [];
15639
- for (const connection of this.inputProblem.netConnections) {
15640
- if (connection.pinIds.length <= 2 || connection.isGround !== false)
15668
+ const directConnections = new Set(
15669
+ this.inputProblem.directConnections
15670
+ );
15671
+ for (const connection of [
15672
+ ...this.inputProblem.netConnections.filter(
15673
+ (connection2) => connection2.pinIds.length > 2 && connection2.isGround === false
15674
+ ),
15675
+ ...this.inputProblem.directConnections
15676
+ ]) {
15677
+ const isDirectConnection = directConnections.has(connection);
15678
+ const globalConnNetId = netConnMap.getNetConnectedToId(
15679
+ connection.pinIds[0]
15680
+ );
15681
+ if (!globalConnNetId || isDirectConnection && groundGlobalConnNetIds.has(globalConnNetId))
15641
15682
  continue;
15642
- const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId);
15643
- if (!globalConnNetId) continue;
15683
+ let connectionPinIds = connection.pinIds;
15684
+ if (isDirectConnection) {
15685
+ connectionPinIds = [...this.pinMap.keys()].filter(
15686
+ (pinId) => netConnMap.getNetConnectedToId(pinId) === globalConnNetId
15687
+ );
15688
+ }
15644
15689
  const traceConnectedPinComponents = getTraceConnectedPinComponents({
15645
- pinIds: connection.pinIds,
15690
+ pinIds: connectionPinIds,
15646
15691
  traces: this.outputTraces.filter(
15647
15692
  (trace) => trace.globalConnNetId === globalConnNetId
15648
15693
  )
@@ -15651,6 +15696,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15651
15696
  for (let secondIndex = firstIndex + 1; secondIndex < traceConnectedPinComponents.length; secondIndex++) {
15652
15697
  const firstComponent = traceConnectedPinComponents[firstIndex];
15653
15698
  const secondComponent = traceConnectedPinComponents[secondIndex];
15699
+ if (isDirectConnection && !(firstComponent.pinIds.includes(connection.pinIds[0]) && secondComponent.pinIds.includes(connection.pinIds[1]) || firstComponent.pinIds.includes(connection.pinIds[1]) && secondComponent.pinIds.includes(connection.pinIds[0])))
15700
+ continue;
15654
15701
  const firstLabel = this.input.netLabelPlacements.find(
15655
15702
  (label) => label.globalConnNetId === globalConnNetId && label.mspConnectionPairIds.length > 0 && label.pinIds.some(
15656
15703
  (pinId) => firstComponent.pinIds.includes(pinId)
@@ -15670,11 +15717,23 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15670
15717
  continue;
15671
15718
  }
15672
15719
  let bestCandidate;
15673
- for (const firstPinId of firstComponent.pinIds) {
15674
- for (const secondPinId of secondComponent.pinIds) {
15720
+ let firstPinIds = firstComponent.pinIds;
15721
+ let secondPinIds = secondComponent.pinIds;
15722
+ if (isDirectConnection) {
15723
+ firstPinIds = connection.pinIds.filter(
15724
+ (pinId) => firstComponent.pinIds.includes(pinId)
15725
+ );
15726
+ secondPinIds = connection.pinIds.filter(
15727
+ (pinId) => secondComponent.pinIds.includes(pinId)
15728
+ );
15729
+ }
15730
+ for (const firstPinId of firstPinIds) {
15731
+ for (const secondPinId of secondPinIds) {
15675
15732
  const firstPin = this.pinMap.get(firstPinId);
15676
15733
  const secondPin = this.pinMap.get(secondPinId);
15677
15734
  if (!firstPin || !secondPin) continue;
15735
+ if (isDirectConnection && firstPin.chipId === secondPin.chipId)
15736
+ continue;
15678
15737
  const perpendicularOffset = getPerpendicularOffset(
15679
15738
  firstPin,
15680
15739
  secondPin
@@ -15693,6 +15752,10 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15693
15752
  continue;
15694
15753
  }
15695
15754
  const routeDistance = Math.abs(firstPin.x - secondPin.x) + Math.abs(firstPin.y - secondPin.y);
15755
+ let recoveryMode = "routed_components";
15756
+ if (isDirectConnection) {
15757
+ recoveryMode = "routed_direct_connection";
15758
+ }
15696
15759
  const candidate = {
15697
15760
  firstLabel,
15698
15761
  secondLabel,
@@ -15700,8 +15763,8 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15700
15763
  perpendicularOffset,
15701
15764
  routeDistance,
15702
15765
  key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
15703
- recoveryMode: "routed_components",
15704
- netConnectionPinIds: connection.pinIds
15766
+ recoveryMode,
15767
+ netConnectionPinIds: connectionPinIds
15705
15768
  };
15706
15769
  if (!bestCandidate || candidate.routeDistance < bestCandidate.routeDistance || candidate.routeDistance === bestCandidate.routeDistance && (candidate.perpendicularOffset < bestCandidate.perpendicularOffset || candidate.perpendicularOffset === bestCandidate.perpendicularOffset && candidate.key.localeCompare(bestCandidate.key) < 0)) {
15707
15770
  bestCandidate = candidate;
@@ -15740,7 +15803,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15740
15803
  (trace) => !this.isSupersededConnectorTrace(trace, candidate)
15741
15804
  );
15742
15805
  let collisionTraces = retainedTraces;
15743
- if (candidate.recoveryMode === "routed_components") {
15806
+ if (candidate.recoveryMode !== "fallback_labels") {
15744
15807
  collisionTraces = retainedTraces.filter(
15745
15808
  (trace) => trace.globalConnNetId !== candidate.firstLabel.globalConnNetId
15746
15809
  );
@@ -15766,7 +15829,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
15766
15829
  pinIds: [firstPin.pinId, secondPin.pinId]
15767
15830
  };
15768
15831
  this.outputTraces = [...retainedTraces, recoveredTrace];
15769
- if (candidate.recoveryMode === "fallback_labels") {
15832
+ if (candidate.recoveryMode !== "routed_components") {
15770
15833
  this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
15771
15834
  (label) => label !== candidate.firstLabel && label !== candidate.secondLabel
15772
15835
  );
@@ -40,7 +40,10 @@ interface CandidatePair {
40
40
  perpendicularOffset: number
41
41
  routeDistance: number
42
42
  key: string
43
- recoveryMode: "fallback_labels" | "routed_components"
43
+ recoveryMode:
44
+ | "fallback_labels"
45
+ | "routed_components"
46
+ | "routed_direct_connection"
44
47
  netConnectionPinIds?: PinId[]
45
48
  }
46
49
 
@@ -260,7 +263,9 @@ export class NetLabelToTraceSolver extends BaseSolver {
260
263
  }
261
264
  }
262
265
 
263
- candidates.push(...this.buildRoutedComponentCandidates())
266
+ candidates.push(
267
+ ...this.buildRoutedComponentCandidates(groundGlobalConnNetIds),
268
+ )
264
269
 
265
270
  candidates.sort(
266
271
  (first, second) =>
@@ -271,20 +276,42 @@ export class NetLabelToTraceSolver extends BaseSolver {
271
276
  return candidates
272
277
  }
273
278
 
274
- private buildRoutedComponentCandidates() {
279
+ private buildRoutedComponentCandidates(
280
+ groundGlobalConnNetIds: Set<GlobalConnNetId>,
281
+ ) {
275
282
  const { netConnMap } = getConnectivityMapsFromInputProblem(
276
283
  this.inputProblem,
277
284
  )
278
285
  const candidates: CandidatePair[] = []
286
+ const directConnections = new Set<object>(
287
+ this.inputProblem.directConnections,
288
+ )
279
289
 
280
- for (const connection of this.inputProblem.netConnections) {
281
- if (connection.pinIds.length <= 2 || connection.isGround !== false)
290
+ for (const connection of [
291
+ ...this.inputProblem.netConnections.filter(
292
+ (connection) =>
293
+ connection.pinIds.length > 2 && connection.isGround === false,
294
+ ),
295
+ ...this.inputProblem.directConnections,
296
+ ]) {
297
+ const isDirectConnection = directConnections.has(connection)
298
+ const globalConnNetId = netConnMap.getNetConnectedToId(
299
+ connection.pinIds[0],
300
+ )
301
+ if (
302
+ !globalConnNetId ||
303
+ (isDirectConnection && groundGlobalConnNetIds.has(globalConnNetId))
304
+ )
282
305
  continue
283
- const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId)
284
- if (!globalConnNetId) continue
306
+ let connectionPinIds: PinId[] = connection.pinIds
307
+ if (isDirectConnection) {
308
+ connectionPinIds = [...this.pinMap.keys()].filter(
309
+ (pinId) => netConnMap.getNetConnectedToId(pinId) === globalConnNetId,
310
+ )
311
+ }
285
312
 
286
313
  const traceConnectedPinComponents = getTraceConnectedPinComponents({
287
- pinIds: connection.pinIds,
314
+ pinIds: connectionPinIds,
288
315
  traces: this.outputTraces.filter(
289
316
  (trace) => trace.globalConnNetId === globalConnNetId,
290
317
  ),
@@ -302,6 +329,16 @@ export class NetLabelToTraceSolver extends BaseSolver {
302
329
  ) {
303
330
  const firstComponent = traceConnectedPinComponents[firstIndex]!
304
331
  const secondComponent = traceConnectedPinComponents[secondIndex]!
332
+ if (
333
+ isDirectConnection &&
334
+ !(
335
+ (firstComponent.pinIds.includes(connection.pinIds[0]) &&
336
+ secondComponent.pinIds.includes(connection.pinIds[1])) ||
337
+ (firstComponent.pinIds.includes(connection.pinIds[1]) &&
338
+ secondComponent.pinIds.includes(connection.pinIds[0]))
339
+ )
340
+ )
341
+ continue
305
342
  const firstLabel = this.input.netLabelPlacements.find(
306
343
  (label) =>
307
344
  label.globalConnNetId === globalConnNetId &&
@@ -330,11 +367,23 @@ export class NetLabelToTraceSolver extends BaseSolver {
330
367
  }
331
368
 
332
369
  let bestCandidate: CandidatePair | undefined
333
- for (const firstPinId of firstComponent.pinIds) {
334
- for (const secondPinId of secondComponent.pinIds) {
370
+ let firstPinIds = firstComponent.pinIds
371
+ let secondPinIds = secondComponent.pinIds
372
+ if (isDirectConnection) {
373
+ firstPinIds = connection.pinIds.filter((pinId) =>
374
+ firstComponent.pinIds.includes(pinId),
375
+ )
376
+ secondPinIds = connection.pinIds.filter((pinId) =>
377
+ secondComponent.pinIds.includes(pinId),
378
+ )
379
+ }
380
+ for (const firstPinId of firstPinIds) {
381
+ for (const secondPinId of secondPinIds) {
335
382
  const firstPin = this.pinMap.get(firstPinId)
336
383
  const secondPin = this.pinMap.get(secondPinId)
337
384
  if (!firstPin || !secondPin) continue
385
+ if (isDirectConnection && firstPin.chipId === secondPin.chipId)
386
+ continue
338
387
  const perpendicularOffset = getPerpendicularOffset(
339
388
  firstPin,
340
389
  secondPin,
@@ -362,6 +411,11 @@ export class NetLabelToTraceSolver extends BaseSolver {
362
411
  const routeDistance =
363
412
  Math.abs(firstPin.x - secondPin.x) +
364
413
  Math.abs(firstPin.y - secondPin.y)
414
+ let recoveryMode: CandidatePair["recoveryMode"] =
415
+ "routed_components"
416
+ if (isDirectConnection) {
417
+ recoveryMode = "routed_direct_connection"
418
+ }
365
419
  const candidate: CandidatePair = {
366
420
  firstLabel,
367
421
  secondLabel,
@@ -369,8 +423,8 @@ export class NetLabelToTraceSolver extends BaseSolver {
369
423
  perpendicularOffset,
370
424
  routeDistance,
371
425
  key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
372
- recoveryMode: "routed_components",
373
- netConnectionPinIds: connection.pinIds,
426
+ recoveryMode,
427
+ netConnectionPinIds: connectionPinIds,
374
428
  }
375
429
  if (
376
430
  !bestCandidate ||
@@ -429,7 +483,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
429
483
  (trace) => !this.isSupersededConnectorTrace(trace, candidate),
430
484
  )
431
485
  let collisionTraces = retainedTraces
432
- if (candidate.recoveryMode === "routed_components") {
486
+ if (candidate.recoveryMode !== "fallback_labels") {
433
487
  collisionTraces = retainedTraces.filter(
434
488
  (trace) =>
435
489
  trace.globalConnNetId !== candidate.firstLabel.globalConnNetId,
@@ -466,7 +520,7 @@ export class NetLabelToTraceSolver extends BaseSolver {
466
520
  }
467
521
 
468
522
  this.outputTraces = [...retainedTraces, recoveredTrace]
469
- if (candidate.recoveryMode === "fallback_labels") {
523
+ if (candidate.recoveryMode !== "routed_components") {
470
524
  this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
471
525
  (label) =>
472
526
  label !== candidate.firstLabel && label !== candidate.secondLabel,
@@ -118,5 +118,54 @@ export const placeGroundRailLabelsAtOuterEnd = ({
118
118
  }
119
119
  }
120
120
  }
121
+
122
+ for (let index = 0; index < output.length; index++) {
123
+ const label = output[index]!
124
+ if (label.orientation !== "y-") continue
125
+ // Use producer metadata so ground aliases do not require name matching.
126
+ const connection = inputProblem.netConnections.find(
127
+ (candidate) =>
128
+ candidate.isGround &&
129
+ candidate.netId === label.netId &&
130
+ candidate.pinIds.length > 2,
131
+ )
132
+ if (!connection) continue
133
+
134
+ // Reuse the lowest point on the existing column without changing the trace.
135
+ const anchorPoint = traces
136
+ .filter((trace) => trace.globalConnNetId === label.globalConnNetId)
137
+ .flatMap((trace) => trace.tracePath)
138
+ .filter(
139
+ (point) =>
140
+ nearlyEqual(point.x, label.anchorPoint.x) &&
141
+ point.y < label.anchorPoint.y,
142
+ )
143
+ .sort((a, b) => a.y - b.y)[0]
144
+ if (!anchorPoint) continue
145
+
146
+ const center = getCenterFromAnchor(
147
+ anchorPoint,
148
+ label.orientation,
149
+ label.width,
150
+ label.height,
151
+ )
152
+ const bounds = getRectBounds(center, label.width, label.height)
153
+ // Keep the original anchor when the ground symbol would collide.
154
+ if (
155
+ obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) ||
156
+ traceCrossesBoundsInterior(bounds, traceMap) ||
157
+ output.some(
158
+ (other, otherIndex) =>
159
+ otherIndex !== index &&
160
+ rectsOverlap(
161
+ bounds,
162
+ getRectBounds(other.center, other.width, other.height),
163
+ ),
164
+ )
165
+ )
166
+ continue
167
+
168
+ output[index] = { ...label, anchorPoint, center }
169
+ }
121
170
  return output
122
171
  }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.171",
8
+ "version": "0.0.173",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",