add very basic undo/redo for move/scale/rotate

This commit is contained in:
Stewart Allen 2021-10-19 21:58:06 -04:00
commit d9037c67ff
9 changed files with 204 additions and 5 deletions

1
app.js
View file

@ -280,6 +280,7 @@ const script = {
"kiri/init",
"kiri/export",
"kiri/tools",
"kiri/do",
"@devices",
"@icons"
].map(p => p.charAt(0) !== '@' ? `src/${p}.js` : p),

View file

@ -12,7 +12,6 @@
* `P` do not move (average) endpoints connected to long lines in decimate
* `F` create event-driven do/undo stack and port relevant actions to this
* `F` convert SVG to mesh https://muffinman.io/blog/three-js-extrude-svg-path/
* `F` show slider range values in workspace units (on hover?)
* `F` allow select of a range by typing in values in slices or workspace units
* `F` add % field type with conversion (like units)

156
src/kiri/do.js Normal file
View file

@ -0,0 +1,156 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
kiri.load(function(API) {
let stack = [];
let stpos = 0;
let event = API.event;
let moved = {x:0, y:0};
let msgid;
event.on("init-done", () => {
$('undo').onclick = undo;
$('redo').onclick = redo;
});
let undo = API.doit.undo = function() {
if (stpos) {
action(stack[--stpos].undo);
} else {
message('nothing to undo');
}
};
let redo = API.doit.redo = function() {
if (stpos < stack.length) {
action(stack[stpos++].redo);
} else {
message('nothing to redo');
}
};
let clear = API.doit.clear = function() {
stack = [];
stpos = 0;
moved = {x:0, y:0};
updateButtons();
};
function updateButtons() {
$('doit').style.display = stack.length ? '' : 'none';
$('undo').disabled = stpos === 0;
$('redo').disabled = stpos == stack.length;
}
function message(txt) {
if (msgid) {
API.hide.alert(msgid);
}
msgid = API.show.alert(txt);
}
function action(rec) {
switch (rec.type) {
case 'move':
for (let w of rec.widgets) {
w._move(rec.dist.x, rec.dist.y, 0);
}
break;
case 'rotate':
for (let w of rec.widgets) {
w.rotate(rec.x, rec.y, rec.z);
}
break;
case 'scale':
for (let w of rec.widgets) {
w._scale(rec.x, rec.y, rec.z);
}
API.selection.update_info();
break;
}
API.space.update();
updateButtons();
}
function pushActions(ur) {
stack.length = stpos++;
stack.push(ur);
updateButtons();
}
event.on([
'platform.layout',
'widget.add',
'widget.delete'
], (widget) => {
clear();
});
// selection event (store position)
event.on('widget.rotate', (rec) => {
let type = "rotate";
let {widget, x, y, z} = rec;
if (!(x || y || z)) {
return;
}
pushActions({
redo: {
type,
widgets: [ widget ],
x, y, z
},
undo: {
type,
widgets: [ widget ],
x: -x, y: -y, z: -z
}
})
});
event.on('selection.scale', (rec) => {
let [x, y, z] = rec;
// ignore scale close to 1
if (Math.abs(1 - (x * y * z)) < 0.0001) {
return;
}
let widgets = API.selection.widgets();
let type = "scale";
pushActions({
redo: {
type,
widgets,
x, y, z
},
undo: {
type,
widgets,
x: 1/x, y: 1/y, z: 1/z
}
});
});
// selection moved (accumulator)
event.on('selection.drag', (delta) => {
moved.x += delta.x;
moved.y += delta.y;
});
// move complete (store updated position)
event.on('mouse.drag.done', () => {
let widgets = API.selection.widgets();
let type = "move";
pushActions({
redo: {
type,
widgets,
dist: moved
},
undo: {
type,
widgets,
dist: {x: -moved.x, y: -moved.y}
}
});
moved = {x:0, y:0};
});
});

View file

@ -2388,10 +2388,13 @@
}
});
SPACE.mouse.onDrag(function(delta) {
SPACE.mouse.onDrag(function(delta, offset, up = false) {
if (API.feature.hover) {
return;
}
if (up) {
API.event.emit('mouse.drag.done', offset);
}
if (delta && UI.freeLayout.checked) {
let set = settings();
let dev = set.device;

View file

@ -272,6 +272,10 @@
SPACE
},
devel,
doit: {
undo: noop, // set in do.js
redo: noop // set in do.js
},
var: {
layer_lo: 0,
layer_hi: 0,
@ -360,7 +364,8 @@
restore: restoreWorkspace,
clear: clearWorkspace,
save: saveWorkspace,
set_focus: setFocus
set_focus: setFocus,
update: SPACE.update
},
tweak,
util: {

View file

@ -1,3 +1,5 @@
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
kiri.load(function(API) {
// circle
let pgeo = new THREE.CircleGeometry(8, 30);

View file

@ -777,7 +777,9 @@
event.preventDefault();
let refresh = false,
selection = null;
if (mouseUpSelect) selection = mouseUpSelect();
if (mouseUpSelect) {
selection = mouseUpSelect();
}
if (selection && selection.length > 0) {
let int = intersect(selection, selectRecurse);
if (mouseUp) {
@ -800,7 +802,9 @@
if (!refresh && platformClickAt) {
platformClick(platformClickAt);
}
if (refresh) requestRefresh();
if (refresh) {
requestRefresh();
}
mouseStart = null;
} else if (mouseDrag && mouseDragStart) {
mouseDrag(null,null,true);

View file

@ -343,6 +343,31 @@ th, tr, td, label {
background-color: white;
}
#doit {
position: absolute;
white-space: nowrap;
font-size: smaller;
font-family: sans-serif;
margin-top: 5px;
padding: 5px;
top: 100%;
left: 50%;
transform: translateX(-50%);
}
#undo, #redo {
color: var(--main-color);
margin: 2px;
padding: 4px;
}
#undo:disabled, #redo:disabled {
color: rgba(0,0,0,.1) !important;
background-color: rgba(0,0,0,0.1) !important;
}
#undo:hover, #redo:hover {
color: black;
background-color: rgba(0,0,0,0.2);
}
#app-acct {
align-items: center;
justify-content: flex-end;

View file

@ -39,6 +39,10 @@
<div>Kiri:Moto</div>
<div id="app-info">version</div>
</div>
<div id="doit" class="f-row a-center">
<button id="undo"><i class="fas fa-undo-alt"></i></button>
<button id="redo"><i class="fas fa-redo-alt"></i></button>
</div>
<div class="grow"></div>
<div id="app-acct">
<i class="fas fa-user-cog"></i>