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
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import type { ConnectivityMap } from "circuit-json-to-connectivity-map"
import type { SimplifiedPcbTrace } from "lib/types"
import type { HighDensityRoute } from "lib/types/high-density-types"
import type { Obstacle } from "lib/types/srj-types"
import { convertHdRouteToSimplifiedRoute } from "lib/utils/convertHdRouteToSimplifiedRoute"
import type { PreloadedHighDensityRoute } from "./convert-preloaded-traces-to-hd-routes"

type ApplyFixedRouteReplacementsParams = {
originalTraces: SimplifiedPcbTrace[]
originalFixedRoutes: PreloadedHighDensityRoute[]
updatedFixedRoutes: HighDensityRoute[]
replacedConnectionNames: ReadonlySet<string>
layerCount: number
defaultViaHoleDiameter: number
obstacles: Obstacle[]
connMap: ConnectivityMap
}

type ApplyFixedRouteReplacementsResult = {
updatedPreloadedTraces: SimplifiedPcbTrace[]
mutatedPreloadedTraces: SimplifiedPcbTrace[]
}

const POINT_EPSILON = 1e-9

const pointsAreEqual = (
a: HighDensityRoute["route"][number],
b: HighDensityRoute["route"][number],
) =>
Math.abs(a.x - b.x) <= POINT_EPSILON &&
Math.abs(a.y - b.y) <= POINT_EPSILON &&
a.z === b.z

const appendConnectedRoute = (
combinedPoints: HighDensityRoute["route"],
route: HighDensityRoute,
) => {
if (combinedPoints.length === 0) {
combinedPoints.push(...route.route)
return
}

const previousEnd = combinedPoints.at(-1)!
if (route.route[0] && pointsAreEqual(previousEnd, route.route[0])) {
combinedPoints.push(...route.route.slice(1))
return
}
if (route.route.at(-1) && pointsAreEqual(previousEnd, route.route.at(-1)!)) {
combinedPoints.push(...route.route.slice(0, -1).reverse())
return
}

throw new Error(
`Pipeline9 could not reconnect mutated preloaded segment "${route.connectionName}"`,
)
}

/**
* Rebuilds only the preloaded traces touched by a regional fallback. Untouched
* traces retain their original serialized representation and PCB trace ids.
*/
export const applyFixedRouteReplacementsToPreloadedTraces = ({
originalTraces,
originalFixedRoutes,
updatedFixedRoutes,
replacedConnectionNames,
layerCount,
defaultViaHoleDiameter,
obstacles,
connMap,
}: ApplyFixedRouteReplacementsParams): ApplyFixedRouteReplacementsResult => {
const originalFixedRoutesByName = new Map(
originalFixedRoutes.map((route) => [route.connectionName, route]),
)
const updatedFixedRoutesByName = new Map(
updatedFixedRoutes.map((route) => [route.connectionName, route]),
)
const fixedRoutesByTraceIndex = new Map<number, PreloadedHighDensityRoute[]>()

for (const originalFixedRoute of originalFixedRoutes) {
const traceRoutes =
fixedRoutesByTraceIndex.get(originalFixedRoute.preloadedTraceIndex) ?? []
traceRoutes.push(originalFixedRoute)
fixedRoutesByTraceIndex.set(
originalFixedRoute.preloadedTraceIndex,
traceRoutes,
)
}

const mutatedPreloadedTraces: SimplifiedPcbTrace[] = []
const updatedPreloadedTraces = originalTraces.map((trace, traceIndex) => {
const originalTraceRoutes = (
fixedRoutesByTraceIndex.get(traceIndex) ?? []
).sort((a, b) => a.preloadedRouteIndex - b.preloadedRouteIndex)
const traceWasMutated = originalTraceRoutes.some((route) =>
replacedConnectionNames.has(route.connectionName),
)
if (!traceWasMutated) return trace
if (
trace.route.some(
(routePoint) => routePoint.route_type === "through_obstacle",
)
) {
throw new Error(
`Pipeline9 cannot yet rebuild mutated through-obstacle trace "${trace.pcb_trace_id}"`,
)
}

const combinedPoints: HighDensityRoute["route"] = []
let traceThickness = 0
let viaDiameter = 0
let rootConnectionName: string | undefined

for (const originalTraceRoute of originalTraceRoutes) {
if (!originalFixedRoutesByName.has(originalTraceRoute.connectionName)) {
throw new Error(
`Pipeline9 lost original fixed route "${originalTraceRoute.connectionName}"`,
)
}
const updatedTraceRoute = updatedFixedRoutesByName.get(
originalTraceRoute.connectionName,
)
if (!updatedTraceRoute) {
throw new Error(
`Pipeline9 lost updated fixed route "${originalTraceRoute.connectionName}"`,
)
}
appendConnectedRoute(combinedPoints, updatedTraceRoute)
traceThickness = Math.max(
traceThickness,
updatedTraceRoute.traceThickness,
)
viaDiameter = Math.max(viaDiameter, updatedTraceRoute.viaDiameter)
rootConnectionName ??= updatedTraceRoute.rootConnectionName
}

const combinedRoute: HighDensityRoute = {
connectionName: trace.connection_name,
rootConnectionName,
traceThickness,
viaDiameter,
route: combinedPoints,
vias: combinedPoints.slice(0, -1).flatMap((point, pointIndex) => {
const nextPoint = combinedPoints[pointIndex + 1]!
return point.z !== nextPoint.z &&
Math.abs(point.x - nextPoint.x) <= POINT_EPSILON &&
Math.abs(point.y - nextPoint.y) <= POINT_EPSILON
? [{ x: nextPoint.x, y: nextPoint.y }]
: []
}),
}
const mutatedTrace: SimplifiedPcbTrace = {
...trace,
route: convertHdRouteToSimplifiedRoute(combinedRoute, layerCount, {
defaultViaHoleDiameter,
obstacles,
connMap,
}),
}
mutatedPreloadedTraces.push(mutatedTrace)
return mutatedTrace
})

return {
updatedPreloadedTraces,
mutatedPreloadedTraces,
}
}
Loading
Loading