mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-01 12:32:19 +02:00
Squash Odysseus development history
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
/** Pure geometry shared by transform-frame drawing and hit testing. */
|
||||
|
||||
const HANDLE_LAYOUT = [
|
||||
['tl', -0.5, -0.5],
|
||||
['t', 0, -0.5],
|
||||
['tr', 0.5, -0.5],
|
||||
['r', 0.5, 0],
|
||||
['br', 0.5, 0.5],
|
||||
['b', 0, 0.5],
|
||||
['bl', -0.5, 0.5],
|
||||
['l', -0.5, 0],
|
||||
];
|
||||
|
||||
function finite(value, fallback = 0) {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : fallback;
|
||||
}
|
||||
|
||||
export function transformFrameGeometry(frame, options = {}) {
|
||||
const centerX = finite(frame?.centerX);
|
||||
const centerY = finite(frame?.centerY);
|
||||
const width = Math.max(1, Math.abs(finite(frame?.width, 1)));
|
||||
const height = Math.max(1, Math.abs(finite(frame?.height, 1)));
|
||||
const rotation = finite(frame?.rotation);
|
||||
const radians = rotation * Math.PI / 180;
|
||||
const cos = Math.cos(radians);
|
||||
const sin = Math.sin(radians);
|
||||
const rotateLocal = (localX, localY) => ({
|
||||
x: centerX + localX * cos - localY * sin,
|
||||
y: centerY + localX * sin + localY * cos,
|
||||
});
|
||||
const resizeHandles = HANDLE_LAYOUT.map(([id, xFactor, yFactor]) => ({
|
||||
id,
|
||||
...rotateLocal(width * xFactor, height * yFactor),
|
||||
}));
|
||||
const byId = Object.fromEntries(resizeHandles.map(handle => [handle.id, handle]));
|
||||
const zoom = Math.max(0.0001, finite(options.zoom, 1));
|
||||
const rotationOffset = Math.max(0, finite(options.rotationOffsetCss, 24)) / zoom;
|
||||
const rotationLocalY = options.rotationInside
|
||||
? -Math.max(0, height / 2 - rotationOffset)
|
||||
: -(height / 2 + rotationOffset);
|
||||
const rotationHandle = {
|
||||
id: 'rot',
|
||||
...rotateLocal(0, rotationLocalY),
|
||||
};
|
||||
|
||||
return {
|
||||
centerX,
|
||||
centerY,
|
||||
width,
|
||||
height,
|
||||
rotation,
|
||||
radians,
|
||||
resizeHandles,
|
||||
handles: [...resizeHandles, rotationHandle],
|
||||
rotationHandle,
|
||||
corners: {
|
||||
tl: { x: byId.tl.x, y: byId.tl.y },
|
||||
tr: { x: byId.tr.x, y: byId.tr.y },
|
||||
br: { x: byId.br.x, y: byId.br.y },
|
||||
bl: { x: byId.bl.x, y: byId.bl.y },
|
||||
},
|
||||
pivot: { x: centerX, y: centerY },
|
||||
};
|
||||
}
|
||||
|
||||
export function hitTestTransformHandle(geometry, x, y, options = {}) {
|
||||
if (!geometry) return null;
|
||||
const zoom = Math.max(0.0001, finite(options.zoom, 1));
|
||||
const pointerType = options.pointerType || 'mouse';
|
||||
const radiusCss = pointerType === 'touch' || pointerType === 'pen' ? 20 : 8;
|
||||
const radius = radiusCss / zoom;
|
||||
const radiusSquared = radius * radius;
|
||||
for (const handle of geometry.handles || []) {
|
||||
const dx = finite(x) - handle.x;
|
||||
const dy = finite(y) - handle.y;
|
||||
if (dx * dx + dy * dy <= radiusSquared) return handle.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function pointInTransformFrame(geometry, x, y) {
|
||||
if (!geometry) return false;
|
||||
const dx = finite(x) - geometry.centerX;
|
||||
const dy = finite(y) - geometry.centerY;
|
||||
const cos = Math.cos(-geometry.radians);
|
||||
const sin = Math.sin(-geometry.radians);
|
||||
const localX = dx * cos - dy * sin;
|
||||
const localY = dx * sin + dy * cos;
|
||||
const epsilon = 0.0001;
|
||||
return Math.abs(localX) <= geometry.width / 2 + epsilon
|
||||
&& Math.abs(localY) <= geometry.height / 2 + epsilon;
|
||||
}
|
||||
|
||||
/** Resize a frame from a screen-space pointer delta on its rotated axes. */
|
||||
export function resizeTransformFrame(frame, handle, startPoint, currentPoint, options = {}) {
|
||||
const geometry = transformFrameGeometry(frame);
|
||||
const xSign = handle?.includes('l') ? -1 : handle?.includes('r') ? 1 : 0;
|
||||
const ySign = handle?.includes('t') ? -1 : handle?.includes('b') ? 1 : 0;
|
||||
if (!xSign && !ySign) return {
|
||||
width: geometry.width,
|
||||
height: geometry.height,
|
||||
centerX: geometry.centerX,
|
||||
centerY: geometry.centerY,
|
||||
flipH: !!options.flipH,
|
||||
flipV: !!options.flipV,
|
||||
};
|
||||
|
||||
const worldDx = finite(currentPoint?.x) - finite(startPoint?.x);
|
||||
const worldDy = finite(currentPoint?.y) - finite(startPoint?.y);
|
||||
const cos = Math.cos(geometry.radians);
|
||||
const sin = Math.sin(geometry.radians);
|
||||
const localDx = worldDx * cos + worldDy * sin;
|
||||
const localDy = -worldDx * sin + worldDy * cos;
|
||||
const multiplier = options.centered ? 2 : 1;
|
||||
let rawWidth = geometry.width + xSign * localDx * multiplier;
|
||||
let rawHeight = geometry.height + ySign * localDy * multiplier;
|
||||
|
||||
if (options.lockAspect) {
|
||||
const aspect = geometry.width / geometry.height;
|
||||
const widthChange = xSign ? Math.abs(Math.abs(rawWidth) - geometry.width) / geometry.width : -1;
|
||||
const heightChange = ySign ? Math.abs(Math.abs(rawHeight) - geometry.height) / geometry.height : -1;
|
||||
if (xSign && (!ySign || widthChange >= heightChange)) {
|
||||
const sign = rawHeight < 0 ? -1 : 1;
|
||||
rawHeight = sign * Math.abs(rawWidth) / aspect;
|
||||
} else if (ySign) {
|
||||
const sign = rawWidth < 0 ? -1 : 1;
|
||||
rawWidth = sign * Math.abs(rawHeight) * aspect;
|
||||
}
|
||||
}
|
||||
|
||||
const width = Math.max(1, Math.round(Math.abs(rawWidth)));
|
||||
const height = Math.max(1, Math.round(Math.abs(rawHeight)));
|
||||
const signedWidth = (rawWidth < 0 ? -1 : 1) * width;
|
||||
const signedHeight = (rawHeight < 0 ? -1 : 1) * height;
|
||||
const localCenterX = options.centered || !xSign
|
||||
? 0
|
||||
: xSign * (signedWidth - geometry.width) / 2;
|
||||
const localCenterY = options.centered || !ySign
|
||||
? 0
|
||||
: ySign * (signedHeight - geometry.height) / 2;
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
centerX: geometry.centerX + localCenterX * cos - localCenterY * sin,
|
||||
centerY: geometry.centerY + localCenterX * sin + localCenterY * cos,
|
||||
flipH: !!options.flipH !== (rawWidth < 0),
|
||||
flipV: !!options.flipV !== (rawHeight < 0),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user