From bdba064d49c796d7e4eac0985c2f3da7eaa58e79 Mon Sep 17 00:00:00 2001 From: Ahmed Alshaybani Date: Thu, 15 Oct 2026 18:20:08 +0200 Subject: [PATCH] Allow vias through empty inner layers --- .../OuterLayerContainmentMergeSolver.ts | 25 +++------ .../rectDiffGridSolverPipeline.snap.svg | 4 +- .../__snapshots__/bugreport08-e3ec95.snap.svg | 2 +- .../__snapshots__/bugreport11-b2de3c.snap.svg | 4 +- .../__snapshots__/bugreport22-2a75ce.snap.svg | 2 +- .../__snapshots__/bugreport24-05597c.snap.svg | 2 +- ...ayer-transit-requires-inner-copper.test.ts | 54 ------------------- ...er-transit-without-inner-obstacles.test.ts | 51 ++++++++++++++++++ ...mp22650-missing-local-via-regions.snap.svg | 2 +- ...pmp22650-missing-local-via-regions.test.ts | 2 +- 10 files changed, 68 insertions(+), 80 deletions(-) delete mode 100644 tests/solver/outer-layer-transit-requires-inner-copper.test.ts create mode 100644 tests/solver/outer-layer-transit-without-inner-obstacles.test.ts diff --git a/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts b/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts index 07cd5a5d..0c4772bf 100644 --- a/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts +++ b/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts @@ -116,13 +116,6 @@ export class OuterLayerContainmentMergeSolver extends BaseSolver { const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0) const originalNodes = this.input.meshNodes.map(cloneNode) const obstaclesByLayer = this.buildObstaclesByLayer(layerCount) - if ( - !obstaclesByLayer - .slice(1, -1) - .some((entries) => entries.some((entry) => entry.obstacle.isCopperPour)) - ) { - return originalNodes - } const mutableOuterNodes = originalNodes.filter( (node) => isFreeNode(node) && @@ -312,17 +305,16 @@ export class OuterLayerContainmentMergeSolver extends BaseSolver { fromZ: number toZ: number obstaclesByLayer: ObstacleWithRect[][] - }) { + }): boolean { const { rect, fromZ, toZ, obstaclesByLayer } = params - const lo = Math.min(fromZ, toZ) - const hi = Math.max(fromZ, toZ) + const firstZ = Math.min(fromZ, toZ) + const lastZ = Math.max(fromZ, toZ) - if (hi - lo < 2) return false + if (lastZ - firstZ < 2) return false - // This merge reconnects outer regions separated by copper planes. Empty - // inner layers permit transit, but do not require a containment merge. - let crossesCopperPlane = false - for (let z = lo + 1; z < hi; z++) { + // Empty inner layers permit a through-via. An inner copper pour also permits + // transit when it covers the whole region, while ordinary obstacles do not. + for (let z = firstZ + 1; z < lastZ; z++) { const overlapping = (obstaclesByLayer[z] ?? []).filter((entry) => overlaps(entry.rect, rect), ) @@ -339,10 +331,9 @@ export class OuterLayerContainmentMergeSolver extends BaseSolver { ) { return false } - crossesCopperPlane = true } - return crossesCopperPlane + return true } override getOutput(): { outputNodes: CapacityMeshNode[] } { diff --git a/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg b/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg index 62703ef9..b8e2d1a2 100644 --- a/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +++ b/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg @@ -1,4 +1,4 @@ -Layer z=0Layer z=1Layer z=2Layer z=3