@tscircuit/schematic-trace-solver 0.0.179 → 0.0.180

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
@@ -831,7 +831,7 @@ type CandidateLabel = {
831
831
  height: number;
832
832
  };
833
833
  type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
834
- type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "outward-trace-anchor" | "shift" | "lateral-shift";
834
+ type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "downward-ground-rail-extension" | "outward-trace-anchor" | "shift" | "lateral-shift";
835
835
  type EvaluatedCandidate = CandidateLabel & {
836
836
  status: CandidateStatus$2;
837
837
  selected: boolean;
@@ -916,7 +916,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
916
916
  private createCandidate;
917
917
  private getNetLabelHeight;
918
918
  private getCandidateStatus;
919
- private isAcceptableTraceAnchorChipCollision;
919
+ private isAcceptableConnectedChipBoundaryOverlap;
920
920
  private staysOutsideConnectedPinRow;
921
921
  private getBoundsStatus;
922
922
  private isTraceTooCloseToLabel;
package/dist/index.js CHANGED
@@ -7709,6 +7709,27 @@ var TraceCleanupSolver = class extends BaseSolver {
7709
7709
  };
7710
7710
 
7711
7711
  // lib/solvers/AvailableNetOrientationSolver/traces.ts
7712
+ var extendVerticalTracePathAtInteriorPoint = ({
7713
+ tracePath,
7714
+ sourcePoint,
7715
+ extensionEndPoint
7716
+ }) => {
7717
+ const sourceIndex = tracePath.findIndex(
7718
+ (point) => pointsEqual(point, sourcePoint)
7719
+ );
7720
+ if (sourceIndex <= 0 || sourceIndex >= tracePath.length - 1) return null;
7721
+ const extensionDirectionY = extensionEndPoint.y - sourcePoint.y;
7722
+ const adjacentIndex = [sourceIndex - 1, sourceIndex + 1].find((index) => {
7723
+ const point = tracePath[index];
7724
+ return isVertical2(point, sourcePoint) && (point.y - sourcePoint.y) * extensionDirectionY < 0;
7725
+ });
7726
+ if (adjacentIndex === void 0) return null;
7727
+ return tracePath.toSpliced(
7728
+ Math.max(sourceIndex, adjacentIndex),
7729
+ 0,
7730
+ extensionEndPoint
7731
+ );
7732
+ };
7712
7733
  var getPinMap = (inputProblem) => {
7713
7734
  const pinMap = {};
7714
7735
  for (const chip of inputProblem.chips) {
@@ -9028,6 +9049,7 @@ function getPaddedBounds(bounds, padding) {
9028
9049
 
9029
9050
  // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
9030
9051
  var LABEL_TRACE_CLEARANCE2 = 0.1;
9052
+ var MIN_DOWNWARD_GROUND_RAIL_EXTENSION = 0.2;
9031
9053
  var CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1;
9032
9054
  var AvailableNetOrientationSolver = class extends BaseSolver {
9033
9055
  inputProblem;
@@ -9267,6 +9289,20 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9267
9289
  }
9268
9290
  addConnectorTrace(label, candidate, labelIndex) {
9269
9291
  if (candidate.phase === "trace-anchor") return;
9292
+ if (candidate.phase === "downward-ground-rail-extension") {
9293
+ for (const mspPairId2 of label.mspConnectionPairIds) {
9294
+ const hostTrace = this.traceMap[mspPairId2];
9295
+ if (!hostTrace) continue;
9296
+ const extendedTracePath = extendVerticalTracePathAtInteriorPoint({
9297
+ tracePath: hostTrace.tracePath,
9298
+ sourcePoint: label.anchorPoint,
9299
+ extensionEndPoint: candidate.anchorPoint
9300
+ });
9301
+ if (!extendedTracePath) continue;
9302
+ hostTrace.tracePath = extendedTracePath;
9303
+ return;
9304
+ }
9305
+ }
9270
9306
  const tracePath = this.getCandidateConnectorTrace(
9271
9307
  label,
9272
9308
  candidate,
@@ -9313,6 +9349,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9313
9349
  (connection) => connection.netId === label.netId
9314
9350
  );
9315
9351
  const isTwoPinNet = netConnection?.pinIds.length === 2;
9352
+ const isDownwardGroundRail = netConnection?.isGround && isXOrientation(label.orientation) && orientations.length === 1 && requiredOrientation === "y-" && this.hasTraceContinuingInOrientation(label, "y+");
9316
9353
  const isDistanceSplitVerticalRail = (netConnection?.pinIds.length ?? 0) > 2 && isYOrientation(requiredOrientation) && this.hasPortOnlyLabelOnSameNet(label);
9317
9354
  const textBlockedMultiPinRailTraces = this.getTextBlockedMultiPinRailTraces(
9318
9355
  label,
@@ -9370,6 +9407,26 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9370
9407
  labelIndex,
9371
9408
  orientations
9372
9409
  );
9410
+ const rotatedCandidateNeedsLateralBranch = rotatedCandidate !== null && Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS13;
9411
+ if (isDownwardGroundRail && rotatedCandidateNeedsLateralBranch) {
9412
+ const downwardRailCandidate = this.findValidCandidateInShiftColumn({
9413
+ label,
9414
+ labelIndex,
9415
+ orientation: requiredOrientation,
9416
+ direction: dir(requiredOrientation),
9417
+ baseAnchor: label.anchorPoint,
9418
+ maxSearchDistance: this.getSearchDistanceLimit(
9419
+ label,
9420
+ requiredOrientation
9421
+ ),
9422
+ outwardDistance: 0,
9423
+ phase: "downward-ground-rail-extension",
9424
+ stopOnTraceCollision: false,
9425
+ connectorSource: label.anchorPoint,
9426
+ startDistance: MIN_DOWNWARD_GROUND_RAIL_EXTENSION
9427
+ });
9428
+ if (downwardRailCandidate) return downwardRailCandidate;
9429
+ }
9373
9430
  if (rotatedCandidate) return rotatedCandidate;
9374
9431
  const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
9375
9432
  label,
@@ -10096,7 +10153,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10096
10153
  label
10097
10154
  });
10098
10155
  if (boundsStatus !== "valid") {
10099
- if (phase !== "trace-anchor" && phase !== "connected-rail-shift" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) || phase === "connected-rail-shift" && !this.staysOutsideConnectedPinRow({ candidate, label, bounds })) {
10156
+ const canOverlapConnectedChipBoundary = phase === "trace-anchor" || phase === "connected-rail-shift" || phase === "downward-ground-rail-extension";
10157
+ if (!canOverlapConnectedChipBoundary || boundsStatus !== "chip-collision" || !this.isAcceptableConnectedChipBoundaryOverlap(
10158
+ candidate,
10159
+ label,
10160
+ bounds
10161
+ ) || phase === "connected-rail-shift" && !this.staysOutsideConnectedPinRow({ candidate, label, bounds })) {
10100
10162
  return boundsStatus;
10101
10163
  }
10102
10164
  const nonChipBoundsStatus = this.getBoundsStatus({
@@ -10140,7 +10202,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
10140
10202
  }
10141
10203
  return "valid";
10142
10204
  }
10143
- isAcceptableTraceAnchorChipCollision(candidate, label, bounds) {
10205
+ isAcceptableConnectedChipBoundaryOverlap(candidate, label, bounds) {
10144
10206
  const collidingChips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
10145
10207
  if (collidingChips.length === 0) return false;
10146
10208
  const labelChipIds = new Set(
@@ -41,7 +41,12 @@ import {
41
41
  tracePathIntersectsBounds,
42
42
  } from "./geometry"
43
43
  import { orderRoutedLabelsBeforeOverlappingPortLabels } from "./orderRoutedLabelsBeforeOverlappingPortLabels"
44
- import { getPinMap, getTracePins, toNetLabelPlacementPatch } from "./traces"
44
+ import {
45
+ extendVerticalTracePathAtInteriorPoint,
46
+ getPinMap,
47
+ getTracePins,
48
+ toNetLabelPlacementPatch,
49
+ } from "./traces"
45
50
  import type {
46
51
  AvailableNetOrientationSolverParams,
47
52
  Bounds,
@@ -55,6 +60,7 @@ import { visualizeAvailableNetOrientationSolver } from "./visualize"
55
60
  import { AvailableNetOrientationObstacleIndex } from "./AvailableNetOrientationObstacleIndex"
56
61
 
57
62
  const LABEL_TRACE_CLEARANCE = 0.1
63
+ const MIN_DOWNWARD_GROUND_RAIL_EXTENSION = 0.2
58
64
  const CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1
59
65
 
60
66
  export class AvailableNetOrientationSolver extends BaseSolver {
@@ -357,6 +363,21 @@ export class AvailableNetOrientationSolver extends BaseSolver {
357
363
  ) {
358
364
  if (candidate.phase === "trace-anchor") return
359
365
 
366
+ if (candidate.phase === "downward-ground-rail-extension") {
367
+ for (const mspPairId of label.mspConnectionPairIds) {
368
+ const hostTrace = this.traceMap[mspPairId]
369
+ if (!hostTrace) continue
370
+ const extendedTracePath = extendVerticalTracePathAtInteriorPoint({
371
+ tracePath: hostTrace.tracePath,
372
+ sourcePoint: label.anchorPoint,
373
+ extensionEndPoint: candidate.anchorPoint,
374
+ })
375
+ if (!extendedTracePath) continue
376
+ hostTrace.tracePath = extendedTracePath
377
+ return
378
+ }
379
+ }
380
+
360
381
  const tracePath = this.getCandidateConnectorTrace(
361
382
  label,
362
383
  candidate,
@@ -416,6 +437,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
416
437
  (connection) => connection.netId === label.netId,
417
438
  )
418
439
  const isTwoPinNet = netConnection?.pinIds.length === 2
440
+ const isDownwardGroundRail =
441
+ netConnection?.isGround &&
442
+ isXOrientation(label.orientation) &&
443
+ orientations.length === 1 &&
444
+ requiredOrientation === "y-" &&
445
+ this.hasTraceContinuingInOrientation(label, "y+")
419
446
  const isDistanceSplitVerticalRail =
420
447
  (netConnection?.pinIds.length ?? 0) > 2 &&
421
448
  isYOrientation(requiredOrientation) &&
@@ -495,6 +522,30 @@ export class AvailableNetOrientationSolver extends BaseSolver {
495
522
  labelIndex,
496
523
  orientations,
497
524
  )
525
+ const rotatedCandidateNeedsLateralBranch =
526
+ rotatedCandidate !== null &&
527
+ Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS
528
+
529
+ if (isDownwardGroundRail && rotatedCandidateNeedsLateralBranch) {
530
+ const downwardRailCandidate = this.findValidCandidateInShiftColumn({
531
+ label,
532
+ labelIndex,
533
+ orientation: requiredOrientation,
534
+ direction: dir(requiredOrientation),
535
+ baseAnchor: label.anchorPoint,
536
+ maxSearchDistance: this.getSearchDistanceLimit(
537
+ label,
538
+ requiredOrientation,
539
+ ),
540
+ outwardDistance: 0,
541
+ phase: "downward-ground-rail-extension",
542
+ stopOnTraceCollision: false,
543
+ connectorSource: label.anchorPoint,
544
+ startDistance: MIN_DOWNWARD_GROUND_RAIL_EXTENSION,
545
+ })
546
+ if (downwardRailCandidate) return downwardRailCandidate
547
+ }
548
+
498
549
  if (rotatedCandidate) return rotatedCandidate
499
550
 
500
551
  const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
@@ -1525,10 +1576,18 @@ export class AvailableNetOrientationSolver extends BaseSolver {
1525
1576
  label,
1526
1577
  })
1527
1578
  if (boundsStatus !== "valid") {
1579
+ const canOverlapConnectedChipBoundary =
1580
+ phase === "trace-anchor" ||
1581
+ phase === "connected-rail-shift" ||
1582
+ phase === "downward-ground-rail-extension"
1528
1583
  if (
1529
- (phase !== "trace-anchor" && phase !== "connected-rail-shift") ||
1584
+ !canOverlapConnectedChipBoundary ||
1530
1585
  boundsStatus !== "chip-collision" ||
1531
- !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds) ||
1586
+ !this.isAcceptableConnectedChipBoundaryOverlap(
1587
+ candidate,
1588
+ label,
1589
+ bounds,
1590
+ ) ||
1532
1591
  (phase === "connected-rail-shift" &&
1533
1592
  !this.staysOutsideConnectedPinRow({ candidate, label, bounds }))
1534
1593
  ) {
@@ -1587,7 +1646,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
1587
1646
  return "valid"
1588
1647
  }
1589
1648
 
1590
- private isAcceptableTraceAnchorChipCollision(
1649
+ private isAcceptableConnectedChipBoundaryOverlap(
1591
1650
  candidate: CandidateLabel,
1592
1651
  label: NetLabelPlacement,
1593
1652
  bounds: Bounds,
@@ -1,8 +1,44 @@
1
+ import type { Point } from "@tscircuit/math-utils"
1
2
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
3
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
+ import {
5
+ isVertical,
6
+ pointsEqual,
7
+ } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
3
8
  import type { InputPin, InputProblem } from "lib/types/InputProblem"
4
9
  import type { CandidateLabel } from "./types"
5
10
 
11
+ export const extendVerticalTracePathAtInteriorPoint = ({
12
+ tracePath,
13
+ sourcePoint,
14
+ extensionEndPoint,
15
+ }: {
16
+ tracePath: Point[]
17
+ sourcePoint: Point
18
+ extensionEndPoint: Point
19
+ }) => {
20
+ const sourceIndex = tracePath.findIndex((point) =>
21
+ pointsEqual(point, sourcePoint),
22
+ )
23
+ if (sourceIndex <= 0 || sourceIndex >= tracePath.length - 1) return null
24
+
25
+ const extensionDirectionY = extensionEndPoint.y - sourcePoint.y
26
+ const adjacentIndex = [sourceIndex - 1, sourceIndex + 1].find((index) => {
27
+ const point = tracePath[index]!
28
+ return (
29
+ isVertical(point, sourcePoint) &&
30
+ (point.y - sourcePoint.y) * extensionDirectionY < 0
31
+ )
32
+ })
33
+ if (adjacentIndex === undefined) return null
34
+
35
+ return tracePath.toSpliced(
36
+ Math.max(sourceIndex, adjacentIndex),
37
+ 0,
38
+ extensionEndPoint,
39
+ )
40
+ }
41
+
6
42
  export const getPinMap = (inputProblem: InputProblem) => {
7
43
  const pinMap: Record<string, InputPin & { chipId: string }> = {}
8
44
  for (const chip of inputProblem.chips) {
@@ -40,6 +40,7 @@ export type CandidatePhase =
40
40
  | "rotate"
41
41
  | "trace-anchor"
42
42
  | "connected-rail-shift"
43
+ | "downward-ground-rail-extension"
43
44
  | "outward-trace-anchor"
44
45
  | "shift"
45
46
  | "lateral-shift"
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.179",
8
+ "version": "0.0.180",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",