From 63f1b157444e869681fdba6ca90d9aa96fcfb26f Mon Sep 17 00:00:00 2001 From: lamb356 Date: Thu, 11 Dec 2025 23:44:29 -0600 Subject: [PATCH 1/5] fix: simplify trace path after rerouting to remove extra lines --- .../sub-solver/UntangleTraceSubsolver.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts index 3519aa9..ef677f7 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts @@ -23,6 +23,7 @@ import { visualizeTightRectangle } from "../visualizeTightRectangle" import { visualizeCandidates } from "./visualizeCandidates" import { mergeGraphicsObjects } from "../mergeGraphicsObjects" import { visualizeCollision } from "./visualizeCollision" +import { simplifyPath } from "../simplifyPath" /** * Defines the input structure for the UntangleTraceSubsolver. @@ -258,11 +259,15 @@ export class UntangleTraceSubsolver extends BaseSolver { p.x === this.currentLShape!.p2.x && p.y === this.currentLShape!.p2.y, ) if (p2Index !== -1) { - const newTracePath = [ + // Build the new trace path by replacing p2 with the bestRoute + const rawNewTracePath = [ ...originalTrace.tracePath.slice(0, p2Index), ...bestRoute, ...originalTrace.tracePath.slice(p2Index + 1), ] + // Simplify the path to remove any redundant collinear points + // This fixes the issue of extra trace lines appearing after rerouting + const newTracePath = simplifyPath(rawNewTracePath) this.input.allTraces[traceIndex] = { ...originalTrace, tracePath: newTracePath, From 78e2fed49f215f34986293c0269de69788f80ef4 Mon Sep 17 00:00:00 2001 From: lamb356 Date: Thu, 11 Dec 2025 23:45:39 -0600 Subject: [PATCH 2/5] fix: enhance simplifyPath to remove duplicate consecutive points --- .../TraceCleanupSolver/simplifyPath.ts | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/simplifyPath.ts b/lib/solvers/TraceCleanupSolver/simplifyPath.ts index e17bfb5..25ce645 100644 --- a/lib/solvers/TraceCleanupSolver/simplifyPath.ts +++ b/lib/solvers/TraceCleanupSolver/simplifyPath.ts @@ -4,13 +4,41 @@ import { isVertical, } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions" +const EPS = 1e-9 + +/** + * Checks if two points are essentially the same (within epsilon tolerance) + */ +const isSamePoint = (a: Point, b: Point): boolean => { + return Math.abs(a.x - b.x) < EPS && Math.abs(a.y - b.y) < EPS +} + +/** + * Removes duplicate consecutive points from a path + */ +const removeDuplicateConsecutivePoints = (path: Point[]): Point[] => { + if (path.length < 2) return path + const result: Point[] = [path[0]] + for (let i = 1; i < path.length; i++) { + if (!isSamePoint(result[result.length - 1], path[i])) { + result.push(path[i]) + } + } + return result +} + export const simplifyPath = (path: Point[]): Point[] => { - if (path.length < 3) return path - const newPath: Point[] = [path[0]] - for (let i = 1; i < path.length - 1; i++) { + // First, remove any duplicate consecutive points + const dedupedPath = removeDuplicateConsecutivePoints(path) + + if (dedupedPath.length < 3) return dedupedPath + + // First pass: remove collinear intermediate points + const newPath: Point[] = [dedupedPath[0]] + for (let i = 1; i < dedupedPath.length - 1; i++) { const p1 = newPath[newPath.length - 1] - const p2 = path[i] - const p3 = path[i + 1] + const p2 = dedupedPath[i] + const p3 = dedupedPath[i + 1] if ( (isVertical(p1, p2) && isVertical(p2, p3)) || (isHorizontal(p1, p2) && isHorizontal(p2, p3)) @@ -19,9 +47,11 @@ export const simplifyPath = (path: Point[]): Point[] => { } newPath.push(p2) } - newPath.push(path[path.length - 1]) + newPath.push(dedupedPath[dedupedPath.length - 1]) if (newPath.length < 3) return newPath + + // Second pass: ensure any remaining collinear segments are merged const finalPath: Point[] = [newPath[0]] for (let i = 1; i < newPath.length - 1; i++) { const p1 = finalPath[finalPath.length - 1] From f9ca6a7afe81b6459d8852e279a885cbe78e6f60 Mon Sep 17 00:00:00 2001 From: lamb356 Date: Thu, 11 Dec 2025 23:46:41 -0600 Subject: [PATCH 3/5] test: add comprehensive tests for simplifyPath --- tests/functions/simplifyPath.test.ts | 120 +++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 tests/functions/simplifyPath.test.ts diff --git a/tests/functions/simplifyPath.test.ts b/tests/functions/simplifyPath.test.ts new file mode 100644 index 0000000..14d5127 --- /dev/null +++ b/tests/functions/simplifyPath.test.ts @@ -0,0 +1,120 @@ +import { expect, test, describe } from "bun:test" +import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath" + +describe("simplifyPath", () => { + test("should return path unchanged if less than 3 points", () => { + expect(simplifyPath([])).toEqual([]) + expect(simplifyPath([{ x: 0, y: 0 }])).toEqual([{ x: 0, y: 0 }]) + expect(simplifyPath([{ x: 0, y: 0 }, { x: 1, y: 1 }])).toEqual([ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ]) + }) + + test("should remove collinear horizontal points", () => { + const path = [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 2, y: 0 }, + { x: 3, y: 0 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 3, y: 0 }, + ]) + }) + + test("should remove collinear vertical points", () => { + const path = [ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 0, y: 2 }, + { x: 0, y: 3 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 0, y: 3 }, + ]) + }) + + test("should preserve L-shaped corners", () => { + const path = [ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 1, y: 1 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 1, y: 1 }, + ]) + }) + + test("should handle Z-shape correctly", () => { + const path = [ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 1, y: 1 }, + { x: 2, y: 1 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 1, y: 1 }, + { x: 2, y: 1 }, + ]) + }) + + test("should remove duplicate consecutive points", () => { + const path = [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, // duplicate + { x: 1, y: 0 }, + { x: 1, y: 0 }, // duplicate + { x: 1, y: 1 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + { x: 1, y: 1 }, + ]) + }) + + test("should remove near-duplicate consecutive points (within epsilon)", () => { + const path = [ + { x: 0, y: 0 }, + { x: 1e-10, y: 1e-10 }, // near-duplicate + { x: 1, y: 0 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 1, y: 0 }, + ]) + }) + + test("should handle complex path with duplicates and collinear points", () => { + const path = [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, // duplicate + { x: 1, y: 0 }, + { x: 2, y: 0 }, // collinear with previous + { x: 2, y: 1 }, + { x: 2, y: 2 }, // collinear with previous + { x: 2, y: 2 }, // duplicate + { x: 3, y: 2 }, + ] + const result = simplifyPath(path) + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 2, y: 0 }, + { x: 2, y: 2 }, + { x: 3, y: 2 }, + ]) + }) +}) From 73c6b1b30635ac234e8e15a2c8a1da580402dd56 Mon Sep 17 00:00:00 2001 From: YOUR_NAME Date: Thu, 11 Dec 2025 23:55:32 -0600 Subject: [PATCH 4/5] test: update example29 snapshot after fix --- tests/examples/__snapshots__/example29.snap.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/examples/__snapshots__/example29.snap.svg b/tests/examples/__snapshots__/example29.snap.svg index c931ee3..77f66c3 100644 --- a/tests/examples/__snapshots__/example29.snap.svg +++ b/tests/examples/__snapshots__/example29.snap.svg @@ -516,7 +516,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - + @@ -831,7 +831,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - + @@ -849,7 +849,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - + @@ -1038,7 +1038,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r - + From 4146ad99a2b8b3ba5fc9eb8abe383136ed84e2b0 Mon Sep 17 00:00:00 2001 From: YOUR_NAME Date: Fri, 12 Dec 2025 01:10:47 -0600 Subject: [PATCH 5/5] fix: format code --- tests/functions/simplifyPath.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/functions/simplifyPath.test.ts b/tests/functions/simplifyPath.test.ts index 14d5127..e3e926f 100644 --- a/tests/functions/simplifyPath.test.ts +++ b/tests/functions/simplifyPath.test.ts @@ -5,7 +5,12 @@ describe("simplifyPath", () => { test("should return path unchanged if less than 3 points", () => { expect(simplifyPath([])).toEqual([]) expect(simplifyPath([{ x: 0, y: 0 }])).toEqual([{ x: 0, y: 0 }]) - expect(simplifyPath([{ x: 0, y: 0 }, { x: 1, y: 1 }])).toEqual([ + expect( + simplifyPath([ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ]), + ).toEqual([ { x: 0, y: 0 }, { x: 1, y: 1 }, ])