@tscircuit/schematic-trace-solver 0.0.148 → 0.0.149

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
@@ -1186,6 +1186,8 @@ declare const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
1186
1186
  * Gap between the trace and the near edge of the inline label text.
1187
1187
  */
1188
1188
  declare const INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
1189
+ /** Clearance between a generated terminal stub and an unrelated trace. */
1190
+ declare const INLINE_NET_LABEL_STUB_TRACE_CLEARANCE = 0.05;
1189
1191
  /**
1190
1192
  * Largest perpendicular deviation a route may have and still be labeled as one
1191
1193
  * span. A route that is straight except for elbow jogs up to this size reads
@@ -1361,4 +1363,4 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
1361
1363
  preview(): GraphicsObject;
1362
1364
  }
1363
1365
 
1364
- export { type ChipId, DEFAULT_INLINE_NET_LABEL_HEIGHT, INLINE_NET_LABEL_MAX_SPAN_JOG, INLINE_NET_LABEL_TRACE_MARGIN, type InlineNetLabelPlacement, InlineNetLabelSolver, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type NetLabelPlacement, type PinId, SchematicTracePipelineSolver, SchematicTraceSingleLineSolver2, type SectionId, type TextBoxes, estimateInlineNetLabelWidth };
1366
+ export { type ChipId, DEFAULT_INLINE_NET_LABEL_HEIGHT, INLINE_NET_LABEL_MAX_SPAN_JOG, INLINE_NET_LABEL_STUB_TRACE_CLEARANCE, INLINE_NET_LABEL_TRACE_MARGIN, type InlineNetLabelPlacement, InlineNetLabelSolver, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type NetLabelPlacement, type PinId, SchematicTracePipelineSolver, SchematicTraceSingleLineSolver2, type SectionId, type TextBoxes, estimateInlineNetLabelWidth };
package/dist/index.js CHANGED
@@ -12678,47 +12678,6 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
12678
12678
  }
12679
12679
  };
12680
12680
 
12681
- // lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts
12682
- var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
12683
- const segments = [];
12684
- let runStart = null;
12685
- let runAxis = null;
12686
- let runSign = 0;
12687
- const closeRun = (runEnd) => {
12688
- if (runStart && runAxis) {
12689
- const length = runAxis === "x" ? Math.abs(runEnd.x - runStart.x) : Math.abs(runEnd.y - runStart.y);
12690
- if (length > epsilon) {
12691
- segments.push({ start: runStart, end: runEnd, axis: runAxis, length });
12692
- }
12693
- }
12694
- runStart = null;
12695
- runAxis = null;
12696
- runSign = 0;
12697
- };
12698
- for (let i = 0; i < path.length - 1; i++) {
12699
- const a = path[i];
12700
- const b = path[i + 1];
12701
- const dx = b.x - a.x;
12702
- const dy = b.y - a.y;
12703
- if (Math.abs(dx) <= epsilon && Math.abs(dy) <= epsilon) continue;
12704
- const axis = Math.abs(dy) <= epsilon ? "x" : Math.abs(dx) <= epsilon ? "y" : null;
12705
- if (!axis) {
12706
- closeRun(a);
12707
- continue;
12708
- }
12709
- const sign = Math.sign(axis === "x" ? dx : dy);
12710
- if (runStart && runAxis === axis && runSign === sign) continue;
12711
- closeRun(a);
12712
- runStart = a;
12713
- runAxis = axis;
12714
- runSign = sign;
12715
- }
12716
- if (path.length >= 2) {
12717
- closeRun(path[path.length - 1]);
12718
- }
12719
- return segments.sort((a, b) => b.length - a.length);
12720
- };
12721
-
12722
12681
  // lib/solvers/InlineNetLabelSolver/alignPortOnlyInlineNetLabelStubs.ts
12723
12682
  var getStubDirection = (path) => {
12724
12683
  const [start, end] = path;
@@ -12867,6 +12826,47 @@ var alignPortOnlyInlineNetLabelStubs = ({
12867
12826
  return alignedPlacements;
12868
12827
  };
12869
12828
 
12829
+ // lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts
12830
+ var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
12831
+ const segments = [];
12832
+ let runStart = null;
12833
+ let runAxis = null;
12834
+ let runSign = 0;
12835
+ const closeRun = (runEnd) => {
12836
+ if (runStart && runAxis) {
12837
+ const length = runAxis === "x" ? Math.abs(runEnd.x - runStart.x) : Math.abs(runEnd.y - runStart.y);
12838
+ if (length > epsilon) {
12839
+ segments.push({ start: runStart, end: runEnd, axis: runAxis, length });
12840
+ }
12841
+ }
12842
+ runStart = null;
12843
+ runAxis = null;
12844
+ runSign = 0;
12845
+ };
12846
+ for (let i = 0; i < path.length - 1; i++) {
12847
+ const a = path[i];
12848
+ const b = path[i + 1];
12849
+ const dx = b.x - a.x;
12850
+ const dy = b.y - a.y;
12851
+ if (Math.abs(dx) <= epsilon && Math.abs(dy) <= epsilon) continue;
12852
+ const axis = Math.abs(dy) <= epsilon ? "x" : Math.abs(dx) <= epsilon ? "y" : null;
12853
+ if (!axis) {
12854
+ closeRun(a);
12855
+ continue;
12856
+ }
12857
+ const sign = Math.sign(axis === "x" ? dx : dy);
12858
+ if (runStart && runAxis === axis && runSign === sign) continue;
12859
+ closeRun(a);
12860
+ runStart = a;
12861
+ runAxis = axis;
12862
+ runSign = sign;
12863
+ }
12864
+ if (path.length >= 2) {
12865
+ closeRun(path[path.length - 1]);
12866
+ }
12867
+ return segments.sort((a, b) => b.length - a.length);
12868
+ };
12869
+
12870
12870
  // lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts
12871
12871
  var LABEL_CLEARANCE2 = 0.05;
12872
12872
  var POINT_EPSILON = 1e-6;
@@ -13229,6 +13229,7 @@ var pushAnchoredNetLabelsAwayFromInlineLabels = ({
13229
13229
  // lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
13230
13230
  var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
13231
13231
  var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
13232
+ var INLINE_NET_LABEL_STUB_TRACE_CLEARANCE = 0.05;
13232
13233
  var INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4;
13233
13234
  var getPinPairKey2 = (pinIds) => [...pinIds].sort().join("::");
13234
13235
  var InlineNetLabelSolver = class extends BaseSolver {
@@ -13329,7 +13330,15 @@ var InlineNetLabelSolver = class extends BaseSolver {
13329
13330
  const stubLength = Math.max(width + 0.2, 0.6);
13330
13331
  const start = inputPin ? { x: inputPin.x, y: inputPin.y } : anchoredPlacement.anchorPoint;
13331
13332
  const direction = inputPin && inputChip ? inputPin._facingDirection ?? getPinDirection(inputPin, inputChip) : anchoredPlacement.orientation;
13332
- const end = direction === "x+" ? { x: start.x + stubLength, y: start.y } : direction === "x-" ? { x: start.x - stubLength, y: start.y } : direction === "y+" ? { x: start.x, y: start.y + stubLength } : { x: start.x, y: start.y - stubLength };
13333
+ const intendedEnd = direction === "x+" ? { x: start.x + stubLength, y: start.y } : direction === "x-" ? { x: start.x - stubLength, y: start.y } : direction === "y+" ? { x: start.x, y: start.y + stubLength } : { x: start.x, y: start.y - stubLength };
13334
+ const end = getCollisionLimitedTerminalStubEnd({
13335
+ start,
13336
+ intendedEnd,
13337
+ minimumLength: width + 2 * INLINE_NET_LABEL_TRACE_MARGIN,
13338
+ traces: this.traces,
13339
+ ownGlobalConnNetId: anchoredPlacement.globalConnNetId
13340
+ });
13341
+ if (!end) return null;
13333
13342
  const axis = direction === "x+" || direction === "x-" ? "x" : "y";
13334
13343
  const side = axis === "x" ? "y+" : "x-";
13335
13344
  const anchorPoint = {
@@ -13681,6 +13690,55 @@ var doesPathIntersectBounds2 = (path, bounds) => {
13681
13690
  }
13682
13691
  return false;
13683
13692
  };
13693
+ var GEOMETRY_EPSILON = 1e-6;
13694
+ var getCollisionLimitedTerminalStubEnd = ({
13695
+ start,
13696
+ intendedEnd,
13697
+ minimumLength,
13698
+ traces,
13699
+ ownGlobalConnNetId
13700
+ }) => {
13701
+ const isHorizontal4 = Math.abs(intendedEnd.x - start.x) >= Math.abs(intendedEnd.y - start.y);
13702
+ const alongAxis = isHorizontal4 ? "x" : "y";
13703
+ const acrossAxis = isHorizontal4 ? "y" : "x";
13704
+ const direction = Math.sign(intendedEnd[alongAxis] - start[alongAxis]) || 1;
13705
+ const intendedLength = Math.abs(intendedEnd[alongAxis] - start[alongAxis]);
13706
+ let availableLength = intendedLength;
13707
+ for (const trace of traces) {
13708
+ if (trace.globalConnNetId === ownGlobalConnNetId) continue;
13709
+ for (let segmentIndex = 0; segmentIndex < trace.tracePath.length - 1; segmentIndex++) {
13710
+ const segmentStart = trace.tracePath[segmentIndex];
13711
+ const segmentEnd = trace.tracePath[segmentIndex + 1];
13712
+ const startAlong = (segmentStart[alongAxis] - start[alongAxis]) * direction;
13713
+ const endAlong = (segmentEnd[alongAxis] - start[alongAxis]) * direction;
13714
+ const startAcross = segmentStart[acrossAxis] - start[acrossAxis];
13715
+ const endAcross = segmentEnd[acrossAxis] - start[acrossAxis];
13716
+ const minAlong = Math.min(startAlong, endAlong);
13717
+ const maxAlong = Math.max(startAlong, endAlong);
13718
+ const minAcross = Math.min(startAcross, endAcross);
13719
+ const maxAcross = Math.max(startAcross, endAcross);
13720
+ let collisionDistance;
13721
+ const isCollinear = Math.abs(startAcross) <= GEOMETRY_EPSILON && Math.abs(endAcross) <= GEOMETRY_EPSILON;
13722
+ if (isCollinear && maxAlong >= -GEOMETRY_EPSILON && minAlong <= intendedLength + GEOMETRY_EPSILON) {
13723
+ collisionDistance = Math.max(0, minAlong);
13724
+ } else {
13725
+ const crossesStubAxis = minAcross <= GEOMETRY_EPSILON && maxAcross >= -GEOMETRY_EPSILON;
13726
+ const perpendicularAlong = (startAlong + endAlong) / 2;
13727
+ if (crossesStubAxis && Math.abs(startAlong - endAlong) <= GEOMETRY_EPSILON && perpendicularAlong >= -GEOMETRY_EPSILON && perpendicularAlong <= intendedLength + GEOMETRY_EPSILON) {
13728
+ collisionDistance = Math.max(0, perpendicularAlong);
13729
+ }
13730
+ }
13731
+ if (collisionDistance !== void 0) {
13732
+ availableLength = Math.min(
13733
+ availableLength,
13734
+ collisionDistance - INLINE_NET_LABEL_STUB_TRACE_CLEARANCE
13735
+ );
13736
+ }
13737
+ }
13738
+ }
13739
+ if (availableLength + GEOMETRY_EPSILON < minimumLength) return null;
13740
+ return isHorizontal4 ? { x: start.x + direction * availableLength, y: start.y } : { x: start.x, y: start.y + direction * availableLength };
13741
+ };
13684
13742
 
13685
13743
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
13686
13744
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
@@ -14246,6 +14304,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
14246
14304
  export {
14247
14305
  DEFAULT_INLINE_NET_LABEL_HEIGHT,
14248
14306
  INLINE_NET_LABEL_MAX_SPAN_JOG,
14307
+ INLINE_NET_LABEL_STUB_TRACE_CLEARANCE,
14249
14308
  INLINE_NET_LABEL_TRACE_MARGIN,
14250
14309
  InlineNetLabelSolver,
14251
14310
  SchematicTracePipelineSolver,
@@ -13,11 +13,11 @@ import type {
13
13
  } from "lib/types/InputProblem"
14
14
  import { getColorFromString } from "lib/utils/getColorFromString"
15
15
  import { boundsOverlap, getTextBoxBounds } from "lib/utils/textBoxBounds"
16
+ import { alignPortOnlyInlineNetLabelStubs } from "./alignPortOnlyInlineNetLabelStubs"
16
17
  import {
17
18
  type AxisAlignedSegment,
18
19
  getAxisAlignedSegments,
19
20
  } from "./getAxisAlignedSegments"
20
- import { alignPortOnlyInlineNetLabelStubs } from "./alignPortOnlyInlineNetLabelStubs"
21
21
  import { pushAnchoredNetLabelsAwayFromInlineLabels } from "./pushAnchoredNetLabelsAwayFromInlineLabels"
22
22
 
23
23
  export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
@@ -27,6 +27,9 @@ export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
27
27
  */
28
28
  export const INLINE_NET_LABEL_TRACE_MARGIN = 0.05
29
29
 
30
+ /** Clearance between a generated terminal stub and an unrelated trace. */
31
+ export const INLINE_NET_LABEL_STUB_TRACE_CLEARANCE = 0.05
32
+
30
33
  /**
31
34
  * Largest perpendicular deviation a route may have and still be labeled as one
32
35
  * span. A route that is straight except for elbow jogs up to this size reads
@@ -248,7 +251,7 @@ export class InlineNetLabelSolver extends BaseSolver {
248
251
  inputPin && inputChip
249
252
  ? (inputPin._facingDirection ?? getPinDirection(inputPin, inputChip))
250
253
  : anchoredPlacement.orientation
251
- const end: Point =
254
+ const intendedEnd: Point =
252
255
  direction === "x+"
253
256
  ? { x: start.x + stubLength, y: start.y }
254
257
  : direction === "x-"
@@ -257,6 +260,15 @@ export class InlineNetLabelSolver extends BaseSolver {
257
260
  ? { x: start.x, y: start.y + stubLength }
258
261
  : { x: start.x, y: start.y - stubLength }
259
262
 
263
+ const end = getCollisionLimitedTerminalStubEnd({
264
+ start,
265
+ intendedEnd,
266
+ minimumLength: width + 2 * INLINE_NET_LABEL_TRACE_MARGIN,
267
+ traces: this.traces,
268
+ ownGlobalConnNetId: anchoredPlacement.globalConnNetId,
269
+ })
270
+ if (!end) return null
271
+
260
272
  const axis: InlineNetLabelPlacement["axis"] =
261
273
  direction === "x+" || direction === "x-" ? "x" : "y"
262
274
  const side: InlineNetLabelPlacement["side"] = axis === "x" ? "y+" : "x-"
@@ -777,3 +789,90 @@ const doesPathIntersectBounds = (path: Point[], bounds: Bounds): boolean => {
777
789
  }
778
790
  return false
779
791
  }
792
+
793
+ const GEOMETRY_EPSILON = 1e-6
794
+
795
+ /**
796
+ * Shortens an axis-aligned terminal stub before the first unrelated trace.
797
+ * Coordinates are schematic-world millimetres with +x right and +y up.
798
+ */
799
+ const getCollisionLimitedTerminalStubEnd = ({
800
+ start,
801
+ intendedEnd,
802
+ minimumLength,
803
+ traces,
804
+ ownGlobalConnNetId,
805
+ }: {
806
+ start: Point
807
+ intendedEnd: Point
808
+ minimumLength: number
809
+ traces: SolvedTracePath[]
810
+ ownGlobalConnNetId: string
811
+ }): Point | null => {
812
+ const isHorizontal =
813
+ Math.abs(intendedEnd.x - start.x) >= Math.abs(intendedEnd.y - start.y)
814
+ const alongAxis = isHorizontal ? "x" : "y"
815
+ const acrossAxis = isHorizontal ? "y" : "x"
816
+ const direction = Math.sign(intendedEnd[alongAxis] - start[alongAxis]) || 1
817
+ const intendedLength = Math.abs(intendedEnd[alongAxis] - start[alongAxis])
818
+ let availableLength = intendedLength
819
+
820
+ for (const trace of traces) {
821
+ if (trace.globalConnNetId === ownGlobalConnNetId) continue
822
+
823
+ for (
824
+ let segmentIndex = 0;
825
+ segmentIndex < trace.tracePath.length - 1;
826
+ segmentIndex++
827
+ ) {
828
+ const segmentStart = trace.tracePath[segmentIndex]!
829
+ const segmentEnd = trace.tracePath[segmentIndex + 1]!
830
+ const startAlong =
831
+ (segmentStart[alongAxis] - start[alongAxis]) * direction
832
+ const endAlong = (segmentEnd[alongAxis] - start[alongAxis]) * direction
833
+ const startAcross = segmentStart[acrossAxis] - start[acrossAxis]
834
+ const endAcross = segmentEnd[acrossAxis] - start[acrossAxis]
835
+ const minAlong = Math.min(startAlong, endAlong)
836
+ const maxAlong = Math.max(startAlong, endAlong)
837
+ const minAcross = Math.min(startAcross, endAcross)
838
+ const maxAcross = Math.max(startAcross, endAcross)
839
+
840
+ let collisionDistance: number | undefined
841
+ const isCollinear =
842
+ Math.abs(startAcross) <= GEOMETRY_EPSILON &&
843
+ Math.abs(endAcross) <= GEOMETRY_EPSILON
844
+ if (
845
+ isCollinear &&
846
+ maxAlong >= -GEOMETRY_EPSILON &&
847
+ minAlong <= intendedLength + GEOMETRY_EPSILON
848
+ ) {
849
+ collisionDistance = Math.max(0, minAlong)
850
+ } else {
851
+ const crossesStubAxis =
852
+ minAcross <= GEOMETRY_EPSILON && maxAcross >= -GEOMETRY_EPSILON
853
+ const perpendicularAlong = (startAlong + endAlong) / 2
854
+ if (
855
+ crossesStubAxis &&
856
+ Math.abs(startAlong - endAlong) <= GEOMETRY_EPSILON &&
857
+ perpendicularAlong >= -GEOMETRY_EPSILON &&
858
+ perpendicularAlong <= intendedLength + GEOMETRY_EPSILON
859
+ ) {
860
+ collisionDistance = Math.max(0, perpendicularAlong)
861
+ }
862
+ }
863
+
864
+ if (collisionDistance !== undefined) {
865
+ availableLength = Math.min(
866
+ availableLength,
867
+ collisionDistance - INLINE_NET_LABEL_STUB_TRACE_CLEARANCE,
868
+ )
869
+ }
870
+ }
871
+ }
872
+
873
+ if (availableLength + GEOMETRY_EPSILON < minimumLength) return null
874
+
875
+ return isHorizontal
876
+ ? { x: start.x + direction * availableLength, y: start.y }
877
+ : { x: start.x, y: start.y + direction * availableLength }
878
+ }
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.148",
8
+ "version": "0.0.149",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/assets/example51.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />