diff --git a/lib/PackSolver/PhasedPackSolver.ts b/lib/PackSolver/PhasedPackSolver.ts index e8b328d..a4af4d5 100644 --- a/lib/PackSolver/PhasedPackSolver.ts +++ b/lib/PackSolver/PhasedPackSolver.ts @@ -136,7 +136,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 }, @@ -144,7 +144,7 @@ export class PhasedPackSolver extends BaseSolver { } const candidateAngles = this.getCandidateAngles(newPackedComponent) - newPackedComponent.ccwRotationOffset = + newPackedComponent.ccwRotationOffsetDegrees = (((candidateAngles[0] ?? 0) % 360) + 360) % 360 setPackedComponentPadCenters(newPackedComponent) this.packedComponents.push(newPackedComponent) @@ -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 }, @@ -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 }, @@ -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 @@ -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 @@ -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) { @@ -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 { @@ -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 @@ -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) @@ -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 diff --git a/lib/PackSolver/RotationSelector.ts b/lib/PackSolver/RotationSelector.ts index 9de4861..84f642b 100644 --- a/lib/PackSolver/RotationSelector.ts +++ b/lib/PackSolver/RotationSelector.ts @@ -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 @@ -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) diff --git a/lib/PackSolver/placeComponentAtPoint.ts b/lib/PackSolver/placeComponentAtPoint.ts index eab3bd0..81a61bb 100644 --- a/lib/PackSolver/placeComponentAtPoint.ts +++ b/lib/PackSolver/placeComponentAtPoint.ts @@ -34,7 +34,7 @@ export function placeComponentAtPoint({ const candidate: PackedComponent = { ...component, center: point, - ccwRotationOffset: angle, + ccwRotationOffsetDegrees: angle, pads, } @@ -48,7 +48,7 @@ export function placeComponentAtPoint({ // Fallback: 0° rotation component.center = point - component.ccwRotationOffset = 0 + component.ccwRotationOffsetDegrees = 0 setPackedComponentPadCenters(component) return evaluatedPositionShadows } diff --git a/lib/PackSolver/setPackedComponentPadCenters.ts b/lib/PackSolver/setPackedComponentPadCenters.ts index 54a36af..9a14336 100644 --- a/lib/PackSolver/setPackedComponentPadCenters.ts +++ b/lib/PackSolver/setPackedComponentPadCenters.ts @@ -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 diff --git a/lib/constructOutlinesFromPackedComponents.ts b/lib/constructOutlinesFromPackedComponents.ts index 5604416..a089131 100644 --- a/lib/constructOutlinesFromPackedComponents.ts +++ b/lib/constructOutlinesFromPackedComponents.ts @@ -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, diff --git a/lib/geometry/getComponentBounds.ts b/lib/geometry/getComponentBounds.ts index 45559f4..0ef92db 100644 --- a/lib/geometry/getComponentBounds.ts +++ b/lib/geometry/getComponentBounds.ts @@ -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 diff --git a/lib/plumbing/convertCircuitJsonToPackOutput.ts b/lib/plumbing/convertCircuitJsonToPackOutput.ts index b3c56a7..0a7b8a1 100644 --- a/lib/plumbing/convertCircuitJsonToPackOutput.ts +++ b/lib/plumbing/convertCircuitJsonToPackOutput.ts @@ -49,7 +49,7 @@ const buildPackedComponent = ( return { componentId, center, - ccwRotationOffset: 0, + ccwRotationOffsetDegrees: 0, pads, } as PackedComponent } diff --git a/lib/plumbing/convertPackOutputToPackInput.ts b/lib/plumbing/convertPackOutputToPackInput.ts index 9be55a3..b25dc95 100644 --- a/lib/plumbing/convertPackOutputToPackInput.ts +++ b/lib/plumbing/convertPackOutputToPackInput.ts @@ -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) => ({ diff --git a/lib/testing/getGraphicsFromPackOutput.ts b/lib/testing/getGraphicsFromPackOutput.ts index 1ed5d73..f21a476 100644 --- a/lib/testing/getGraphicsFromPackOutput.ts +++ b/lib/testing/getGraphicsFromPackOutput.ts @@ -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) diff --git a/lib/types.ts b/lib/types.ts index 363d926..fc4cdb1 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -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[] } diff --git a/site/min-sum-distance-to-network/min-sum-distance-to-network01.page.tsx b/site/min-sum-distance-to-network/min-sum-distance-to-network01.page.tsx index 0894d04..ce10a15 100644 --- a/site/min-sum-distance-to-network/min-sum-distance-to-network01.page.tsx +++ b/site/min-sum-distance-to-network/min-sum-distance-to-network01.page.tsx @@ -6,7 +6,7 @@ const manualPackOutput: PackOutput = { { componentId: "U1", center: { x: 0, y: 0 }, - ccwRotationOffset: 0, + ccwRotationOffsetDegrees: 0, availableRotationDegrees: [0], pads: [ { @@ -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: [ { diff --git a/site/pack/pack04.page.tsx b/site/pack/pack04.page.tsx index a7b4403..c1b99d0 100644 --- a/site/pack/pack04.page.tsx +++ b/site/pack/pack04.page.tsx @@ -150,7 +150,7 @@ const manualPackOutput: PackOutput = { x: 0, y: 0, }, - ccwRotationOffset: 0, + ccwRotationOffsetDegrees: 0, }, { componentId: "pcb_component_1", @@ -194,7 +194,7 @@ const manualPackOutput: PackOutput = { x: 0, y: 0, }, - ccwRotationOffset: 0, + ccwRotationOffsetDegrees: 0, }, { componentId: "pcb_component_2", @@ -238,7 +238,7 @@ const manualPackOutput: PackOutput = { x: 0, y: 0, }, - ccwRotationOffset: 0, + ccwRotationOffsetDegrees: 0, }, ], minGap: 2, diff --git a/tests/debug-90-degree-rotation.test.ts b/tests/debug-90-degree-rotation.test.ts index 0c21213..1213161 100644 --- a/tests/debug-90-degree-rotation.test.ts +++ b/tests/debug-90-degree-rotation.test.ts @@ -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)})`, ) @@ -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) diff --git a/tests/debug-dimension-rotation.test.ts b/tests/debug-dimension-rotation.test.ts index 67e9240..05b162c 100644 --- a/tests/debug-dimension-rotation.test.ts +++ b/tests/debug-dimension-rotation.test.ts @@ -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 diff --git a/tests/debug-step-behavior.test.ts b/tests/debug-step-behavior.test.ts index fd66fc2..eb1e2e3 100644 --- a/tests/debug-step-behavior.test.ts +++ b/tests/debug-step-behavior.test.ts @@ -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: [ { @@ -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: [ { @@ -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)})`, ) @@ -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)}°`, ) } diff --git a/tests/pad-position-integrity.test.ts b/tests/pad-position-integrity.test.ts index 25729e8..4236e5f 100644 --- a/tests/pad-position-integrity.test.ts +++ b/tests/pad-position-integrity.test.ts @@ -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( @@ -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)})`, ) @@ -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)})`, ) @@ -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}`) }) diff --git a/tests/rotation-math.test.ts b/tests/rotation-math.test.ts index 32847d1..ec9106c 100644 --- a/tests/rotation-math.test.ts +++ b/tests/rotation-math.test.ts @@ -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")! diff --git a/tests/simple-rotation-dimension.test.ts b/tests/simple-rotation-dimension.test.ts index 5279ed1..3b3f6ec 100644 --- a/tests/simple-rotation-dimension.test.ts +++ b/tests/simple-rotation-dimension.test.ts @@ -29,13 +29,13 @@ test("single component forced to 90° should have swapped pad dimensions", () => const pad = u1.pads[0]! console.log(`=== Single Component 90° Rotation Test ===`) - console.log(`U1 rotation: ${u1.ccwRotationOffset}°`) + console.log(`U1 rotation: ${u1.ccwRotationOffsetDegrees}°`) console.log(`Available rotations: ${u1.availableRotationDegrees}`) console.log(`Original pad size: 4x2`) console.log(`Rotated pad size: ${pad.size.x}x${pad.size.y}`) // Should be exactly 90 degrees since that's the only option - expect(u1.ccwRotationOffset).toBe(90) + expect(u1.ccwRotationOffsetDegrees).toBe(90) // At 90° rotation, 4x2 should become 2x4 expect(pad.size.x).toBe(2) // was height (2) diff --git a/tests/test-degree-fixes.test.ts b/tests/test-degree-fixes.test.ts index b0738a0..61e6cdf 100644 --- a/tests/test-degree-fixes.test.ts +++ b/tests/test-degree-fixes.test.ts @@ -60,7 +60,7 @@ test("degrees should work correctly with rotation constraints", () => { console.log( `U2 center: (${u2.center.x.toFixed(1)}, ${u2.center.y.toFixed(1)})`, ) - console.log(`U2 rotation: ${u2.ccwRotationOffset}° (should be 90°)`) + console.log(`U2 rotation: ${u2.ccwRotationOffsetDegrees}° (should be 90°)`) const pad1 = u2.pads[0] const pad2 = u2.pads[1] @@ -73,7 +73,7 @@ test("degrees should work correctly with rotation constraints", () => { ) // Should be exactly 90 degrees (not radians!) - expect(u2.ccwRotationOffset).toBe(90) + expect(u2.ccwRotationOffsetDegrees).toBe(90) // Should be vertical (same X, different Y) const sameX = @@ -142,10 +142,12 @@ test("multiple rotation options should work", () => { const u2 = result.components.find((c) => c.componentId === "U2")! console.log(`\n=== Multiple Rotation Options ===`) - console.log(`U2 rotation: ${u2.ccwRotationOffset}° (should be 90° or 270°)`) + console.log( + `U2 rotation: ${u2.ccwRotationOffsetDegrees}° (should be 90° or 270°)`, + ) // Should be one of the allowed rotations (270° is equivalent to -90°) - expect([90, 270]).toContain(u2.ccwRotationOffset) + expect([90, 270]).toContain(u2.ccwRotationOffsetDegrees) - console.log(`✅ U2 correctly chose rotation: ${u2.ccwRotationOffset}°`) + console.log(`✅ U2 correctly chose rotation: ${u2.ccwRotationOffsetDegrees}°`) })