@tscircuit/schematic-trace-solver 0.0.199 → 0.0.201

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
@@ -13243,6 +13243,52 @@ var getUnconnectedPins = ({
13243
13243
  );
13244
13244
  return connectionPair.pins.filter((pin) => !connectedPinIds.has(pin.pinId));
13245
13245
  };
13246
+ var pathsIntersect = (firstPath, secondPath) => {
13247
+ for (let firstIndex = 0; firstIndex < firstPath.length - 1; firstIndex++) {
13248
+ for (let secondIndex = 0; secondIndex < secondPath.length - 1; secondIndex++) {
13249
+ if (doSegmentsIntersect3(
13250
+ firstPath[firstIndex],
13251
+ firstPath[firstIndex + 1],
13252
+ secondPath[secondIndex],
13253
+ secondPath[secondIndex + 1]
13254
+ )) {
13255
+ return true;
13256
+ }
13257
+ }
13258
+ }
13259
+ return false;
13260
+ };
13261
+ var connectionPairPinsAreAlreadyConnected = ({
13262
+ connectionPair,
13263
+ sameNetTraces
13264
+ }) => {
13265
+ const [firstPin, secondPin] = connectionPair.pins;
13266
+ const connectedTraceIndexes = new Set(
13267
+ sameNetTraces.flatMap(
13268
+ (trace, index) => trace.pinIds.includes(firstPin.pinId) ? [index] : []
13269
+ )
13270
+ );
13271
+ let previousSize = -1;
13272
+ while (connectedTraceIndexes.size !== previousSize) {
13273
+ previousSize = connectedTraceIndexes.size;
13274
+ for (let traceIndex = 0; traceIndex < sameNetTraces.length; traceIndex++) {
13275
+ if (connectedTraceIndexes.has(traceIndex)) continue;
13276
+ const trace = sameNetTraces[traceIndex];
13277
+ const connectsToComponent = [...connectedTraceIndexes].some(
13278
+ (connectedTraceIndex) => {
13279
+ const connectedTrace = sameNetTraces[connectedTraceIndex];
13280
+ return trace.pinIds.some(
13281
+ (pinId) => connectedTrace.pinIds.includes(pinId)
13282
+ ) || pathsIntersect(trace.tracePath, connectedTrace.tracePath);
13283
+ }
13284
+ );
13285
+ if (connectsToComponent) connectedTraceIndexes.add(traceIndex);
13286
+ }
13287
+ }
13288
+ return [...connectedTraceIndexes].some(
13289
+ (traceIndex) => sameNetTraces[traceIndex].pinIds.includes(secondPin.pinId)
13290
+ );
13291
+ };
13246
13292
  var getJunctionCandidates = ({
13247
13293
  connectionPair,
13248
13294
  sameNetTraces,
@@ -13507,6 +13553,12 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
13507
13553
  const sameNetTraces = existingTraces.filter(
13508
13554
  (trace) => trace.globalConnNetId === connectionPair.globalConnNetId
13509
13555
  );
13556
+ if (connectionPairPinsAreAlreadyConnected({
13557
+ connectionPair,
13558
+ sameNetTraces
13559
+ })) {
13560
+ return;
13561
+ }
13510
13562
  const junctionCandidates = getJunctionCandidates({
13511
13563
  connectionPair,
13512
13564
  sameNetTraces,
@@ -14519,7 +14571,7 @@ var getAlignedAttachedLabelConnectorPath = ({
14519
14571
  const reverseCandidate = findCandidate([...forwardPath].reverse());
14520
14572
  return reverseCandidate ? reverseCandidate.reverse() : null;
14521
14573
  };
14522
- var pathsIntersect = (firstPath, secondPath) => {
14574
+ var pathsIntersect2 = (firstPath, secondPath) => {
14523
14575
  if (firstPath.some((point) => tracePathContainsPoint(secondPath, point)) || secondPath.some((point) => tracePathContainsPoint(firstPath, point))) {
14524
14576
  return true;
14525
14577
  }
@@ -14546,7 +14598,7 @@ var preserveAttachedTraceJunctions = ({
14546
14598
  (trace) => trace.mspPairId === originalTrace.mspPairId ? candidateTrace : trace
14547
14599
  );
14548
14600
  for (const connector of traces) {
14549
- if (connector.mspPairId === originalTrace.mspPairId || connector.globalConnNetId !== originalTrace.globalConnNetId || !pathsIntersect(originalTrace.tracePath, connector.tracePath) || pathsIntersect(candidateTrace.tracePath, connector.tracePath)) {
14601
+ if (connector.mspPairId === originalTrace.mspPairId || connector.globalConnNetId !== originalTrace.globalConnNetId || !pathsIntersect2(originalTrace.tracePath, connector.tracePath) || pathsIntersect2(candidateTrace.tracePath, connector.tracePath)) {
14550
14602
  continue;
14551
14603
  }
14552
14604
  const endpointIndex = [0, connector.tracePath.length - 1].find(
@@ -14579,7 +14631,7 @@ var preserveAttachedTraceJunctions = ({
14579
14631
  (point, index) => index === endpointIndex ? movedJunction : point
14580
14632
  )
14581
14633
  };
14582
- if (!pathsIntersect(candidateTrace.tracePath, movedConnector.tracePath)) {
14634
+ if (!pathsIntersect2(candidateTrace.tracePath, movedConnector.tracePath)) {
14583
14635
  return null;
14584
14636
  }
14585
14637
  outputTraces = outputTraces.map(
@@ -15107,6 +15159,38 @@ var placeGroundRailLabelsAtOuterEnd = ({
15107
15159
  return output;
15108
15160
  };
15109
15161
 
15162
+ // lib/solvers/SameNetJunctionAlignmentSolver/shortenSameSideRailToLabelAnchor.ts
15163
+ var MAX_LOCAL_RAIL_SHIFT = 0.5;
15164
+ var MIN_ESCAPE_LENGTH = 1;
15165
+ var shortenSameSideRailToLabelAnchor = ({
15166
+ traces,
15167
+ netLabelPlacements
15168
+ }) => traces.map((trace) => {
15169
+ const path = trace.tracePath;
15170
+ const firstPin = path[0];
15171
+ const secondPin = path.at(-1);
15172
+ if (!firstPin || !secondPin || !nearlyEqual(firstPin.x, secondPin.x)) {
15173
+ return trace;
15174
+ }
15175
+ const railIndex = path.findIndex(
15176
+ (point, index) => index > 0 && index < path.length - 2 && isHorizontal2(path[index - 1], point) && isVertical2(point, path[index + 1]) && isHorizontal2(path[index + 1], path[index + 2])
15177
+ );
15178
+ if (railIndex < 0) return trace;
15179
+ const firstRail = path[railIndex];
15180
+ const secondRail = path[railIndex + 1];
15181
+ const escapeLength = Math.abs(firstRail.x - firstPin.x);
15182
+ const label = netLabelPlacements.find(
15183
+ ({ anchorPoint, orientation, mspConnectionPairIds }) => mspConnectionPairIds.includes(trace.mspPairId) && (orientation === "y-" && anchorPoint.y < Math.min(firstPin.y, secondPin.y) || orientation === "y+" && anchorPoint.y > Math.max(firstPin.y, secondPin.y)) && anchorPoint.x >= Math.min(firstPin.x, firstRail.x) && anchorPoint.x <= Math.max(firstPin.x, firstRail.x) && Math.abs(anchorPoint.x - firstPin.x) < escapeLength && Math.abs(anchorPoint.x - firstRail.x) <= MAX_LOCAL_RAIL_SHIFT && escapeLength >= MIN_ESCAPE_LENGTH
15184
+ );
15185
+ if (!label) return trace;
15186
+ return {
15187
+ ...trace,
15188
+ tracePath: path.map(
15189
+ (point, index) => index === railIndex || index === railIndex + 1 ? { ...point, x: label.anchorPoint.x } : point
15190
+ )
15191
+ };
15192
+ });
15193
+
15110
15194
  // lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts
15111
15195
  var SameNetJunctionAlignmentSolver = class extends BaseSolver {
15112
15196
  input;
@@ -15120,10 +15204,13 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
15120
15204
  }
15121
15205
  _step() {
15122
15206
  const alignment = alignSameNetJunctions(this.input);
15123
- this.outputTraces = alignment.traces;
15207
+ this.outputTraces = shortenSameSideRailToLabelAnchor({
15208
+ traces: alignment.traces,
15209
+ netLabelPlacements: alignment.netLabelPlacements
15210
+ });
15124
15211
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
15125
15212
  inputProblem: this.input.inputProblem,
15126
- traces: alignment.traces,
15213
+ traces: this.outputTraces,
15127
15214
  netLabelPlacements: alignment.netLabelPlacements
15128
15215
  });
15129
15216
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
@@ -8,6 +8,7 @@ import type { InputProblem } from "lib/types/InputProblem"
8
8
  import { getColorFromString } from "lib/utils/getColorFromString"
9
9
  import { alignSameNetJunctions } from "./alignSameNetJunctions"
10
10
  import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
11
+ import { shortenSameSideRailToLabelAnchor } from "./shortenSameSideRailToLabelAnchor"
11
12
 
12
13
  interface SameNetJunctionAlignmentSolverInput {
13
14
  inputProblem: InputProblem
@@ -31,10 +32,13 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
31
32
 
32
33
  override _step() {
33
34
  const alignment = alignSameNetJunctions(this.input)
34
- this.outputTraces = alignment.traces
35
+ this.outputTraces = shortenSameSideRailToLabelAnchor({
36
+ traces: alignment.traces,
37
+ netLabelPlacements: alignment.netLabelPlacements,
38
+ })
35
39
  this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
36
40
  inputProblem: this.input.inputProblem,
37
- traces: alignment.traces,
41
+ traces: this.outputTraces,
38
42
  netLabelPlacements: alignment.netLabelPlacements,
39
43
  })
40
44
  this.stats.alignedJunctionCount = alignment.alignedJunctionCount
@@ -0,0 +1,62 @@
1
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
+ import {
4
+ isHorizontal,
5
+ isVertical,
6
+ nearlyEqual,
7
+ } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
8
+
9
+ const MAX_LOCAL_RAIL_SHIFT = 0.5
10
+ const MIN_ESCAPE_LENGTH = 1
11
+
12
+ export const shortenSameSideRailToLabelAnchor = ({
13
+ traces,
14
+ netLabelPlacements,
15
+ }: {
16
+ traces: SolvedTracePath[]
17
+ netLabelPlacements: NetLabelPlacement[]
18
+ }) =>
19
+ traces.map((trace) => {
20
+ const path = trace.tracePath
21
+ const firstPin = path[0]
22
+ const secondPin = path.at(-1)
23
+ if (!firstPin || !secondPin || !nearlyEqual(firstPin.x, secondPin.x)) {
24
+ return trace
25
+ }
26
+
27
+ const railIndex = path.findIndex(
28
+ (point, index) =>
29
+ index > 0 &&
30
+ index < path.length - 2 &&
31
+ isHorizontal(path[index - 1]!, point) &&
32
+ isVertical(point, path[index + 1]!) &&
33
+ isHorizontal(path[index + 1]!, path[index + 2]!),
34
+ )
35
+ if (railIndex < 0) return trace
36
+ const firstRail = path[railIndex]!
37
+ const secondRail = path[railIndex + 1]!
38
+ const escapeLength = Math.abs(firstRail.x - firstPin.x)
39
+ const label = netLabelPlacements.find(
40
+ ({ anchorPoint, orientation, mspConnectionPairIds }) =>
41
+ mspConnectionPairIds.includes(trace.mspPairId) &&
42
+ ((orientation === "y-" &&
43
+ anchorPoint.y < Math.min(firstPin.y, secondPin.y)) ||
44
+ (orientation === "y+" &&
45
+ anchorPoint.y > Math.max(firstPin.y, secondPin.y))) &&
46
+ anchorPoint.x >= Math.min(firstPin.x, firstRail.x) &&
47
+ anchorPoint.x <= Math.max(firstPin.x, firstRail.x) &&
48
+ Math.abs(anchorPoint.x - firstPin.x) < escapeLength &&
49
+ Math.abs(anchorPoint.x - firstRail.x) <= MAX_LOCAL_RAIL_SHIFT &&
50
+ escapeLength >= MIN_ESCAPE_LENGTH,
51
+ )
52
+ if (!label) return trace
53
+
54
+ return {
55
+ ...trace,
56
+ tracePath: path.map((point, index) =>
57
+ index === railIndex || index === railIndex + 1
58
+ ? { ...point, x: label.anchorPoint.x }
59
+ : point,
60
+ ),
61
+ }
62
+ })
@@ -204,6 +204,67 @@ const getUnconnectedPins = ({
204
204
  return connectionPair.pins.filter((pin) => !connectedPinIds.has(pin.pinId))
205
205
  }
206
206
 
207
+ const pathsIntersect = (firstPath: Point[], secondPath: Point[]): boolean => {
208
+ for (let firstIndex = 0; firstIndex < firstPath.length - 1; firstIndex++) {
209
+ for (
210
+ let secondIndex = 0;
211
+ secondIndex < secondPath.length - 1;
212
+ secondIndex++
213
+ ) {
214
+ if (
215
+ doSegmentsIntersect(
216
+ firstPath[firstIndex]!,
217
+ firstPath[firstIndex + 1]!,
218
+ secondPath[secondIndex]!,
219
+ secondPath[secondIndex + 1]!,
220
+ )
221
+ ) {
222
+ return true
223
+ }
224
+ }
225
+ }
226
+ return false
227
+ }
228
+
229
+ const connectionPairPinsAreAlreadyConnected = ({
230
+ connectionPair,
231
+ sameNetTraces,
232
+ }: {
233
+ connectionPair: MspConnectionPair
234
+ sameNetTraces: SolvedTracePath[]
235
+ }): boolean => {
236
+ const [firstPin, secondPin] = connectionPair.pins
237
+ const connectedTraceIndexes = new Set(
238
+ sameNetTraces.flatMap((trace, index) =>
239
+ trace.pinIds.includes(firstPin.pinId) ? [index] : [],
240
+ ),
241
+ )
242
+
243
+ let previousSize = -1
244
+ while (connectedTraceIndexes.size !== previousSize) {
245
+ previousSize = connectedTraceIndexes.size
246
+ for (let traceIndex = 0; traceIndex < sameNetTraces.length; traceIndex++) {
247
+ if (connectedTraceIndexes.has(traceIndex)) continue
248
+ const trace = sameNetTraces[traceIndex]!
249
+ const connectsToComponent = [...connectedTraceIndexes].some(
250
+ (connectedTraceIndex) => {
251
+ const connectedTrace = sameNetTraces[connectedTraceIndex]!
252
+ return (
253
+ trace.pinIds.some((pinId) =>
254
+ connectedTrace.pinIds.includes(pinId),
255
+ ) || pathsIntersect(trace.tracePath, connectedTrace.tracePath)
256
+ )
257
+ },
258
+ )
259
+ if (connectsToComponent) connectedTraceIndexes.add(traceIndex)
260
+ }
261
+ }
262
+
263
+ return [...connectedTraceIndexes].some((traceIndex) =>
264
+ sameNetTraces[traceIndex]!.pinIds.includes(secondPin.pinId),
265
+ )
266
+ }
267
+
207
268
  const getJunctionCandidates = ({
208
269
  connectionPair,
209
270
  sameNetTraces,
@@ -552,6 +613,14 @@ export class UnroutedTraceRecoverySolver extends BaseSolver {
552
613
  const sameNetTraces = existingTraces.filter(
553
614
  (trace) => trace.globalConnNetId === connectionPair.globalConnNetId,
554
615
  )
616
+ if (
617
+ connectionPairPinsAreAlreadyConnected({
618
+ connectionPair,
619
+ sameNetTraces,
620
+ })
621
+ ) {
622
+ return
623
+ }
555
624
  const junctionCandidates = getJunctionCandidates({
556
625
  connectionPair,
557
626
  sameNetTraces,
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.199",
8
+ "version": "0.0.201",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",