imrishabh18/corne-keyboard
The code defines and renders two surface-mount chip components with SMT pads, silkscreen outlines, and 3D CAD models (OBJ and STEP) for PCB assembly.
- Version
- 2.0.21
- License
- unset
- Stars
- 1
scripts/copper-pour-obstacles.test.ts
import { beforeAll, expect, test } from 'bun:test'
import { pathToFileURL } from 'node:url'
import type { AnyCircuitElement, BRepShape, PcbPlatedHole } from 'circuit-json'
import type { InputPad, InputProblem } from '@tscircuit/copper-pour-solver'
const solver = process.env.COPPER_POUR_TEST_BUNDLE
? await import(pathToFileURL(process.env.COPPER_POUR_TEST_BUNDLE).href)
: await import('@tscircuit/copper-pour-solver')
beforeAll(async () => { await solver.initializeManifoldGeometry() })
const square = [{ x: -1, y: -1 }, { x: 1, y: -1 }, { x: 1, y: 1 }, { x: -1, y: 1 }]
const polygon: InputPad = {
shape: 'polygon', padId: 'polygon', points: square,
layer: 'top', connectivityKey: 'signal', isSmtPad: true,
}
const rect: InputPad = {
shape: 'rect', padId: 'rect',
bounds: { minX: -1, maxX: 1, minY: -1, maxY: 1 },
layer: 'top', connectivityKey: 'signal', isSmtPad: true,
}
const inputFor = (pad: InputPad, overrides = {}): InputProblem => ({
pads: [pad],
regionsForPour: [{
shape: 'rect', layer: 'top', connectivityKey: 'ground',
bounds: { minX: -5, maxX: 5, minY: -5, maxY: 5 },
padMargin: 0.2, traceMargin: 0.1, ...overrides,
}],
})
function solve(input: InputProblem): BRepShape[] {
return new solver.CopperPourPipelineSolver(input).getOutput().brep_shapes
}
function holeBounds(shapes: BRepShape[]) {
expect(shapes).toHaveLength(1)
expect(shapes[0]!.inner_rings).toHaveLength(1)
const vertices = shapes[0]!.inner_rings[0]!.vertices
return {
minX: Math.min(...vertices.map(p => p.x)), maxX: Math.max(...vertices.map(p => p.x)),
minY: Math.min(...vertices.map(p => p.y)), maxY: Math.max(...vertices.map(p => p.y)),
}
}
function expectBounds(shapes: BRepShape[], x: number, y = x) {
const bounds = holeBounds(shapes)
expect(bounds.minX).toBeCloseTo(-x, 5)
expect(bounds.maxX).toBeCloseTo(x, 5)
expect(bounds.minY).toBeCloseTo(-y, 5)
expect(bounds.maxY).toBeCloseTo(y, 5)
}
test('different-net square polygon gets the same 0.2 mm clearance as a rectangular pad', () => {
expectBounds(solve(inputFor(rect)), 1.2)
expectBounds(solve(inputFor(polygon)), 1.2)
})
test('polygon clearance follows the configured pad margin', () => {
expectBounds(solve(inputFor(polygon, { padMargin: 0.4 })), 1.4)
expectBounds(solve(inputFor(polygon, { padMargin: 0 })), 1)
})
test('same-net polygon remains joined to a solid pour', () => {
const shapes = solve(inputFor({ ...polygon, connectivityKey: 'ground' }))
expect(shapes).toHaveLength(1)
expect(shapes[0]!.inner_rings).toHaveLength(0)
})
test('polygon keepout uses its exact outline, and cutout uses cutout_margin', () => {
expectBounds(solve(inputFor({ ...polygon, connectivityKey: 'keepout:1' })), 1)
expectBounds(solve(inputFor({ ...polygon, connectivityKey: 'cutout:1' }, { cutout_margin: 0.35 })), 1.35)
})
const circuitWith = (hole: PcbPlatedHole): AnyCircuitElement[] => [{
type: 'pcb_board', pcb_board_id: 'pcb_board_0', center: { x: 0, y: 0 },
width: 10, height: 10, num_layers: 2, thickness: 1.6, material: 'fr4',
}, hole]
function fromCircuit(hole: PcbPlatedHole) {
return solver.convertCircuitJsonToInputProblem(circuitWith(hole), {
layer: 'top', subcircuit_connectivity_map_key: 'ground',
pad_margin: 0.2, trace_margin: 0.1,
}) as InputProblem
}
const baseHole = {
type: 'pcb_plated_hole' as const, pcb_plated_hole_id: 'pcb_plated_hole_0',
x: 0, y: 0, layers: ['top', 'bottom'] as ['top', 'bottom'],
pad_shape: 'rect' as const, rect_pad_width: 4, rect_pad_height: 2,
hole_offset_x: 0, hole_offset_y: 0,
}
test('unassigned circular-hole rectangular pad remains a clearance obstacle', () => {
const problem = fromCircuit({ ...baseHole,
shape: 'circular_hole_with_rect_pad', hole_shape: 'circle', hole_diameter: 0.8,
})
expect(problem.pads).toHaveLength(1)
expectBounds(solve(problem), 2.2, 1.2)
})
test('unassigned pill-hole rectangular pad is included as a clearance obstacle', () => {
const problem = fromCircuit({ ...baseHole,
shape: 'pill_hole_with_rect_pad', hole_shape: 'pill', hole_width: 1.2, hole_height: 0.8,
})
expect(problem.pads).toHaveLength(1)
expect(problem.pads[0]!.connectivityKey).toBe('unconnected-plated-hole:pcb_plated_hole_0')
expectBounds(solve(problem), 2.2, 1.2)
})
for (const [padRotation, holeRotation] of [[0, 90], [90, 0], [270, 270]]) {
test(`rotated pill-hole pad uses pad rotation ${padRotation}, independently of drill rotation ${holeRotation}`, () => {
const problem = fromCircuit({ ...baseHole,
shape: 'rotated_pill_hole_with_rect_pad', hole_shape: 'rotated_pill',
hole_width: 1.2, hole_height: 0.8,
rect_ccw_rotation: padRotation!, hole_ccw_rotation: holeRotation!,
})
expect(problem.pads).toHaveLength(1)
if (padRotation === 0) expectBounds(solve(problem), 2.2, 1.2)
else expectBounds(solve(problem), 1.2, 2.2)
})
}