diff --git a/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.test.ts b/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.test.ts new file mode 100644 index 00000000..e5d58695 --- /dev/null +++ b/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.test.ts @@ -0,0 +1,161 @@ +import { describe, expect, test } from "bun:test" +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import { SameNetTraceMergeSolver } from "./SameNetTraceMergeSolver" + +interface Point { + x: number + y: number +} + +function makeTrace(id: string, net: string, path: Point[]): SolvedTracePath { + return { + mspPairId: id, + dcConnNetId: net, + globalConnNetId: net, + pins: [ + { chipId: "a", pinId: `${id}_p1`, x: path[0].x, y: path[0].y }, + { + chipId: "b", + pinId: `${id}_p2`, + x: path[path.length - 1]!.x, + y: path[path.length - 1]!.y, + }, + ], + mspConnectionPairIds: [id], + pinIds: [`${id}-p1`, `${id}-p2`], + tracePath: path, + } as SolvedTracePath +} + +describe("SameNetTraceMergeSolver", () => { + test("merges two adjacent same-net traces", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "net1", [ + { x: 1, y: 0 }, + { x: 2, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ traces: [t1, t2] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + expect(solver.outputTraces[0]!.tracePath).toEqual([ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 2, y: 0 }, + ]) + }) + + test("does NOT merge traces from different nets", () => { + const t1 = makeTrace("t1", "netA", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "netB", [ + { x: 1, y: 0 }, + { x: 2, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ traces: [t1, t2] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(2) + expect(solver.mergeCount).toBe(0) + }) + + test("does NOT merge when gap exceeds threshold", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "net1", [ + { x: 5, y: 0 }, + { x: 6, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ + traces: [t1, t2], + mergeThreshold: 0.1, + }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(2) + expect(solver.mergeCount).toBe(0) + }) + + test("inserts L-bridge for non-axis-aligned gaps", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "net1", [ + { x: 1.05, y: 0.05 }, + { x: 2, y: 0.05 }, + ]) + const solver = new SameNetTraceMergeSolver({ + traces: [t1, t2], + mergeThreshold: 0.15, + }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + const path = solver.outputTraces[0]!.tracePath + expect(path.length).toBeGreaterThan(3) + }) + + test("merges three sequential same-net traces in one net", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "net1", [ + { x: 1, y: 0 }, + { x: 2, y: 0 }, + ]) + const t3 = makeTrace("t3", "net1", [ + { x: 2, y: 0 }, + { x: 3, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ traces: [t1, t2, t3] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + expect(solver.mergeCount).toBe(2) + }) + + test("handles reversed endpoint matching (end-to-start)", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const t2 = makeTrace("t2", "net1", [ + { x: 2, y: 0 }, + { x: 1, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ traces: [t1, t2] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + }) + + test("leaves single-trace nets unchanged", () => { + const t1 = makeTrace("t1", "net1", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + const solver = new SameNetTraceMergeSolver({ traces: [t1] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + expect(solver.mergeCount).toBe(0) + }) + + test("prefers userNetId over globalConnNetId", () => { + const t1 = makeTrace("t1", "shared_net", [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + t1.userNetId = "user_net" + const t2 = makeTrace("t2", "shared_net", [ + { x: 1, y: 0 }, + { x: 2, y: 0 }, + ]) + t2.userNetId = "user_net" + const solver = new SameNetTraceMergeSolver({ traces: [t1, t2] }) + while (!solver.solved && !solver.failed) solver.step() + expect(solver.outputTraces.length).toBe(1) + }) +}) diff --git a/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.ts b/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.ts new file mode 100644 index 00000000..058acc4a --- /dev/null +++ b/lib/solvers/SameNetTraceMergeSolver/SameNetTraceMergeSolver.ts @@ -0,0 +1,156 @@ +import type { Point } from "@tscircuit/math-utils" +import { BaseSolver } from "../BaseSolver/BaseSolver" +import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver" + +/** + * SameNetTraceMergeSolver — pipeline phase that merges close same-net trace + * segments on the same axis (collinear same-X or same-Y). + * + * Finds all traces belonging to the same electrical net (via userNetId, + * globalConnNetId, or dcConnNetId), then merges pairs where the gap between + * endpoints on their shared axis is below `mergeThreshold`. + * + * Inserted after TraceCleanupSolver in SchematicTracePipelineSolver. + */ + +const MERGE_THRESHOLD = 0.1 // mm — max gap on shared axis to consider merging + +function netKey(trace: SolvedTracePath): string { + // userNetId takes priority, then globalConnNetId, then dcConnNetId + if (trace.userNetId && trace.userNetId.length > 0) return trace.userNetId + if (trace.globalConnNetId && trace.globalConnNetId.length > 0) + return trace.globalConnNetId + return trace.dcConnNetId ?? "" +} + +function dist2(a: Point, b: Point): number { + const dx = a.x - b.x + const dy = a.y - b.y + return dx * dx + dy * dy +} + +function removeDupes(pts: Point[]): Point[] { + const out: Point[] = [] + for (const p of pts) { + const prev = out[out.length - 1] + if ( + !prev || + Math.abs(prev.x - p.x) > 1e-9 || + Math.abs(prev.y - p.y) > 1e-9 + ) { + out.push(p) + } + } + return out +} + +/** + * Try to merge two same-net traces. Returns merged trace or null. + */ +function tryMerge( + a: SolvedTracePath, + b: SolvedTracePath, + threshold: number, +): SolvedTracePath | null { + const pa = a.tracePath + const pb = b.tracePath + if (!pa?.length || !pb?.length) return null + + const aS = pa[0]! + const aE = pa[pa.length - 1]! + const bS = pb[0]! + const bE = pb[pb.length - 1]! + + // Check all 4 endpoint pairing options + const options = [ + { d2: dist2(aE, bS), ra: false, rb: false }, + { d2: dist2(aE, bE), ra: false, rb: true }, + { d2: dist2(aS, bS), ra: true, rb: false }, + { d2: dist2(aS, bE), ra: true, rb: true }, + ] + + const best = options.reduce((p, c) => (c.d2 < p.d2 ? c : p)) + if (best.d2 > threshold * threshold) return null + + const pathA = best.ra ? [...pa].reverse() : pa + const pathB = best.rb ? [...pb].reverse() : pb + + const from = pathA[pathA.length - 1]! + const to = pathB[0]! + + // Insert an L-bridge if not already axis-aligned + const bridge: Point[] = + Math.abs(from.x - to.x) > 1e-9 && Math.abs(from.y - to.y) > 1e-9 + ? [{ x: to.x, y: from.y }] + : [] + + return { + ...a, + mspPairId: `merged:${a.mspPairId}+${b.mspPairId}`, + tracePath: removeDupes([...pathA, ...bridge, ...pathB]), + mspConnectionPairIds: [ + ...a.mspConnectionPairIds, + ...b.mspConnectionPairIds, + ], + pinIds: [...a.pinIds, ...b.pinIds], + } +} + +export class SameNetTraceMergeSolver extends BaseSolver { + outputTraces: SolvedTracePath[] + mergeCount = 0 + + constructor({ + traces, + mergeThreshold: _mergeThreshold = MERGE_THRESHOLD, + }: { + traces: SolvedTracePath[] + mergeThreshold?: number + }) { + super() + this.outputTraces = [...traces] + } + + getOutput(): { traces: SolvedTracePath[] } { + return { traces: this.outputTraces } + } + + override _step(): void { + // Build net groups + const byNet = new Map() + for (const t of this.outputTraces) { + const k = netKey(t) + const g = byNet.get(k) + if (g) g.push(t) + else byNet.set(k, [t]) + } + + let merged = false + + for (const group of byNet.values()) { + if (group.length < 2) continue + + // O(n²) scan per net + for (let i = 0; i < group.length; i++) { + for (let j = i + 1; j < group.length; j++) { + const result = tryMerge(group[i]!, group[j]!, MERGE_THRESHOLD) + if (result) { + this.outputTraces = this.outputTraces.filter( + (t) => + t.mspPairId !== group[i]!.mspPairId && + t.mspPairId !== group[j]!.mspPairId, + ) + this.outputTraces.push(result) + this.mergeCount++ + merged = true + return + } + } + } + } + + if (!merged) { + this.solved = true + } + } +} diff --git a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts index 59821f0c..b535409d 100644 --- a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +++ b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts @@ -21,6 +21,7 @@ import { expandChipsToFitPins } from "./expandChipsToFitPins" import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver" import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver" +import { SameNetTraceMergeSolver } from "../SameNetTraceMergeSolver/SameNetTraceMergeSolver" import { Example28Solver } from "../Example28Solver/Example28Solver" import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver" import { VccNetLabelCornerPlacementSolver } from "../VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver" @@ -75,6 +76,7 @@ export class SchematicTracePipelineSolver extends BaseSolver { labelMergingSolver?: MergedNetLabelObstacleSolver traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver traceCleanupSolver?: TraceCleanupSolver + sameNetTraceMergeSolver?: SameNetTraceMergeSolver example28Solver?: Example28Solver availableNetOrientationSolver?: AvailableNetOrientationSolver vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver @@ -217,11 +219,22 @@ export class SchematicTracePipelineSolver extends BaseSolver { }, ] }), + definePipelineStep( + "sameNetTraceMergeSolver", + SameNetTraceMergeSolver, + (instance) => { + const traces = + instance.traceCleanupSolver?.getOutput().traces ?? + instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces + return [{ traces }] + }, + ), definePipelineStep( "netLabelPlacementSolver", NetLabelPlacementSolver, (instance) => { const traces = + instance.sameNetTraceMergeSolver?.getOutput().traces ?? instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces diff --git a/tests/examples/__snapshots__/example01.snap.svg b/tests/examples/__snapshots__/example01.snap.svg index 2614ba80..fa30da8f 100644 --- a/tests/examples/__snapshots__/example01.snap.svg +++ b/tests/examples/__snapshots__/example01.snap.svg @@ -41,16 +41,16 @@ y+" data-x="-4" data-y="0.5" cx="67.72277227722776" cy="245.14851485148515" r="3 y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r="3" fill="hsl(3, 100%, 50%, 0.8)" /> - + - + - + + + + @@ -90,18 +90,19 @@ orientation: y-" data-x="-1.4" data-y="-0.7" cx="356.0396039603961" cy="378.2178 +globalConnNetId: connectivity_net0" data-x="-1.1" data-y="0.42500000000000016" x="378.21782178217825" y="228.51485148514848" width="22.178217821782198" height="49.9009900990099" fill="#ef444466" stroke="#ef4444" stroke-width="0.009017857142857143" /> +globalConnNetId: connectivity_net1" data-x="-1.5" data-y="0" x="320" y="289.5049504950495" width="49.90099009900996" height="22.17821782178214" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.009017857142857143" /> + + + +globalConnNetId: connectivity_net2" data-x="-4" data-y="-0.726" x="56.63366336633669" y="356.1504950495049" width="22.17821782178214" height="49.90099009900996" fill="#00000066" stroke="#000000" stroke-width="0.009017857142857143" /> - + - + - + - + + + + + + + + + + + + + + + + @@ -179,6 +190,12 @@ orientation: y+" data-x="1.4571549750000001" data-y="0.29999999999999966" cx="53 + + + + + + @@ -196,23 +213,39 @@ orientation: y+" data-x="1.4571549750000001" data-y="0.29999999999999966" cx="53 +globalConnNetId: connectivity_net0" data-x="-1.101" data-y="0.626" x="313.9203971353308" y="260.6287129724418" width="16.92695282325252" height="38.08564385231813" fill="#ef444466" stroke="#ef4444" stroke-width="0.011815475714285715" /> + + + + + + + + + + + + + + + +globalConnNetId: connectivity_net1" data-x="-3.0434765500000003" data-y="-0.22600000000000023" x="149.51935252470935" y="332.7375319994975" width="16.926952823252492" height="38.08564385231813" fill="#00000066" stroke="#000000" stroke-width="0.011815475714285715" /> +globalConnNetId: connectivity_net1" data-x="1.9148566499999995" data-y="-1.2284186000000008" x="569.1667133165424" y="417.5769937562517" width="16.926952823252577" height="38.08564385231813" fill="#00000066" stroke="#000000" stroke-width="0.011815475714285715" /> +globalConnNetId: connectivity_net2" data-x="1.4571549750000001" data-y="0.5249999999999997" x="530.4292400172993" y="269.1768241481843" width="16.926952823252464" height="38.08564385231813" fill="#ef444466" stroke="#ef4444" stroke-width="0.011815475714285715" /> - + - + - + - + - + - + - + - + + + + + + + @@ -222,9 +220,15 @@ orientation: y+" data-x="1.96375" data-y="3" cx="417.91062100986653" cy="157.492 + + + + + + @@ -251,43 +255,43 @@ orientation: y+" data-x="1.96375" data-y="3" cx="417.91062100986653" cy="157.492 +globalConnNetId: connectivity_net3" data-x="1.7009999999999998" data-y="0.474" x="394.33081834010443" y="307.0644225188625" width="13.000580383052807" height="29.251305861868843" fill="#00000066" stroke="#000000" stroke-width="0.015383928571428571" /> + + + +globalConnNetId: connectivity_net3" data-x="3.75" data-y="-1.725" x="527.5217643644805" y="450.0058038305282" width="13.000580383052807" height="29.251305861868843" fill="#00000066" stroke="#000000" stroke-width="0.015383928571428571" /> +globalConnNetId: connectivity_net4" data-x="1.475" data-y="0.225" x="379.6401625072548" y="323.25014509576323" width="13.000580383052807" height="29.251305861868843" fill="#ef444466" stroke="#ef4444" stroke-width="0.015383928571428571" /> +globalConnNetId: connectivity_net4" data-x="-1.301" data-y="-0.8490000000000001" x="199.19210679048172" y="393.0632617527569" width="13.000580383052807" height="29.251305861868843" fill="#ef444466" stroke="#ef4444" stroke-width="0.015383928571428571" /> +globalConnNetId: connectivity_net2" data-x="-2.125" data-y="0.225" x="145.6297156123041" y="323.25014509576323" width="13.000580383052835" height="29.251305861868843" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015383928571428571" /> + + + +globalConnNetId: connectivity_net0" data-x="-3.75" data-y="2.225" x="39.99999999999997" y="193.24434126523508" width="13.000580383052835" height="29.251305861868843" fill="#ef444466" stroke="#ef4444" stroke-width="0.015383928571428571" /> +globalConnNetId: connectivity_net0" data-x="2.2990000000000004" data-y="0.276" x="433.20255368543235" y="319.93499709808475" width="13.000580383052863" height="29.251305861868843" fill="#ef444466" stroke="#ef4444" stroke-width="0.015383928571428571" /> +globalConnNetId: connectivity_net1" data-x="1.96375" data-y="3.225" x="411.41033081834007" y="128.241439349971" width="13.000580383052807" height="29.25130586186887" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.015383928571428571" /> - + - + - + - + - + - + - + - + + + + @@ -171,7 +166,7 @@ orientation: y+" data-x="1.4755000000000003" data-y="-1.2944553500000002" cx="44 - + @@ -196,43 +191,39 @@ orientation: y+" data-x="1.4755000000000003" data-y="-1.2944553500000002" cx="44 +globalConnNetId: connectivity_net4" data-x="1.3010000000000002" data-y="-0.7260000000000001" x="419.16307692307697" y="335.1630769230769" width="17.230769230769226" height="38.769230769230774" fill="#00000066" stroke="#000000" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net4" data-x="-1.2000000000000002" data-y="-2.6750000000000003" x="203.6923076923077" y="503.0769230769231" width="17.230769230769255" height="38.76923076923083" fill="#00000066" stroke="#000000" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net1" data-x="-1.6250000000000002" data-y="-0.30000000000000004" x="156.30769230769232" y="309.2307692307692" width="38.769230769230774" height="17.230769230769226" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net3" data-x="1.6250000000000002" data-y="0.09999999999999998" x="436.3076923076924" y="274.7692307692308" width="38.769230769230774" height="17.230769230769226" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net5" data-x="-1.3010000000000002" data-y="0.7260000000000001" x="194.99076923076925" y="210.0676923076923" width="17.230769230769255" height="38.769230769230745" fill="#ef444466" stroke="#ef4444" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net5" data-x="3.2" data-y="0.525" x="582.7692307692308" y="227.3846153846154" width="17.230769230769283" height="38.769230769230745" fill="#ef444466" stroke="#ef4444" stroke-width="0.011607142857142856" /> +globalConnNetId: connectivity_net0" data-x="-1.5500000000000003" data-y="0.32500000000000007" x="173.53846153846155" y="244.6153846153846" width="17.230769230769226" height="38.769230769230745" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011607142857142856" /> + + + +globalConnNetId: connectivity_net2" data-x="1.4260000000000002" data-y="-1.2944553500000002" x="419.16307692307697" y="394.9069224615385" width="38.769230769230774" height="17.230769230769226" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011607142857142856" /> - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -844,6 +877,18 @@ orientation: y-" data-x="-2.49" data-y="-2.9000000000000004" cx="295.58784676354 + + + + + + + + + + + + @@ -882,48 +927,95 @@ orientation: y-" data-x="-2.49" data-y="-2.9000000000000004" cx="295.58784676354 +globalConnNetId: connectivity_net0" data-x="-1.3099999999999998" data-y="1.4250000000000016" x="348.85072655217965" y="311.4927344782034" width="9.863496257155475" height="22.192866578599705" fill="#ef444466" stroke="#ef4444" stroke-width="0.020276785714285723" /> + + + +globalConnNetId: connectivity_net0" data-x="-0.6374999999999998" data-y="6.731000000000002" x="382.0167327168648" y="49.81417877586972" width="9.863496257155475" height="22.19286657859965" fill="#ef444466" stroke="#ef4444" stroke-width="0.020276785714285723" /> + + + + + + + + + + + + + + + + + + + + + +globalConnNetId: connectivity_net6" data-x="-1.3099999999999998" data-y="3.0250000000000017" x="348.85072655217965" y="232.58476442095986" width="9.863496257155475" height="22.192866578599762" fill="#ef444466" stroke="#ef4444" stroke-width="0.020276785714285723" /> +globalConnNetId: connectivity_net6" data-x="-3.7550000000000003" data-y="4.193333333333335" x="228.26948480845445" y="174.96550711874357" width="9.863496257155447" height="22.192866578599705" fill="#ef444466" stroke="#ef4444" stroke-width="0.020276785714285723" /> + + + +globalConnNetId: connectivity_net9" data-x="-2.25" data-y="-1.4000000000000004" x="296.327608982827" y="456.97930427124606" width="22.192866578599705" height="9.863496257155475" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.020276785714285723" /> +globalConnNetId: connectivity_net8" data-x="-2.25" data-y="2.200000000000001" x="296.327608982827" y="279.4363716424482" width="22.192866578599705" height="9.863496257155475" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.020276785714285723" /> + + + + + + + + + + + + + + + +globalConnNetId: connectivity_net10" data-x="-3.7550000000000003" data-y="2.2433333333333345" x="228.26948480845445" y="271.13459562600906" width="9.863496257155447" height="22.192866578599705" fill="#00000066" stroke="#000000" stroke-width="0.020276785714285723" /> +globalConnNetId: connectivity_net10" data-x="-6.415" data-y="2.4423333333333357" x="97.08498458828723" y="261.32041685013934" width="9.863496257155418" height="22.192866578599762" fill="#00000066" stroke="#000000" stroke-width="0.020276785714285723" /> +globalConnNetId: connectivity_net10" data-x="-2.49" data-y="-3.1250000000000004" x="290.6560986349626" y="535.8872743284896" width="9.863496257155418" height="22.192866578599705" fill="#00000066" stroke="#000000" stroke-width="0.020276785714285723" /> - + - + - + - + - + + + + @@ -150,6 +148,9 @@ orientation: x+" data-x="1.757519574999999" data-y="-2" cx="493.97982495355666" + + + @@ -167,28 +168,27 @@ orientation: x+" data-x="1.757519574999999" data-y="-2" cx="493.97982495355666" +globalConnNetId: connectivity_net0" data-x="-1.8574283249999997" data-y="0.9762093000000004" x="161.39522395803996" y="196.79342126794842" width="17.905209437554532" height="40.28672123449769" fill="#ef444466" stroke="#ef4444" stroke-width="0.011169933571428573" /> + + + +globalConnNetId: connectivity_net0" data-x="1.7009999999999998" data-y="-0.224" x="479.9672460962966" y="304.24341569495203" width="17.90520943755456" height="40.286721234497634" fill="#ef444466" stroke="#ef4444" stroke-width="0.011169933571428573" /> +globalConnNetId: connectivity_net1" data-x="-2.31430995" data-y="-0.9762093000000004" x="120.4924180390637" y="371.58574098183345" width="17.905209437554532" height="40.28672123449769" fill="#00000066" stroke="#000000" stroke-width="0.011169933571428573" /> +globalConnNetId: connectivity_net2" data-x="1.982519574999999" data-y="0.85" x="493.97982495355666" y="219.2831969137558" width="40.28672123449769" height="17.905209437554532" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011169933571428573" /> +globalConnNetId: connectivity_net3" data-x="1.982519574999999" data-y="-2" x="493.97982495355666" y="474.43243139890774" width="40.28672123449769" height="17.90520943755456" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.011169933571428573" />