Skip to content
55 changes: 53 additions & 2 deletions lib/components/normal-components/Board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class Board extends Group<typeof boardProps> {
const computedHeight = hasComponents ? maxY - minY + padding * 2 : 0

// Center the board around the components or use (0,0) for empty boards
const center = {
let center = {
x: hasComponents
? (minX + maxX) / 2 + (props.outlineOffsetX ?? 0)
: (props.outlineOffsetX ?? 0),
Expand All @@ -174,7 +174,6 @@ export class Board extends Group<typeof boardProps> {
: (props.outlineOffsetY ?? 0),
}

// Update the board dimensions, preserving any explicit dimension provided
// by the user while auto-calculating the missing one.
const finalWidth = props.width ?? computedWidth
const finalHeight = props.height ?? computedHeight
Expand Down Expand Up @@ -266,6 +265,58 @@ export class Board extends Group<typeof boardProps> {
y: (props.pcbY ?? 0) + (props.outlineOffsetY ?? 0),
}

const { boardAnchorPosition, boardAnchorAlignment } = props as any

if (boardAnchorPosition) {
const { x: ax, y: ay } = boardAnchorPosition as any
const W = props.width ?? computedWidth
const H = props.height ?? computedHeight

let cx = ax
let cy = ay

switch (boardAnchorAlignment) {
case "top_left":
cx = ax + W / 2
cy = ay - H / 2
break
case "top_right":
cx = ax - W / 2
cy = ay - H / 2
break
case "bottom_left":
cx = ax + W / 2
cy = ay + H / 2
break
case "bottom_right":
cx = ax - W / 2
cy = ay + H / 2
break
case "top":
cx = ax
cy = ay - H / 2
break
case "bottom":
cx = ax
cy = ay + H / 2
break
case "left":
cx = ax + W / 2
cy = ay
break
case "right":
cx = ax - W / 2
cy = ay
break
case "center":
default:
// center is the default
break
}

center = { x: cx, y: cy }
}

// Compute width and height from outline if not provided
if (props.outline) {
const xValues = props.outline.map((point) => point.x)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions tests/examples/example34-board-anchor1.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, it, expect } from "bun:test"
import { getTestFixture } from "../fixtures/get-test-fixture"

describe("Board Anchor", () => {
it("should anchor the board to the top-left", async () => {
const { circuit } = getTestFixture()
const board = (
<board
width="30mm"
height="30mm"
boardAnchorPosition={{ x: 0, y: 0 }}
boardAnchorAlignment="top_left"
>
<resistor name="R1" resistance="10k" footprint="0805" />
<silkscreencircle pcbX={0} pcbY={0} radius="1mm" />
<silkscreentext pcbX={1} pcbY={1} text="(0,0)" />
<silkscreentext
pcbX={0}
pcbY={-2}
text="board.anchor: top_left @ (0,0)"
/>
</board>
)

circuit.add(board)

await circuit.render()

expect(circuit).toMatchPcbSnapshot(import.meta.path)
})
})
32 changes: 32 additions & 0 deletions tests/examples/example34-board-anchor2.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, it, expect } from "bun:test"
import { getTestFixture } from "../fixtures/get-test-fixture"

describe("Board Anchor", () => {
it("should anchor the board to the bottom-right", async () => {
const { circuit } = getTestFixture()
const board = (
<board
width="30mm"
height="30mm"
boardAnchorPosition={{ x: 10, y: 10 }}
boardAnchorAlignment="bottom_right"
>
<resistor name="R1" resistance="10k" footprint="0805" />
<silkscreencircle pcbX={0} pcbY={0} radius="1mm" />
<silkscreentext pcbX={1} pcbY={1} text="(0,0)" />
<silkscreencircle pcbX={10} pcbY={10} radius="1mm" />
<silkscreentext
pcbX={10}
pcbY={8}
text="board.anchor: bottom_right @ (10,10)"
/>
</board>
)

circuit.add(board)

await circuit.render()

expect(circuit).toMatchPcbSnapshot(import.meta.path)
})
})
28 changes: 28 additions & 0 deletions tests/examples/example34-board-anchor3.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, it, expect } from "bun:test"
import { getTestFixture } from "../fixtures/get-test-fixture"

describe("Board Anchor", () => {
it("should anchor the board to the center", async () => {
const { circuit } = getTestFixture()
const board = (
<board
width="30mm"
height="30mm"
boardAnchorPosition={{ x: 5, y: 5 }}
boardAnchorAlignment="center"
>
<resistor name="R1" resistance="10k" footprint="0805" />
<silkscreencircle pcbX={0} pcbY={0} radius="1mm" />
<silkscreentext pcbX={1} pcbY={1} text="(0,0)" />
<silkscreencircle pcbX={5} pcbY={5} radius="1mm" />
<silkscreentext pcbX={5} pcbY={3} text="board.anchor: center @ (5,5)" />
</board>
)

circuit.add(board)

await circuit.render()

expect(circuit).toMatchPcbSnapshot(import.meta.path)
})
})