add very basic undo/redo for move/scale/rotate
This commit is contained in:
parent
bf51d56bb5
commit
d9037c67ff
9 changed files with 204 additions and 5 deletions
1
app.js
1
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),
|
||||
|
|
|
|||
1
notes.md
1
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)
|
||||
|
|
|
|||
156
src/kiri/do.js
Normal file
156
src/kiri/do.js
Normal 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};
|
||||
});
|
||||
});
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue