fix(canvas): use wrapper top-left for rotation pivot (#205 follow-up)
The previous fix rotated overlay hotspots but the pivot was off by +6px in each axis, which manifested as a 12px X-offset for a 90° rotation (because (I - R) maps (6,6) to (12, 0) for R = 90° CW). The wrapper top-left in container-local coords is -wrapperOffsetX, not -(6 - wrapperOffsetX). The container origin already sits INSIDE the wrapper's padding+border by exactly wrapperOffsetX/Y; we have to back out by that same amount, not by 6 - that amount. Visual verification on https://velxio.dev/example/esp32-pwm-led-rgb: overlay centers of rotated resistor now match the rotated pin tips exactly (was 12px off in X).
This commit is contained in:
parent
0a69c9facf
commit
5ca7e293b7
|
|
@ -114,29 +114,18 @@ export const PinOverlay: React.FC<PinOverlayProps> = ({
|
|||
}}
|
||||
>
|
||||
{pins.map((pin, index) => {
|
||||
// Container origin in CANVAS space is (componentX + wrapperOffsetX,
|
||||
// componentY + wrapperOffsetY). The wrapper top-left in canvas
|
||||
// space is (componentX - (6 - wrapperOffsetX), componentY - (6 -
|
||||
// wrapperOffsetY)) for the DynamicComponent path, but more
|
||||
// simply: padding+border on the wrapper is 6px on each side, so
|
||||
// the wrapper-top-left equals containerOrigin - (6 - offset).
|
||||
// Pivot for CSS rotation (transform-origin: center center) is
|
||||
// wrapperTopLeft + (wrapperW/2, wrapperH/2). Express it in
|
||||
// container-local coords by subtracting containerOrigin.
|
||||
// Container origin in CANVAS = (componentX + wrapperOffsetX,
|
||||
// componentY + wrapperOffsetY) — i.e. shifted INTO the wrapper
|
||||
// by the wrapper's padding+border so pin.x can be added directly.
|
||||
// The wrapper itself sits at (componentX, componentY), so its
|
||||
// top-left in container-local coords is (-wrapperOffsetX,
|
||||
// -wrapperOffsetY). CSS rotates around the wrapper's centre.
|
||||
let pinX = pin.x;
|
||||
let pinY = pin.y;
|
||||
const angle = ((rotation % 360) + 360) % 360;
|
||||
if (angle !== 0 && wrapperBox) {
|
||||
// Wrapper interior padding+border is 6 px on each side
|
||||
// (DynamicComponent: padding:4 + border:2). For boards rendered
|
||||
// directly the caller passes wrapperOffsetX/Y = 0, so the
|
||||
// 6 - offset arithmetic still resolves correctly (6 - 0 = 6
|
||||
// would be wrong for boards which don't have a wrapper at
|
||||
// all — but boards also pass rotation = 0 so we never enter
|
||||
// this branch for them).
|
||||
const wrapperInner = 6;
|
||||
const wrapperLeftLocal = -(wrapperInner - wrapperOffsetX);
|
||||
const wrapperTopLocal = -(wrapperInner - wrapperOffsetY);
|
||||
const wrapperLeftLocal = -wrapperOffsetX;
|
||||
const wrapperTopLocal = -wrapperOffsetY;
|
||||
const pivotX = wrapperLeftLocal + wrapperBox.w / 2;
|
||||
const pivotY = wrapperTopLocal + wrapperBox.h / 2;
|
||||
const theta = (angle * Math.PI) / 180;
|
||||
|
|
|
|||
Loading…
Reference in New Issue