Added rotation (but around the wrong point)

This commit is contained in:
Skyler Lehmkuhl 2024-12-30 02:10:53 -05:00
parent d02b487649
commit df32b43915
2 changed files with 67 additions and 2 deletions

View File

@ -3,7 +3,7 @@ import * as fitCurve from '/fit-curve.js';
import { Bezier } from "/bezier.js";
import { Quadtree } from './quadtree.js';
import { createNewFileDialog, showNewFileDialog, closeDialog } from './newfile.js';
import { titleCase, getMousePositionFraction, getKeyframesSurrounding, invertPixels, lerpColor, lerp, camelToWords, generateWaveform, floodFillRegion, getShapeAtPoint, hslToRgb, drawCheckerboardBackground, hexToHsl, hsvToRgb, hexToHsv, rgbToHex, clamp, drawBorderedRect, drawCenteredText, drawHorizontallyCenteredText, deepMerge, getPointNearBox, arraysAreEqual, drawRegularPolygon, getFileExtension, createModal, deeploop } from './utils.js';
import { titleCase, getMousePositionFraction, getKeyframesSurrounding, invertPixels, lerpColor, lerp, camelToWords, generateWaveform, floodFillRegion, getShapeAtPoint, hslToRgb, drawCheckerboardBackground, hexToHsl, hsvToRgb, hexToHsv, rgbToHex, clamp, drawBorderedRect, drawCenteredText, drawHorizontallyCenteredText, deepMerge, getPointNearBox, arraysAreEqual, drawRegularPolygon, getFileExtension, createModal, deeploop, signedAngleBetweenVectors } from './utils.js';
import { backgroundColor, darkMode, foregroundColor, frameWidth, gutterHeight, highlight, iconSize, triangleSize, labelColor, layerHeight, layerWidth, scrubberColor, shade, shadow } from './styles.js';
import { Icon } from './icon.js';
const { writeTextFile: writeTextFile, readTextFile: readTextFile, writeFile: writeFile, readFile: readFile }= window.__TAURI__.fs;
@ -3735,7 +3735,7 @@ function stage() {
} else {
growBoundingBox(bbox, item.bbox())
}
selection[item.idx] = {x: item.x, y: item.y, scale_x: item.scale_x, scale_y: item.scale_y}
selection[item.idx] = {x: item.x, y: item.y, scale_x: item.scale_x, scale_y: item.scale_y, rotation: item.rotation}
}
let transformPoint = getPointNearBox(bbox, mouse, 10)
if (transformPoint) {
@ -3744,11 +3744,13 @@ function stage() {
initial: {
x: {min: bbox.x.min, max: bbox.x.max},
y: {min: bbox.y.min, max: bbox.y.max},
rotation: 0,
selection: selection
},
current: {
x: {min: bbox.x.min, max: bbox.x.max},
y: {min: bbox.y.min, max: bbox.y.max},
rotation: 0,
selection: structuredClone(selection)
}
}
@ -3757,6 +3759,23 @@ function stage() {
transformPoint = getPointNearBox(bbox, mouse, 30, false)
if (transformPoint) {
stage.style.cursor = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='currentColor' class='bi bi-arrow-counterclockwise' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2z'/%3E%3Cpath d='M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466'/%3E%3C/svg%3E") 12 12, auto`
context.dragDirection = 'r'
context.activeTransform = {
initial: {
x: {min: bbox.x.min, max: bbox.x.max},
y: {min: bbox.y.min, max: bbox.y.max},
rotation: 0,
mouse: {x: mouse.x, y: mouse.y},
selection: selection
},
current: {
x: {min: bbox.x.min, max: bbox.x.max},
y: {min: bbox.y.min, max: bbox.y.max},
rotation: 0,
mouse: {x: mouse.x, y: mouse.y},
selection: structuredClone(selection)
}
}
} else {
stage.style.cursor = "default"
}
@ -4088,11 +4107,21 @@ function stage() {
current.x.min = mouse.x
} else if (context.dragDirection.indexOf('e') != -1) {
current.x.max = mouse.x
}
if (context.dragDirection == 'r') {
let pivot = {
x: (initial.x.min+initial.x.max)/2,
y: (initial.y.min+initial.y.max)/2,
}
current.rotation = signedAngleBetweenVectors(pivot, initial.mouse, mouse)
}
// Calculate the translation difference between current and initial values
const delta_x = current.x.min - initial.x.min;
const delta_y = current.y.min - initial.y.min;
// This is probably unnecessary since initial rotation is 0
const delta_rot = current.rotation - initial.rotation
// Calculate the scaling factor based on the difference between current and initial values
const scale_x_ratio = (current.x.max - current.x.min) / (initial.x.max - initial.x.min);
const scale_y_ratio = (current.y.max - current.y.min) / (initial.y.max - initial.y.min);
@ -4105,6 +4134,7 @@ function stage() {
item.y = initial.y.min + delta_y + yoffset * scale_y_ratio
item.scale_x = initialSelection[idx].scale_x * scale_x_ratio
item.scale_y = initialSelection[idx].scale_y * scale_y_ratio
item.rotation = initialSelection[idx].rotation + delta_rot
}
}
break;

View File

@ -547,6 +547,40 @@ function clamp(n) {
return Math.min(Math.max(n,0),1)
}
function signedAngleBetweenVectors(a, b, c) {
// Vector AB = (bx - ax, by - ay)
const ABx = b.x - a.x;
const ABy = b.y - a.y;
// Vector AC = (cx - ax, cy - ay)
const ACx = c.x - a.x;
const ACy = c.y - a.y;
// Dot product of AB and AC
const dotProduct = ABx * ACx + ABy * ACy;
// Magnitudes of AB and AC
const magnitudeAB = Math.sqrt(ABx * ABx + ABy * ABy);
const magnitudeAC = Math.sqrt(ACx * ACx + ACy * ACy);
// Cosine of the angle between AB and AC
const cosTheta = dotProduct / (magnitudeAB * magnitudeAC);
// Clamp the value to avoid floating point errors
const clampedCosTheta = Math.max(-1, Math.min(1, cosTheta));
// Angle in radians
const angleRadians = Math.acos(clampedCosTheta);
// Cross product to determine the sign of the angle
const crossProduct = ABx * ACy - ABy * ACx;
// If the cross product is positive, the angle is counterclockwise, otherwise it's clockwise
const signedAngle = crossProduct > 0 ? angleRadians : -angleRadians;
return signedAngle;
}
function drawBorderedRect(ctx, x, y, width, height, top, bottom, left, right) {
ctx.fillRect(x, y, width, height)
if (top) {
@ -797,6 +831,7 @@ export {
rgbToHex,
drawCheckerboardBackground,
clamp,
signedAngleBetweenVectors,
drawBorderedRect,
drawCenteredText,
drawHorizontallyCenteredText,