From 345656dc3d8e636d8fe135895203373c45013906 Mon Sep 17 00:00:00 2001 From: Charles Wong Date: Tue, 31 Mar 2026 05:43:32 -0700 Subject: [PATCH 1/4] feat: add SameNetTraceSegmentMergingSolver pipeline phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements new pipeline phase that combines collinear trace segments belonging to the same net when they are close together (gap ≤ 0.15 units) or overlapping. This reduces visual clutter in schematics where same-net traces run near each other. Closes #29 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../SameNetTraceSegmentMergingSolver.ts | 182 +++++++++++++++++ .../SchematicTracePipelineSolver.ts | 18 ++ .../same-net-trace-segment-merging.test.ts | 191 ++++++++++++++++++ 3 files changed, 391 insertions(+) create mode 100644 lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver.ts create mode 100644 tests/solvers/same-net-trace-segment-merging.test.ts diff --git a/lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver.ts b/lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver.ts new file mode 100644 index 00000000..97dfae7e --- /dev/null +++ b/lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver.ts @@ -0,0 +1,182 @@ +import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { InputProblem } from "lib/types/InputProblem" +import type { Point } from "@tscircuit/math-utils" + +const COLLINEAR_TOLERANCE = 0.05 +const GAP_TOLERANCE = 0.15 + +interface Segment { + traceIndex: number + segStart: number // index of first point in the segment within tracePath + p1: Point + p2: Point + orientation: "horizontal" | "vertical" +} + +/** + * Merges collinear trace segments belonging to the same net that are close + * together or overlapping, reducing visual clutter in schematics. + */ +export class SameNetTraceSegmentMergingSolver extends BaseSolver { + private allTraces: SolvedTracePath[] + private inputProblem: InputProblem + private outputTraces: SolvedTracePath[] + + constructor(params: { + allTraces: SolvedTracePath[] + inputProblem: InputProblem + }) { + super() + this.allTraces = params.allTraces + this.inputProblem = params.inputProblem + this.outputTraces = params.allTraces.map((t) => ({ + ...t, + tracePath: [...t.tracePath], + })) + } + + override _step() { + this.mergeCollinearSegments() + this.solved = true + } + + private mergeCollinearSegments() { + // Group traces by globalConnNetId + const netGroups = new Map() + for (let i = 0; i < this.outputTraces.length; i++) { + const trace = this.outputTraces[i] + const netId = trace.globalConnNetId + if (!netGroups.has(netId)) { + netGroups.set(netId, []) + } + netGroups.get(netId)!.push(i) + } + + // Process each net group + for (const [, traceIndices] of netGroups) { + if (traceIndices.length < 2) continue + this.mergeSegmentsInGroup(traceIndices) + } + } + + private mergeSegmentsInGroup(traceIndices: number[]) { + // Extract all segments from all traces in this group + const segments: Segment[] = [] + for (const traceIdx of traceIndices) { + const trace = this.outputTraces[traceIdx] + for (let i = 0; i < trace.tracePath.length - 1; i++) { + const p1 = trace.tracePath[i] + const p2 = trace.tracePath[i + 1] + const dx = Math.abs(p2.x - p1.x) + const dy = Math.abs(p2.y - p1.y) + + if (dx < COLLINEAR_TOLERANCE && dy >= COLLINEAR_TOLERANCE) { + segments.push({ + traceIndex: traceIdx, + segStart: i, + p1, + p2, + orientation: "vertical", + }) + } else if (dy < COLLINEAR_TOLERANCE && dx >= COLLINEAR_TOLERANCE) { + segments.push({ + traceIndex: traceIdx, + segStart: i, + p1, + p2, + orientation: "horizontal", + }) + } + } + } + + // Find mergeable pairs across different traces + const merged = new Set() // indices into segments array that were consumed + + for (let i = 0; i < segments.length; i++) { + if (merged.has(i)) continue + for (let j = i + 1; j < segments.length; j++) { + if (merged.has(j)) continue + if (segments[i].traceIndex === segments[j].traceIndex) continue + if (segments[i].orientation !== segments[j].orientation) continue + + const canMerge = this.canMergeSegments(segments[i], segments[j]) + if (canMerge) { + this.performMerge(segments[i], segments[j]) + merged.add(j) + } + } + } + } + + private canMergeSegments(a: Segment, b: Segment): boolean { + if (a.orientation === "horizontal") { + // Same y within tolerance + const avgYa = (a.p1.y + a.p2.y) / 2 + const avgYb = (b.p1.y + b.p2.y) / 2 + if (Math.abs(avgYa - avgYb) > COLLINEAR_TOLERANCE) return false + + // Check x overlap or small gap + const aMinX = Math.min(a.p1.x, a.p2.x) + const aMaxX = Math.max(a.p1.x, a.p2.x) + const bMinX = Math.min(b.p1.x, b.p2.x) + const bMaxX = Math.max(b.p1.x, b.p2.x) + + const gap = Math.max(0, Math.max(aMinX, bMinX) - Math.min(aMaxX, bMaxX)) + return gap <= GAP_TOLERANCE + } + + // Vertical + const avgXa = (a.p1.x + a.p2.x) / 2 + const avgXb = (b.p1.x + b.p2.x) / 2 + if (Math.abs(avgXa - avgXb) > COLLINEAR_TOLERANCE) return false + + const aMinY = Math.min(a.p1.y, a.p2.y) + const aMaxY = Math.max(a.p1.y, a.p2.y) + const bMinY = Math.min(b.p1.y, b.p2.y) + const bMaxY = Math.max(b.p1.y, b.p2.y) + + const gap = Math.max(0, Math.max(aMinY, bMinY) - Math.min(aMaxY, bMaxY)) + return gap <= GAP_TOLERANCE + } + + private performMerge(a: Segment, b: Segment) { + const traceA = this.outputTraces[a.traceIndex] + const traceB = this.outputTraces[b.traceIndex] + + if (a.orientation === "horizontal") { + const allX = [a.p1.x, a.p2.x, b.p1.x, b.p2.x] + const minX = Math.min(...allX) + const maxX = Math.max(...allX) + const avgY = (a.p1.y + a.p2.y + b.p1.y + b.p2.y) / 4 + + // Extend segment A to cover both + traceA.tracePath[a.segStart] = { x: minX, y: avgY } + traceA.tracePath[a.segStart + 1] = { x: maxX, y: avgY } + + // Collapse segment B to a single point (midpoint) + const midX = (b.p1.x + b.p2.x) / 2 + traceB.tracePath[b.segStart] = { x: midX, y: avgY } + traceB.tracePath[b.segStart + 1] = { x: midX, y: avgY } + } else { + const allY = [a.p1.y, a.p2.y, b.p1.y, b.p2.y] + const minY = Math.min(...allY) + const maxY = Math.max(...allY) + const avgX = (a.p1.x + a.p2.x + b.p1.x + b.p2.x) / 4 + + traceA.tracePath[a.segStart] = { x: avgX, y: minY } + traceA.tracePath[a.segStart + 1] = { x: avgX, y: maxY } + + const midY = (b.p1.y + b.p2.y) / 2 + traceB.tracePath[b.segStart] = { x: avgX, y: midY } + traceB.tracePath[b.segStart + 1] = { x: avgX, y: midY } + } + } + + getOutput() { + return { + traces: this.outputTraces, + } + } +} diff --git a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts index c9d5a995..6879a67f 100644 --- a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +++ b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts @@ -20,6 +20,7 @@ import { expandChipsToFitPins } from "./expandChipsToFitPins" import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver" import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver" import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver" +import { SameNetTraceSegmentMergingSolver } from "../SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver" type PipelineStep BaseSolver> = { solverName: string @@ -69,6 +70,7 @@ export class SchematicTracePipelineSolver extends BaseSolver { labelMergingSolver?: MergedNetLabelObstacleSolver traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver traceCleanupSolver?: TraceCleanupSolver + sameNetTraceSegmentMergingSolver?: SameNetTraceSegmentMergingSolver startTimeOfPhase: Record endTimeOfPhase: Record @@ -206,11 +208,27 @@ export class SchematicTracePipelineSolver extends BaseSolver { }, ] }), + definePipelineStep( + "sameNetTraceSegmentMergingSolver", + SameNetTraceSegmentMergingSolver, + (instance) => { + const traces = + instance.traceCleanupSolver?.getOutput().traces ?? + instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces + return [ + { + allTraces: traces, + inputProblem: instance.inputProblem, + }, + ] + }, + ), definePipelineStep( "netLabelPlacementSolver", NetLabelPlacementSolver, (instance) => { const traces = + instance.sameNetTraceSegmentMergingSolver?.getOutput().traces ?? instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces diff --git a/tests/solvers/same-net-trace-segment-merging.test.ts b/tests/solvers/same-net-trace-segment-merging.test.ts new file mode 100644 index 00000000..19bf5b90 --- /dev/null +++ b/tests/solvers/same-net-trace-segment-merging.test.ts @@ -0,0 +1,191 @@ +import { expect, test } from "bun:test" +import { SameNetTraceSegmentMergingSolver } from "lib/solvers/SameNetTraceSegmentMergingSolver/SameNetTraceSegmentMergingSolver" +import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" +import type { InputProblem } from "lib/types/InputProblem" + +const emptyInputProblem: InputProblem = { + chips: [], + directConnections: [], + netConnections: [], + availableNetLabelOrientations: {}, +} + +function makeTrace( + overrides: Partial & { + tracePath: { x: number; y: number }[] + globalConnNetId: string + mspPairId: string + }, +): SolvedTracePath { + return { + dcConnNetId: overrides.dcConnNetId ?? overrides.globalConnNetId, + globalConnNetId: overrides.globalConnNetId, + mspPairId: overrides.mspPairId, + pins: overrides.pins ?? ([ + { pinId: "p1", x: 0, y: 0, chipId: "c1" }, + { pinId: "p2", x: 1, y: 0, chipId: "c2" }, + ] as SolvedTracePath["pins"]), + tracePath: overrides.tracePath, + mspConnectionPairIds: overrides.mspConnectionPairIds ?? [ + overrides.mspPairId, + ], + pinIds: overrides.pinIds ?? ["p1", "p2"], + } +} + +test("merges two horizontal same-net segments with a tiny gap", () => { + const traceA = makeTrace({ + mspPairId: "pair1", + globalConnNetId: "net1", + tracePath: [ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 2, y: 1 }, // horizontal segment from x=0 to x=2 at y=1 + { x: 2, y: 2 }, + ], + }) + + const traceB = makeTrace({ + mspPairId: "pair2", + globalConnNetId: "net1", + tracePath: [ + { x: 5, y: 3 }, + { x: 5, y: 1 }, + { x: 2.1, y: 1 }, // horizontal segment from x=5 to x=2.1 at y=1 (gap = 0.1) + { x: 2.1, y: 0 }, + ], + }) + + const solver = new SameNetTraceSegmentMergingSolver({ + allTraces: [traceA, traceB], + inputProblem: emptyInputProblem, + }) + + solver.solve() + expect(solver.solved).toBe(true) + + const output = solver.getOutput() + expect(output.traces).toHaveLength(2) + + // The merged segment should span from x=0 to x=5 + const mergedTraceA = output.traces[0] + const horizontalSegA = mergedTraceA.tracePath.slice(1, 3) + const xValues = horizontalSegA.map((p) => p.x) + expect(Math.min(...xValues)).toBe(0) + expect(Math.max(...xValues)).toBe(5) +}) + +test("does NOT merge segments of different nets", () => { + const traceA = makeTrace({ + mspPairId: "pair1", + globalConnNetId: "net1", + tracePath: [ + { x: 0, y: 0 }, + { x: 2, y: 0 }, // horizontal at y=0 + ], + }) + + const traceB = makeTrace({ + mspPairId: "pair2", + globalConnNetId: "net2", // different net + tracePath: [ + { x: 2.1, y: 0 }, + { x: 4, y: 0 }, // horizontal at y=0, gap=0.1 + ], + }) + + const solver = new SameNetTraceSegmentMergingSolver({ + allTraces: [traceA, traceB], + inputProblem: emptyInputProblem, + }) + + solver.solve() + expect(solver.solved).toBe(true) + + const output = solver.getOutput() + // Segments should remain unchanged + expect(output.traces[0].tracePath[0].x).toBe(0) + expect(output.traces[0].tracePath[1].x).toBe(2) + expect(output.traces[1].tracePath[0].x).toBe(2.1) + expect(output.traces[1].tracePath[1].x).toBe(4) +}) + +test("does NOT merge same-net segments that are far apart", () => { + const traceA = makeTrace({ + mspPairId: "pair1", + globalConnNetId: "net1", + tracePath: [ + { x: 0, y: 0 }, + { x: 2, y: 0 }, // horizontal at y=0 + ], + }) + + const traceB = makeTrace({ + mspPairId: "pair2", + globalConnNetId: "net1", + tracePath: [ + { x: 3, y: 0 }, + { x: 5, y: 0 }, // horizontal at y=0, but gap=1.0 + ], + }) + + const solver = new SameNetTraceSegmentMergingSolver({ + allTraces: [traceA, traceB], + inputProblem: emptyInputProblem, + }) + + solver.solve() + expect(solver.solved).toBe(true) + + const output = solver.getOutput() + // Segments should remain unchanged + expect(output.traces[0].tracePath[0].x).toBe(0) + expect(output.traces[0].tracePath[1].x).toBe(2) + expect(output.traces[1].tracePath[0].x).toBe(3) + expect(output.traces[1].tracePath[1].x).toBe(5) +}) + +test("handles empty trace list without crashing", () => { + const solver = new SameNetTraceSegmentMergingSolver({ + allTraces: [], + inputProblem: emptyInputProblem, + }) + + solver.solve() + expect(solver.solved).toBe(true) + expect(solver.getOutput().traces).toHaveLength(0) +}) + +test("merges vertical collinear same-net segments", () => { + const traceA = makeTrace({ + mspPairId: "pair1", + globalConnNetId: "net1", + tracePath: [ + { x: 1, y: 0 }, + { x: 1, y: 2 }, // vertical at x=1 + ], + }) + + const traceB = makeTrace({ + mspPairId: "pair2", + globalConnNetId: "net1", + tracePath: [ + { x: 1, y: 2.1 }, + { x: 1, y: 4 }, // vertical at x=1, gap=0.1 + ], + }) + + const solver = new SameNetTraceSegmentMergingSolver({ + allTraces: [traceA, traceB], + inputProblem: emptyInputProblem, + }) + + solver.solve() + expect(solver.solved).toBe(true) + + const output = solver.getOutput() + // The merged segment A should span from y=0 to y=4 + const yValues = output.traces[0].tracePath.map((p) => p.y) + expect(Math.min(...yValues)).toBe(0) + expect(Math.max(...yValues)).toBe(4) +}) From 3e50fbcf6d964a291446411b6b9daa3ef1aa0a91 Mon Sep 17 00:00:00 2001 From: Charles Wong Date: Tue, 31 Mar 2026 05:48:02 -0700 Subject: [PATCH 2/4] chore: update visual snapshots for SameNetTraceSegmentMergingSolver The new pipeline phase changes the visual output of schematic traces by merging close collinear same-net segments. Update snapshots accordingly. --- .../examples/__snapshots__/example01.snap.svg | 8 +- .../examples/__snapshots__/example02.snap.svg | 18 +- .../examples/__snapshots__/example13.snap.svg | 168 ++++----- .../examples/__snapshots__/example15.snap.svg | 54 +-- .../examples/__snapshots__/example18.snap.svg | 8 +- .../examples/__snapshots__/example19.snap.svg | 8 +- .../examples/__snapshots__/example20.snap.svg | 8 +- .../examples/__snapshots__/example21.snap.svg | 130 +++---- .../examples/__snapshots__/example25.snap.svg | 4 +- .../examples/__snapshots__/example29.snap.svg | 342 +++--------------- 10 files changed, 254 insertions(+), 494 deletions(-) diff --git a/tests/examples/__snapshots__/example01.snap.svg b/tests/examples/__snapshots__/example01.snap.svg index 293bf05a..2bb8b182 100644 --- a/tests/examples/__snapshots__/example01.snap.svg +++ b/tests/examples/__snapshots__/example01.snap.svg @@ -47,7 +47,7 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" - + @@ -71,10 +71,10 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" - + - + @@ -92,7 +92,7 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" - + - + @@ -152,25 +152,25 @@ x+" data-x="1" data-y="0.1" cx="500.20151295522464" cy="324.189420823755" r="3" - + - + - + - + - + - + - + @@ -191,7 +191,7 @@ x+" data-x="1" data-y="0.1" cx="500.20151295522464" cy="324.189420823755" r="3" - + diff --git a/tests/examples/__snapshots__/example13.snap.svg b/tests/examples/__snapshots__/example13.snap.svg index 08e47d6d..41004f03 100644 --- a/tests/examples/__snapshots__/example13.snap.svg +++ b/tests/examples/__snapshots__/example13.snap.svg @@ -2,262 +2,262 @@ +x+" data-x="1.15" data-y="0.75" cx="374.18851087562746" cy="304.38371444506413" r="3" fill="hsl(159, 100%, 50%, 0.8)" /> +x+" data-x="1.15" data-y="0" cx="374.18851087562746" cy="351.23257110987174" r="3" fill="hsl(158, 100%, 50%, 0.8)" /> +x+" data-x="1.15" data-y="-0.75" cx="374.18851087562746" cy="398.08142777467936" r="3" fill="hsl(157, 100%, 50%, 0.8)" /> +x-" data-x="-1.15" data-y="-1.125" cx="230.51868377021754" cy="421.5058561070831" r="3" fill="hsl(156, 100%, 50%, 0.8)" /> +x-" data-x="-1.15" data-y="-0.375" cx="230.51868377021754" cy="374.65699944227555" r="3" fill="hsl(155, 100%, 50%, 0.8)" /> +x-" data-x="-1.15" data-y="0.375" cx="230.51868377021754" cy="327.80814277746794" r="3" fill="hsl(154, 100%, 50%, 0.8)" /> +x-" data-x="-1.15" data-y="1.125" cx="230.51868377021754" cy="280.9592861126604" r="3" fill="hsl(153, 100%, 50%, 0.8)" /> +x-" data-x="-3.55" data-y="0" cx="80.60234244283328" cy="351.23257110987174" r="3" fill="hsl(351, 100%, 50%, 0.8)" /> +x+" data-x="-2.45" data-y="0" cx="149.31399888455104" cy="351.23257110987174" r="3" fill="hsl(352, 100%, 50%, 0.8)" /> +x-" data-x="-3.55" data-y="-1.9999999999999998" cx="80.60234244283328" cy="476.1628555493586" r="3" fill="hsl(232, 100%, 50%, 0.8)" /> +x+" data-x="-2.45" data-y="-1.9999999999999998" cx="149.31399888455104" cy="476.1628555493586" r="3" fill="hsl(233, 100%, 50%, 0.8)" /> +x-" data-x="-3.55" data-y="2" cx="80.60234244283328" cy="226.30228667038483" r="3" fill="hsl(113, 100%, 50%, 0.8)" /> +x+" data-x="-2.45" data-y="2" cx="149.31399888455104" cy="226.30228667038483" r="3" fill="hsl(114, 100%, 50%, 0.8)" /> +x-" data-x="2.45" data-y="-1.5" cx="455.39319576129395" cy="444.9302844394869" r="3" fill="hsl(246, 100%, 50%, 0.8)" /> +x+" data-x="3.55" data-y="-1.5" cx="524.1048522030117" cy="444.9302844394869" r="3" fill="hsl(247, 100%, 50%, 0.8)" /> +x-" data-x="2.45" data-y="0" cx="455.39319576129395" cy="351.23257110987174" r="3" fill="hsl(127, 100%, 50%, 0.8)" /> +x+" data-x="3.55" data-y="0" cx="524.1048522030117" cy="351.23257110987174" r="3" fill="hsl(128, 100%, 50%, 0.8)" /> +x-" data-x="3.435" data-y="3" cx="516.9213608477412" cy="163.8371444506414" r="3" fill="hsl(57, 100%, 50%, 0.8)" /> +x+" data-x="4.5649999999999995" data-y="3" cx="587.5069715560513" cy="163.8371444506414" r="3" fill="hsl(58, 100%, 50%, 0.8)" /> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -335,10 +335,10 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + @@ -767,40 +767,40 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + - + - + - + - + - + - + - + - + - + - + @@ -812,25 +812,25 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + - + - + - + - + - + @@ -875,13 +875,13 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + @@ -890,10 +890,10 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + diff --git a/tests/examples/__snapshots__/example18.snap.svg b/tests/examples/__snapshots__/example18.snap.svg index d3b910b0..f3f24f4d 100644 --- a/tests/examples/__snapshots__/example18.snap.svg +++ b/tests/examples/__snapshots__/example18.snap.svg @@ -68,7 +68,7 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="493.97982495355666" cy="501.290 - + @@ -131,10 +131,10 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="493.97982495355666" cy="501.290 - + - + @@ -164,7 +164,7 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="493.97982495355666" cy="501.290 - + diff --git a/tests/examples/__snapshots__/example19.snap.svg b/tests/examples/__snapshots__/example19.snap.svg index 1ae98880..add9c265 100644 --- a/tests/examples/__snapshots__/example19.snap.svg +++ b/tests/examples/__snapshots__/example19.snap.svg @@ -91,16 +91,16 @@ x-" data-x="1.5541992" data-y="-1.2014628704999997" cx="255.41992000000002" cy=" - + - + - + - + diff --git a/tests/examples/__snapshots__/example20.snap.svg b/tests/examples/__snapshots__/example20.snap.svg index ba057c2c..6c7eb0c3 100644 --- a/tests/examples/__snapshots__/example20.snap.svg +++ b/tests/examples/__snapshots__/example20.snap.svg @@ -68,7 +68,7 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="479.91860312291124" cy="509.268 - + @@ -131,10 +131,10 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="479.91860312291124" cy="509.268 - + - + @@ -164,7 +164,7 @@ y+" data-x="1.757519574999999" data-y="-2.2" cx="479.91860312291124" cy="509.268 - + diff --git a/tests/examples/__snapshots__/example21.snap.svg b/tests/examples/__snapshots__/example21.snap.svg index 5e7e7ceb..bb7a2be9 100644 --- a/tests/examples/__snapshots__/example21.snap.svg +++ b/tests/examples/__snapshots__/example21.snap.svg @@ -2,209 +2,209 @@ +x-" data-x="-1.4" data-y="-0.6" cx="65.61715719826601" cy="313.6116814966918" r="3" fill="hsl(65, 100%, 50%, 0.8)" /> +x-" data-x="-1.4" data-y="0.6" cx="65.61715719826601" cy="236.95185945699296" r="3" fill="hsl(66, 100%, 50%, 0.8)" /> +x+" data-x="1.4" data-y="0.6" cx="244.49007529089664" cy="236.95185945699296" r="3" fill="hsl(67, 100%, 50%, 0.8)" /> +x+" data-x="1.4" data-y="-0.6" cx="244.49007529089664" cy="313.6116814966918" r="3" fill="hsl(68, 100%, 50%, 0.8)" /> +x-" data-x="3.785" data-y="-0.29999999999999993" cx="396.8514715947981" cy="294.4467259867671" r="3" fill="hsl(175, 100%, 50%, 0.8)" /> +y-" data-x="4.785" data-y="-1.2999999999999998" cx="460.73465662788044" cy="358.3299110198494" r="3" fill="hsl(176, 100%, 50%, 0.8)" /> +x-" data-x="3.785" data-y="-0.4999999999999999" cx="396.8514715947981" cy="307.22336299338355" r="3" fill="hsl(177, 100%, 50%, 0.8)" /> +x+" data-x="5.785" data-y="-0.3999999999999999" cx="524.6178416609628" cy="300.83504449007535" r="3" fill="hsl(179, 100%, 50%, 0.8)" /> +y+" data-x="6.7" data-y="-0.39999999999999925" cx="583.0709559662332" cy="300.8350444900753" r="3" fill="hsl(218, 100%, 50%, 0.8)" /> +y-" data-x="6.7" data-y="-1.4999999999999993" cx="583.0709559662332" cy="371.1065480264659" r="3" fill="hsl(219, 100%, 50%, 0.8)" /> +y+" data-x="2.8699999999999997" data-y="-1.2" cx="338.39835728952767" cy="351.9415925165412" r="3" fill="hsl(337, 100%, 50%, 0.8)" /> +y-" data-x="2.8699999999999997" data-y="-2.3" cx="338.39835728952767" cy="422.2130960529318" r="3" fill="hsl(338, 100%, 50%, 0.8)" /> +y+" data-x="2.9752723250000006" data-y="0.6000000000000003" cx="345.1234887063655" cy="236.95185945699293" r="3" fill="hsl(82, 100%, 50%, 0.8)" /> +y-" data-x="2.9752723250000006" data-y="-0.4999999999999998" cx="345.1234887063655" cy="307.22336299338355" r="3" fill="hsl(83, 100%, 50%, 0.8)" /> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/tests/examples/__snapshots__/example29.snap.svg b/tests/examples/__snapshots__/example29.snap.svg index c931ee37..e1594358 100644 --- a/tests/examples/__snapshots__/example29.snap.svg +++ b/tests/examples/__snapshots__/example29.snap.svg @@ -489,142 +489,22 @@ x+" data-x="-8.4" data-y="-16.6" cx="198.31710258539454" cy="392.52251162760695" x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r="3" fill="hsl(226, 100%, 50%, 0.8)" /> - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -795,121 +675,121 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1011,142 +891,22 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - +