@tscircuit/schematic-trace-solver 0.0.68 → 0.0.70
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 +86 -29
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +12 -3
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +11 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +13 -8
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts +54 -20
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts +45 -10
- package/package.json +1 -1
- package/site/examples/example42.page.tsx +4 -0
- package/tests/assets/example42.json +139 -0
- package/tests/examples/__snapshots__/example03.snap.svg +66 -66
- package/tests/examples/__snapshots__/example16.snap.svg +5 -9
- package/tests/examples/__snapshots__/example20.snap.svg +4 -7
- package/tests/examples/__snapshots__/example22.snap.svg +5 -9
- package/tests/examples/__snapshots__/example32.snap.svg +108 -93
- package/tests/examples/__snapshots__/example42.snap.svg +195 -0
- package/tests/examples/example42.test.ts +12 -0
- package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +396 -0
- package/tests/repros/assets/repro-bq24074-battery-charger.input.json +219 -0
- package/tests/repros/repro-bq24074-battery-charger.test.ts +18 -0
package/dist/index.js
CHANGED
|
@@ -1234,12 +1234,43 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
|
|
|
1234
1234
|
}) {
|
|
1235
1235
|
const blockingCollisions = this.getBlockingCollisions({ group, offset });
|
|
1236
1236
|
if (blockingCollisions.length === 0) return offset;
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
(
|
|
1242
|
-
|
|
1237
|
+
const crossesThroughObstacle = (candidateOffset) => blockingCollisions.some(({ start, isVertical: isVertical4, obstacle }) => {
|
|
1238
|
+
let orig;
|
|
1239
|
+
let min;
|
|
1240
|
+
let max;
|
|
1241
|
+
if (isVertical4) {
|
|
1242
|
+
orig = start.x;
|
|
1243
|
+
min = obstacle.minX;
|
|
1244
|
+
max = obstacle.maxX;
|
|
1245
|
+
} else {
|
|
1246
|
+
orig = start.y;
|
|
1247
|
+
min = obstacle.minY;
|
|
1248
|
+
max = obstacle.maxY;
|
|
1249
|
+
}
|
|
1250
|
+
const next = orig + candidateOffset;
|
|
1251
|
+
return orig <= min + EPS4 && next >= max - EPS4 || orig >= max - EPS4 && next <= min + EPS4;
|
|
1252
|
+
});
|
|
1253
|
+
const edges = blockingCollisions.map(({ start, isVertical: isVertical4, obstacle }) => {
|
|
1254
|
+
if (isVertical4) {
|
|
1255
|
+
return {
|
|
1256
|
+
coord: start.x,
|
|
1257
|
+
lowEdge: obstacle.minX,
|
|
1258
|
+
highEdge: obstacle.maxX
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
return { coord: start.y, lowEdge: obstacle.minY, highEdge: obstacle.maxY };
|
|
1262
|
+
});
|
|
1263
|
+
const bestCandidate = (clearance) => {
|
|
1264
|
+
const candidates = edges.flatMap(({ coord, lowEdge, highEdge }) => [
|
|
1265
|
+
lowEdge - coord - clearance,
|
|
1266
|
+
highEdge - coord + clearance
|
|
1267
|
+
]);
|
|
1268
|
+
return candidates.filter((candidateOffset) => !crossesThroughObstacle(candidateOffset)).filter(
|
|
1269
|
+
(candidateOffset) => this.getBlockingCollisions({ group, offset: candidateOffset }).length === 0
|
|
1270
|
+
).sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0];
|
|
1271
|
+
};
|
|
1272
|
+
const EDGE_MARGIN = this.SHIFT_DISTANCE / 10;
|
|
1273
|
+
return bestCandidate(this.SHIFT_DISTANCE) ?? bestCandidate(EDGE_MARGIN) ?? offset;
|
|
1243
1274
|
}
|
|
1244
1275
|
getBlockingCollisions({
|
|
1245
1276
|
group,
|
|
@@ -2799,19 +2830,32 @@ var detectTraceLabelOverlap = ({
|
|
|
2799
2830
|
};
|
|
2800
2831
|
|
|
2801
2832
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts
|
|
2802
|
-
var findTraceViolationZone = (
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2833
|
+
var findTraceViolationZone = ({
|
|
2834
|
+
path,
|
|
2835
|
+
labelBounds,
|
|
2836
|
+
paddedBounds = labelBounds
|
|
2837
|
+
}) => {
|
|
2838
|
+
let firstCollidingSeg = -1;
|
|
2839
|
+
let lastCollidingSeg = -1;
|
|
2840
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
2841
|
+
if (segmentIntersectsRect2(path[i], path[i + 1], labelBounds)) {
|
|
2842
|
+
if (firstCollidingSeg === -1) firstCollidingSeg = i;
|
|
2843
|
+
lastCollidingSeg = i;
|
|
2812
2844
|
}
|
|
2813
2845
|
}
|
|
2814
|
-
|
|
2846
|
+
if (firstCollidingSeg === -1) {
|
|
2847
|
+
return { firstInsideIndex: -1, lastInsideIndex: -1 };
|
|
2848
|
+
}
|
|
2849
|
+
const isInsidePadded = (p) => p.x > paddedBounds.minX && p.x < paddedBounds.maxX && p.y > paddedBounds.minY && p.y < paddedBounds.maxY;
|
|
2850
|
+
let entryIndex = firstCollidingSeg;
|
|
2851
|
+
while (entryIndex > 0 && isInsidePadded(path[entryIndex])) {
|
|
2852
|
+
entryIndex--;
|
|
2853
|
+
}
|
|
2854
|
+
let exitIndex = lastCollidingSeg + 1;
|
|
2855
|
+
while (exitIndex < path.length - 1 && isInsidePadded(path[exitIndex])) {
|
|
2856
|
+
exitIndex++;
|
|
2857
|
+
}
|
|
2858
|
+
return { firstInsideIndex: entryIndex + 1, lastInsideIndex: exitIndex - 1 };
|
|
2815
2859
|
};
|
|
2816
2860
|
|
|
2817
2861
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts
|
|
@@ -2917,20 +2961,21 @@ var generateFourPointDetourCandidates = ({
|
|
|
2917
2961
|
}
|
|
2918
2962
|
exitPoint = initialTrace.tracePath[exitIndex];
|
|
2919
2963
|
} else {
|
|
2920
|
-
let
|
|
2964
|
+
let firstCollidingSeg = -1;
|
|
2965
|
+
let lastCollidingSeg = -1;
|
|
2921
2966
|
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
2922
2967
|
if (segmentIntersectsRect(
|
|
2923
2968
|
initialTrace.tracePath[i],
|
|
2924
2969
|
initialTrace.tracePath[i + 1],
|
|
2925
|
-
{ ...
|
|
2970
|
+
{ ...labelBounds, chipId: "temp-obstacle" }
|
|
2926
2971
|
)) {
|
|
2927
|
-
|
|
2928
|
-
|
|
2972
|
+
if (firstCollidingSeg === -1) firstCollidingSeg = i;
|
|
2973
|
+
lastCollidingSeg = i;
|
|
2929
2974
|
}
|
|
2930
2975
|
}
|
|
2931
|
-
if (
|
|
2932
|
-
entryIndex =
|
|
2933
|
-
exitIndex =
|
|
2976
|
+
if (firstCollidingSeg === -1) return [];
|
|
2977
|
+
entryIndex = firstCollidingSeg;
|
|
2978
|
+
exitIndex = lastCollidingSeg + 1;
|
|
2934
2979
|
entryPoint = initialTrace.tracePath[entryIndex];
|
|
2935
2980
|
exitPoint = initialTrace.tracePath[exitIndex];
|
|
2936
2981
|
}
|
|
@@ -3039,10 +3084,18 @@ var generateRerouteCandidates = ({
|
|
|
3039
3084
|
paddingBuffer,
|
|
3040
3085
|
detourCount
|
|
3041
3086
|
});
|
|
3042
|
-
const
|
|
3043
|
-
|
|
3044
|
-
labelBounds
|
|
3045
|
-
|
|
3087
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
3088
|
+
const paddedLabelBounds = {
|
|
3089
|
+
minX: labelBounds.minX - effectivePadding,
|
|
3090
|
+
maxX: labelBounds.maxX + effectivePadding,
|
|
3091
|
+
minY: labelBounds.minY - effectivePadding,
|
|
3092
|
+
maxY: labelBounds.maxY + effectivePadding
|
|
3093
|
+
};
|
|
3094
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone({
|
|
3095
|
+
path: initialTrace.tracePath,
|
|
3096
|
+
labelBounds,
|
|
3097
|
+
paddedBounds: paddedLabelBounds
|
|
3098
|
+
});
|
|
3046
3099
|
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
3047
3100
|
initialTrace,
|
|
3048
3101
|
firstInsideIndex,
|
|
@@ -3131,7 +3184,11 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
3131
3184
|
this._tried++;
|
|
3132
3185
|
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
3133
3186
|
const simplifiedPath = simplifyPath(nextCandidatePath);
|
|
3134
|
-
|
|
3187
|
+
const stillOverlapsLabel = detectTraceLabelOverlap({
|
|
3188
|
+
traces: [{ ...this.initialTrace, tracePath: simplifiedPath }],
|
|
3189
|
+
netLabels: [this.label]
|
|
3190
|
+
}).length > 0;
|
|
3191
|
+
if (!stillOverlapsLabel && !isPathCollidingWithObstacles(simplifiedPath, this.obstacles) && !doesPathCoincideWithTraces(simplifiedPath, this.tracesToAvoidOverlapping)) {
|
|
3135
3192
|
this.solvedTracePath = simplifiedPath;
|
|
3136
3193
|
this.solved = true;
|
|
3137
3194
|
}
|
|
@@ -55,10 +55,19 @@ export const generateRerouteCandidates = ({
|
|
|
55
55
|
detourCount,
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer
|
|
59
|
+
const paddedLabelBounds = {
|
|
60
|
+
minX: labelBounds.minX - effectivePadding,
|
|
61
|
+
maxX: labelBounds.maxX + effectivePadding,
|
|
62
|
+
minY: labelBounds.minY - effectivePadding,
|
|
63
|
+
maxY: labelBounds.maxY + effectivePadding,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone({
|
|
67
|
+
path: initialTrace.tracePath,
|
|
60
68
|
labelBounds,
|
|
61
|
-
|
|
69
|
+
paddedBounds: paddedLabelBounds,
|
|
70
|
+
})
|
|
62
71
|
|
|
63
72
|
const snipReconnectCandidates = generateSnipAndReconnectCandidates({
|
|
64
73
|
initialTrace,
|
|
@@ -9,6 +9,7 @@ import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/Schemati
|
|
|
9
9
|
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
10
10
|
import { generateRerouteCandidates } from "../../rerouteCollidingTrace"
|
|
11
11
|
import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
|
|
12
|
+
import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap"
|
|
12
13
|
|
|
13
14
|
interface SingleOverlapSolverInput {
|
|
14
15
|
trace: SolvedTracePath
|
|
@@ -141,7 +142,17 @@ export class SingleOverlapSolver extends BaseSolver {
|
|
|
141
142
|
const nextCandidatePath = this.queuedCandidatePaths.shift()!
|
|
142
143
|
const simplifiedPath = simplifyPath(nextCandidatePath)
|
|
143
144
|
|
|
145
|
+
// A candidate is only valid if it actually clears the label it is meant to
|
|
146
|
+
// avoid. Without this check a "detour" that still grazes the label (e.g. one
|
|
147
|
+
// built around the wrong segment) would be accepted as solved.
|
|
148
|
+
const stillOverlapsLabel =
|
|
149
|
+
detectTraceLabelOverlap({
|
|
150
|
+
traces: [{ ...this.initialTrace, tracePath: simplifiedPath }],
|
|
151
|
+
netLabels: [this.label],
|
|
152
|
+
}).length > 0
|
|
153
|
+
|
|
144
154
|
if (
|
|
155
|
+
!stillOverlapsLabel &&
|
|
145
156
|
!isPathCollidingWithObstacles(simplifiedPath, this.obstacles) &&
|
|
146
157
|
!doesPathCoincideWithTraces(simplifiedPath, this.tracesToAvoidOverlapping)
|
|
147
158
|
) {
|
|
@@ -71,25 +71,30 @@ export const generateFourPointDetourCandidates = ({
|
|
|
71
71
|
}
|
|
72
72
|
exitPoint = initialTrace.tracePath[exitIndex]
|
|
73
73
|
} else {
|
|
74
|
-
// STRATEGY 1: Single Label -
|
|
75
|
-
|
|
74
|
+
// STRATEGY 1: Single Label - reroute around the contiguous span of segments
|
|
75
|
+
// that actually overlap the label. Detection uses the *unpadded* bounds so
|
|
76
|
+
// a short pin stub that merely pokes into the padding zone is not mistaken
|
|
77
|
+
// for the real crossing (which would leave the offending segment in place
|
|
78
|
+
// and create a useless loop).
|
|
79
|
+
let firstCollidingSeg = -1
|
|
80
|
+
let lastCollidingSeg = -1
|
|
76
81
|
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
77
82
|
if (
|
|
78
83
|
segmentIntersectsRect(
|
|
79
84
|
initialTrace.tracePath[i],
|
|
80
85
|
initialTrace.tracePath[i + 1],
|
|
81
|
-
{ ...
|
|
86
|
+
{ ...labelBounds, chipId: "temp-obstacle" },
|
|
82
87
|
)
|
|
83
88
|
) {
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
if (firstCollidingSeg === -1) firstCollidingSeg = i
|
|
90
|
+
lastCollidingSeg = i
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
93
|
|
|
89
|
-
if (
|
|
94
|
+
if (firstCollidingSeg === -1) return []
|
|
90
95
|
|
|
91
|
-
entryIndex =
|
|
92
|
-
exitIndex =
|
|
96
|
+
entryIndex = firstCollidingSeg
|
|
97
|
+
exitIndex = lastCollidingSeg + 1
|
|
93
98
|
entryPoint = initialTrace.tracePath[entryIndex]
|
|
94
99
|
exitPoint = initialTrace.tracePath[exitIndex]
|
|
95
100
|
}
|
|
@@ -1,25 +1,59 @@
|
|
|
1
|
-
import type { Point } from "@tscircuit/math-utils"
|
|
1
|
+
import type { Point, Bounds } from "@tscircuit/math-utils"
|
|
2
|
+
import { segmentIntersectsRect } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Locates the portion of a trace that must be rerouted to clear a label.
|
|
6
|
+
*
|
|
7
|
+
* Detection is segment-based against the (unpadded) label bounds so it also
|
|
8
|
+
* catches traces that cross the label edge-to-edge, or run *along* one of its
|
|
9
|
+
* edges, without ever placing a vertex strictly inside it (the previous
|
|
10
|
+
* vertex-only test missed both cases).
|
|
11
|
+
*
|
|
12
|
+
* The returned range describes the vertices to remove: the reroute reconnects
|
|
13
|
+
* `path[firstInsideIndex - 1]` to `path[lastInsideIndex + 1]`. Those two anchor
|
|
14
|
+
* vertices are pushed outward until they sit outside the padded label so the
|
|
15
|
+
* detour has room to pivot without immediately re-entering it.
|
|
16
|
+
*/
|
|
17
|
+
export const findTraceViolationZone = ({
|
|
18
|
+
path,
|
|
19
|
+
labelBounds,
|
|
20
|
+
paddedBounds = labelBounds,
|
|
21
|
+
}: {
|
|
22
|
+
path: Point[]
|
|
23
|
+
labelBounds: Bounds
|
|
24
|
+
paddedBounds?: Bounds
|
|
25
|
+
}) => {
|
|
26
|
+
let firstCollidingSeg = -1
|
|
27
|
+
let lastCollidingSeg = -1
|
|
28
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
29
|
+
if (segmentIntersectsRect(path[i]!, path[i + 1]!, labelBounds)) {
|
|
30
|
+
if (firstCollidingSeg === -1) firstCollidingSeg = i
|
|
31
|
+
lastCollidingSeg = i
|
|
32
|
+
}
|
|
33
|
+
}
|
|
12
34
|
|
|
13
|
-
|
|
14
|
-
|
|
35
|
+
if (firstCollidingSeg === -1) {
|
|
36
|
+
return { firstInsideIndex: -1, lastInsideIndex: -1 }
|
|
37
|
+
}
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
const isInsidePadded = (p: Point) =>
|
|
40
|
+
p.x > paddedBounds.minX &&
|
|
41
|
+
p.x < paddedBounds.maxX &&
|
|
42
|
+
p.y > paddedBounds.minY &&
|
|
43
|
+
p.y < paddedBounds.maxY
|
|
44
|
+
|
|
45
|
+
// Anchor before the crossing: walk back to the first vertex clear of the
|
|
46
|
+
// padded label.
|
|
47
|
+
let entryIndex = firstCollidingSeg
|
|
48
|
+
while (entryIndex > 0 && isInsidePadded(path[entryIndex]!)) {
|
|
49
|
+
entryIndex--
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Anchor after the crossing.
|
|
53
|
+
let exitIndex = lastCollidingSeg + 1
|
|
54
|
+
while (exitIndex < path.length - 1 && isInsidePadded(path[exitIndex]!)) {
|
|
55
|
+
exitIndex++
|
|
23
56
|
}
|
|
24
|
-
|
|
57
|
+
|
|
58
|
+
return { firstInsideIndex: entryIndex + 1, lastInsideIndex: exitIndex - 1 }
|
|
25
59
|
}
|
package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
CHANGED
|
@@ -180,21 +180,56 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
180
180
|
const blockingCollisions = this.getBlockingCollisions({ group, offset })
|
|
181
181
|
if (blockingCollisions.length === 0) return offset
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
blockingCollisions
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
const crossesThroughObstacle = (candidateOffset: number) =>
|
|
184
|
+
blockingCollisions.some(({ start, isVertical, obstacle }) => {
|
|
185
|
+
let orig: number
|
|
186
|
+
let min: number
|
|
187
|
+
let max: number
|
|
188
|
+
if (isVertical) {
|
|
189
|
+
orig = start.x
|
|
190
|
+
min = obstacle.minX
|
|
191
|
+
max = obstacle.maxX
|
|
192
|
+
} else {
|
|
193
|
+
orig = start.y
|
|
194
|
+
min = obstacle.minY
|
|
195
|
+
max = obstacle.maxY
|
|
196
|
+
}
|
|
197
|
+
const next = orig + candidateOffset
|
|
198
|
+
return (
|
|
199
|
+
(orig <= min + EPS && next >= max - EPS) ||
|
|
200
|
+
(orig >= max - EPS && next <= min + EPS)
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const edges = blockingCollisions.map(({ start, isVertical, obstacle }) => {
|
|
205
|
+
if (isVertical) {
|
|
206
|
+
return {
|
|
207
|
+
coord: start.x,
|
|
208
|
+
lowEdge: obstacle.minX,
|
|
209
|
+
highEdge: obstacle.maxX,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { coord: start.y, lowEdge: obstacle.minY, highEdge: obstacle.maxY }
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
const bestCandidate = (clearance: number) => {
|
|
216
|
+
const candidates = edges.flatMap(({ coord, lowEdge, highEdge }) => [
|
|
217
|
+
lowEdge - coord - clearance,
|
|
218
|
+
highEdge - coord + clearance,
|
|
219
|
+
])
|
|
220
|
+
return candidates
|
|
221
|
+
.filter((candidateOffset) => !crossesThroughObstacle(candidateOffset))
|
|
191
222
|
.filter(
|
|
192
223
|
(candidateOffset) =>
|
|
193
224
|
this.getBlockingCollisions({ group, offset: candidateOffset })
|
|
194
225
|
.length === 0,
|
|
195
226
|
)
|
|
196
|
-
.sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0]
|
|
197
|
-
|
|
227
|
+
.sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const EDGE_MARGIN = this.SHIFT_DISTANCE / 10
|
|
231
|
+
return (
|
|
232
|
+
bestCandidate(this.SHIFT_DISTANCE) ?? bestCandidate(EDGE_MARGIN) ?? offset
|
|
198
233
|
)
|
|
199
234
|
}
|
|
200
235
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_16",
|
|
5
|
+
"center": {
|
|
6
|
+
"x": 0,
|
|
7
|
+
"y": 0
|
|
8
|
+
},
|
|
9
|
+
"width": 4.4,
|
|
10
|
+
"height": 2,
|
|
11
|
+
"pins": [
|
|
12
|
+
{
|
|
13
|
+
"pinId": "U3.1",
|
|
14
|
+
"x": 2.6,
|
|
15
|
+
"y": 0.8499999999999999
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"pinId": "U3.2",
|
|
19
|
+
"x": 2.6,
|
|
20
|
+
"y": 0.6499999999999999
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"pinId": "U3.3",
|
|
24
|
+
"x": 2.6,
|
|
25
|
+
"y": 0.44999999999999996
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"pinId": "U3.4",
|
|
29
|
+
"x": 2.6,
|
|
30
|
+
"y": 0.10000000000000009
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"pinId": "U3.5",
|
|
34
|
+
"x": 2.6,
|
|
35
|
+
"y": -0.09999999999999987
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"pinId": "U3.6",
|
|
39
|
+
"x": 2.6,
|
|
40
|
+
"y": -0.44999999999999984
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"pinId": "U3.7",
|
|
44
|
+
"x": 2.6,
|
|
45
|
+
"y": -0.6499999999999999
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"pinId": "U3.8",
|
|
49
|
+
"x": 2.6,
|
|
50
|
+
"y": -0.8499999999999999
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"chipId": "schematic_component_17",
|
|
56
|
+
"center": {
|
|
57
|
+
"x": 3.2,
|
|
58
|
+
"y": -1.4
|
|
59
|
+
},
|
|
60
|
+
"width": 1.1,
|
|
61
|
+
"height": 0.7789106999999986,
|
|
62
|
+
"pins": [
|
|
63
|
+
{
|
|
64
|
+
"pinId": "R8.1",
|
|
65
|
+
"x": 2.6500000000000004,
|
|
66
|
+
"y": -1.4
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"pinId": "R8.2",
|
|
70
|
+
"x": 3.75,
|
|
71
|
+
"y": -1.4
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"chipId": "schematic_component_18",
|
|
77
|
+
"center": {
|
|
78
|
+
"x": 3.2,
|
|
79
|
+
"y": -2.4
|
|
80
|
+
},
|
|
81
|
+
"width": 1.1,
|
|
82
|
+
"height": 0.7789106999999986,
|
|
83
|
+
"pins": [
|
|
84
|
+
{
|
|
85
|
+
"pinId": "R9.1",
|
|
86
|
+
"x": 2.6500000000000004,
|
|
87
|
+
"y": -2.4
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"pinId": "R9.2",
|
|
91
|
+
"x": 3.75,
|
|
92
|
+
"y": -2.4
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"directConnections": [],
|
|
98
|
+
"netConnections": [
|
|
99
|
+
{
|
|
100
|
+
"netId": "GND",
|
|
101
|
+
"pinIds": ["U3.8", "R8.2", "R9.2"],
|
|
102
|
+
"netLabelWidth": 0.48
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"netId": "D_MINUS",
|
|
106
|
+
"pinIds": ["U3.3"],
|
|
107
|
+
"netLabelWidth": 0.96
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"netId": "D_PLUS",
|
|
111
|
+
"pinIds": ["U3.2"],
|
|
112
|
+
"netLabelWidth": 0.84
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"netId": "VBUS",
|
|
116
|
+
"pinIds": ["U3.1"],
|
|
117
|
+
"netLabelWidth": 0.6
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"netId": "CC1",
|
|
121
|
+
"pinIds": ["U3.4", "R8.1"],
|
|
122
|
+
"netLabelWidth": 0.48
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"netId": "CC2",
|
|
126
|
+
"pinIds": ["U3.5", "R9.1"],
|
|
127
|
+
"netLabelWidth": 0.48
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"availableNetLabelOrientations": {
|
|
131
|
+
"D_MINUS": ["x-", "x+"],
|
|
132
|
+
"D_PLUS": ["x-", "x+"],
|
|
133
|
+
"GND": ["y-"],
|
|
134
|
+
"VBUS": ["y+"],
|
|
135
|
+
"CC1": ["x-", "x+"],
|
|
136
|
+
"CC2": ["x-", "x+"]
|
|
137
|
+
},
|
|
138
|
+
"maxMspPairDistance": 2.4
|
|
139
|
+
}
|