@tscircuit/schematic-trace-solver 0.0.128 → 0.0.130
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 +15 -6
- package/dist/index.js +97 -10
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +142 -23
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +23 -2
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260811T075142Z.page.tsx +4 -0
- package/tests/assets/inline-net-label02.json +112 -0
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +34 -22
- package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -3
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +305 -303
- package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +5 -3
- package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +74 -0
- package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.json +194 -0
- package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example10.snap.svg +20 -16
- package/tests/examples/__snapshots__/example31.snap.svg +8 -6
- package/tests/examples/__snapshots__/example33.snap.svg +32 -30
- package/tests/examples/__snapshots__/example46.snap.svg +5 -3
- package/tests/examples/__snapshots__/example48.snap.svg +25 -23
- package/tests/examples/__snapshots__/example50.snap.svg +32 -30
- package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +48 -46
- package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +7 -5
- package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +24 -18
- package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +25 -23
- package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +46 -44
- package/tests/repros/repro-max-msp-pair-distance-fallback.test.ts +45 -0
- package/tests/repros/repro-missing-trace-netlabel.test.ts +9 -11
- package/tests/repros/rotated-components-rail-label.test.ts +5 -11
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +57 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label02.test.ts +59 -0
package/dist/index.d.ts
CHANGED
|
@@ -621,6 +621,7 @@ declare class LongDistancePairSolver extends BaseSolver {
|
|
|
621
621
|
private netConnMap;
|
|
622
622
|
private newlyConnectedPinIds;
|
|
623
623
|
private allSolvedTraces;
|
|
624
|
+
private maxMspPairDistance;
|
|
624
625
|
constructor(params: {
|
|
625
626
|
inputProblem: InputProblem;
|
|
626
627
|
alreadySolvedTraces: SolvedTracePath[];
|
|
@@ -1151,12 +1152,13 @@ declare const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
|
1151
1152
|
*/
|
|
1152
1153
|
declare const INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
1153
1154
|
/**
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1155
|
+
* Largest perpendicular deviation a route may have and still be labeled as one
|
|
1156
|
+
* span. A route that is straight except for elbow jogs up to this size reads
|
|
1157
|
+
* as a single wire, so its name may be centered over the whole route,
|
|
1158
|
+
* bridging the jogs. Anything more meandering falls back to an anchored net
|
|
1159
|
+
* label.
|
|
1158
1160
|
*/
|
|
1159
|
-
declare const
|
|
1161
|
+
declare const INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4;
|
|
1160
1162
|
/**
|
|
1161
1163
|
* A net label drawn parallel to the trace it names, rather than anchored to the
|
|
1162
1164
|
* end of it. Used for point-to-point signal traces, where an anchored label
|
|
@@ -1213,6 +1215,13 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1213
1215
|
getConstructorParams(): [InlineNetLabelSolverInput];
|
|
1214
1216
|
_step(): void;
|
|
1215
1217
|
private computeInlinePlacement;
|
|
1218
|
+
/**
|
|
1219
|
+
* Places the label over the whole route when the route is straight except
|
|
1220
|
+
* for perpendicular jogs of at most INLINE_NET_LABEL_MAX_SPAN_JOG. The label
|
|
1221
|
+
* sits clear of the route's full perpendicular extent and stays within its
|
|
1222
|
+
* endpoints along the dominant axis.
|
|
1223
|
+
*/
|
|
1224
|
+
private computeSpanPlacement;
|
|
1216
1225
|
/**
|
|
1217
1226
|
* An inline label may not sit on top of a chip, a component's text, or a
|
|
1218
1227
|
* trace belonging to another net.
|
|
@@ -1297,4 +1306,4 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
1297
1306
|
preview(): GraphicsObject;
|
|
1298
1307
|
}
|
|
1299
1308
|
|
|
1300
|
-
export { type ChipId, DEFAULT_INLINE_NET_LABEL_HEIGHT, INLINE_NET_LABEL_TRACE_MARGIN, type InlineNetLabelPlacement, InlineNetLabelSolver, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem,
|
|
1309
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -4618,6 +4618,7 @@ var NEAREST_NEIGHBOR_COUNT = 3;
|
|
|
4618
4618
|
var distance3 = (p1, p2) => {
|
|
4619
4619
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
4620
4620
|
};
|
|
4621
|
+
var manhattanDistance = (p1, p2) => Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
4621
4622
|
var LongDistancePairSolver = class extends BaseSolver {
|
|
4622
4623
|
constructor(params) {
|
|
4623
4624
|
super();
|
|
@@ -4625,6 +4626,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4625
4626
|
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
|
|
4626
4627
|
this.inputProblem = inputProblem;
|
|
4627
4628
|
this.allSolvedTraces = [...alreadySolvedTraces];
|
|
4629
|
+
this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
4628
4630
|
const primaryConnectedPinIds = /* @__PURE__ */ new Set();
|
|
4629
4631
|
for (const pair of primaryMspConnectionPairs) {
|
|
4630
4632
|
primaryConnectedPinIds.add(pair.pins[0].pinId);
|
|
@@ -4660,6 +4662,12 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4660
4662
|
const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
|
|
4661
4663
|
const targetPin = pinMap.get(otherPinId);
|
|
4662
4664
|
if (!targetPin) return [];
|
|
4665
|
+
const isNamedTwoPinConnection = inputProblem.netConnections.some(
|
|
4666
|
+
(connection) => connection.pinIds.length === 2 && connection.pinIds.includes(sourcePin.pinId) && connection.pinIds.includes(targetPin.pinId)
|
|
4667
|
+
);
|
|
4668
|
+
if (isNamedTwoPinConnection && manhattanDistance(sourcePin, targetPin) > this.maxMspPairDistance) {
|
|
4669
|
+
return [];
|
|
4670
|
+
}
|
|
4663
4671
|
return [
|
|
4664
4672
|
{
|
|
4665
4673
|
pin: targetPin,
|
|
@@ -4694,6 +4702,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4694
4702
|
netConnMap;
|
|
4695
4703
|
newlyConnectedPinIds = /* @__PURE__ */ new Set();
|
|
4696
4704
|
allSolvedTraces = [];
|
|
4705
|
+
maxMspPairDistance;
|
|
4697
4706
|
getConstructorParams() {
|
|
4698
4707
|
return this.params;
|
|
4699
4708
|
}
|
|
@@ -11907,7 +11916,7 @@ var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
|
|
|
11907
11916
|
// lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
|
|
11908
11917
|
var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
11909
11918
|
var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
11910
|
-
var
|
|
11919
|
+
var INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4;
|
|
11911
11920
|
var getPinPairKey2 = (pinIds) => [...pinIds].sort().join("::");
|
|
11912
11921
|
var InlineNetLabelSolver = class extends BaseSolver {
|
|
11913
11922
|
inputProblem;
|
|
@@ -11966,14 +11975,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
11966
11975
|
const height = directConnection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
|
|
11967
11976
|
const width = directConnection.inlineNetLabelWidth ?? directConnection.netLabelWidth ?? estimateInlineNetLabelWidth(directConnection.netId, height);
|
|
11968
11977
|
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN;
|
|
11969
|
-
const usableSegments = segments.filter(
|
|
11970
|
-
(segment) => segment.length >= width * MIN_INLINE_NET_LABEL_SEGMENT_RATIO
|
|
11971
|
-
).sort((a, b) => {
|
|
11972
|
-
const aFits = a.length >= width;
|
|
11973
|
-
const bFits = b.length >= width;
|
|
11974
|
-
if (aFits !== bFits) return aFits ? -1 : 1;
|
|
11975
|
-
return b.length - a.length;
|
|
11976
|
-
});
|
|
11978
|
+
const usableSegments = segments.filter((segment) => segment.length >= width).sort((a, b) => b.length - a.length);
|
|
11977
11979
|
for (const segment of usableSegments) {
|
|
11978
11980
|
const sides = segment.axis === "x" ? ["y+", "y-"] : ["x-", "x+"];
|
|
11979
11981
|
for (const side of sides) {
|
|
@@ -12008,6 +12010,91 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12008
12010
|
}
|
|
12009
12011
|
}
|
|
12010
12012
|
}
|
|
12013
|
+
const spanPlacement = this.computeSpanPlacement({
|
|
12014
|
+
trace,
|
|
12015
|
+
directConnection,
|
|
12016
|
+
width,
|
|
12017
|
+
height,
|
|
12018
|
+
offset
|
|
12019
|
+
});
|
|
12020
|
+
if (spanPlacement) return spanPlacement;
|
|
12021
|
+
return null;
|
|
12022
|
+
}
|
|
12023
|
+
/**
|
|
12024
|
+
* Places the label over the whole route when the route is straight except
|
|
12025
|
+
* for perpendicular jogs of at most INLINE_NET_LABEL_MAX_SPAN_JOG. The label
|
|
12026
|
+
* sits clear of the route's full perpendicular extent and stays within its
|
|
12027
|
+
* endpoints along the dominant axis.
|
|
12028
|
+
*/
|
|
12029
|
+
computeSpanPlacement({
|
|
12030
|
+
trace,
|
|
12031
|
+
directConnection,
|
|
12032
|
+
width,
|
|
12033
|
+
height,
|
|
12034
|
+
offset
|
|
12035
|
+
}) {
|
|
12036
|
+
const path = trace.tracePath;
|
|
12037
|
+
if (path.length < 2) return null;
|
|
12038
|
+
let minX = Infinity;
|
|
12039
|
+
let maxX = -Infinity;
|
|
12040
|
+
let minY = Infinity;
|
|
12041
|
+
let maxY = -Infinity;
|
|
12042
|
+
for (const point of path) {
|
|
12043
|
+
minX = Math.min(minX, point.x);
|
|
12044
|
+
maxX = Math.max(maxX, point.x);
|
|
12045
|
+
minY = Math.min(minY, point.y);
|
|
12046
|
+
maxY = Math.max(maxY, point.y);
|
|
12047
|
+
}
|
|
12048
|
+
const extentX = maxX - minX;
|
|
12049
|
+
const extentY = maxY - minY;
|
|
12050
|
+
const axis = extentX >= extentY ? "x" : "y";
|
|
12051
|
+
const spanLength = axis === "x" ? extentX : extentY;
|
|
12052
|
+
const jog = axis === "x" ? extentY : extentX;
|
|
12053
|
+
if (jog > INLINE_NET_LABEL_MAX_SPAN_JOG) return null;
|
|
12054
|
+
if (spanLength < width) return null;
|
|
12055
|
+
const spanStart = axis === "x" ? minX : minY;
|
|
12056
|
+
const spanEnd = axis === "x" ? maxX : maxY;
|
|
12057
|
+
const mid = (spanStart + spanEnd) / 2;
|
|
12058
|
+
const halfRange = (spanLength - width) / 2;
|
|
12059
|
+
const offsets = [0];
|
|
12060
|
+
for (let value = 0.1; value <= halfRange + 1e-9; value += 0.1) {
|
|
12061
|
+
offsets.push(value, -value);
|
|
12062
|
+
}
|
|
12063
|
+
offsets.push(halfRange, -halfRange);
|
|
12064
|
+
const sides = axis === "x" ? ["y+", "y-"] : ["x-", "x+"];
|
|
12065
|
+
for (const side of sides) {
|
|
12066
|
+
for (const alongOffset of offsets) {
|
|
12067
|
+
const along = mid + alongOffset;
|
|
12068
|
+
const center = side === "y+" ? { x: along, y: maxY + offset } : side === "y-" ? { x: along, y: minY - offset } : side === "x-" ? { x: minX - offset, y: along } : { x: maxX + offset, y: along };
|
|
12069
|
+
const halfAlong = width / 2;
|
|
12070
|
+
const halfAcross = height / 2;
|
|
12071
|
+
const bounds = axis === "x" ? {
|
|
12072
|
+
minX: center.x - halfAlong,
|
|
12073
|
+
maxX: center.x + halfAlong,
|
|
12074
|
+
minY: center.y - halfAcross,
|
|
12075
|
+
maxY: center.y + halfAcross
|
|
12076
|
+
} : {
|
|
12077
|
+
minX: center.x - halfAcross,
|
|
12078
|
+
maxX: center.x + halfAcross,
|
|
12079
|
+
minY: center.y - halfAlong,
|
|
12080
|
+
maxY: center.y + halfAlong
|
|
12081
|
+
};
|
|
12082
|
+
if (this.isObstructed(bounds, trace)) continue;
|
|
12083
|
+
const anchorPoint = axis === "x" ? { x: along, y: side === "y+" ? maxY : minY } : { x: side === "x-" ? minX : maxX, y: along };
|
|
12084
|
+
return {
|
|
12085
|
+
globalConnNetId: trace.globalConnNetId,
|
|
12086
|
+
netId: directConnection.netId,
|
|
12087
|
+
mspPairId: trace.mspPairId,
|
|
12088
|
+
pinIds: [...directConnection.pinIds],
|
|
12089
|
+
axis,
|
|
12090
|
+
anchorPoint,
|
|
12091
|
+
center,
|
|
12092
|
+
width,
|
|
12093
|
+
height,
|
|
12094
|
+
side
|
|
12095
|
+
};
|
|
12096
|
+
}
|
|
12097
|
+
}
|
|
12011
12098
|
return null;
|
|
12012
12099
|
}
|
|
12013
12100
|
/**
|
|
@@ -12674,9 +12761,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
12674
12761
|
};
|
|
12675
12762
|
export {
|
|
12676
12763
|
DEFAULT_INLINE_NET_LABEL_HEIGHT,
|
|
12764
|
+
INLINE_NET_LABEL_MAX_SPAN_JOG,
|
|
12677
12765
|
INLINE_NET_LABEL_TRACE_MARGIN,
|
|
12678
12766
|
InlineNetLabelSolver,
|
|
12679
|
-
MIN_INLINE_NET_LABEL_SEGMENT_RATIO,
|
|
12680
12767
|
SchematicTracePipelineSolver,
|
|
12681
12768
|
SchematicTraceSingleLineSolver2,
|
|
12682
12769
|
estimateInlineNetLabelWidth
|
|
@@ -24,12 +24,13 @@ export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
|
|
|
24
24
|
export const INLINE_NET_LABEL_TRACE_MARGIN = 0.05
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* Largest perpendicular deviation a route may have and still be labeled as one
|
|
28
|
+
* span. A route that is straight except for elbow jogs up to this size reads
|
|
29
|
+
* as a single wire, so its name may be centered over the whole route,
|
|
30
|
+
* bridging the jogs. Anything more meandering falls back to an anchored net
|
|
31
|
+
* label.
|
|
31
32
|
*/
|
|
32
|
-
export const
|
|
33
|
+
export const INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* A net label drawn parallel to the trace it names, rather than anchored to the
|
|
@@ -165,20 +166,14 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
165
166
|
|
|
166
167
|
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN
|
|
167
168
|
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
// side, and positions near the middle before
|
|
169
|
+
// First choice: a straight run the label fully fits on, longest first, so
|
|
170
|
+
// the text hugs the wire it names. Within a run, try the preferred side
|
|
171
|
+
// before the far side, and positions near the middle before the ends. The
|
|
172
|
+
// label never extends past the end of a run - a name hanging off its wire
|
|
173
|
+
// reads as floating text.
|
|
171
174
|
const usableSegments = segments
|
|
172
|
-
.filter(
|
|
173
|
-
|
|
174
|
-
segment.length >= width * MIN_INLINE_NET_LABEL_SEGMENT_RATIO,
|
|
175
|
-
)
|
|
176
|
-
.sort((a, b) => {
|
|
177
|
-
const aFits = a.length >= width
|
|
178
|
-
const bFits = b.length >= width
|
|
179
|
-
if (aFits !== bFits) return aFits ? -1 : 1
|
|
180
|
-
return b.length - a.length
|
|
181
|
-
})
|
|
175
|
+
.filter((segment) => segment.length >= width)
|
|
176
|
+
.sort((a, b) => b.length - a.length)
|
|
182
177
|
|
|
183
178
|
for (const segment of usableSegments) {
|
|
184
179
|
// "Above" the trace: +y for a horizontal trace, and -x for a vertical one
|
|
@@ -232,11 +227,138 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
232
227
|
}
|
|
233
228
|
}
|
|
234
229
|
|
|
230
|
+
// Second choice: no single run fits, but a route that is straight except
|
|
231
|
+
// for small jogs still reads as one wire - center the label over the
|
|
232
|
+
// route's overall span, bridging the jogs, without extending past its
|
|
233
|
+
// endpoints.
|
|
234
|
+
const spanPlacement = this.computeSpanPlacement({
|
|
235
|
+
trace,
|
|
236
|
+
directConnection,
|
|
237
|
+
width,
|
|
238
|
+
height,
|
|
239
|
+
offset,
|
|
240
|
+
})
|
|
241
|
+
if (spanPlacement) return spanPlacement
|
|
242
|
+
|
|
235
243
|
// No room anywhere along the trace. Leave the net to the regular anchored
|
|
236
244
|
// net label rather than drawing the name over a chip.
|
|
237
245
|
return null
|
|
238
246
|
}
|
|
239
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Places the label over the whole route when the route is straight except
|
|
250
|
+
* for perpendicular jogs of at most INLINE_NET_LABEL_MAX_SPAN_JOG. The label
|
|
251
|
+
* sits clear of the route's full perpendicular extent and stays within its
|
|
252
|
+
* endpoints along the dominant axis.
|
|
253
|
+
*/
|
|
254
|
+
private computeSpanPlacement({
|
|
255
|
+
trace,
|
|
256
|
+
directConnection,
|
|
257
|
+
width,
|
|
258
|
+
height,
|
|
259
|
+
offset,
|
|
260
|
+
}: {
|
|
261
|
+
trace: SolvedTracePath
|
|
262
|
+
directConnection: InputDirectConnection
|
|
263
|
+
width: number
|
|
264
|
+
height: number
|
|
265
|
+
offset: number
|
|
266
|
+
}): InlineNetLabelPlacement | null {
|
|
267
|
+
const path = trace.tracePath
|
|
268
|
+
if (path.length < 2) return null
|
|
269
|
+
|
|
270
|
+
let minX = Infinity
|
|
271
|
+
let maxX = -Infinity
|
|
272
|
+
let minY = Infinity
|
|
273
|
+
let maxY = -Infinity
|
|
274
|
+
for (const point of path) {
|
|
275
|
+
minX = Math.min(minX, point.x)
|
|
276
|
+
maxX = Math.max(maxX, point.x)
|
|
277
|
+
minY = Math.min(minY, point.y)
|
|
278
|
+
maxY = Math.max(maxY, point.y)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const extentX = maxX - minX
|
|
282
|
+
const extentY = maxY - minY
|
|
283
|
+
const axis: InlineNetLabelPlacement["axis"] = extentX >= extentY ? "x" : "y"
|
|
284
|
+
const spanLength = axis === "x" ? extentX : extentY
|
|
285
|
+
const jog = axis === "x" ? extentY : extentX
|
|
286
|
+
|
|
287
|
+
if (jog > INLINE_NET_LABEL_MAX_SPAN_JOG) return null
|
|
288
|
+
if (spanLength < width) return null
|
|
289
|
+
|
|
290
|
+
const spanStart = axis === "x" ? minX : minY
|
|
291
|
+
const spanEnd = axis === "x" ? maxX : maxY
|
|
292
|
+
const mid = (spanStart + spanEnd) / 2
|
|
293
|
+
|
|
294
|
+
// Slide candidates within the span, centered first (same scheme as the
|
|
295
|
+
// per-segment stage).
|
|
296
|
+
const halfRange = (spanLength - width) / 2
|
|
297
|
+
const offsets: number[] = [0]
|
|
298
|
+
for (let value = 0.1; value <= halfRange + 1e-9; value += 0.1) {
|
|
299
|
+
offsets.push(value, -value)
|
|
300
|
+
}
|
|
301
|
+
offsets.push(halfRange, -halfRange)
|
|
302
|
+
|
|
303
|
+
const sides: InlineNetLabelPlacement["side"][] =
|
|
304
|
+
axis === "x" ? ["y+", "y-"] : ["x-", "x+"]
|
|
305
|
+
|
|
306
|
+
for (const side of sides) {
|
|
307
|
+
for (const alongOffset of offsets) {
|
|
308
|
+
const along = mid + alongOffset
|
|
309
|
+
// The label clears the far side of every jog, not just the run it is
|
|
310
|
+
// nearest to.
|
|
311
|
+
const center: Point =
|
|
312
|
+
side === "y+"
|
|
313
|
+
? { x: along, y: maxY + offset }
|
|
314
|
+
: side === "y-"
|
|
315
|
+
? { x: along, y: minY - offset }
|
|
316
|
+
: side === "x-"
|
|
317
|
+
? { x: minX - offset, y: along }
|
|
318
|
+
: { x: maxX + offset, y: along }
|
|
319
|
+
|
|
320
|
+
const halfAlong = width / 2
|
|
321
|
+
const halfAcross = height / 2
|
|
322
|
+
const bounds: Bounds =
|
|
323
|
+
axis === "x"
|
|
324
|
+
? {
|
|
325
|
+
minX: center.x - halfAlong,
|
|
326
|
+
maxX: center.x + halfAlong,
|
|
327
|
+
minY: center.y - halfAcross,
|
|
328
|
+
maxY: center.y + halfAcross,
|
|
329
|
+
}
|
|
330
|
+
: {
|
|
331
|
+
minX: center.x - halfAcross,
|
|
332
|
+
maxX: center.x + halfAcross,
|
|
333
|
+
minY: center.y - halfAlong,
|
|
334
|
+
maxY: center.y + halfAlong,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (this.isObstructed(bounds, trace)) continue
|
|
338
|
+
|
|
339
|
+
const anchorPoint: Point =
|
|
340
|
+
axis === "x"
|
|
341
|
+
? { x: along, y: side === "y+" ? maxY : minY }
|
|
342
|
+
: { x: side === "x-" ? minX : maxX, y: along }
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
globalConnNetId: trace.globalConnNetId,
|
|
346
|
+
netId: directConnection.netId,
|
|
347
|
+
mspPairId: trace.mspPairId,
|
|
348
|
+
pinIds: [...directConnection.pinIds],
|
|
349
|
+
axis,
|
|
350
|
+
anchorPoint,
|
|
351
|
+
center,
|
|
352
|
+
width,
|
|
353
|
+
height,
|
|
354
|
+
side,
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return null
|
|
360
|
+
}
|
|
361
|
+
|
|
240
362
|
/**
|
|
241
363
|
* An inline label may not sit on top of a chip, a component's text, or a
|
|
242
364
|
* trace belonging to another net.
|
|
@@ -374,11 +496,8 @@ export const estimateInlineNetLabelWidth = (
|
|
|
374
496
|
/**
|
|
375
497
|
* Points along a segment where the label's midpoint could sit, ordered from the
|
|
376
498
|
* middle of the segment outwards. Sliding the label along the wire lets it
|
|
377
|
-
* dodge a chip that only crowds one end.
|
|
378
|
-
*
|
|
379
|
-
* When the label is shorter than the segment, candidates are limited to
|
|
380
|
-
* positions that keep it fully on the wire; when it is longer, only the
|
|
381
|
-
* midpoint is offered (it will overhang either way).
|
|
499
|
+
* dodge a chip that only crowds one end. Candidates always keep the label
|
|
500
|
+
* fully on the wire; callers only pass segments the label fits on.
|
|
382
501
|
*/
|
|
383
502
|
const getAnchorCandidates = (
|
|
384
503
|
segment: AxisAlignedSegment,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_MAX_MSP_PAIR_DISTANCE,
|
|
4
|
+
type MspConnectionPair,
|
|
5
|
+
} from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
3
6
|
import type {
|
|
4
7
|
InputProblem,
|
|
5
8
|
InputPin,
|
|
@@ -8,10 +11,10 @@ import type {
|
|
|
8
11
|
} from "lib/types/InputProblem"
|
|
9
12
|
import { BaseSolver } from "../BaseSolver/BaseSolver"
|
|
10
13
|
import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
11
|
-
import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"
|
|
12
14
|
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
13
15
|
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
14
16
|
import type { ConnectivityMap } from "connectivity-map"
|
|
17
|
+
import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"
|
|
15
18
|
import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
|
|
16
19
|
import { isLabeledPeripheralConnection } from "../MspConnectionPairSolver/isLabeledPeripheralConnection"
|
|
17
20
|
|
|
@@ -21,6 +24,9 @@ const distance = (p1: InputPin, p2: InputPin) => {
|
|
|
21
24
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
const manhattanDistance = (p1: InputPin, p2: InputPin) =>
|
|
28
|
+
Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
|
|
29
|
+
|
|
24
30
|
export class LongDistancePairSolver extends BaseSolver {
|
|
25
31
|
public solvedLongDistanceTraces: SolvedTracePath[] = []
|
|
26
32
|
private queuedCandidatePairs: Array<
|
|
@@ -37,6 +43,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
37
43
|
private netConnMap: ConnectivityMap
|
|
38
44
|
private newlyConnectedPinIds = new Set<PinId>()
|
|
39
45
|
private allSolvedTraces: SolvedTracePath[] = []
|
|
46
|
+
private maxMspPairDistance: number
|
|
40
47
|
|
|
41
48
|
constructor(
|
|
42
49
|
private params: {
|
|
@@ -53,6 +60,8 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
53
60
|
|
|
54
61
|
this.inputProblem = inputProblem
|
|
55
62
|
this.allSolvedTraces = [...alreadySolvedTraces]
|
|
63
|
+
this.maxMspPairDistance =
|
|
64
|
+
inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE
|
|
56
65
|
|
|
57
66
|
// 1. Create initial maps and sets for efficient lookup
|
|
58
67
|
const primaryConnectedPinIds = new Set<PinId>()
|
|
@@ -104,6 +113,18 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
104
113
|
.flatMap((otherPinId) => {
|
|
105
114
|
const targetPin = pinMap.get(otherPinId)
|
|
106
115
|
if (!targetPin) return [] // Gracefully handle missing pins
|
|
116
|
+
const isNamedTwoPinConnection = inputProblem.netConnections.some(
|
|
117
|
+
(connection) =>
|
|
118
|
+
connection.pinIds.length === 2 &&
|
|
119
|
+
connection.pinIds.includes(sourcePin.pinId) &&
|
|
120
|
+
connection.pinIds.includes(targetPin.pinId),
|
|
121
|
+
)
|
|
122
|
+
if (
|
|
123
|
+
isNamedTwoPinConnection &&
|
|
124
|
+
manhattanDistance(sourcePin, targetPin) > this.maxMspPairDistance
|
|
125
|
+
) {
|
|
126
|
+
return []
|
|
127
|
+
}
|
|
107
128
|
return [
|
|
108
129
|
{
|
|
109
130
|
pin: targetPin,
|
package/package.json
CHANGED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_0",
|
|
5
|
+
"center": {
|
|
6
|
+
"x": 0,
|
|
7
|
+
"y": 0
|
|
8
|
+
},
|
|
9
|
+
"width": 0.4,
|
|
10
|
+
"height": 1,
|
|
11
|
+
"pins": [
|
|
12
|
+
{
|
|
13
|
+
"pinId": "schematic_port_0",
|
|
14
|
+
"x": -0.6000000000000001,
|
|
15
|
+
"y": 0.30000000000000004,
|
|
16
|
+
"_facingDirection": "x-"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"pinId": "schematic_port_1",
|
|
20
|
+
"x": -0.6000000000000001,
|
|
21
|
+
"y": 0.10000000000000003,
|
|
22
|
+
"_facingDirection": "x-"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"pinId": "schematic_port_2",
|
|
26
|
+
"x": -0.6000000000000001,
|
|
27
|
+
"y": -0.09999999999999998,
|
|
28
|
+
"_facingDirection": "x-"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"pinId": "schematic_port_3",
|
|
32
|
+
"x": -0.6000000000000001,
|
|
33
|
+
"y": -0.30000000000000004,
|
|
34
|
+
"_facingDirection": "x-"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"pinId": "schematic_port_4",
|
|
38
|
+
"x": 0.6000000000000001,
|
|
39
|
+
"y": -0.30000000000000004,
|
|
40
|
+
"_facingDirection": "x+"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"pinId": "schematic_port_5",
|
|
44
|
+
"x": 0.6000000000000001,
|
|
45
|
+
"y": -0.10000000000000003,
|
|
46
|
+
"_facingDirection": "x+"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"pinId": "schematic_port_6",
|
|
50
|
+
"x": 0.6000000000000001,
|
|
51
|
+
"y": 0.09999999999999998,
|
|
52
|
+
"_facingDirection": "x+"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"pinId": "schematic_port_7",
|
|
56
|
+
"x": 0.6000000000000001,
|
|
57
|
+
"y": 0.30000000000000004,
|
|
58
|
+
"_facingDirection": "x+"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"chipId": "schematic_component_1",
|
|
64
|
+
"center": {
|
|
65
|
+
"x": 3,
|
|
66
|
+
"y": 0.13249999999999998
|
|
67
|
+
},
|
|
68
|
+
"width": 1.13,
|
|
69
|
+
"height": 0.915,
|
|
70
|
+
"pins": [
|
|
71
|
+
{
|
|
72
|
+
"pinId": "schematic_port_8",
|
|
73
|
+
"x": 2.46,
|
|
74
|
+
"y": 0,
|
|
75
|
+
"_facingDirection": "x-"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"pinId": "schematic_port_9",
|
|
79
|
+
"x": 3.54,
|
|
80
|
+
"y": 0,
|
|
81
|
+
"_facingDirection": "x+"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"directConnections": [
|
|
87
|
+
{
|
|
88
|
+
"netId": "USER_LED_ANODE",
|
|
89
|
+
"allowInlineNetLabel": true,
|
|
90
|
+
"inlineNetLabelWidth": 1.8,
|
|
91
|
+
"pinIds": [
|
|
92
|
+
"schematic_port_4",
|
|
93
|
+
"schematic_port_8"
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"netConnections": [],
|
|
98
|
+
"textBoxes": [
|
|
99
|
+
{
|
|
100
|
+
"chipId": "schematic_component_0",
|
|
101
|
+
"center": {
|
|
102
|
+
"x": -0.08000000000000002,
|
|
103
|
+
"y": 0.615
|
|
104
|
+
},
|
|
105
|
+
"width": 0.36,
|
|
106
|
+
"height": 0.25,
|
|
107
|
+
"text": "U1"
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"availableNetLabelOrientations": {},
|
|
111
|
+
"maxMspPairDistance": 2.4
|
|
112
|
+
}
|