draw transform from action instead of stage

This commit is contained in:
Skyler Lehmkuhl 2025-01-03 10:58:08 -05:00
parent f0e0546a66
commit c2e99866cf
1 changed files with 232 additions and 51 deletions

View File

@ -769,10 +769,167 @@ let actions = {
updateLayers() updateLayers()
} }
}, },
transformObjects: {
initialize: (frame, _selection, direction, mouse, transform=undefined) => {
let bbox = undefined
const selection = {}
for (let item of _selection) {
if (bbox==undefined) {
bbox = getRotatedBoundingBox(item)
} else {
growBoundingBox(bbox, getRotatedBoundingBox(item))
}
selection[item.idx] = {x: item.x, y: item.y, scale_x: item.scale_x, scale_y: item.scale_y, rotation: item.rotation}
}
let action = {
type: "transformObjects",
oldState: structuredClone(frame.keys),
frame: frame.idx,
transform: {
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},
scale_x: 1,
scale_y: 1,
rotation: 0,
mouse: {x: mouse.x, y: mouse.y},
selection: structuredClone(selection)
}
},
selection: selection,
direction: direction
}
if (transform) {
action.transform = transform
}
return action
},
update: (action, mouse) => {
const initial = action.transform.initial
const current = action.transform.current
console.log(action.direction)
if (action.direction.indexOf('n') != -1) {
current.y.min = mouse.y
} else if (action.direction.indexOf('s') != -1) {
current.y.max = mouse.y
}
if (action.direction.indexOf('w') != -1) {
current.x.min = mouse.x
} else if (action.direction.indexOf('e') != -1) {
current.x.max = mouse.x
}
if (context.dragDirection == 'r') {
const pivot = {
x: (initial.x.min+initial.x.max)/2,
y: (initial.y.min+initial.y.max)/2,
}
current.rotation = signedAngleBetweenVectors(pivot, initial.mouse, mouse)
const {dx, dy} = rotateAroundPointIncremental(current.x.min, current.y.min, pivot, current.rotation)
}
// Calculate the scaling factor based on the difference between current and initial values
action.transform.current.scale_x = (current.x.max - current.x.min) / (initial.x.max - initial.x.min);
action.transform.current.scale_y = (current.y.max - current.y.min) / (initial.y.max - initial.y.min);
return action
},
render: (action, ctx) => {
const initial = action.transform.initial
const current = action.transform.current
ctx.save()
ctx.translate((current.x.max+current.x.min)/2, (current.y.max - current.y.min)/2)
ctx.rotate(current.rotation)
ctx.translate(-(current.x.max+current.x.min)/2, -(current.y.max - current.y.min)/2)
console.log(action.selection)
const cxt = {
ctx: ctx,
selection: [],
shapeselection: []
}
for (let obj in action.selection) {
const object = pointerList[obj]
object.draw(cxt)
}
ctx.strokeStyle = '#00ffff'
ctx.lineWidth = 1
ctx.beginPath()
ctx.rect(current.x.min, current.y.min, current.x.max-current.x.min, current.y.max-current.y.min)
ctx.stroke()
ctx.fillStyle = "#000000"
const rectRadius = 5
const xdiff = current.x.max - current.x.min
const ydiff = current.y.max - current.y.min
for (let i of [[0,0],[0.5,0],[1,0],[1,0.5],[1,1],[0.5,1],[0,1],[0,0.5]]) {
ctx.beginPath()
ctx.rect(
current.x.min + xdiff * i[0] - rectRadius,
current.y.min + ydiff * i[1] - rectRadius,
rectRadius*2, rectRadius*2
)
ctx.fill()
}
ctx.restore()
},
finalize: (action) => {
undoStack.push({name: "transformObjects", action: action})
actions.transformObjects.execute(action)
context.activeAction = undefined
updateMenu()
},
execute: (action) => {
const frame = pointerList[action.frame]
const initial = action.transform.initial
const current = action.transform.current
const delta_x = current.x.min - initial.x.min;
const delta_y = current.y.min - initial.y.min;
const delta_rot = current.rotation - initial.rotation
// frame.keys = structuredClone(action.newState)
console.log(action)
for (let idx in action.selection) {
const item = frame.keys[idx]
const xoffset = action.selection[idx].x - initial.x.min
const yoffset = action.selection[idx].y - initial.y.min
item.x = initial.x.min + delta_x + xoffset * current.scale_x
item.y = initial.y.min + delta_y + yoffset * current.scale_y
item.scale_x = action.selection[idx].scale_x * current.scale_x
item.scale_y = action.selection[idx].scale_y * current.scale_y
item.rotation = action.selection[idx].rotation + delta_rot
}
updateUI()
},
rollback: (action) => {
let frame = pointerList[action.frame]
frame.keys = structuredClone(action.oldState)
updateUI()
}
},
editFrame: { editFrame: {
initialize: (frame) => {
let action = {
type: "editFrame",
oldState: structuredClone(frame.keys),
frame: frame.idx
}
return action
},
finalize: (action, frame) => {
action.newState = structuredClone(frame.keys)
undoStack.push({name: "editFrame", action: action})
actions.editFrame.execute(action)
context.activeAction = undefined
updateMenu()
},
render: (action, ctx) => {
},
create: (frame) => { create: (frame) => {
redoStack.length = 0; // Clear redo stack redoStack.length = 0; // Clear redo stack
console.log(frame.idx in startProps)
if (!(frame.idx in startProps)) return; if (!(frame.idx in startProps)) return;
let action = { let action = {
newState: structuredClone(frame.keys), newState: structuredClone(frame.keys),
@ -2652,12 +2809,14 @@ class GraphicsObject {
} }
draw(context) { draw(context) {
let ctx = context.ctx; let ctx = context.ctx;
ctx.save()
ctx.translate(this.x, this.y) ctx.translate(this.x, this.y)
ctx.rotate(this.rotation) ctx.rotate(this.rotation)
ctx.scale(this.scale_x, this.scale_y) ctx.scale(this.scale_x, this.scale_y)
// if (this.currentFrameNum>=this.maxFrame) { // if (this.currentFrameNum>=this.maxFrame) {
// this.currentFrameNum = 0; // this.currentFrameNum = 0;
// } // }
if (context.activeAction && this.idx in context.activeAction.selection) return;
for (let layer of this.layers) { for (let layer of this.layers) {
if (context.activeObject==this && !layer.visible) continue; if (context.activeObject==this && !layer.visible) continue;
let frame = layer.getFrame(this.currentFrameNum) let frame = layer.getFrame(this.currentFrameNum)
@ -2790,6 +2949,7 @@ class GraphicsObject {
} }
} }
} }
ctx.restore()
} }
transformMouse(mouse) { transformMouse(mouse) {
if (this.parent) { if (this.parent) {
@ -3754,7 +3914,7 @@ function stage() {
if (hitTest(mouse, child)) { if (hitTest(mouse, child)) {
context.dragging = true context.dragging = true
context.lastMouse = mouse context.lastMouse = mouse
context.activeObject.currentFrame.saveState() context.activeAction = actions.editFrame.initialize(context.activeObject.currentFrame)
break break
} }
} }
@ -3777,7 +3937,7 @@ function stage() {
} }
context.dragging = true context.dragging = true
selected = true selected = true
context.activeObject.currentFrame.saveState() context.activeAction = actions.editFrame.initialize(context.activeObject.currentFrame)
break break
} }
} }
@ -3825,7 +3985,12 @@ function stage() {
selection: structuredClone(selection) selection: structuredClone(selection)
} }
} }
context.activeObject.currentFrame.saveState() context.activeAction = actions.transformObjects.initialize(
context.activeObject.currentFrame,
context.selection,
transformPoint,
mouse
)
} else { } else {
transformPoint = getPointNearBox(bbox, mouse, 30, false) transformPoint = getPointNearBox(bbox, mouse, 30, false)
if (transformPoint) { if (transformPoint) {
@ -3847,7 +4012,12 @@ function stage() {
selection: structuredClone(selection) selection: structuredClone(selection)
} }
} }
context.activeObject.currentFrame.saveState() context.activeAction = actions.transformObjects.initialize(
context.activeObject.currentFrame,
context.selection,
'r',
mouse
)
} else { } else {
stage.style.cursor = "default" stage.style.cursor = "default"
} }
@ -3950,7 +4120,9 @@ function stage() {
context.activeShape = undefined context.activeShape = undefined
break; break;
case "select": case "select":
if (context.activeVertex) { if (context.activeAction) {
actions[context.activeAction.type].finalize(context.activeAction, context.activeObject.currentFrame)
} else if (context.activeVertex) {
let newCurves = [] let newCurves = []
for (let i in context.activeVertex.shape.curves) { for (let i in context.activeVertex.shape.curves) {
if (i in context.activeVertex.current.startCurves) { if (i in context.activeVertex.current.startCurves) {
@ -3974,13 +4146,16 @@ function stage() {
actions.editShape.create(context.activeCurve.shape, newCurves) actions.editShape.create(context.activeCurve.shape, newCurves)
} else if (context.selection.length) { } else if (context.selection.length) {
actions.select.create() actions.select.create()
actions.editFrame.create(context.activeObject.currentFrame) // actions.editFrame.create(context.activeObject.currentFrame)
} else if (context.shapeselection.length) { } else if (context.shapeselection.length) {
actions.select.create() actions.select.create()
} }
break; break;
case "transform": case "transform":
actions.editFrame.create(context.activeObject.currentFrame) if (context.activeAction) {
actions[context.activeAction.type].finalize(context.activeAction, context.activeObject.currentFrame)
}
// actions.editFrame.create(context.activeObject.currentFrame)
break; break;
default: default:
break; break;
@ -4166,53 +4341,56 @@ function stage() {
stage.style.cursor = "default" stage.style.cursor = "default"
} }
} }
if (context.dragDirection) { // if (context.dragDirection) {
let initial = context.activeTransform.initial // let initial = context.activeTransform.initial
let current = context.activeTransform.current // let current = context.activeTransform.current
let initialSelection = context.activeTransform.initial.selection // let initialSelection = context.activeTransform.initial.selection
if (context.dragDirection.indexOf('n') != -1) { // if (context.dragDirection.indexOf('n') != -1) {
current.y.min = mouse.y // current.y.min = mouse.y
} else if (context.dragDirection.indexOf('s') != -1) { // } else if (context.dragDirection.indexOf('s') != -1) {
current.y.max = mouse.y // current.y.max = mouse.y
} // }
if (context.dragDirection.indexOf('w') != -1) { // if (context.dragDirection.indexOf('w') != -1) {
current.x.min = mouse.x // current.x.min = mouse.x
} else if (context.dragDirection.indexOf('e') != -1) { // } else if (context.dragDirection.indexOf('e') != -1) {
current.x.max = mouse.x // current.x.max = mouse.x
} // }
// Calculate the translation difference between current and initial values // // Calculate the translation difference between current and initial values
let delta_x = current.x.min - initial.x.min; // let delta_x = current.x.min - initial.x.min;
let delta_y = current.y.min - initial.y.min; // let delta_y = current.y.min - initial.y.min;
if (context.dragDirection == 'r') { // if (context.dragDirection == 'r') {
let pivot = { // let pivot = {
x: (initial.x.min+initial.x.max)/2, // x: (initial.x.min+initial.x.max)/2,
y: (initial.y.min+initial.y.max)/2, // y: (initial.y.min+initial.y.max)/2,
} // }
current.rotation = signedAngleBetweenVectors(pivot, initial.mouse, mouse) // current.rotation = signedAngleBetweenVectors(pivot, initial.mouse, mouse)
const {dx, dy} = rotateAroundPointIncremental(current.x.min, current.y.min, pivot, current.rotation) // const {dx, dy} = rotateAroundPointIncremental(current.x.min, current.y.min, pivot, current.rotation)
// delta_x -= dx // // delta_x -= dx
// delta_y -= dy // // delta_y -= dy
// console.log(dx, dy) // // console.log(dx, dy)
} // }
// This is probably unnecessary since initial rotation is 0 // // This is probably unnecessary since initial rotation is 0
const delta_rot = current.rotation - initial.rotation // const delta_rot = current.rotation - initial.rotation
// Calculate the scaling factor based on the difference between current and initial values // // 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_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); // const scale_y_ratio = (current.y.max - current.y.min) / (initial.y.max - initial.y.min);
for (let idx in initialSelection) { // for (let idx in initialSelection) {
let item = context.activeObject.currentFrame.keys[idx] // let item = context.activeObject.currentFrame.keys[idx]
let xoffset = initialSelection[idx].x - initial.x.min // let xoffset = initialSelection[idx].x - initial.x.min
let yoffset = initialSelection[idx].y - initial.y.min // let yoffset = initialSelection[idx].y - initial.y.min
item.x = initial.x.min + delta_x + xoffset * scale_x_ratio // item.x = initial.x.min + delta_x + xoffset * scale_x_ratio
item.y = initial.y.min + delta_y + yoffset * scale_y_ratio // item.y = initial.y.min + delta_y + yoffset * scale_y_ratio
item.scale_x = initialSelection[idx].scale_x * scale_x_ratio // item.scale_x = initialSelection[idx].scale_x * scale_x_ratio
item.scale_y = initialSelection[idx].scale_y * scale_y_ratio // item.scale_y = initialSelection[idx].scale_y * scale_y_ratio
item.rotation = initialSelection[idx].rotation + delta_rot // item.rotation = initialSelection[idx].rotation + delta_rot
} // }
// }
if (context.activeAction) {
actions[context.activeAction.type].update(context.activeAction, mouse)
} }
break; break;
default: default:
@ -5224,6 +5402,9 @@ function renderUI() {
ctx.arc(point.x, point.y, 3, 0, 2*Math.PI) ctx.arc(point.x, point.y, 3, 0, 2*Math.PI)
ctx.fill() ctx.fill()
} }
if (context.activeAction) {
actions[context.activeAction.type].render(context.activeAction, ctx)
}
} }
for (let selectionRect of document.querySelectorAll(".selectionRect")) { for (let selectionRect of document.querySelectorAll(".selectionRect")) {