From d9037c67ff44bc8896a2987a75ee9f9a9bfbe130 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Tue, 19 Oct 2021 21:58:06 -0400 Subject: [PATCH] add very basic undo/redo for move/scale/rotate --- app.js | 1 + notes.md | 1 - src/kiri/do.js | 156 ++++++++++++++++++++++++++++++++++++++++++++ src/kiri/init.js | 5 +- src/kiri/main.js | 7 +- src/kiri/tools.js | 2 + src/moto/space.js | 8 ++- web/kiri/index.css | 25 +++++++ web/kiri/index.html | 4 ++ 9 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 src/kiri/do.js diff --git a/app.js b/app.js index 4371601f..5981fdaa 100644 --- a/app.js +++ b/app.js @@ -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), diff --git a/notes.md b/notes.md index 1df5bb79..da05bec0 100644 --- a/notes.md +++ b/notes.md @@ -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) diff --git a/src/kiri/do.js b/src/kiri/do.js new file mode 100644 index 00000000..bc98f685 --- /dev/null +++ b/src/kiri/do.js @@ -0,0 +1,156 @@ +/** Copyright Stewart Allen -- 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}; + }); +}); \ No newline at end of file diff --git a/src/kiri/init.js b/src/kiri/init.js index 527869ae..eb8c0a04 100644 --- a/src/kiri/init.js +++ b/src/kiri/init.js @@ -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; diff --git a/src/kiri/main.js b/src/kiri/main.js index 35a2d751..06a2c2e4 100644 --- a/src/kiri/main.js +++ b/src/kiri/main.js @@ -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: { diff --git a/src/kiri/tools.js b/src/kiri/tools.js index bfe7fb79..8951ccda 100644 --- a/src/kiri/tools.js +++ b/src/kiri/tools.js @@ -1,3 +1,5 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + kiri.load(function(API) { // circle let pgeo = new THREE.CircleGeometry(8, 30); diff --git a/src/moto/space.js b/src/moto/space.js index 8e741b72..a37ea853 100644 --- a/src/moto/space.js +++ b/src/moto/space.js @@ -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); diff --git a/web/kiri/index.css b/web/kiri/index.css index cbc41e09..1a741817 100644 --- a/web/kiri/index.css +++ b/web/kiri/index.css @@ -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; diff --git a/web/kiri/index.html b/web/kiri/index.html index 4a3750d7..5bdf0aa9 100644 --- a/web/kiri/index.html +++ b/web/kiri/index.html @@ -39,6 +39,10 @@
Kiri:Moto
version
+
+ + +