@tscircuit/schematic-trace-solver 0.0.49 → 0.0.50

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.
@@ -0,0 +1,192 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
+ import type { InputProblem } from "lib/types/InputProblem"
4
+ import type { FacingDirection } from "lib/utils/dir"
5
+ import { EPS, TRACE_BOUNDARY_TOLERANCE } from "./constants"
6
+ import type { Bounds, ChipSide } from "./types"
7
+
8
+ export const isYOrientation = (
9
+ orientation: FacingDirection,
10
+ ): orientation is "y+" | "y-" => orientation === "y+" || orientation === "y-"
11
+
12
+ export const isXOrientation = (
13
+ orientation: FacingDirection,
14
+ ): orientation is "x+" | "x-" => orientation === "x+" || orientation === "x-"
15
+
16
+ export const rectsOverlap = (a: Bounds, b: Bounds) =>
17
+ Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS &&
18
+ Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS
19
+
20
+ export const rangesOverlap = (
21
+ minA: number,
22
+ maxA: number,
23
+ minB: number,
24
+ maxB: number,
25
+ ) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS
26
+
27
+ export const traceCrossesBoundsInterior = (
28
+ bounds: Bounds,
29
+ traceMap: Record<string, SolvedTracePath>,
30
+ ) => {
31
+ for (const trace of Object.values(traceMap)) {
32
+ const points = trace.tracePath
33
+ for (let i = 0; i < points.length - 1; i++) {
34
+ if (segmentCrossesBoundsInterior(points[i]!, points[i + 1]!, bounds)) {
35
+ return true
36
+ }
37
+ }
38
+ }
39
+
40
+ return false
41
+ }
42
+
43
+ const segmentCrossesBoundsInterior = (p1: Point, p2: Point, bounds: Bounds) => {
44
+ const interiorBounds = {
45
+ minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE,
46
+ minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE,
47
+ maxX: bounds.maxX - TRACE_BOUNDARY_TOLERANCE,
48
+ maxY: bounds.maxY - TRACE_BOUNDARY_TOLERANCE,
49
+ }
50
+
51
+ if (
52
+ interiorBounds.minX >= interiorBounds.maxX ||
53
+ interiorBounds.minY >= interiorBounds.maxY
54
+ ) {
55
+ return false
56
+ }
57
+
58
+ if (sameX(p1, p2)) {
59
+ if (
60
+ p1.x <= interiorBounds.minX + EPS ||
61
+ p1.x >= interiorBounds.maxX - EPS
62
+ ) {
63
+ return false
64
+ }
65
+ return rangesOverlap(
66
+ Math.min(p1.y, p2.y),
67
+ Math.max(p1.y, p2.y),
68
+ interiorBounds.minY,
69
+ interiorBounds.maxY,
70
+ )
71
+ }
72
+
73
+ if (sameY(p1, p2)) {
74
+ if (
75
+ p1.y <= interiorBounds.minY + EPS ||
76
+ p1.y >= interiorBounds.maxY - EPS
77
+ ) {
78
+ return false
79
+ }
80
+ return rangesOverlap(
81
+ Math.min(p1.x, p2.x),
82
+ Math.max(p1.x, p2.x),
83
+ interiorBounds.minX,
84
+ interiorBounds.maxX,
85
+ )
86
+ }
87
+
88
+ return false
89
+ }
90
+
91
+ export const tracePathCrossesAnyTrace = (
92
+ tracePath: Point[],
93
+ traceMap: Record<string, SolvedTracePath>,
94
+ ) => {
95
+ for (const trace of Object.values(traceMap)) {
96
+ const points = trace.tracePath
97
+ for (let i = 0; i < tracePath.length - 1; i++) {
98
+ for (let j = 0; j < points.length - 1; j++) {
99
+ if (
100
+ segmentsStrictlyCross(
101
+ tracePath[i]!,
102
+ tracePath[i + 1]!,
103
+ points[j]!,
104
+ points[j + 1]!,
105
+ )
106
+ ) {
107
+ return true
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ return false
114
+ }
115
+
116
+ const segmentsStrictlyCross = (a1: Point, a2: Point, b1: Point, b2: Point) => {
117
+ if (sameX(a1, a2) && sameY(b1, b2)) {
118
+ return (
119
+ a1.x > Math.min(b1.x, b2.x) + EPS &&
120
+ a1.x < Math.max(b1.x, b2.x) - EPS &&
121
+ b1.y > Math.min(a1.y, a2.y) + EPS &&
122
+ b1.y < Math.max(a1.y, a2.y) - EPS
123
+ )
124
+ }
125
+
126
+ if (sameY(a1, a2) && sameX(b1, b2)) {
127
+ return (
128
+ b1.x > Math.min(a1.x, a2.x) + EPS &&
129
+ b1.x < Math.max(a1.x, a2.x) - EPS &&
130
+ a1.y > Math.min(b1.y, b2.y) + EPS &&
131
+ a1.y < Math.max(b1.y, b2.y) - EPS
132
+ )
133
+ }
134
+
135
+ return false
136
+ }
137
+
138
+ export const getSideDistances = (point: Point, bounds: Bounds) =>
139
+ [
140
+ ["left", Math.abs(point.x - bounds.minX)] as const,
141
+ ["right", Math.abs(point.x - bounds.maxX)] as const,
142
+ ["bottom", Math.abs(point.y - bounds.minY)] as const,
143
+ ["top", Math.abs(point.y - bounds.maxY)] as const,
144
+ ] satisfies Array<readonly [ChipSide, number]>
145
+
146
+ export const getConnectorTracePath = (
147
+ source: Point,
148
+ target: Point,
149
+ orientation: FacingDirection,
150
+ ) =>
151
+ simplifyOrthogonalPath(
152
+ isYOrientation(orientation)
153
+ ? [source, { x: target.x, y: source.y }, target]
154
+ : [source, { x: source.x, y: target.y }, target],
155
+ )
156
+
157
+ const simplifyOrthogonalPath = (path: Point[]) => {
158
+ const deduped = path.filter(
159
+ (point, index) => index === 0 || !pointsEqual(point, path[index - 1]!),
160
+ )
161
+ if (deduped.length < 3) return deduped
162
+
163
+ const simplified: Point[] = [deduped[0]!]
164
+ for (let i = 1; i < deduped.length - 1; i++) {
165
+ const prev = simplified[simplified.length - 1]!
166
+ const point = deduped[i]!
167
+ const next = deduped[i + 1]!
168
+ if (
169
+ (sameX(prev, point) && sameX(point, next)) ||
170
+ (sameY(prev, point) && sameY(point, next))
171
+ ) {
172
+ continue
173
+ }
174
+ simplified.push(point)
175
+ }
176
+ simplified.push(deduped[deduped.length - 1]!)
177
+ return simplified
178
+ }
179
+
180
+ const pointsEqual = (a: Point, b: Point) => sameX(a, b) && sameY(a, b)
181
+
182
+ const sameX = (a: Point, b: Point) => Math.abs(a.x - b.x) <= EPS
183
+
184
+ const sameY = (a: Point, b: Point) => Math.abs(a.y - b.y) <= EPS
185
+
186
+ export const getMaxSearchDistance = (inputProblem: InputProblem) => {
187
+ const maxChipWidth = Math.max(
188
+ ...inputProblem.chips.map((chip) => chip.width),
189
+ 1,
190
+ )
191
+ return maxChipWidth * 3
192
+ }
@@ -0,0 +1,43 @@
1
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
+ import type { InputPin, InputProblem } from "lib/types/InputProblem"
4
+ import type { CandidateLabel } from "./types"
5
+
6
+ export const getPinMap = (inputProblem: InputProblem) => {
7
+ const pinMap: Record<string, InputPin & { chipId: string }> = {}
8
+ for (const chip of inputProblem.chips) {
9
+ for (const pin of chip.pins) {
10
+ pinMap[pin.pinId] = { ...pin, chipId: chip.chipId }
11
+ }
12
+ }
13
+ return pinMap
14
+ }
15
+
16
+ export const getTracePins = (
17
+ label: NetLabelPlacement,
18
+ pinMap: Record<string, InputPin & { chipId: string }>,
19
+ ): SolvedTracePath["pins"] => {
20
+ const pins = label.pinIds.flatMap((pinId) => {
21
+ const pin = pinMap[pinId]
22
+ return pin ? [pin] : []
23
+ })
24
+
25
+ if (pins.length >= 2) return [pins[0]!, pins[1]!]
26
+ if (pins.length === 1) return [pins[0]!, pins[0]!]
27
+
28
+ const syntheticPin = {
29
+ pinId: `${label.globalConnNetId}-netlabel-anchor`,
30
+ x: label.anchorPoint.x,
31
+ y: label.anchorPoint.y,
32
+ chipId: "available-net-orientation",
33
+ }
34
+ return [syntheticPin, syntheticPin]
35
+ }
36
+
37
+ export const toNetLabelPlacementPatch = (candidate: CandidateLabel) => ({
38
+ orientation: candidate.orientation,
39
+ anchorPoint: candidate.anchorPoint,
40
+ center: candidate.center,
41
+ width: candidate.width,
42
+ height: candidate.height,
43
+ })
@@ -0,0 +1,44 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
+ import type { InputProblem } from "lib/types/InputProblem"
5
+ import type { FacingDirection } from "lib/utils/dir"
6
+
7
+ export interface AvailableNetOrientationSolverParams {
8
+ inputProblem: InputProblem
9
+ traces: SolvedTracePath[]
10
+ netLabelPlacements: NetLabelPlacement[]
11
+ }
12
+
13
+ export type Bounds = {
14
+ minX: number
15
+ minY: number
16
+ maxX: number
17
+ maxY: number
18
+ }
19
+
20
+ export type ChipSide = "left" | "right" | "top" | "bottom"
21
+
22
+ export type CandidateLabel = {
23
+ orientation: FacingDirection
24
+ anchorPoint: Point
25
+ center: Point
26
+ width: number
27
+ height: number
28
+ }
29
+
30
+ export type CandidateStatus =
31
+ | "valid"
32
+ | "chip-collision"
33
+ | "trace-collision"
34
+ | "netlabel-collision"
35
+
36
+ export type CandidatePhase = "rotate" | "shift"
37
+
38
+ export type EvaluatedCandidate = CandidateLabel & {
39
+ status: CandidateStatus
40
+ selected: boolean
41
+ phase: CandidatePhase
42
+ distance?: number
43
+ outwardDistance?: number
44
+ }
@@ -0,0 +1,123 @@
1
+ import type { GraphicsObject } from "graphics-debug"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
+ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
5
+ import type { InputProblem } from "lib/types/InputProblem"
6
+ import { getColorFromString } from "lib/utils/getColorFromString"
7
+ import { CANDIDATE_REJECTED_COLOR, CANDIDATE_SELECTED_COLOR } from "./constants"
8
+ import type { EvaluatedCandidate } from "./types"
9
+
10
+ export const visualizeAvailableNetOrientationSolver = (params: {
11
+ inputProblem: InputProblem
12
+ traces: SolvedTracePath[]
13
+ outputNetLabelPlacements: NetLabelPlacement[]
14
+ currentLabel: NetLabelPlacement | null
15
+ currentCandidateResults: EvaluatedCandidate[]
16
+ solved: boolean
17
+ }): GraphicsObject => {
18
+ const graphics = visualizeInputProblem(params.inputProblem)
19
+ ensureGraphicsArrays(graphics)
20
+
21
+ drawTraces(graphics, params.traces)
22
+ drawNetLabels(graphics, params.outputNetLabelPlacements)
23
+ drawCurrentLabel(graphics, params.currentLabel, params.solved)
24
+ drawCurrentCandidates(graphics, params.currentCandidateResults, params.solved)
25
+
26
+ return graphics
27
+ }
28
+
29
+ const drawTraces = (graphics: GraphicsObject, traces: SolvedTracePath[]) => {
30
+ for (const trace of traces) {
31
+ graphics.lines!.push({
32
+ points: trace.tracePath,
33
+ strokeColor: "purple",
34
+ } as any)
35
+ }
36
+ }
37
+
38
+ const drawNetLabels = (
39
+ graphics: GraphicsObject,
40
+ labels: NetLabelPlacement[],
41
+ ) => {
42
+ for (const label of labels) {
43
+ graphics.rects!.push({
44
+ center: label.center,
45
+ width: label.width,
46
+ height: label.height,
47
+ fill: getColorFromString(label.globalConnNetId, 0.35),
48
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
49
+ label: `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`,
50
+ } as any)
51
+ graphics.points!.push({
52
+ x: label.anchorPoint.x,
53
+ y: label.anchorPoint.y,
54
+ color: getColorFromString(label.globalConnNetId, 0.9),
55
+ label: `anchorPoint\norientation: ${label.orientation}`,
56
+ } as any)
57
+ }
58
+ }
59
+
60
+ const drawCurrentLabel = (
61
+ graphics: GraphicsObject,
62
+ currentLabel: NetLabelPlacement | null,
63
+ solved: boolean,
64
+ ) => {
65
+ if (!currentLabel || solved) return
66
+
67
+ graphics.rects!.push({
68
+ center: currentLabel.center,
69
+ width: currentLabel.width,
70
+ height: currentLabel.height,
71
+ fill: "rgba(255, 0, 0, 0.2)",
72
+ strokeColor: CANDIDATE_REJECTED_COLOR,
73
+ label: `available orientation target\n${currentLabel.netId ?? currentLabel.globalConnNetId}`,
74
+ } as any)
75
+ }
76
+
77
+ const drawCurrentCandidates = (
78
+ graphics: GraphicsObject,
79
+ candidates: EvaluatedCandidate[],
80
+ solved: boolean,
81
+ ) => {
82
+ if (solved) return
83
+
84
+ for (const candidate of candidates) {
85
+ const color = candidate.selected
86
+ ? CANDIDATE_SELECTED_COLOR
87
+ : CANDIDATE_REJECTED_COLOR
88
+ const distanceLabel =
89
+ candidate.distance === undefined
90
+ ? ""
91
+ : `\ndistance: ${candidate.distance.toFixed(3)}`
92
+ const outwardDistanceLabel =
93
+ candidate.outwardDistance === undefined || candidate.outwardDistance === 0
94
+ ? ""
95
+ : `\noutward distance: ${candidate.outwardDistance.toFixed(3)}`
96
+
97
+ graphics.rects!.push({
98
+ center: candidate.center,
99
+ width: candidate.width,
100
+ height: candidate.height,
101
+ fill: candidate.selected
102
+ ? "rgba(0, 0, 255, 0.2)"
103
+ : "rgba(255, 0, 0, 0.15)",
104
+ strokeColor: color,
105
+ strokeDash: candidate.selected ? undefined : "4 2",
106
+ label: `${candidate.selected ? "selected" : candidate.status} available orientation\nphase: ${candidate.phase}\norientation: ${candidate.orientation}${distanceLabel}${outwardDistanceLabel}`,
107
+ } as any)
108
+ graphics.points!.push({
109
+ x: candidate.anchorPoint.x,
110
+ y: candidate.anchorPoint.y,
111
+ color,
112
+ label: `candidate anchor\n${candidate.phase}\n${candidate.orientation}`,
113
+ } as any)
114
+ }
115
+ }
116
+
117
+ const ensureGraphicsArrays = (graphics: GraphicsObject) => {
118
+ if (!graphics.lines) graphics.lines = []
119
+ if (!graphics.points) graphics.points = []
120
+ if (!graphics.rects) graphics.rects = []
121
+ if (!graphics.circles) graphics.circles = []
122
+ if (!graphics.texts) graphics.texts = []
123
+ }
@@ -21,6 +21,7 @@ import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePa
21
21
  import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"
22
22
  import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
23
23
  import { Example28Solver } from "../Example28Solver/Example28Solver"
24
+ import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver"
24
25
 
25
26
  type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
26
27
  solverName: string
@@ -71,6 +72,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
71
72
  traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver
72
73
  traceCleanupSolver?: TraceCleanupSolver
73
74
  example28Solver?: Example28Solver
75
+ availableNetOrientationSolver?: AvailableNetOrientationSolver
74
76
 
75
77
  startTimeOfPhase: Record<string, number>
76
78
  endTimeOfPhase: Record<string, number>
@@ -240,6 +242,18 @@ export class SchematicTracePipelineSolver extends BaseSolver {
240
242
  },
241
243
  ]
242
244
  }),
245
+ definePipelineStep(
246
+ "availableNetOrientationSolver",
247
+ AvailableNetOrientationSolver,
248
+ (instance) => [
249
+ {
250
+ inputProblem: instance.inputProblem,
251
+ traces: instance.example28Solver!.outputTraces,
252
+ netLabelPlacements:
253
+ instance.example28Solver!.outputNetLabelPlacements,
254
+ },
255
+ ],
256
+ ),
243
257
  ]
244
258
 
245
259
  constructor(inputProblem: InputProblem) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.49",
4
+ "version": "0.0.50",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -94,7 +94,7 @@
94
94
  }
95
95
  ],
96
96
  "availableNetLabelOrientations": {
97
- "VCC": ["y-"],
97
+ "VCC": ["y+"],
98
98
  "OUT": ["x-", "x+"],
99
99
  "GND": ["y-"],
100
100
  "MMM": ["x+", "x-"]
@@ -2,96 +2,99 @@
2
2
  <rect width="100%" height="100%" fill="white" />
3
3
  <g>
4
4
  <circle data-type="point" data-label="J1.1
5
- x+" data-x="1.1" data-y="0.2" cx="288.33702882483374" cy="207.43247369481963" r="3" fill="hsl(218, 100%, 50%, 0.8)" />
5
+ x+" data-x="1.1" data-y="0.2" cx="301.5156017830609" cy="201.4588202080238" r="3" fill="hsl(218, 100%, 50%, 0.8)" />
6
6
  </g>
7
7
  <g>
8
8
  <circle data-type="point" data-label="J1.2
9
- x+" data-x="1.1" data-y="0" cx="288.33702882483374" cy="230.00856722434997" r="3" fill="hsl(219, 100%, 50%, 0.8)" />
9
+ x+" data-x="1.1" data-y="0" cx="301.5156017830609" cy="225.2329658246657" r="3" fill="hsl(219, 100%, 50%, 0.8)" />
10
10
  </g>
11
11
  <g>
12
12
  <circle data-type="point" data-label="J1.3
13
- x+" data-x="1.1" data-y="-0.2" cx="288.33702882483374" cy="252.5846607538803" r="3" fill="hsl(220, 100%, 50%, 0.8)" />
13
+ x+" data-x="1.1" data-y="-0.2" cx="301.5156017830609" cy="249.00711144130761" r="3" fill="hsl(220, 100%, 50%, 0.8)" />
14
14
  </g>
15
15
  <g>
16
16
  <circle data-type="point" data-label="R1.1
17
- x-" data-x="0.5499999999999996" data-y="-1.7944553499999996" cx="226.25277161862525" cy="432.5675263051804" r="3" fill="hsl(226, 100%, 50%, 0.8)" />
17
+ x-" data-x="0.5499999999999996" data-y="-1.7944553499999996" cx="236.13670133729568" cy="438.5411797919762" r="3" fill="hsl(226, 100%, 50%, 0.8)" />
18
18
  </g>
19
19
  <g>
20
20
  <circle data-type="point" data-label="R1.2
21
- x+" data-x="1.6499999999999997" data-y="-1.7944553499999996" cx="350.4212860310421" cy="432.5675263051804" r="3" fill="hsl(227, 100%, 50%, 0.8)" />
21
+ x+" data-x="1.6499999999999997" data-y="-1.7944553499999996" cx="366.8945022288261" cy="438.5411797919762" r="3" fill="hsl(227, 100%, 50%, 0.8)" />
22
22
  </g>
23
23
  <g>
24
24
  <circle data-type="point" data-label="C1.1
25
- x-" data-x="2.3099999999999996" data-y="0.01999999999999985" cx="424.9223946784922" cy="227.75095787139696" r="3" fill="hsl(121, 100%, 50%, 0.8)" />
25
+ x-" data-x="2.3099999999999996" data-y="0.01999999999999985" cx="445.34918276374435" cy="222.85555126300153" r="3" fill="hsl(121, 100%, 50%, 0.8)" />
26
26
  </g>
27
27
  <g>
28
28
  <circle data-type="point" data-label="C1.2
29
- x+" data-x="3.41" data-y="0.01999999999999985" cx="549.090909090909" cy="227.75095787139696" r="3" fill="hsl(122, 100%, 50%, 0.8)" />
29
+ x+" data-x="3.41" data-y="0.01999999999999985" cx="576.1069836552749" cy="222.85555126300153" r="3" fill="hsl(122, 100%, 50%, 0.8)" />
30
30
  </g>
31
31
  <g>
32
32
  <circle data-type="point" data-label="anchorPoint
33
- orientation: y+" data-x="1.4024999999999999" data-y="0.2" cx="322.48337028824835" cy="207.43247369481963" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
33
+ orientation: y+" data-x="1.4024999999999999" data-y="0.2" cx="337.4739970282318" cy="201.4588202080238" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
34
34
  </g>
35
35
  <g>
36
36
  <circle data-type="point" data-label="anchorPoint
37
- orientation: x+" data-x="1.3" data-y="-0.4486138374999999" cx="310.9131223543641" cy="280.6483069945576" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
37
+ orientation: x+" data-x="1.3" data-y="-0.4486138374999999" cx="325.28974739970283" cy="278.5600193164933" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
38
38
  </g>
39
39
  <g>
40
40
  <circle data-type="point" data-label="anchorPoint
41
- orientation: y-" data-x="1.8499999999999996" data-y="-1.7944553499999996" cx="372.9973795605724" cy="432.5675263051804" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
41
+ orientation: y-" data-x="1.8499999999999996" data-y="-1.7944553499999996" cx="390.668647845468" cy="438.5411797919762" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
42
42
  </g>
43
43
  <g>
44
44
  <circle data-type="point" data-label="anchorPoint
45
- orientation: x+" data-x="3.41" data-y="0.01999999999999985" cx="549.090909090909" cy="227.75095787139696" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
45
+ orientation: y-" data-x="3.511" data-y="-0.4310000000000001" cx="588.1129271916791" cy="276.466249628529" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
46
46
  </g>
47
47
  <g>
48
- <polyline data-points="1.1,0.2 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="288.33702882483374,207.43247369481963 424.9223946784922,227.75095787139696" fill="none" stroke="hsl(190, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
48
+ <polyline data-points="1.1,0.2 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,201.4588202080238 445.34918276374435,222.85555126300153" fill="none" stroke="hsl(190, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
49
49
  </g>
50
50
  <g>
51
- <polyline data-points="1.1,0 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="288.33702882483374,230.00856722434997 226.25277161862525,432.5675263051804" fill="none" stroke="hsl(158, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
51
+ <polyline data-points="1.1,0 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,225.2329658246657 236.13670133729568,438.5411797919762" fill="none" stroke="hsl(158, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
52
52
  </g>
53
53
  <g>
54
- <polyline data-points="1.1,-0.2 1.6499999999999997,-1.7944553499999996" data-type="line" data-label="" points="288.33702882483374,252.5846607538803 350.4212860310421,432.5675263051804" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
54
+ <polyline data-points="1.1,-0.2 1.6499999999999997,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,249.00711144130761 366.8945022288261,438.5411797919762" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
55
55
  </g>
56
56
  <g>
57
- <polyline data-points="1.1,-0.2 3.41,0.01999999999999985" data-type="line" data-label="" points="288.33702882483374,252.5846607538803 549.090909090909,227.75095787139696" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
57
+ <polyline data-points="1.1,-0.2 3.41,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,249.00711144130761 576.1069836552749,222.85555126300153" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
58
58
  </g>
59
59
  <g>
60
- <polyline data-points="1.6499999999999997,-1.7944553499999996 3.41,0.01999999999999985" data-type="line" data-label="" points="350.4212860310421,432.5675263051804 549.090909090909,227.75095787139696" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
60
+ <polyline data-points="1.6499999999999997,-1.7944553499999996 3.41,0.01999999999999985" data-type="line" data-label="" points="366.8945022288261,438.5411797919762 576.1069836552749,222.85555126300153" fill="none" stroke="hsl(157, 100%, 50%, 0.8)" stroke-width="1" stroke-dasharray="4 2" />
61
61
  </g>
62
62
  <g>
63
- <polyline data-points="1.1,0.2 1.7049999999999998,0.2 1.7049999999999998,0.01999999999999985 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="288.33702882483374,207.43247369481963 356.62971175166297,207.43247369481963 356.62971175166297,227.75095787139696 424.9223946784922,227.75095787139696" fill="none" stroke="purple" stroke-width="1" />
63
+ <polyline data-points="1.1,0.2 1.7049999999999998,0.2 1.7049999999999998,0.01999999999999985 2.3099999999999996,0.01999999999999985" data-type="line" data-label="" points="301.5156017830609,201.4588202080238 373.4323922734027,201.4588202080238 373.4323922734027,222.85555126300153 445.34918276374435,222.85555126300153" fill="none" stroke="purple" stroke-width="1" />
64
64
  </g>
65
65
  <g>
66
- <polyline data-points="1.1,0 1.3,0 1.3,-0.8972276749999998 0.34999999999999964,-0.8972276749999998 0.34999999999999964,-1.7944553499999996 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="288.33702882483374,230.00856722434997 310.9131223543641,230.00856722434997 310.9131223543641,331.28804676476517 203.6766780890949,331.28804676476517 203.6766780890949,432.5675263051804 226.25277161862525,432.5675263051804" fill="none" stroke="purple" stroke-width="1" />
66
+ <polyline data-points="1.1,0 1.3,0 1.3,-0.8972276749999998 0.34999999999999964,-0.8972276749999998 0.34999999999999964,-1.7944553499999996 0.5499999999999996,-1.7944553499999996" data-type="line" data-label="" points="301.5156017830609,225.2329658246657 325.28974739970283,225.2329658246657 325.28974739970283,331.88707280832097 212.36255572065375,331.88707280832097 212.36255572065375,438.5411797919762 236.13670133729568,438.5411797919762" fill="none" stroke="purple" stroke-width="1" />
67
67
  </g>
68
68
  <g>
69
- <polyline data-points="1.6499999999999997,-1.7944553499999996 1.8499999999999996,-1.7944553499999996 1.8499999999999996,-0.2 1.1,-0.2" data-type="line" data-label="" points="350.4212860310421,432.5675263051804 372.9973795605724,432.5675263051804 372.9973795605724,252.5846607538803 288.33702882483374,252.5846607538803" fill="none" stroke="purple" stroke-width="1" />
69
+ <polyline data-points="1.6499999999999997,-1.7944553499999996 1.8499999999999996,-1.7944553499999996 1.8499999999999996,-0.2 1.1,-0.2" data-type="line" data-label="" points="366.8945022288261,438.5411797919762 390.668647845468,438.5411797919762 390.668647845468,249.00711144130761 301.5156017830609,249.00711144130761" fill="none" stroke="purple" stroke-width="1" />
70
70
  </g>
71
71
  <g>
72
- <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40.000000000000014" y="184.8563801652893" width="248.33702882483374" height="90.30437411812136" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008858928571428573" />
72
+ <polyline data-points="3.41,0.01999999999999985 3.511,0.01999999999999985 3.511,-0.4310000000000001" data-type="line" data-label="" points="576.1069836552749,222.85555126300153 588.1129271916791,222.85555126300153 588.1129271916791,276.466249628529" fill="none" stroke="purple" stroke-width="1" />
73
73
  </g>
74
74
  <g>
75
- <rect data-type="rect" data-label="schematic_component_1" data-x="1.0999999999999996" data-y="-1.7944553499999996" x="226.25277161862525" y="410.61731546059264" width="124.16851441241684" height="43.90042168917546" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008858928571428573" />
75
+ <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40.00000000000003" y="177.6846745913819" width="261.5156017830609" height="95.09658246656761" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
76
76
  </g>
77
77
  <g>
78
- <rect data-type="rect" data-label="schematic_component_2" data-x="2.86" data-y="0.01999999999999985" x="424.9223946784922" y="180.34116145938327" width="124.16851441241681" height="94.81959282402738" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008858928571428573" />
78
+ <rect data-type="rect" data-label="schematic_component_1" data-x="1.0999999999999996" data-y="-1.7944553499999996" x="236.13670133729568" y="415.42613075780093" width="130.75780089153045" height="46.230098068350514" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
79
+ </g>
80
+ <g>
81
+ <rect data-type="rect" data-label="schematic_component_2" data-x="2.86" data-y="0.01999999999999985" x="445.34918276374435" y="172.92984546805354" width="130.75780089153056" height="99.85141158989597" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.008412500000000002" />
79
82
  </g>
80
83
  <g>
81
84
  <rect data-type="rect" data-label="netId: VCC
82
- globalConnNetId: connectivity_net0" data-x="1.4024999999999999" data-y="0.42500000000000004" x="311.1953235234831" y="156.6362632533764" width="22.57609352953034" height="50.796210441443236" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008858928571428573" />
85
+ globalConnNetId: connectivity_net0" data-x="1.4024999999999999" data-y="0.42500000000000004" x="325.5869242199108" y="147.96699257057952" width="23.77414561664193" height="53.49182763744429" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
83
86
  </g>
84
87
  <g>
85
88
  <rect data-type="rect" data-label="netId: OUT
86
- globalConnNetId: connectivity_net1" data-x="1.5250000000000001" data-y="-0.4486138374999999" x="310.9131223543641" y="269.3602602297924" width="50.796210441443236" height="22.57609352953034" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008858928571428573" />
89
+ globalConnNetId: connectivity_net1" data-x="1.5250000000000001" data-y="-0.4486138374999999" x="325.28974739970283" y="266.6729465081724" width="53.49182763744432" height="23.774145616641874" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
87
90
  </g>
88
91
  <g>
89
92
  <rect data-type="rect" data-label="netId: GND
90
- globalConnNetId: connectivity_net2" data-x="1.8499999999999996" data-y="-2.0194553499999994" x="361.7093327958072" y="432.56752630518037" width="22.576093529530397" height="50.79621044144329" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008858928571428573" />
93
+ globalConnNetId: connectivity_net2" data-x="1.8499999999999996" data-y="-2.0194553499999994" x="378.78157503714704" y="438.54117979197616" width="23.77414561664193" height="53.49182763744432" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
91
94
  </g>
92
95
  <g>
93
96
  <rect data-type="rect" data-label="netId: GND
94
- globalConnNetId: connectivity_net2" data-x="3.636" data-y="0.01999999999999985" x="549.2037895585568" y="216.4629111066318" width="50.796210441443236" height="22.57609352953034" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008858928571428573" />
97
+ globalConnNetId: connectivity_net2" data-x="3.511" data-y="-0.6560000000000001" x="576.2258543833581" y="276.466249628529" width="23.77414561664193" height="53.49182763744432" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008412500000000002" />
95
98
  </g>
96
99
  <g id="crosshair" style="display: none">
97
100
  <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
@@ -121,12 +124,12 @@ globalConnNetId: connectivity_net2" data-x="3.636" data-y="0.01999999999999985"
121
124
 
122
125
  // Calculate real coordinates using inverse transformation
123
126
  const matrix = {
124
- "a": 112.88046764765167,
127
+ "a": 118.8707280832095,
125
128
  "c": 0,
126
- "e": 164.16851441241687,
129
+ "e": 170.75780089153048,
127
130
  "b": 0,
128
- "d": -112.88046764765167,
129
- "f": 230.00856722434997
131
+ "d": -118.8707280832095,
132
+ "f": 225.2329658246657
130
133
  };
131
134
  // Manually invert and apply the affine transform
132
135
  // Since we only use translate and scale, we can directly compute: