Add layers

This commit is contained in:
Skyler Lehmkuhl 2024-12-07 00:47:09 -05:00
parent 6b7e7eae16
commit e1e48ede30
2 changed files with 218 additions and 130 deletions

View File

@ -349,6 +349,7 @@ let actions = {
let action = {
audiosrc:audiosrc,
uuid: uuidv4(),
layeruuid: uuidv4(),
frameNum: object.currentFrameNum,
object: object.idx
}
@ -360,7 +361,7 @@ let actions = {
const player = new Tone.Player().toDestination();
await player.load(action.audiosrc)
// player.autostart = true;
let newAudioLayer = new AudioLayer()
let newAudioLayer = new AudioLayer(action.layeruuid)
let object = pointerList[action.object]
const img = new Image();
img.className = "audioWaveform"
@ -379,7 +380,9 @@ let actions = {
updateLayers()
},
rollback: (action) => {
// your code here
let object = pointerList[action.object]
let layer = pointerList[action.layeruuid]
object.audioLayers.splice(object.audioLayers.indexOf(layer),1)
updateLayers()
}
},
@ -450,6 +453,59 @@ let actions = {
updateUI()
}
},
addLayer: {
create: () => {
redoStack.length = 0
let action = {
object: context.activeObject.idx,
uuid: uuidv4()
}
undoStack.push({name: 'addLayer', action: action})
actions.addLayer.execute(action)
updateMenu()
},
execute: (action) => {
let object = pointerList[action.object]
let layer = new Layer(action.uuid)
layer.name = `Layer ${object.layers.length + 1}`
object.layers.push(layer)
updateLayers()
},
rollback: (action) => {
let object = pointerList[action.object]
let layer = pointerList[action.uuid]
object.layers.splice(object.layers.indexOf(layer),1)
updateLayers()
}
},
deleteLayer: {
create: (layer) => {
redoStack.length = 0
if (!(layer instanceof Layer)) {
layer = context.activeObject.activeLayer
}
let action = {
object: context.activeObject.idx,
layer: layer.idx,
index: context.activeObject.layers.indexOf(layer)
}
undoStack.push({name: 'deleteLayer', action: action})
actions.deleteLayer.execute(action)
updateMenu()
},
execute: (action) => {
let object = pointerList[action.object]
let layer = pointerList[action.layer]
object.layers.splice(object.layers.indexOf(layer),1)
updateLayers()
},
rollback: (action) => {
let object = pointerList[action.object]
let layer = pointerList[action.layer]
object.layers.splice(action.index,0,layer)
updateLayers()
}
},
editFrame: {
create: (frame) => {
redoStack.length = 0; // Clear redo stack
@ -1060,9 +1116,102 @@ class Layer {
} else {
this.idx = uuid
}
this.name = "Layer"
this.frames = [new Frame("keyframe", this.idx+"-F1")]
pointerList[this.idx] = this
}
getFrame(num) {
if (this.frames[num]) {
if (this.frames[num].frameType == "keyframe") {
return this.frames[num]
} else if (this.frames[num].frameType == "motion") {
let frameKeys = {}
let prevFrame = this.frames[num].prev
let nextFrame = this.frames[num].next
const t = (num - this.frames[num].prevIndex) / (this.frames[num].nextIndex - this.frames[num].prevIndex);
for (let key in prevFrame.keys) {
frameKeys[key] = {}
let prevKeyDict = prevFrame.keys[key]
let nextKeyDict = nextFrame.keys[key]
for (let prop in prevKeyDict) {
frameKeys[key][prop] = (1 - t) * prevKeyDict[prop] + t * nextKeyDict[prop];
}
}
let frame = new Frame("motion", "temp")
frame.keys = frameKeys
return frame
} else if (this.frames[num].frameType == "shape") {
let prevFrame = this.frames[num].prev
let nextFrame = this.frames[num].next
const t = (num - this.frames[num].prevIndex) / (this.frames[num].nextIndex - this.frames[num].prevIndex);
let shapes = []
for (let shape1 of prevFrame.shapes) {
if (shape1.curves.length == 0) continue;
let shape2 = undefined
for (let i of nextFrame.shapes) {
if (shape1.shapeId == i.shapeId) {
shape2 = i
}
}
if (shape2 != undefined) {
let path1 = [{type: "M", x:shape1.curves[0].points[0].x, y:shape1.curves[0].points[0].y}]
for (let curve of shape1.curves) {
path1.push({type:"C", x1:curve.points[1].x, y1:curve.points[1].y,
x2: curve.points[2].x, y2: curve.points[2].y,
x: curve.points[3].x, y:curve.points[3].y
})
}
let path2 = []
if (shape2.curves.length > 0) {
path2.push({type: "M", x:shape2.curves[0].points[0].x, y:shape2.curves[0].points[0].y})
for (let curve of shape2.curves) {
path2.push({type:"C", x1:curve.points[1].x, y1:curve.points[1].y,
x2: curve.points[2].x, y2: curve.points[2].y,
x: curve.points[3].x, y:curve.points[3].y
})
}
}
const interpolator = d3.interpolatePathCommands(path1, path2)
let current = interpolator(t)
let curves = []
let start = current.shift()
let {x, y} = start
for (let curve of current) {
curves.push(new Bezier(x, y, curve.x1, curve.y1, curve.x2, curve.y2, curve.x, curve.y))
x = curve.x
y = curve.y
}
let lineWidth = lerp(shape1.lineWidth, shape2.lineWidth, t)
let strokeStyle = lerpColor(shape1.strokeStyle, shape2.strokeStyle, t)
let fillStyle;
if (!shape1.fillImage) {
fillStyle = lerpColor(shape1.fillStyle, shape2.fillStyle, t)
}
shapes.push(new TempShape(
start.x, start.y, curves, shape1.lineWidth,
shape1.stroked, shape1.filled, strokeStyle, fillStyle
))
}
}
let frame = new Frame("shape", "temp")
frame.shapes = shapes
return frame
} else {
for (let i=Math.min(num, this.frames.length-1); i>=0; i--) {
if (this.frames[i].frameType == "keyframe") {
return this.frames[i]
}
}
}
} else {
for (let i=Math.min(num, this.frames.length-1); i>=0; i--) {
if (this.frames[i].frameType == "keyframe") {
return this.frames[i]
}
}
}
}
copy() {
let newLayer = new Layer()
let idxMapping = {}
@ -1091,12 +1240,6 @@ class AudioLayer {
console.log(this.sounds[sound])
this.sounds[sound].player.start(time)
}))
// const synth = new Tone.Synth().toDestination();
// this.track = new Tone.Part(((time, note) => {
// // the notes given as the second element in the array
// // will be passed in as the second argument
// synth.triggerAttackRelease(note, "8n", time);
// }), [[0, "C2"], ["0:2", "C3"], ["0:3:2", "G2"]]).start(0);
if (!uuid) {
this.idx = uuidv4()
} else {
@ -1546,103 +1689,7 @@ class GraphicsObject {
return Math.max(this.layers.map((layer)=>{return layer.frames.length}))
}
getFrame(num) {
if (this.activeLayer.frames[num]) {
if (this.activeLayer.frames[num].frameType == "keyframe") {
return this.activeLayer.frames[num]
} else if (this.activeLayer.frames[num].frameType == "motion") {
let frameKeys = {}
let prevFrame = this.activeLayer.frames[num].prev
let nextFrame = this.activeLayer.frames[num].next
const t = (num - this.activeLayer.frames[num].prevIndex) / (this.activeLayer.frames[num].nextIndex - this.activeLayer.frames[num].prevIndex);
for (let key in prevFrame.keys) {
frameKeys[key] = {}
let prevKeyDict = prevFrame.keys[key]
let nextKeyDict = nextFrame.keys[key]
for (let prop in prevKeyDict) {
frameKeys[key][prop] = (1 - t) * prevKeyDict[prop] + t * nextKeyDict[prop];
}
}
let frame = new Frame("motion", "temp")
frame.keys = frameKeys
return frame
} else if (this.activeLayer.frames[num].frameType == "shape") {
let prevFrame = this.activeLayer.frames[num].prev
let nextFrame = this.activeLayer.frames[num].next
const t = (num - this.activeLayer.frames[num].prevIndex) / (this.activeLayer.frames[num].nextIndex - this.activeLayer.frames[num].prevIndex);
let shapes = []
for (let shape1 of prevFrame.shapes) {
if (shape1.curves.length == 0) continue;
let shape2 = undefined
for (let i of nextFrame.shapes) {
if (shape1.shapeId == i.shapeId) {
shape2 = i
}
}
if (shape2 != undefined) {
let path1 = [{type: "M", x:shape1.curves[0].points[0].x, y:shape1.curves[0].points[0].y}]
for (let curve of shape1.curves) {
path1.push({type:"C", x1:curve.points[1].x, y1:curve.points[1].y,
x2: curve.points[2].x, y2: curve.points[2].y,
x: curve.points[3].x, y:curve.points[3].y
})
}
let path2 = []
if (shape2.curves.length > 0) {
path2.push({type: "M", x:shape2.curves[0].points[0].x, y:shape2.curves[0].points[0].y})
for (let curve of shape2.curves) {
path2.push({type:"C", x1:curve.points[1].x, y1:curve.points[1].y,
x2: curve.points[2].x, y2: curve.points[2].y,
x: curve.points[3].x, y:curve.points[3].y
})
}
}
const interpolator = d3.interpolatePathCommands(path1, path2)
let current = interpolator(t)
let curves = []
let start = current.shift()
let {x, y} = start
for (let curve of current) {
curves.push(new Bezier(x, y, curve.x1, curve.y1, curve.x2, curve.y2, curve.x, curve.y))
x = curve.x
y = curve.y
}
let lineWidth = lerp(shape1.lineWidth, shape2.lineWidth, t)
let strokeStyle = lerpColor(shape1.strokeStyle, shape2.strokeStyle, t)
let fillStyle;
if (!shape1.fillImage) {
fillStyle = lerpColor(shape1.fillStyle, shape2.fillStyle, t)
}
shapes.push(new TempShape(
start.x, start.y, curves, shape1.lineWidth,
shape1.stroked, shape1.filled, strokeStyle, fillStyle
))
}
}
let frame = new Frame("shape", "temp")
frame.shapes = shapes
return frame
} else {
for (let i=Math.min(num, this.activeLayer.frames.length-1); i>=0; i--) {
if (this.activeLayer.frames[i].frameType == "keyframe") {
return this.activeLayer.frames[i]
}
}
}
} else {
for (let i=Math.min(num, this.activeLayer.frames.length-1); i>=0; i--) {
if (this.activeLayer.frames[i].frameType == "keyframe") {
return this.activeLayer.frames[i]
}
}
}
}
get maxFrame() {
let maxFrames = []
for (let layer of this.layers) {
maxFrames.push(layer.frames.length)
}
return Math.max(maxFrames)
return this.activeLayer.getFrame(num)
}
bbox() {
let bbox;
@ -1679,7 +1726,9 @@ class GraphicsObject {
// if (this.currentFrameNum>=this.maxFrame) {
// this.currentFrameNum = 0;
// }
for (let shape of this.currentFrame.shapes) {
for (let layer of this.layers) {
let frame = layer.getFrame(this.currentFrameNum)
for (let shape of frame.shapes) {
if (context.shapeselection.indexOf(shape) >= 0) {
invertPixels(ctx, fileWidth, fileHeight)
}
@ -1688,14 +1737,14 @@ class GraphicsObject {
invertPixels(ctx, fileWidth, fileHeight)
}
}
for (let child of this.children) {
for (let child of layer.children) {
let idx = child.idx
if (idx in this.currentFrame.keys) {
child.x = this.currentFrame.keys[idx].x;
child.y = this.currentFrame.keys[idx].y;
child.rotation = this.currentFrame.keys[idx].rotation;
child.scale_x = this.currentFrame.keys[idx].scale_x;
child.scale_y = this.currentFrame.keys[idx].scale_y;
if (idx in frame.keys) {
child.x = frame.keys[idx].x;
child.y = frame.keys[idx].y;
child.rotation = frame.keys[idx].rotation;
child.scale_x = frame.keys[idx].scale_x;
child.scale_y = frame.keys[idx].scale_y;
ctx.save()
child.draw(context)
if (true) {
@ -1704,6 +1753,7 @@ class GraphicsObject {
ctx.restore()
}
}
}
if (this == context.activeObject) {
if (context.activeCurve) {
ctx.strokeStyle = "magenta"
@ -3117,7 +3167,19 @@ function updateLayers() {
for (let layer of context.activeObject.layers) {
let layerHeader = document.createElement("div")
layerHeader.className = "layer-header"
if (context.activeObject.activeLayer == layer) {
layerHeader.classList.add("active")
}
layerspanel.appendChild(layerHeader)
let layerName = document.createElement("div")
layerName.className = "layer-name"
layerName.innerText = layer.name
layerHeader.appendChild(layerName)
layerHeader.addEventListener("click", (e) => {
context.activeObject.currentLayer = context.activeObject.layers.indexOf(layer)
updateLayers()
updateUI()
})
let layerTrack = document.createElement("div")
layerTrack.className = "layer-track"
framescontainer.appendChild(layerTrack)
@ -3360,6 +3422,22 @@ async function updateMenu() {
]
})
const layerSubmenu = await Submenu.new({
text: "Layer",
items: [
{
text: "Add Layer",
enabled: true,
action: actions.addLayer.create
},
{
text: "Delete Layer",
enabled: context.activeObject.layers.length > 1,
action: actions.deleteLayer.create
},
]
})
newFrameMenuItem = {
text: "New Frame",
enabled: !activeFrame,
@ -3440,7 +3518,7 @@ async function updateMenu() {
]
});
let items = [fileSubmenu, editSubmenu, modifySubmenu, timelineSubmenu, viewSubmenu, helpSubmenu]
let items = [fileSubmenu, editSubmenu, modifySubmenu, layerSubmenu, timelineSubmenu, viewSubmenu, helpSubmenu]
if (macOS) {
items.unshift(appSubmenu)
}

View File

@ -322,16 +322,23 @@ button {
.layer-header {
width: 100%;
height: calc( 2 * var(--lineheight));
background-color: #ccc;
background-color: #bbb;
border-top: 1px solid #ddd;
border-bottom: 1px solid #bbb;
border-bottom: 1px solid #aaa;
flex-shrink: 0;
}
.layer-header.active {
background-color: #ccc;
}
.layer-header.audio {
background-color: #8281cc;
border-top: 1px solid #9a99db;
border-bottom: 1px solid #817db9;
}
.layer-name {
padding-left: 1em;
padding-top: 5px;
}
.layer-track {
min-width: 100%;
height: calc( 2 * var(--lineheight));
@ -552,6 +559,9 @@ button {
border-top: 1px solid #4f4f4f;
border-bottom: 1px solid #222222;
}
.layer-header.active {
background-color: #444;
}
.layer-header.audio {
background-color: #23253b;
border-top: 1px solid #403f4e;