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
22 changes: 18 additions & 4 deletions lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import type { InputProblem } from "lib/types/InputProblem"
import type { GraphicsObject, Line } from "graphics-debug"
import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels"
import { balanceZShapes } from "./balanceZShapes"
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
import type { InputProblem } from "lib/types/InputProblem"
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
import { alignSameNetRails } from "./alignSameNetRails"
import { balanceZShapes } from "./balanceZShapes"
import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels"
import { straightenNearOrthogonalSegments } from "./straightenNearOrthogonalSegments"

export type TraceCleanupOperation =
| "untangling_traces"
| "minimizing_turns"
| "balancing_l_shapes"
| "aligning_same_net_rails"
| "straightening_near_orthogonal_segments"

/**
* Defines the input structure for the TraceCleanupSolver.
Expand All @@ -27,8 +29,8 @@ export interface TraceCleanupSolverInput {
eligibleTraceIds?: ReadonlySet<string>
}

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

/**
* Represents the different stages or steps within the trace cleanup pipeline.
Expand All @@ -37,6 +39,7 @@ const DEFAULT_OPERATIONS: readonly TraceCleanupOperation[] = [
"untangling_traces",
"minimizing_turns",
"balancing_l_shapes",
"straightening_near_orthogonal_segments",
]

/**
Expand Down Expand Up @@ -106,6 +109,9 @@ export class TraceCleanupSolver extends BaseSolver {
case "aligning_same_net_rails":
this._runAlignSameNetRailsStep()
break
case "straightening_near_orthogonal_segments":
this._runStraightenNearOrthogonalSegmentsStep()
break
}
}

Expand Down Expand Up @@ -188,6 +194,14 @@ export class TraceCleanupSolver extends BaseSolver {
this._advancePipeline()
}

private _runStraightenNearOrthogonalSegmentsStep() {
const straightened = straightenNearOrthogonalSegments(this.outputTraces)
this.outputTraces = straightened.traces
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]))
this.stats.straightenedSegmentCount = straightened.straightenedSegmentCount
this._advancePipeline()
}

getOutput() {
return {
traces: this.outputTraces,
Expand Down
59 changes: 59 additions & 0 deletions lib/solvers/TraceCleanupSolver/straightenNearOrthogonalSegments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"

/**
* Largest deviation, in schematic units, that is treated as a segment meant to
* be orthogonal. Pin coordinates in real inputs are occasionally off by a
* fraction of a mil (observed: `5.3999378` against a neighbouring `5.4`), which
* leaves a segment a few hundredths of a percent off axis. That renders as a
* visibly skewed trace even though every other segment is orthogonal.
*
* Kept well below any intentional offset — the smallest deliberate jog in the
* solvers is `LABEL_SEARCH_STEP` at 0.1 — so a real diagonal is never
* flattened.
*/
const MAX_SNAP_DEVIATION = 1e-3

/**
* Snaps segments that are within `MAX_SNAP_DEVIATION` of horizontal or vertical
* onto the axis exactly.
*
* Schematic traces are orthogonal by construction, so a segment that is almost
* but not quite axis-aligned is numeric noise inherited from the input rather
* than a routing decision.
*/
export const straightenNearOrthogonalSegments = (
traces: SolvedTracePath[],
): { traces: SolvedTracePath[]; straightenedSegmentCount: number } => {
let straightenedSegmentCount = 0

const outputTraces = traces.map((trace) => {
const path = trace.tracePath.map((point) => ({ ...point }))
let changed = false

for (let i = 0; i < path.length - 1; i++) {
const a = path[i]!
const b = path[i + 1]!
const dx = Math.abs(a.x - b.x)
const dy = Math.abs(a.y - b.y)

// Already axis-aligned, or a genuine diagonal — leave both alone.
if (dx === 0 || dy === 0) continue
if (dx > MAX_SNAP_DEVIATION && dy > MAX_SNAP_DEVIATION) continue

// Collapse the smaller deviation. Moving the later point keeps the
// trace's starting pin exactly where the input put it.
if (dx < dy) {
b.x = a.x
} else {
b.y = a.y
}

changed = true
straightenedSegmentCount++
}

return changed ? { ...trace, tracePath: path } : trace
})

return { traces: outputTraces, straightenedSegmentCount }
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading