blockbench/js/undo.js
2019-02-03 21:09:35 +01:00

384 lines
9.3 KiB
JavaScript

var Undo = {
index: 0,
history: [],
initEdit: function(aspects) {
//Before
if (aspects && Undo.current_save) {
//This "before" is the same as the "after" of the previous step
if (Objector.equalKeys(aspects, Undo.current_save.aspects) && aspects.cubes !== selected) {
//return;
}
}
Undo.current_save = new Undo.save(aspects)
},
finishEdit: function(action, aspects) {
if (!Undo.current_save) return;
aspects = aspects || Undo.current_save.aspects
//After
Blockbench.dispatchEvent('finish_edit', {aspects})
var entry = {
before: Undo.current_save,
post: new Undo.save(aspects),
action: action
}
Undo.current_save = entry.post
if (Undo.history.length-1 > Undo.index) {
Undo.history.length = Undo.index+1
}
Undo.history.push(entry)
if (Undo.history.length > settings.undo_limit.value) {
Undo.history.shift()
}
Undo.index = Undo.history.length
if (!aspects || !aspects.keep_saved) {
Prop.project_saved = false;
}
Blockbench.dispatchEvent('finished_edit', {aspects})
},
cancelEdit: function() {
if (!Undo.current_save) return;
outlines.children.length = 0
Undo.loadSave(Undo.current_save, new Undo.save(Undo.current_save.aspects))
delete Undo.current_save;
},
undo: function() {
if (Undo.history.length <= 0 || Undo.index < 1) return;
Prop.project_saved = false;
Undo.index--;
var entry = Undo.history[Undo.index]
Undo.loadSave(entry.before, entry.post)
console.log('Undo: '+entry.action)
Blockbench.dispatchEvent('undo', {entry})
},
redo: function() {
if (Undo.history.length <= 0) return;
if (Undo.index >= Undo.history.length) {
return;
}
Prop.project_saved = false;
Undo.index++;
var entry = Undo.history[Undo.index-1]
Undo.loadSave(entry.post, entry.before)
console.log('Redo: '+entry.action)
Blockbench.dispatchEvent('redo', {entry})
},
getItemByUUID: function(list, uuid) {
if (!list || typeof list !== 'object' || !list.length) {return false;}
var i = 0;
while (i < list.length) {
if (list[i].uuid === uuid) {
return list[i]
}
i++;
}
return false;
},
save: function(aspects) {
var scope = this;
this.aspects = aspects
if (aspects.selection) {
this.selection = []
selected.forEach(function(obj) {
scope.selection.push(obj.uuid)
})
if (selected_group) {
this.selection_group = selected_group.uuid
}
}
if (aspects.cubes) {
this.cubes = {}
aspects.cubes.forEach(function(obj) {
if (aspects.uv_only) {
var copy = new Cube(obj)
copy = {
uv_offset: copy.uv_offset,
faces: copy.faces,
}
} else {
var copy = new Cube(obj)
}
copy.uuid = obj.uuid
scope.cubes[obj.uuid] = copy
})
}
if (aspects.outliner) {
this.outliner = compileGroups(true)
}
if (aspects.group) {
this.group = aspects.group.getChildlessCopy()
this.group.uuid = aspects.group.uuid
}
if (aspects.textures) {
this.textures = {}
aspects.textures.forEach(function(t) {
var tex = t.getUndoCopy(aspects.bitmap)
scope.textures[t.uuid] = tex
})
}
if (aspects.settings) {
this.settings = aspects.settings
}
if (aspects.resolution) {
this.resolution = {
width: Project.texture_width,
height: Project.texture_height
}
}
if (aspects.animation) {
this.animation = aspects.animation ? aspects.animation.undoCopy() : null;
}
if (aspects.keyframes && Animator.selected && Animator.selected.getBoneAnimator()) {
this.keyframes = {
animation: Animator.selected.uuid,
bone: Animator.selected.getBoneAnimator().uuid
}
aspects.keyframes.forEach(kf => {
scope.keyframes[kf.uuid] = kf.undoCopy()
})
}
if (aspects.display_slots) {
scope.display_slots = {}
aspects.display_slots.forEach(slot => {
if (display[slot]) {
scope.display_slots[slot] = display[slot].copy()
} else {
scope.display_slots[slot] = null
}
})
}
},
loadSave: function(save, reference) {
if (save.cubes) {
for (var uuid in save.cubes) {
if (save.cubes.hasOwnProperty(uuid)) {
var data = save.cubes[uuid]
var obj = elements.findInArray('uuid', uuid)
if (obj) {
for (var face in obj.faces) {
obj.faces[face].reset()
}
obj.extend(data)
Canvas.adaptObjectPosition(obj)
Canvas.adaptObjectFaces(obj)
Canvas.updateUV(obj)
} else {
obj = new Cube(data, uuid).init(false)
}
}
}
for (var uuid in reference.cubes) {
if (reference.cubes.hasOwnProperty(uuid) && !save.cubes.hasOwnProperty(uuid)) {
var obj = elements.findInArray('uuid', uuid)
if (obj) {
obj.remove(false)
}
}
}
loadOutlinerDraggable()
Canvas.updateVisibility()
}
if (save.outliner) {
selected_group = undefined
parseGroups(save.outliner)
if (Blockbench.entity_mode) {
Canvas.updateAllPositions()
}
}
if (save.selection_group) {
selected_group = undefined
var sel_group = TreeElements.findRecursive('uuid', save.selection_group)
if (sel_group) {
sel_group.select()
}
}
if (save.selection) {
selected.length = 0;
elements.forEach(function(obj) {
if (save.selection.includes(obj.uuid)) {
selected.push(obj)
}
})
}
if (save.group) {
var group = TreeElements.findRecursive('uuid', save.group.uuid)
if (group) {
group.extend(save.group)
if (Blockbench.entity_mode) {
group.forEachChild(function(obj) {
if (obj.type === 'cube') {
Canvas.adaptObjectPosition(obj)
}
})
}
}
}
if (save.textures) {
Painter.current = {}
for (var uuid in save.textures) {
if (reference.textures[uuid]) {
var tex = Undo.getItemByUUID(textures, uuid)
if (tex) {
tex.extend(save.textures[uuid]).updateMaterial()
}
} else {
new Texture(save.textures[uuid]).load().add(false)
}
}
for (var uuid in reference.textures) {
if (!save.textures[uuid]) {
var tex = Undo.getItemByUUID(textures, uuid)
if (tex) {
textures.splice(textures.indexOf(tex), 1)
}
}
}
Canvas.updateAllFaces()
}
if (save.settings) {
for (var key in save.settings) {
settings[key].value = save.settings[key]
}
}
if (save.resolution) {
Project.texture_width = save.resolution.width
Project.texture_height = save.resolution.height
}
if (save.animation) {
var animation = Animator.animations.findInArray('uuid', save.animation)
if (!animation) {
//new
animation = new Animation()
}
//populate
Animation.extend(save.animation)
} else if (reference.animation) {
//remove
var animation = Animator.animations.findInArray('uuid', reference.animation.uuid)
if (animation.remove) {
animation.remove()
}
}
if (save.keyframes && Animator.selected) {
var animation = false;
if (Animator.selected.uuid !== save.keyframes.animation) {
animation = Animator.animations.findInArray('uuid', save.keyframes.animation)
if (animation.select) {
animation.select()
}
}
var bone = Animator.selected.getBoneAnimator();
if (!bone || bone.uuid !== save.keyframes.bone) {
for (var uuid in Animator.selected.bones) {
if (uuid === save.keyframes.bone) {
bone = Animator.selected.bones[uuid]
bone.select()
}
}
}
function getKeyframe(uuid) {
var i = 0;
while (i < Timeline.keyframes.length) {
if (Timeline.keyframes[i].uuid === uuid) {
return Timeline.keyframes[i];
}
i++;
}
}
var added = 0;
for (var uuid in save.keyframes) {
if (uuid.length === 36 && save.keyframes.hasOwnProperty(uuid)) {
var data = save.keyframes[uuid]
var kf = getKeyframe(uuid)
if (kf) {
kf.extend(data)
} else {
kf = new Keyframe(data)
kf.parent = bone;
kf.uuid = uuid;
Timeline.keyframes.push(kf)
added++;
}
}
}
for (var uuid in reference.keyframes) {
if (uuid.length === 36 && reference.keyframes.hasOwnProperty(uuid) && !save.keyframes.hasOwnProperty(uuid)) {
var kf = getKeyframe(uuid)
if (kf) {
kf.remove()
}
}
}
if (added) {
Vue.nextTick(Timeline.update)
}
updateKeyframeSelection()
Animator.preview()
}
if (save.display_slots) {
for (var slot in save.display_slots) {
var data = save.display_slots[slot]
if (!display[slot] && data) {
display[slot] = new DisplaySlot()
} else if (data === null && display[slot]) {
display[slot].default()
}
display[slot].extend(data).update()
}
}
updateSelection()
}
}
Undo.save.prototype.addTexture = function(texture) {
if (!this.textures) return;
if (this.aspects.textures.safePush(texture)) {
this.textures[texture.uuid] = texture.getUndoCopy(this.aspects.bitmap)
}
}
BARS.defineActions(function() {
new Action({
id: 'undo',
icon: 'undo',
category: 'edit',
keybind: new Keybind({key: 90, ctrl: true}),
click: function () {Undo.undo()}
})
new Action({
id: 'redo',
icon: 'redo',
category: 'edit',
keybind: new Keybind({key: 89, ctrl: true}),
click: function () {Undo.redo()}
})
})