Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ interface TraceCleanupSolverInput {

import { UntangleTraceSubsolver } from "./sub-solver/UntangleTraceSubsolver"
import { is4PointRectangle } from "./is4PointRectangle"
import { mergeCloseSameNetTraceSegments } from "./mergeCloseSameNetTraceSegments"

/**
* Represents the different stages or steps within the trace cleanup pipeline.
*/
type PipelineStep =
| "minimizing_turns"
| "balancing_l_shapes"
| "merging_close_same_net_segments"
| "untangling_traces"

Comment on lines 28 to 33
/**
Expand Down Expand Up @@ -84,6 +86,9 @@ export class TraceCleanupSolver extends BaseSolver {
case "balancing_l_shapes":
this._runBalanceLShapesStep()
break
case "merging_close_same_net_segments":
this._runMergeCloseSameNetSegmentsStep()
break
}
}

Expand All @@ -108,13 +113,19 @@ export class TraceCleanupSolver extends BaseSolver {

private _runBalanceLShapesStep() {
if (this.traceIdQueue.length === 0) {
this.solved = true
this.pipelineStep = "merging_close_same_net_segments"
return
}

this._processTrace("balancing_l_shapes")
}

private _runMergeCloseSameNetSegmentsStep() {
this.outputTraces = mergeCloseSameNetTraceSegments(this.outputTraces)
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]))
this.solved = true
}

private _processTrace(step: "minimizing_turns" | "balancing_l_shapes") {
const targetMspConnectionPairId = this.traceIdQueue.shift()!
this.activeTraceId = targetMspConnectionPairId
Expand Down
138 changes: 138 additions & 0 deletions lib/solvers/TraceCleanupSolver/mergeCloseSameNetTraceSegments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import type { Point } from "graphics-debug"
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
import { simplifyPath } from "./simplifyPath"

const EPS = 1e-6
const DEFAULT_MERGE_DISTANCE = 0.12

type Segment = {
traceIndex: number
segmentIndex: number
p1: Point
p2: Point
orientation: "horizontal" | "vertical"
fixedCoord: number
minRange: number
maxRange: number
}

const getSegment = (
trace: SolvedTracePath,
traceIndex: number,
segmentIndex: number,
): Segment | null => {
const p1 = trace.tracePath[segmentIndex]!
const p2 = trace.tracePath[segmentIndex + 1]!

if (Math.abs(p1.y - p2.y) < EPS) {
return {
traceIndex,
segmentIndex,
p1,
p2,
orientation: "horizontal",
fixedCoord: p1.y,
minRange: Math.min(p1.x, p2.x),
maxRange: Math.max(p1.x, p2.x),
}
}

if (Math.abs(p1.x - p2.x) < EPS) {
return {
traceIndex,
segmentIndex,
p1,
p2,
orientation: "vertical",
fixedCoord: p1.x,
minRange: Math.min(p1.y, p2.y),
maxRange: Math.max(p1.y, p2.y),
}
}

return null
}

const rangesOverlap = (a: Segment, b: Segment) =>
Math.min(a.maxRange, b.maxRange) - Math.max(a.minRange, b.minRange) > EPS

const sameNet = (a: SolvedTracePath, b: SolvedTracePath) =>
a.globalConnNetId === b.globalConnNetId

const setSegmentFixedCoord = (
path: Point[],
segmentIndex: number,
orientation: Segment["orientation"],
coord: number,
) => {
if (orientation === "horizontal") {
path[segmentIndex] = { ...path[segmentIndex]!, y: coord }
path[segmentIndex + 1] = { ...path[segmentIndex + 1]!, y: coord }
} else {
path[segmentIndex] = { ...path[segmentIndex]!, x: coord }
path[segmentIndex + 1] = { ...path[segmentIndex + 1]!, x: coord }
}
}

const isInternalSegment = (trace: SolvedTracePath, segmentIndex: number) =>
segmentIndex > 0 && segmentIndex < trace.tracePath.length - 2

Comment on lines +77 to +79
export const mergeCloseSameNetTraceSegments = (
traces: SolvedTracePath[],
mergeDistance = DEFAULT_MERGE_DISTANCE,
): SolvedTracePath[] => {
const outputTraces = traces.map((trace) => ({
...trace,
tracePath: trace.tracePath.map((point) => ({ ...point })),
}))

for (let traceAIndex = 0; traceAIndex < outputTraces.length; traceAIndex++) {
const traceA = outputTraces[traceAIndex]!

for (
let segAIndex = 0;
segAIndex < traceA.tracePath.length - 1;
segAIndex++
) {
const segmentA = getSegment(traceA, traceAIndex, segAIndex)
if (!segmentA) continue

for (
let traceBIndex = traceAIndex + 1;
traceBIndex < outputTraces.length;
traceBIndex++
) {
const traceB = outputTraces[traceBIndex]!
if (!sameNet(traceA, traceB)) continue

for (
let segBIndex = 0;
segBIndex < traceB.tracePath.length - 1;
segBIndex++
) {
if (!isInternalSegment(traceB, segBIndex)) continue

const segmentB = getSegment(traceB, traceBIndex, segBIndex)
if (!segmentB) continue
if (segmentA.orientation !== segmentB.orientation) continue
if (!rangesOverlap(segmentA, segmentB)) continue

const distance = Math.abs(segmentA.fixedCoord - segmentB.fixedCoord)
if (distance < EPS || distance > mergeDistance) continue

setSegmentFixedCoord(
traceB.tracePath,
Comment on lines +120 to +124
segmentB.segmentIndex,
segmentB.orientation,
segmentA.fixedCoord,
)
Comment on lines +123 to +128
}
}
}
}

return outputTraces.map((trace) => ({
...trace,
tracePath: simplifyPath(trace.tracePath),
}))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { expect, test } from "bun:test"
import { mergeCloseSameNetTraceSegments } from "lib/solvers/TraceCleanupSolver/mergeCloseSameNetTraceSegments"
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"

const makeTrace = (
mspPairId: string,
globalConnNetId: string,
tracePath: SolvedTracePath["tracePath"],
): SolvedTracePath =>
({
mspPairId,
globalConnNetId,
tracePath,
mspConnectionPairIds: [mspPairId],
pinIds: [],
pins: [],
}) as unknown as SolvedTracePath

test("merges close same-net parallel trace segments", () => {
const traces = [
makeTrace("a", "net1", [
{ x: 0, y: 0 },
{ x: 3, y: 0 },
]),
makeTrace("b", "net1", [
{ x: 1, y: -1 },
{ x: 1, y: 0.08 },
{ x: 4, y: 0.08 },
{ x: 4, y: 1 },
]),
]

const result = mergeCloseSameNetTraceSegments(traces)

expect(result[1]!.tracePath).toEqual([
{ x: 1, y: -1 },
{ x: 1, y: 0 },
{ x: 4, y: 0 },
{ x: 4, y: 1 },
])
})

test("does not merge close parallel trace segments from different nets", () => {
const traces = [
makeTrace("a", "net1", [
{ x: 0, y: 0 },
{ x: 3, y: 0 },
]),
makeTrace("b", "net2", [
{ x: 1, y: -1 },
{ x: 1, y: 0.08 },
{ x: 4, y: 0.08 },
{ x: 4, y: 1 },
]),
]

const result = mergeCloseSameNetTraceSegments(traces)

expect(result[1]!.tracePath).toEqual([
{ x: 1, y: -1 },
{ x: 1, y: 0.08 },
{ x: 4, y: 0.08 },
{ x: 4, y: 1 },
])
})
Loading