Skip to content
Closed
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
Expand Up @@ -41,6 +41,14 @@ export interface ImplicitBreakoutPointSolverContext {
PcbGroupId,
ReadonlyMap<SourceTraceId, SourcePortId>
>
singleRegionPreferredEdgeByConnectionId?: ReadonlyMap<
SourceTraceId,
BreakoutEdge
>
singleRegionExternalTargetByConnectionId?: ReadonlyMap<
SourceTraceId,
{ x: number; y: number }
>
}

const getRoutingScopeOrThrow = (breakout: Breakout): Group<z.ZodType> => {
Expand Down Expand Up @@ -264,26 +272,38 @@ const getFacingEdge = ({
return "bottom"
}

const getSingleRegionPreferredEdge = ({
const getSingleRegionEdgePreferences = ({
region,
sourceTraces,
}: {
region: BreakoutRegionGeometry
sourceTraces: readonly SourceTrace[]
}): BreakoutEdge | undefined => {
}):
| {
preferredEdge: BreakoutEdge
preferredEdgeByConnectionId: ReadonlyMap<SourceTraceId, BreakoutEdge>
externalTargetByConnectionId: ReadonlyMap<
SourceTraceId,
{ x: number; y: number }
>
}
| undefined => {
const root = region.breakout.root
if (!root) return undefined

const internalSourcePortIds = new Set(
region.sourcePortIdBySourceTraceId.values(),
)
const externalPcbPorts = new Map<string, { x: number; y: number }>()
const externalTargetsByConnectionId = new Map<
SourceTraceId,
Array<{ x: number; y: number }>
>()
const aggregateExternalTargetsByPcbPortId = new Map<
string,
{ x: number; y: number }
>()
for (const sourceTrace of sourceTraces) {
if (
!sourceTrace.connected_source_port_ids.some((sourcePortId) =>
internalSourcePortIds.has(sourcePortId),
)
) {
if (!region.endpointBySourceTraceId.has(sourceTrace.source_trace_id)) {
continue
}
for (const sourcePortId of sourceTrace.connected_source_port_ids) {
Expand All @@ -298,19 +318,27 @@ const getSingleRegionPreferredEdge = ({
) {
continue
}
const isInsideRegion =
const isInsideRegionBounds =
pcbPort.x >= region.bounds.minX &&
pcbPort.x <= region.bounds.maxX &&
pcbPort.y >= region.bounds.minY &&
pcbPort.y <= region.bounds.maxY
if (isInsideRegion) continue
externalPcbPorts.set(pcbPort.pcb_port_id, {
if (!isInsideRegionBounds) {
aggregateExternalTargetsByPcbPortId.set(pcbPort.pcb_port_id, {
x: pcbPort.x,
y: pcbPort.y,
})
}
const targets =
externalTargetsByConnectionId.get(sourceTrace.source_trace_id) ?? []
targets.push({
x: pcbPort.x,
y: pcbPort.y,
})
externalTargetsByConnectionId.set(sourceTrace.source_trace_id, targets)
Comment on lines +321 to +338

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical bug: externalTargetsByConnectionId is being populated with ALL targets (both internal and external) but should only contain external targets. After the isInsideRegionBounds check determines a target is NOT inside bounds (line 326), it correctly adds to aggregateExternalTargetsByPcbPortId. However, lines 332-338 unconditionally add ALL pcb ports to externalTargetsByConnectionId regardless of whether they're inside or outside the region bounds.

Fix:

if (!isInsideRegionBounds) {
  aggregateExternalTargetsByPcbPortId.set(pcbPort.pcb_port_id, {
    x: pcbPort.x,
    y: pcbPort.y,
  })
  const targets =
    externalTargetsByConnectionId.get(sourceTrace.source_trace_id) ?? []
  targets.push({
    x: pcbPort.x,
    y: pcbPort.y,
  })
  externalTargetsByConnectionId.set(sourceTrace.source_trace_id, targets)
}

This will cause incorrect routing decisions since internal targets will be included when computing preferred edges and center points for external routing.

Suggested change
const isInsideRegionBounds =
pcbPort.x >= region.bounds.minX &&
pcbPort.x <= region.bounds.maxX &&
pcbPort.y >= region.bounds.minY &&
pcbPort.y <= region.bounds.maxY
if (isInsideRegion) continue
externalPcbPorts.set(pcbPort.pcb_port_id, {
if (!isInsideRegionBounds) {
aggregateExternalTargetsByPcbPortId.set(pcbPort.pcb_port_id, {
x: pcbPort.x,
y: pcbPort.y,
})
}
const targets =
externalTargetsByConnectionId.get(sourceTrace.source_trace_id) ?? []
targets.push({
x: pcbPort.x,
y: pcbPort.y,
})
externalTargetsByConnectionId.set(sourceTrace.source_trace_id, targets)
const isInsideRegionBounds =
pcbPort.x >= region.bounds.minX &&
pcbPort.x <= region.bounds.maxX &&
pcbPort.y >= region.bounds.minY &&
pcbPort.y <= region.bounds.maxY
if (!isInsideRegionBounds) {
aggregateExternalTargetsByPcbPortId.set(pcbPort.pcb_port_id, {
x: pcbPort.x,
y: pcbPort.y,
})
const targets =
externalTargetsByConnectionId.get(sourceTrace.source_trace_id) ?? []
targets.push({
x: pcbPort.x,
y: pcbPort.y,
})
externalTargetsByConnectionId.set(sourceTrace.source_trace_id, targets)
}

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

}
}
if (externalPcbPorts.size === 0) return undefined
if (externalTargetsByConnectionId.size === 0) return undefined

const distanceToEdge = (
target: { x: number; y: number },
Expand All @@ -335,17 +363,44 @@ const getSingleRegionPreferredEdge = ({
return Math.hypot(target.x - edgePoint.x, target.y - edgePoint.y)
}
const edges: BreakoutEdge[] = ["right", "left", "top", "bottom"]
return edges.reduce((bestEdge, edge) => {
const bestCost = [...externalPcbPorts.values()].reduce(
(sum, target) => sum + distanceToEdge(target, bestEdge),
0,
)
const edgeCost = [...externalPcbPorts.values()].reduce(
(sum, target) => sum + distanceToEdge(target, edge),
0,
)
return edgeCost < bestCost ? edge : bestEdge
}, edges[0]!)
const getPreferredEdge = (targets: readonly { x: number; y: number }[]) =>
edges.reduce((bestEdge, edge) => {
const bestCost = targets.reduce(
(sum, target) => sum + distanceToEdge(target, bestEdge),
0,
)
const edgeCost = targets.reduce(
(sum, target) => sum + distanceToEdge(target, edge),
0,
)
return edgeCost < bestCost ? edge : bestEdge
}, edges[0]!)
const allExternalTargets = [...externalTargetsByConnectionId.values()].flat()
const aggregateExternalTargets = [
...aggregateExternalTargetsByPcbPortId.values(),
]
const preferredEdgeByConnectionId = new Map<SourceTraceId, BreakoutEdge>()
const externalTargetByConnectionId = new Map<
SourceTraceId,
{ x: number; y: number }
>()
for (const [connectionId, targets] of externalTargetsByConnectionId) {
preferredEdgeByConnectionId.set(connectionId, getPreferredEdge(targets))
externalTargetByConnectionId.set(connectionId, {
x: targets.reduce((sum, target) => sum + target.x, 0) / targets.length,
y: targets.reduce((sum, target) => sum + target.y, 0) / targets.length,
})
}

return {
preferredEdge: getPreferredEdge(
aggregateExternalTargets.length > 0
? aggregateExternalTargets
: allExternalTargets,
),
preferredEdgeByConnectionId,
externalTargetByConnectionId,
}
}

const getSourceTraceForConnectionOrThrow = ({
Expand Down Expand Up @@ -667,9 +722,9 @@ export const createImplicitBreakoutPointSolverContext = (
PcbGroupId,
ReadonlyMap<SourceTraceId, SourcePortId>
>()
const singleRegionPreferredEdge =
const singleRegionEdgePreferences =
regions.length === 1
? getSingleRegionPreferredEdge({ region: regions[0]!, sourceTraces })
? getSingleRegionEdgePreferences({ region: regions[0]!, sourceTraces })
: undefined
const solverRegions = regions.map((region, regionIndex) => {
sourcePortIdByConnectionIdByRegionId.set(
Expand All @@ -684,7 +739,7 @@ export const createImplicitBreakoutPointSolverContext = (
regionIndex,
regions,
horizontalPlacement,
singleRegionPreferredEdge,
singleRegionPreferredEdge: singleRegionEdgePreferences?.preferredEdge,
}),
}
})
Expand All @@ -697,5 +752,9 @@ export const createImplicitBreakoutPointSolverContext = (
boundaryPointSpacing: getBoundaryPointSpacing(breakout),
},
sourcePortIdByConnectionIdByRegionId,
singleRegionPreferredEdgeByConnectionId:
singleRegionEdgePreferences?.preferredEdgeByConnectionId,
singleRegionExternalTargetByConnectionId:
singleRegionEdgePreferences?.externalTargetByConnectionId,
}
}
Loading
Loading