@tscircuit/schematic-trace-solver 0.0.132 → 0.0.134
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 +265 -134
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +12 -6
- package/lib/solvers/AvailableNetOrientationSolver/orderRoutedLabelsBeforeOverlappingPortLabels.ts +47 -0
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +140 -6
- package/package.json +1 -1
- package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +66 -0
- package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +3 -3
- package/tests/repros/repro-type-c-vbus-led-ground-label.test.ts +149 -0
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +20 -1
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
tracePathCrossesAnyBounds,
|
|
32
32
|
tracePathIntersectsBounds,
|
|
33
33
|
} from "./geometry"
|
|
34
|
+
import { orderRoutedLabelsBeforeOverlappingPortLabels } from "./orderRoutedLabelsBeforeOverlappingPortLabels"
|
|
34
35
|
import { getPinMap, getTracePins, toNetLabelPlacementPatch } from "./traces"
|
|
35
36
|
import type {
|
|
36
37
|
AvailableNetOrientationSolverParams,
|
|
@@ -120,7 +121,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
120
121
|
}
|
|
121
122
|
this.sortCrowdedTopBankByPin(indices)
|
|
122
123
|
|
|
123
|
-
const
|
|
124
|
+
const orderedIndices = orderRoutedLabelsBeforeOverlappingPortLabels({
|
|
125
|
+
labelIndices: indices,
|
|
126
|
+
netLabelPlacements: this.outputNetLabelPlacements,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const blockedStandaloneLabelIndex = orderedIndices.find((index) => {
|
|
124
130
|
const label = this.outputNetLabelPlacements[index]!
|
|
125
131
|
const orientations = this.getAvailableOrientations(label)
|
|
126
132
|
return (
|
|
@@ -131,7 +137,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
131
137
|
this.labelIntersectsDifferentNetTrace(label)
|
|
132
138
|
)
|
|
133
139
|
})
|
|
134
|
-
if (blockedStandaloneLabelIndex === undefined) return
|
|
140
|
+
if (blockedStandaloneLabelIndex === undefined) return orderedIndices
|
|
135
141
|
this.blockedStandaloneLabelIndex = blockedStandaloneLabelIndex
|
|
136
142
|
this.blockedStandaloneLabelTargetAnchorX = null
|
|
137
143
|
|
|
@@ -142,8 +148,8 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
142
148
|
const affectedSlots: number[] = []
|
|
143
149
|
const labelsToOrder: number[] = []
|
|
144
150
|
|
|
145
|
-
for (let slot = 0; slot <
|
|
146
|
-
const index =
|
|
151
|
+
for (let slot = 0; slot < orderedIndices.length; slot++) {
|
|
152
|
+
const index = orderedIndices[slot]!
|
|
147
153
|
const label = this.outputNetLabelPlacements[index]!
|
|
148
154
|
const orientations = this.getAvailableOrientations(label)
|
|
149
155
|
if (
|
|
@@ -182,10 +188,10 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
182
188
|
).x
|
|
183
189
|
}
|
|
184
190
|
for (let i = 0; i < affectedSlots.length; i++) {
|
|
185
|
-
|
|
191
|
+
orderedIndices[affectedSlots[i]!] = labelsToOrder[i]!
|
|
186
192
|
}
|
|
187
193
|
|
|
188
|
-
return
|
|
194
|
+
return orderedIndices
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
private sortCrowdedTopBankByPin(indices: number[]) {
|
package/lib/solvers/AvailableNetOrientationSolver/orderRoutedLabelsBeforeOverlappingPortLabels.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
2
|
+
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
3
|
+
import { rectsOverlap } from "./geometry"
|
|
4
|
+
|
|
5
|
+
const isPortOnlyLabel = (label: NetLabelPlacement) =>
|
|
6
|
+
label.mspConnectionPairIds.length === 0
|
|
7
|
+
|
|
8
|
+
export const orderRoutedLabelsBeforeOverlappingPortLabels = ({
|
|
9
|
+
labelIndices,
|
|
10
|
+
netLabelPlacements,
|
|
11
|
+
}: {
|
|
12
|
+
labelIndices: number[]
|
|
13
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
14
|
+
}) => {
|
|
15
|
+
const orderedLabelIndices = [...labelIndices]
|
|
16
|
+
|
|
17
|
+
for (const routedLabelIndex of labelIndices) {
|
|
18
|
+
const routedLabel = netLabelPlacements[routedLabelIndex]!
|
|
19
|
+
if (isPortOnlyLabel(routedLabel)) continue
|
|
20
|
+
|
|
21
|
+
const routedBounds = getRectBounds(
|
|
22
|
+
routedLabel.center,
|
|
23
|
+
routedLabel.width,
|
|
24
|
+
routedLabel.height,
|
|
25
|
+
)
|
|
26
|
+
const blockingPosition = orderedLabelIndices.findIndex((labelIndex) => {
|
|
27
|
+
const portOnlyLabel = netLabelPlacements[labelIndex]!
|
|
28
|
+
if (!isPortOnlyLabel(portOnlyLabel)) return false
|
|
29
|
+
if (portOnlyLabel.globalConnNetId === routedLabel.globalConnNetId) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
const portOnlyBounds = getRectBounds(
|
|
33
|
+
portOnlyLabel.center,
|
|
34
|
+
portOnlyLabel.width,
|
|
35
|
+
portOnlyLabel.height,
|
|
36
|
+
)
|
|
37
|
+
return rectsOverlap(routedBounds, portOnlyBounds)
|
|
38
|
+
})
|
|
39
|
+
const routedPosition = orderedLabelIndices.indexOf(routedLabelIndex)
|
|
40
|
+
if (blockingPosition === -1 || blockingPosition > routedPosition) continue
|
|
41
|
+
|
|
42
|
+
orderedLabelIndices.splice(routedPosition, 1)
|
|
43
|
+
orderedLabelIndices.splice(blockingPosition, 0, routedLabelIndex)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return orderedLabelIndices
|
|
47
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
2
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
3
|
import {
|
|
3
4
|
DEFAULT_MAX_MSP_PAIR_DISTANCE,
|
|
4
5
|
type MspConnectionPair,
|
|
@@ -20,13 +21,118 @@ import { isLabeledPeripheralConnection } from "../MspConnectionPairSolver/isLabe
|
|
|
20
21
|
|
|
21
22
|
const NEAREST_NEIGHBOR_COUNT = 3
|
|
22
23
|
|
|
23
|
-
const distance = (p1:
|
|
24
|
+
const distance = (p1: Point, p2: Point) => {
|
|
24
25
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const manhattanDistance = (p1: InputPin, p2: InputPin) =>
|
|
28
29
|
Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)
|
|
29
30
|
|
|
31
|
+
const EPS = 1e-9
|
|
32
|
+
|
|
33
|
+
const between = (value: number, first: number, second: number) =>
|
|
34
|
+
value >= Math.min(first, second) - EPS &&
|
|
35
|
+
value <= Math.max(first, second) + EPS
|
|
36
|
+
|
|
37
|
+
const getAxisAlignedIntersection = (
|
|
38
|
+
firstStart: Point,
|
|
39
|
+
firstEnd: Point,
|
|
40
|
+
secondStart: Point,
|
|
41
|
+
secondEnd: Point,
|
|
42
|
+
) => {
|
|
43
|
+
const firstIsHorizontal = Math.abs(firstStart.y - firstEnd.y) <= EPS
|
|
44
|
+
const firstIsVertical = Math.abs(firstStart.x - firstEnd.x) <= EPS
|
|
45
|
+
const secondIsHorizontal = Math.abs(secondStart.y - secondEnd.y) <= EPS
|
|
46
|
+
const secondIsVertical = Math.abs(secondStart.x - secondEnd.x) <= EPS
|
|
47
|
+
|
|
48
|
+
if (firstIsHorizontal && secondIsVertical) {
|
|
49
|
+
const point = { x: secondStart.x, y: firstStart.y }
|
|
50
|
+
return between(point.x, firstStart.x, firstEnd.x) &&
|
|
51
|
+
between(point.y, secondStart.y, secondEnd.y)
|
|
52
|
+
? point
|
|
53
|
+
: null
|
|
54
|
+
}
|
|
55
|
+
if (firstIsVertical && secondIsHorizontal) {
|
|
56
|
+
const point = { x: firstStart.x, y: secondStart.y }
|
|
57
|
+
return between(point.y, firstStart.y, firstEnd.y) &&
|
|
58
|
+
between(point.x, secondStart.x, secondEnd.x)
|
|
59
|
+
? point
|
|
60
|
+
: null
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
firstIsHorizontal &&
|
|
64
|
+
secondIsHorizontal &&
|
|
65
|
+
Math.abs(firstStart.y - secondStart.y) <= EPS
|
|
66
|
+
) {
|
|
67
|
+
const overlapMin = Math.max(
|
|
68
|
+
Math.min(firstStart.x, firstEnd.x),
|
|
69
|
+
Math.min(secondStart.x, secondEnd.x),
|
|
70
|
+
)
|
|
71
|
+
const overlapMax = Math.min(
|
|
72
|
+
Math.max(firstStart.x, firstEnd.x),
|
|
73
|
+
Math.max(secondStart.x, secondEnd.x),
|
|
74
|
+
)
|
|
75
|
+
if (overlapMin > overlapMax + EPS) return null
|
|
76
|
+
return {
|
|
77
|
+
x: Math.max(overlapMin, Math.min(firstStart.x, overlapMax)),
|
|
78
|
+
y: firstStart.y,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
firstIsVertical &&
|
|
83
|
+
secondIsVertical &&
|
|
84
|
+
Math.abs(firstStart.x - secondStart.x) <= EPS
|
|
85
|
+
) {
|
|
86
|
+
const overlapMin = Math.max(
|
|
87
|
+
Math.min(firstStart.y, firstEnd.y),
|
|
88
|
+
Math.min(secondStart.y, secondEnd.y),
|
|
89
|
+
)
|
|
90
|
+
const overlapMax = Math.min(
|
|
91
|
+
Math.max(firstStart.y, firstEnd.y),
|
|
92
|
+
Math.max(secondStart.y, secondEnd.y),
|
|
93
|
+
)
|
|
94
|
+
if (overlapMin > overlapMax + EPS) return null
|
|
95
|
+
return {
|
|
96
|
+
x: firstStart.x,
|
|
97
|
+
y: Math.max(overlapMin, Math.min(firstStart.y, overlapMax)),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const clipPathAtFirstIntersection = (
|
|
104
|
+
tracePath: SolvedTracePath["tracePath"],
|
|
105
|
+
existingTraces: SolvedTracePath[],
|
|
106
|
+
) => {
|
|
107
|
+
for (let pathIndex = 0; pathIndex < tracePath.length - 1; pathIndex++) {
|
|
108
|
+
const pathStart = tracePath[pathIndex]!
|
|
109
|
+
const pathEnd = tracePath[pathIndex + 1]!
|
|
110
|
+
const intersections = existingTraces.flatMap((trace) =>
|
|
111
|
+
trace.tracePath.slice(0, -1).flatMap((traceStart, traceIndex) => {
|
|
112
|
+
const intersection = getAxisAlignedIntersection(
|
|
113
|
+
pathStart,
|
|
114
|
+
pathEnd,
|
|
115
|
+
traceStart,
|
|
116
|
+
trace.tracePath[traceIndex + 1]!,
|
|
117
|
+
)
|
|
118
|
+
return intersection ? [intersection] : []
|
|
119
|
+
}),
|
|
120
|
+
)
|
|
121
|
+
intersections.sort(
|
|
122
|
+
(first, second) =>
|
|
123
|
+
distance(pathStart, first) - distance(pathStart, second),
|
|
124
|
+
)
|
|
125
|
+
const intersection = intersections[0]
|
|
126
|
+
if (!intersection) continue
|
|
127
|
+
const clippedPath = tracePath.slice(0, pathIndex + 1)
|
|
128
|
+
if (distance(clippedPath.at(-1)!, intersection) > EPS) {
|
|
129
|
+
clippedPath.push(intersection)
|
|
130
|
+
}
|
|
131
|
+
return clippedPath
|
|
132
|
+
}
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
30
136
|
export class LongDistancePairSolver extends BaseSolver {
|
|
31
137
|
public solvedLongDistanceTraces: SolvedTracePath[] = []
|
|
32
138
|
private queuedCandidatePairs: Array<
|
|
@@ -196,14 +302,42 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
196
302
|
} else if (this.subSolver?.solved) {
|
|
197
303
|
const newTracePath = this.subSolver.solvedTracePath
|
|
198
304
|
if (newTracePath && this.currentCandidatePair) {
|
|
305
|
+
const [p1, p2] = this.currentCandidatePair
|
|
306
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)!
|
|
307
|
+
const sameNetTraces = this.allSolvedTraces.filter(
|
|
308
|
+
(trace) => trace.globalConnNetId === globalConnNetId,
|
|
309
|
+
)
|
|
310
|
+
const inputNetConnection = this.inputProblem.netConnections.find(
|
|
311
|
+
(connection) =>
|
|
312
|
+
connection.pinIds.length === 3 &&
|
|
313
|
+
connection.pinIds.includes(p1.pinId) &&
|
|
314
|
+
connection.pinIds.includes(p2.pinId),
|
|
315
|
+
)
|
|
316
|
+
const sourceChip = this.chipMap[p1.chipId]
|
|
317
|
+
const targetRailTrace = sameNetTraces.find(
|
|
318
|
+
(trace) =>
|
|
319
|
+
trace.pinIds.includes(p2.pinId) &&
|
|
320
|
+
trace.pins.every((pin) => pin.chipId === p2.chipId),
|
|
321
|
+
)
|
|
322
|
+
const canJoinThreePinNet =
|
|
323
|
+
inputNetConnection !== undefined &&
|
|
324
|
+
sourceChip?.pins.length === 2 &&
|
|
325
|
+
targetRailTrace !== undefined
|
|
326
|
+
const junctionTracePath = canJoinThreePinNet
|
|
327
|
+
? clipPathAtFirstIntersection(newTracePath, sameNetTraces)
|
|
328
|
+
: null
|
|
329
|
+
const acceptedTracePath = junctionTracePath ?? newTracePath
|
|
330
|
+
const tracesToCheck = junctionTracePath
|
|
331
|
+
? this.allSolvedTraces.filter(
|
|
332
|
+
(trace) => trace.globalConnNetId !== globalConnNetId,
|
|
333
|
+
)
|
|
334
|
+
: this.allSolvedTraces
|
|
199
335
|
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
200
|
-
|
|
201
|
-
|
|
336
|
+
acceptedTracePath,
|
|
337
|
+
tracesToCheck,
|
|
202
338
|
)
|
|
203
339
|
|
|
204
340
|
if (isTraceClear) {
|
|
205
|
-
const [p1, p2] = this.currentCandidatePair
|
|
206
|
-
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)!
|
|
207
341
|
const mspPairId = `${p1.pinId}-${p2.pinId}`
|
|
208
342
|
|
|
209
343
|
const newSolvedTrace: SolvedTracePath = {
|
|
@@ -211,7 +345,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
211
345
|
dcConnNetId: globalConnNetId,
|
|
212
346
|
globalConnNetId,
|
|
213
347
|
pins: [p1, p2],
|
|
214
|
-
tracePath:
|
|
348
|
+
tracePath: acceptedTracePath,
|
|
215
349
|
mspConnectionPairIds: [mspPairId],
|
|
216
350
|
pinIds: [p1.pinId, p2.pinId],
|
|
217
351
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><polyline data-points="0.4,1.5 2.5,1.5" data-type="line" data-label="" points="339.77220956719816,237.08428246013665 473.71298405466973,237.08428246013665" fill="none" stroke="hsl(238, 100%, 50%, 0.8)" stroke-width="1px"/></g><g><polyline data-points="-1.7,1.65 -1.7,1.3499999999999999" data-type="line" data-label="" points="205.83143507972665,227.51708428246013 205.83143507972665,246.65148063781322" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,1.65 -0.4,1.5" data-type="line" data-label="" points="205.83143507972665,227.51708428246013 288.74715261959,237.08428246013665" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,1.3499999999999999 -0.4,1.5" data-type="line" data-label="" points="205.83143507972665,246.65148063781322 288.74715261959,237.08428246013665" fill="none" stroke="hsl(170, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.35 -1.7,-1.65" data-type="line" data-label="" points="205.83143507972665,418.8610478359909 205.83143507972665,437.99544419134395" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.35 3.5,1.5" data-type="line" data-label="" points="205.83143507972665,418.8610478359909 537.49430523918,237.08428246013665" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="-1.7,-1.65 3.5,1.5" data-type="line" data-label="" points="205.83143507972665,437.99544419134395 537.49430523918,237.08428246013665" fill="none" stroke="hsl(124, 100%, 50%, 0.8)" stroke-width="1px" stroke-dasharray="4 2"/></g><g><polyline data-points="0.4,1.5 2.5,1.5" data-type="line" data-label="" points="339.77220956719816,237.08428246013665 473.71298405466973,237.08428246013665" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.7,1.3499999999999999 -1.5,1.3499999999999999 -1.5,1.65 -1.7,1.65" data-type="line" data-label="" points="205.83143507972665,246.65148063781322 218.5876993166287,246.65148063781322 218.5876993166287,227.51708428246013 205.83143507972665,227.51708428246013" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.7,-1.65 -1.5,-1.65 -1.5,-1.35 -1.7,-1.35" data-type="line" data-label="" points="205.83143507972665,437.99544419134395 218.5876993166287,437.99544419134395 218.5876993166287,418.8610478359909 205.83143507972665,418.8610478359909" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="3.5,1.5 3.7,1.5 3.7,-1.35 -1.5,-1.35" data-type="line" data-label="" points="537.49430523918,237.08428246013665 550.2505694760821,237.08428246013665 550.2505694760821,418.8610478359909 218.5876993166287,418.8610478359909" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-0.4,1.5 -1.5,1.5" data-type="line" data-label="" points="288.74715261959,237.08428246013665 218.5876993166287,237.08428246013665" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="J1" data-x="-3" data-y="0" x="40" y="205.19362186788152" width="165.83143507972665" height="255.12528473804105" fill="hsl(183, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="R1" data-x="0" data-y="1.5" x="288.74715261959" y="221.13895216400908" width="51.025056947608164" height="31.89066059225513" fill="hsl(71, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="D1" data-x="3" data-y="1.5" x="473.71298405466973" y="211.57175398633254" width="63.78132118451026" height="51.02505694760822" fill="hsl(357, 100%, 50%, 0.8)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="J1 TYPE-C-31-M-12" data-x="-3" data-y="2.3" x="71.89066059225513" y="179.68109339407744" width="102.05011389521638" height="12.75626423690207" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="R1 1kΩ" data-x="0" data-y="1.95" x="301.50341685649204" y="202.004555808656" width="25.512528473804082" height="12.75626423690207" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="D1 white" data-x="3" data-y="2.15" x="489.6583143507973" y="189.24829157175398" width="31.89066059225513" height="12.756264236902041" fill="rgba(160, 0, 220, 0.14)" stroke="black" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: VBUS
|
|
2
|
+
globalConnNetId: connectivity_net1" data-x="-0.95" data-y="1.74" x="238.3599088838269" y="206.46924829157177" width="30.615034168564904" height="30.615034168564904" fill="#ef444466" stroke="#ef4444" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: J1_GND1
|
|
3
|
+
globalConnNetId: connectivity_net2" data-x="4.09" data-y="1.5" x="550.2505694760821" y="221.7767653758542" width="49.74943052391802" height="30.615034168564904" fill="#00000066" stroke="#000000" stroke-width="0.015678571428571427"/></g><g><rect data-type="rect" data-label="netId: VBUS_LED
|
|
4
|
+
globalConnNetId: connectivity_net0" data-x="1.45" data-y="1.725" x="400.3644646924829" y="208.38268792710704" width="12.756264236902098" height="28.701594533029606" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015678571428571427"/></g><g><circle data-type="point" data-label="J1.VBUS1
|
|
5
|
+
x+" data-x="-1.7" data-y="1.65" cx="205.83143507972665" cy="227.51708428246013" r="3" fill="hsl(208, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.VBUS2
|
|
6
|
+
x+" data-x="-1.7" data-y="1.3499999999999999" cx="205.83143507972665" cy="246.65148063781322" r="3" fill="hsl(209, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.CC1
|
|
7
|
+
x+" data-x="-1.7" data-y="1.0499999999999998" cx="205.83143507972665" cy="265.7858769931663" r="3" fill="hsl(322, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.CC2
|
|
8
|
+
x+" data-x="-1.7" data-y="0.75" cx="205.83143507972665" cy="284.92027334851934" r="3" fill="hsl(323, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DP1
|
|
9
|
+
x+" data-x="-1.7" data-y="0.44999999999999996" cx="205.83143507972665" cy="304.05466970387243" r="3" fill="hsl(246, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DP2
|
|
10
|
+
x+" data-x="-1.7" data-y="0.1499999999999999" cx="205.83143507972665" cy="323.1890660592255" r="3" fill="hsl(247, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DM1
|
|
11
|
+
x+" data-x="-1.7" data-y="-0.1499999999999999" cx="205.83143507972665" cy="342.32346241457856" r="3" fill="hsl(153, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.DM2
|
|
12
|
+
x+" data-x="-1.7" data-y="-0.4500000000000002" cx="205.83143507972665" cy="361.45785876993165" r="3" fill="hsl(154, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.SBU1
|
|
13
|
+
x+" data-x="-1.7" data-y="-0.75" cx="205.83143507972665" cy="380.59225512528474" r="3" fill="hsl(122, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.SBU2
|
|
14
|
+
x+" data-x="-1.7" data-y="-1.0499999999999998" cx="205.83143507972665" cy="399.72665148063777" r="3" fill="hsl(123, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.GND1
|
|
15
|
+
x+" data-x="-1.7" data-y="-1.35" cx="205.83143507972665" cy="418.8610478359909" r="3" fill="hsl(315, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="J1.GND2
|
|
16
|
+
x+" data-x="-1.7" data-y="-1.65" cx="205.83143507972665" cy="437.99544419134395" r="3" fill="hsl(316, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.1
|
|
17
|
+
x-" data-x="-0.4" data-y="1.5" cx="288.74715261959" cy="237.08428246013665" r="3" fill="hsl(226, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="R1.2
|
|
18
|
+
x+" data-x="0.4" data-y="1.5" cx="339.77220956719816" cy="237.08428246013665" r="3" fill="hsl(227, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="D1.1
|
|
19
|
+
x-" data-x="2.5" data-y="1.5" cx="473.71298405466973" cy="237.08428246013665" r="3" fill="hsl(32, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="D1.2
|
|
20
|
+
x+" data-x="3.5" data-y="1.5" cx="537.49430523918" cy="237.08428246013665" r="3" fill="hsl(33, 100%, 50%, 0.8)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
21
|
+
orientation: y+" data-x="-0.95" data-y="1.5" cx="253.66742596810934" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
22
|
+
orientation: x+" data-x="3.7" data-y="1.5" cx="550.2505694760821" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g><circle data-type="point" data-label="anchorPoint
|
|
23
|
+
orientation: y+" data-x="1.45" data-y="1.5" cx="406.74259681093395" cy="237.08428246013665" r="3" fill="hsl(40, 100%, 50%, 0.9)"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
24
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
25
|
+
const svg = e.currentTarget;
|
|
26
|
+
const rect = svg.getBoundingClientRect();
|
|
27
|
+
const x = e.clientX - rect.left;
|
|
28
|
+
const y = e.clientY - rect.top;
|
|
29
|
+
const crosshair = svg.getElementById('crosshair');
|
|
30
|
+
const h = svg.getElementById('crosshair-h');
|
|
31
|
+
const v = svg.getElementById('crosshair-v');
|
|
32
|
+
const coords = svg.getElementById('coordinates');
|
|
33
|
+
|
|
34
|
+
crosshair.style.display = 'block';
|
|
35
|
+
h.setAttribute('x1', '0');
|
|
36
|
+
h.setAttribute('x2', '640');
|
|
37
|
+
h.setAttribute('y1', y);
|
|
38
|
+
h.setAttribute('y2', y);
|
|
39
|
+
v.setAttribute('x1', x);
|
|
40
|
+
v.setAttribute('x2', x);
|
|
41
|
+
v.setAttribute('y1', '0');
|
|
42
|
+
v.setAttribute('y2', '640');
|
|
43
|
+
|
|
44
|
+
// Calculate real coordinates using inverse transformation
|
|
45
|
+
const matrix = {"a":63.781321184510254,"c":0,"e":314.2596810933941,"b":0,"d":-63.781321184510254,"f":332.75626423690204};
|
|
46
|
+
// Manually invert and apply the affine transform
|
|
47
|
+
// Since we only use translate and scale, we can directly compute:
|
|
48
|
+
// x' = (x - tx) / sx
|
|
49
|
+
// y' = (y - ty) / sy
|
|
50
|
+
const sx = matrix.a;
|
|
51
|
+
const sy = matrix.d;
|
|
52
|
+
const tx = matrix.e;
|
|
53
|
+
const ty = matrix.f;
|
|
54
|
+
const realPoint = {
|
|
55
|
+
x: (x - tx) / sx,
|
|
56
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
60
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
61
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
62
|
+
});
|
|
63
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
64
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
65
|
+
});
|
|
66
|
+
]]></script></svg>
|