Skip to content

Commit

Permalink
tracehints working
Browse files Browse the repository at this point in the history
  • Loading branch information
seveibar committed Aug 25, 2024
1 parent b7b5680 commit 7f49c95
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 7 deletions.
106 changes: 99 additions & 7 deletions lib/components/primitive-components/Trace.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { traceProps } from "@tscircuit/props"
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
import type { Port } from "./Port"
import { IJumpAutorouter, autoroute } from "@tscircuit/infgrid-ijump-astar"
import {
IJumpAutorouter,
autoroute,
getObstaclesFromSoup,
markObstaclesAsConnected,
} from "@tscircuit/infgrid-ijump-astar"
import type {
AnySoupElement,
PCBTrace,
Expand All @@ -17,6 +22,20 @@ import { computeObstacleBounds } from "lib/utils/autorouting/computeObstacleBoun
import { projectPointInDirection } from "lib/utils/projectPointInDirection"
import type { TraceHint } from "./TraceHint"
import { findPossibleTraceLayerCombinations } from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
import { pairs } from "lib/utils/pairs"
import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"

type PcbRouteObjective =
| RouteHintPoint
| { layers: string[]; x: number; y: number; via?: boolean }

const portToObjective = (port: Port): PcbRouteObjective => {
const portPosition = port.getGlobalPcbPosition()
return {
...portPosition,
layers: port.getAvailablePcbLayers(),
}
}

export class Trace extends PrimitiveComponent<typeof traceProps> {
source_trace_id: string | null = null
Expand Down Expand Up @@ -162,16 +181,89 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
// When we have hints, we have to order the hints then route between each
// terminal of the trace and the hints
// TODO order based on proximity to ports
const orderedHintsAndPorts: Array<RouteHintPoint | { layers: string[] }> = [
{ layers: ports[0].port.getAvailablePcbLayers() },
const orderedRouteObjectives: PcbRouteObjective[] = [
portToObjective(ports[0].port),
...pcbRouteHints,
{ layers: ports[1].port.getAvailablePcbLayers() },
portToObjective(ports[1].port),
]

const candidateLayerCombinations =
findPossibleTraceLayerCombinations(orderedHintsAndPorts)
// Hints can indicate where there should be a via, but the layer is allowed
// to be unspecified, therefore we need to find possible layer combinations
// to go to each hint and still route to the start and end points
const candidateLayerCombinations = findPossibleTraceLayerCombinations(
orderedRouteObjectives,
)

if (candidateLayerCombinations.length === 0) {
this.renderError(
`Could not find a common layer (using hints) for trace ${this.getString()}`,
)
}

// Cache the PCB obstacles, they'll be needed for each segment between
// ports/hints
const obstacles = getObstaclesFromSoup(this.project!.db.toArray())
markObstaclesAsConnected(
obstacles,
orderedRouteObjectives,
this.source_trace_id!,
)

// TODO explore all candidate layer combinations if one fails
const candidateLayerSelections = candidateLayerCombinations[0].layer_path

/**
* Apply the candidate layer selections to the route objectives, now we
* have a set of points that have definite layers
*/
const orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
if (t.via) {
return {
...t,
via_to_layer: candidateLayerSelections[idx],
}
}
return { ...t, layers: [candidateLayerSelections[idx]] }
})

const routes: PCBTrace["route"][] = []
for (const [a, b] of pairs(orderedRoutePoints)) {
const BOUNDS_MARGIN = 2 //mm
const ijump = new IJumpAutorouter({
input: {
obstacles,
connections: [
{
name: this.source_trace_id!,
pointsToConnect: [a, b],
},
],
layerCount: 1,
bounds: {
minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
minY: Math.min(a.y, b.y) - BOUNDS_MARGIN,
maxY: Math.max(a.y, b.y) + BOUNDS_MARGIN,
},
},
})
const traces = ijump.solveAndMapToTraces()
if (traces.length === 0) {
this.renderError(
`Could not find a route between ${a.x}, ${a.y} and ${b.x}, ${b.y}`,
)
return
}
// TODO ijump returns multiple traces for some reason
const [trace] = traces as PCBTrace[]
routes.push(trace.route)
}

console.log({ candidateLayerCombinations })
const pcb_trace = db.pcb_trace.insert({
route: mergeRoutes(routes),
source_trace_id: this.source_trace_id!,
})
this.pcb_trace_id = pcb_trace.pcb_trace_id
}

doInitialSchematicTraceRender(): void {
Expand Down
70 changes: 70 additions & 0 deletions lib/utils/autorouting/mergeRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { PCBTrace } from "@tscircuit/soup"

function pdist(a: any, b: any) {
return Math.hypot(a.x - b.x, a.y - b.y)
}

/**
* Merge multiple routes into a single route.
*
* If the end of the next route is closer to the end of the previous route,
* reverse the next route and append it to the previous route.
*/
export const mergeRoutes = (routes: PCBTrace["route"][]) => {
// routes = routes.filter((route) => route.length > 0)
if (routes.some((r) => r.length === 0)) {
throw new Error("Cannot merge routes with zero length")
}
// for (const route of routes) {
// console.table(route)
// }
const merged: PCBTrace["route"] = []
// const reverse_log: boolean[] = []

// Determine if the first route should be reversed
const first_route_fp = routes[0][0]
const first_route_lp = routes[0][routes[0].length - 1]

const second_route_fp = routes[1][0]
const second_route_lp = routes[1][routes[1].length - 1]

const best_reverse_dist = Math.min(
pdist(first_route_fp, second_route_fp),
pdist(first_route_fp, second_route_lp),
)

const best_normal_dist = Math.min(
pdist(first_route_lp, second_route_fp),
pdist(first_route_lp, second_route_lp),
)

if (best_reverse_dist < best_normal_dist) {
merged.push(...routes[0].reverse())
// reverse_log.push(true)
} else {
merged.push(...routes[0])
// reverse_log.push(false)
}

for (let i = 1; i < routes.length; i++) {
const last_merged_point = merged[merged.length - 1]
const next_route = routes[i]

const next_first_point = next_route[0]
const next_last_point = next_route[next_route.length - 1]

const distance_to_first = pdist(last_merged_point, next_first_point)
const distance_to_last = pdist(last_merged_point, next_last_point)

if (distance_to_first < distance_to_last) {
// reverse_log.push(false)
merged.push(...next_route)
} else {
// reverse_log.push(true)
merged.push(...next_route.reverse())
}
}
// console.log(reverse_log)
// console.table(merged)
return merged
}
10 changes: 10 additions & 0 deletions lib/utils/pairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Return pairs of adjacent elements in an array.
*/
export function pairs<T>(arr: Array<T>): Array<[T, T]> {
const result: Array<[T, T]> = []
for (let i = 0; i < arr.length - 1; i++) {
result.push([arr[i], arr[i + 1]])
}
return result
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7f49c95

Please sign in to comment.