Skip to content
Open
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
28 changes: 15 additions & 13 deletions lib/PackSolver/PhasedPackSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,15 @@ export class PhasedPackSolver extends BaseSolver {
const newPackedComponent: PackedComponent = {
...this.currentComponent,
center: { x: 0, y: 0 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
pads: this.currentComponent.pads.map((p) => ({
...p,
absoluteCenter: { x: 0, y: 0 },
})),
}

const candidateAngles = this.getCandidateAngles(newPackedComponent)
newPackedComponent.ccwRotationOffset =
newPackedComponent.ccwRotationOffsetDegrees =
(((candidateAngles[0] ?? 0) % 360) + 360) % 360
setPackedComponentPadCenters(newPackedComponent)
this.packedComponents.push(newPackedComponent)
Expand All @@ -163,7 +163,7 @@ export class PhasedPackSolver extends BaseSolver {
const newPackedComponent: PackedComponent = {
...this.currentComponent,
center: { x: 0, y: 0 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
pads: this.currentComponent.pads.map((p) => ({
...p,
absoluteCenter: { x: 0, y: 0 },
Expand Down Expand Up @@ -469,7 +469,7 @@ export class PhasedPackSolver extends BaseSolver {
const newPackedComponent: PackedComponent = {
...this.currentComponent,
center: { x: 0, y: 0 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
pads: this.currentComponent.pads.map((p) => ({
...p,
absoluteCenter: { x: 0, y: 0 },
Expand Down Expand Up @@ -529,7 +529,7 @@ export class PhasedPackSolver extends BaseSolver {

const trial = { ...newPackedComponent }
trial.center = componentCenter
trial.ccwRotationOffset = ((angle % 360) + 360) % 360
trial.ccwRotationOffsetDegrees = ((angle % 360) + 360) % 360
setPackedComponentPadCenters(trial)

// Check for overlap
Expand Down Expand Up @@ -566,7 +566,7 @@ export class PhasedPackSolver extends BaseSolver {
// Trial 2: Position component center at the good candidate point
const centerTrial = { ...newPackedComponent }
centerTrial.center = { x: point.x, y: point.y }
centerTrial.ccwRotationOffset = ((angle % 360) + 360) % 360
centerTrial.ccwRotationOffsetDegrees = ((angle % 360) + 360) % 360
setPackedComponentPadCenters(centerTrial)

// Check for overlap
Expand Down Expand Up @@ -613,7 +613,8 @@ export class PhasedPackSolver extends BaseSolver {

const selectedComponent = { ...newPackedComponent }
selectedComponent.center = bestTrial.center
selectedComponent.ccwRotationOffset = bestTrial.ccwRotationOffset
selectedComponent.ccwRotationOffsetDegrees =
bestTrial.ccwRotationOffsetDegrees
selectedComponent.pads = bestTrial.pads
this.phaseData.selectedRotation = selectedComponent
} else if (rotationTrials.length > 0) {
Expand All @@ -624,7 +625,8 @@ export class PhasedPackSolver extends BaseSolver {

const selectedComponent = { ...newPackedComponent }
selectedComponent.center = bestTrial.center
selectedComponent.ccwRotationOffset = bestTrial.ccwRotationOffset
selectedComponent.ccwRotationOffsetDegrees =
bestTrial.ccwRotationOffsetDegrees
selectedComponent.pads = bestTrial.pads
this.phaseData.selectedRotation = selectedComponent
} else {
Expand All @@ -642,14 +644,14 @@ export class PhasedPackSolver extends BaseSolver {
const newPackedComponent: PackedComponent = {
...this.currentComponent,
center: { x: 5, y: 5 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
pads: this.currentComponent.pads.map((p) => ({
...p,
absoluteCenter: { x: 0, y: 0 },
})),
}
const candidateAngles = this.getCandidateAngles(newPackedComponent)
newPackedComponent.ccwRotationOffset =
newPackedComponent.ccwRotationOffsetDegrees =
(((candidateAngles[0] ?? 0) % 360) + 360) % 360
setPackedComponentPadCenters(newPackedComponent)
this.phaseData.selectedRotation = newPackedComponent
Expand Down Expand Up @@ -821,14 +823,14 @@ export class PhasedPackSolver extends BaseSolver {

// Show component center as a point with rotation, cost and anchor info
// Offset point slightly based on rotation to avoid overlap
const rotationOffset = 0.02 * (trial.ccwRotationOffset / 90)
const rotationOffset = 0.02 * (trial.ccwRotationOffsetDegrees / 90)
const anchorInfo =
trial.anchorType === "pad" ? `pad: ${trial.anchorPadId}` : "center"
const overlapText = trial.hasOverlap ? "\nOVERLAP" : ""
graphics.points!.push({
x: trial.center.x + rotationOffset,
y: trial.center.y + rotationOffset,
label: `${trial.ccwRotationOffset}° (cost: ${trial.cost.toFixed(3)}, anchor: ${anchorInfo})${overlapText}`,
label: `${trial.ccwRotationOffsetDegrees}° (cost: ${trial.cost.toFixed(3)}, anchor: ${anchorInfo})${overlapText}`,
fill: "rgba(0,255,255,0.8)",
radius: 0.05,
} as Point)
Expand Down Expand Up @@ -868,7 +870,7 @@ export class PhasedPackSolver extends BaseSolver {
fill: "rgba(0,255,0,0.3)",
stroke: "#00FF00",
strokeWidth: 0.05,
label: `PLACED at ${component.ccwRotationOffset}°`,
label: `PLACED at ${component.ccwRotationOffsetDegrees}°`,
} as Rect)

// Show the pads
Expand Down
4 changes: 2 additions & 2 deletions lib/PackSolver/RotationSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function selectOptimalRotation(
const tempComponent: PackedComponent = {
...component,
center: initialCenter,
ccwRotationOffset: ((angle % 360) + 360) % 360,
ccwRotationOffsetDegrees: ((angle % 360) + 360) % 360,
pads: component.pads.map((p) => ({
...p,
absoluteCenter: { x: 0, y: 0 }, // Will be set by setPackedComponentPadCenters
Expand Down Expand Up @@ -141,7 +141,7 @@ export function selectOptimalRotation(
const centerTrial: PackedComponent = {
...component,
center: { x: candidatePoint.x, y: candidatePoint.y },
ccwRotationOffset: ((angle % 360) + 360) % 360,
ccwRotationOffsetDegrees: ((angle % 360) + 360) % 360,
pads: component.pads.map((p) => {
const rotatedOffset = rotatePoint(p.offset, (angle * Math.PI) / 180)

Expand Down
4 changes: 2 additions & 2 deletions lib/PackSolver/placeComponentAtPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function placeComponentAtPoint({
const candidate: PackedComponent = {
...component,
center: point,
ccwRotationOffset: angle,
ccwRotationOffsetDegrees: angle,
pads,
}

Expand All @@ -48,7 +48,7 @@ export function placeComponentAtPoint({

// Fallback: 0° rotation
component.center = point
component.ccwRotationOffset = 0
component.ccwRotationOffsetDegrees = 0
setPackedComponentPadCenters(component)
return evaluatedPositionShadows
}
4 changes: 2 additions & 2 deletions lib/PackSolver/setPackedComponentPadCenters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export const setPackedComponentPadCenters = (
/* rotate the local offset, then translate by component centre */
const rotated = rotatePoint(
pad.offset,
(packedComponent.ccwRotationOffset * Math.PI) / 180,
(packedComponent.ccwRotationOffsetDegrees * Math.PI) / 180,
) // Convert to radians for math

/* rotate the pad dimensions based on component rotation */
const normalizedRotation =
((packedComponent.ccwRotationOffset % 360) + 360) % 360
((packedComponent.ccwRotationOffsetDegrees % 360) + 360) % 360
const shouldSwapDimensions =
normalizedRotation === 90 || normalizedRotation === 270

Expand Down
2 changes: 1 addition & 1 deletion lib/constructOutlinesFromPackedComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const createPadPolygons = (
const worldCorners = localCorners.map((corner) => {
const rotated = rotatePoint(
corner,
(component.ccwRotationOffset * Math.PI) / 180,
(component.ccwRotationOffsetDegrees * Math.PI) / 180,
)
return [
rotated.x + component.center.x,
Expand Down
2 changes: 1 addition & 1 deletion lib/geometry/getComponentBounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const getComponentBounds = (
localCorners.forEach((corner) => {
const world = rotatePoint(
corner,
(component.ccwRotationOffset * Math.PI) / 180,
(component.ccwRotationOffsetDegrees * Math.PI) / 180,
) // Convert to radians for math
const x = world.x + component.center.x
const y = world.y + component.center.y
Expand Down
2 changes: 1 addition & 1 deletion lib/plumbing/convertCircuitJsonToPackOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const buildPackedComponent = (
return {
componentId,
center,
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
pads,
} as PackedComponent
}
Expand Down
2 changes: 1 addition & 1 deletion lib/plumbing/convertPackOutputToPackInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { PackInput, PackOutput } from "../types"
* We therefore have to:
* • copy componentId
* • copy each pad but drop `absoluteCenter`
* • drop `center` and `ccwRotationOffset`
* • drop `center` and `ccwRotationOffsetDegrees`
*/
export const convertPackOutputToPackInput = (packed: PackOutput): PackInput => {
const strippedComponents = packed.components.map((pc) => ({
Expand Down
2 changes: 1 addition & 1 deletion lib/testing/getGraphicsFromPackOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const getGraphicsFromPackOutput = (
fill: "rgba(0,0,0,0.25)",
label: [
component.componentId,
`ccwRotationOffset: ${component.ccwRotationOffset.toFixed(1)}°`,
`ccwRotationOffsetDegrees: ${component.ccwRotationOffsetDegrees.toFixed(1)}°`,
].join("\n"),
}
rects.push(rect)
Expand Down
2 changes: 1 addition & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface InputComponent {
export interface PackedComponent extends InputComponent {
center: { x: number; y: number }
/** Rotation in degrees (counterclockwise) */
ccwRotationOffset: number
ccwRotationOffsetDegrees: number
pads: OutputPad[]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const manualPackOutput: PackOutput = {
{
componentId: "U1",
center: { x: 0, y: 0 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
availableRotationDegrees: [0],
pads: [
{
Expand Down Expand Up @@ -46,7 +46,7 @@ const manualPackOutput: PackOutput = {
{
componentId: "U2",
center: { x: 0, y: 10 },
ccwRotationOffset: 90, // 90 degrees
ccwRotationOffsetDegrees: 90, // 90 degrees
availableRotationDegrees: [-90, 90],
pads: [
{
Expand Down
6 changes: 3 additions & 3 deletions site/pack/pack04.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const manualPackOutput: PackOutput = {
x: 0,
y: 0,
},
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
},
{
componentId: "pcb_component_1",
Expand Down Expand Up @@ -194,7 +194,7 @@ const manualPackOutput: PackOutput = {
x: 0,
y: 0,
},
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
},
{
componentId: "pcb_component_2",
Expand Down Expand Up @@ -238,7 +238,7 @@ const manualPackOutput: PackOutput = {
x: 0,
y: 0,
},
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
},
],
minGap: 2,
Expand Down
4 changes: 2 additions & 2 deletions tests/debug-90-degree-rotation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ test("test actual packing with 90 degree constraint", () => {
const u2 = result.components.find((c: any) => c.componentId === "U2")!

console.log("\n=== Actual Packing Result ===")
console.log(`U2 rotation: ${u2.ccwRotationOffset.toFixed(1)}°`)
console.log(`U2 rotation: ${u2.ccwRotationOffsetDegrees.toFixed(1)}°`)
console.log(
`U2 center: (${u2.center.x.toFixed(1)}, ${u2.center.y.toFixed(1)})`,
)
Expand Down Expand Up @@ -131,7 +131,7 @@ test("test actual packing with 90 degree constraint", () => {
}

// Must be exactly 90 degrees
expect(Math.abs(u2.ccwRotationOffset - 90)).toBeLessThan(0.01)
expect(Math.abs(u2.ccwRotationOffsetDegrees - 90)).toBeLessThan(0.01)
// Must be vertical
expect(sameX).toBe(true)
expect(differentY).toBe(true)
Expand Down
4 changes: 2 additions & 2 deletions tests/debug-dimension-rotation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ test("debug exactly where pad dimensions get lost", () => {
const u2 = result.components.find((c) => c.componentId === "U2")!
const bodyPad = u2.pads[0]!

console.log(`Output U2 rotation: ${u2.ccwRotationOffset}°`)
console.log(`Output U2 rotation: ${u2.ccwRotationOffsetDegrees}°`)
console.log(`Output U2 body pad: ${bodyPad.size.x} x ${bodyPad.size.y}`)
console.log(`Expected after 90°: 4 x 2`)

// At 90°, should swap from 2x4 to 4x2
expect(u2.ccwRotationOffset).toBe(90)
expect(u2.ccwRotationOffsetDegrees).toBe(90)
expect(bodyPad.size.x).toBe(4) // was height
expect(bodyPad.size.y).toBe(2) // was width

Expand Down
12 changes: 6 additions & 6 deletions tests/debug-step-behavior.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test("simulate PackDebugger step behavior with 90° constraint", () => {
{
componentId: "U1",
center: { x: 0, y: 0 },
ccwRotationOffset: 0,
ccwRotationOffsetDegrees: 0,
availableRotationDegrees: [0],
pads: [
{
Expand All @@ -34,7 +34,7 @@ test("simulate PackDebugger step behavior with 90° constraint", () => {
{
componentId: "U2",
center: { x: 0, y: 10 },
ccwRotationOffset: Math.PI / 2, // 90°
ccwRotationOffsetDegrees: Math.PI / 2, // 90°
availableRotationDegrees: [90], // Should force vertical
pads: [
{
Expand Down Expand Up @@ -85,8 +85,8 @@ test("simulate PackDebugger step behavior with 90° constraint", () => {
const u2Result = result.find((c) => c.componentId === "U2")!

console.log(`\nAfter stepping:`)
console.log(`U2 rotation (raw): ${u2Result.ccwRotationOffset}`)
console.log(`U2 rotation: ${u2Result.ccwRotationOffset.toFixed(1)}°`)
console.log(`U2 rotation (raw): ${u2Result.ccwRotationOffsetDegrees}`)
console.log(`U2 rotation: ${u2Result.ccwRotationOffsetDegrees.toFixed(1)}°`)
console.log(
`U2 center: (${u2Result.center.x.toFixed(1)}, ${u2Result.center.y.toFixed(1)})`,
)
Expand All @@ -111,12 +111,12 @@ test("simulate PackDebugger step behavior with 90° constraint", () => {
console.log(`Is U2 vertical? ${isVertical}`)

// U2 should be constrained to 90° and therefore vertical
const rotation90 = Math.abs(u2Result.ccwRotationOffset - 90) < 0.1
const rotation90 = Math.abs(u2Result.ccwRotationOffsetDegrees - 90) < 0.1
console.log(`Is U2 at 90°? ${rotation90}`)

if (!rotation90) {
console.log(
`❌ BUG: U2 should be forced to 90° but got ${u2Result.ccwRotationOffset.toFixed(1)}°`,
`❌ BUG: U2 should be forced to 90° but got ${u2Result.ccwRotationOffsetDegrees.toFixed(1)}°`,
)
}

Expand Down
13 changes: 9 additions & 4 deletions tests/pad-position-integrity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ test("pads should maintain correct offsets when rotation is constrained to [0]",
console.log(
` Center: (${component.center.x.toFixed(2)}, ${component.center.y.toFixed(2)})`,
)
console.log(` Rotation: ${component.ccwRotationOffset.toFixed(1)}°`)
console.log(` Rotation: ${component.ccwRotationOffsetDegrees.toFixed(1)}°`)

// Find original component to compare offsets
const originalComponent = input.components.find(
Expand Down Expand Up @@ -182,7 +182,9 @@ test("compare rotation=0 vs unconstrained to see the difference", () => {
console.log(
` Center: (${u2Constrained.center.x.toFixed(2)}, ${u2Constrained.center.y.toFixed(2)})`,
)
console.log(` Rotation: ${u2Constrained.ccwRotationOffset.toFixed(1)}°`)
console.log(
` Rotation: ${u2Constrained.ccwRotationOffsetDegrees.toFixed(1)}°`,
)
console.log(
` VCC pad: (${u2Constrained.pads[0]?.absoluteCenter?.x.toFixed(2)}, ${u2Constrained.pads[0]?.absoluteCenter?.y.toFixed(2)})`,
)
Expand All @@ -194,7 +196,9 @@ test("compare rotation=0 vs unconstrained to see the difference", () => {
console.log(
` Center: (${u2Unconstrained.center.x.toFixed(2)}, ${u2Unconstrained.center.y.toFixed(2)})`,
)
console.log(` Rotation: ${u2Unconstrained.ccwRotationOffset.toFixed(1)}°`)
console.log(
` Rotation: ${u2Unconstrained.ccwRotationOffsetDegrees.toFixed(1)}°`,
)
console.log(
` VCC pad: (${u2Unconstrained.pads[0]?.absoluteCenter?.x.toFixed(2)}, ${u2Unconstrained.pads[0]?.absoluteCenter?.y.toFixed(2)})`,
)
Expand All @@ -204,6 +208,7 @@ test("compare rotation=0 vs unconstrained to see the difference", () => {

// They should be different if rotation constraints are working
const same =
u2Constrained.ccwRotationOffset === u2Unconstrained.ccwRotationOffset
u2Constrained.ccwRotationOffsetDegrees ===
u2Unconstrained.ccwRotationOffsetDegrees
console.log(`Same rotation: ${same}`)
})
4 changes: 2 additions & 2 deletions tests/rotation-math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ test("90 degree rotation should correctly position pads", () => {
console.log(
`U2 center: (${u2.center.x.toFixed(2)}, ${u2.center.y.toFixed(2)})`,
)
console.log(`U2 rotation: ${u2.ccwRotationOffset.toFixed(1)}°`)
console.log(`U2 rotation: ${u2.ccwRotationOffsetDegrees.toFixed(1)}°`)

// Should be exactly 90 degrees
expect(Math.abs(u2.ccwRotationOffset - 90)).toBeLessThan(0.01)
expect(Math.abs(u2.ccwRotationOffsetDegrees - 90)).toBeLessThan(0.01)

// Check pad positions
const u2P1 = u2.pads.find((p) => p.padId === "U2_P1")!
Expand Down
Loading
Loading