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/init",
|
||||||
"kiri/export",
|
"kiri/export",
|
||||||
"kiri/tools",
|
"kiri/tools",
|
||||||
|
"kiri/do",
|
||||||
"@devices",
|
"@devices",
|
||||||
"@icons"
|
"@icons"
|
||||||
].map(p => p.charAt(0) !== '@' ? `src/${p}.js` : p),
|
].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
|
* `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` 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` 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` allow select of a range by typing in values in slices or workspace units
|
||||||
* `F` add % field type with conversion (like 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) {
|
if (API.feature.hover) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (up) {
|
||||||
|
API.event.emit('mouse.drag.done', offset);
|
||||||
|
}
|
||||||
if (delta && UI.freeLayout.checked) {
|
if (delta && UI.freeLayout.checked) {
|
||||||
let set = settings();
|
let set = settings();
|
||||||
let dev = set.device;
|
let dev = set.device;
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,10 @@
|
||||||
SPACE
|
SPACE
|
||||||
},
|
},
|
||||||
devel,
|
devel,
|
||||||
|
doit: {
|
||||||
|
undo: noop, // set in do.js
|
||||||
|
redo: noop // set in do.js
|
||||||
|
},
|
||||||
var: {
|
var: {
|
||||||
layer_lo: 0,
|
layer_lo: 0,
|
||||||
layer_hi: 0,
|
layer_hi: 0,
|
||||||
|
|
@ -360,7 +364,8 @@
|
||||||
restore: restoreWorkspace,
|
restore: restoreWorkspace,
|
||||||
clear: clearWorkspace,
|
clear: clearWorkspace,
|
||||||
save: saveWorkspace,
|
save: saveWorkspace,
|
||||||
set_focus: setFocus
|
set_focus: setFocus,
|
||||||
|
update: SPACE.update
|
||||||
},
|
},
|
||||||
tweak,
|
tweak,
|
||||||
util: {
|
util: {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/** Copyright Stewart Allen <sa@grid.space> -- All Rights Reserved */
|
||||||
|
|
||||||
kiri.load(function(API) {
|
kiri.load(function(API) {
|
||||||
// circle
|
// circle
|
||||||
let pgeo = new THREE.CircleGeometry(8, 30);
|
let pgeo = new THREE.CircleGeometry(8, 30);
|
||||||
|
|
|
||||||
|
|
@ -777,7 +777,9 @@
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let refresh = false,
|
let refresh = false,
|
||||||
selection = null;
|
selection = null;
|
||||||
if (mouseUpSelect) selection = mouseUpSelect();
|
if (mouseUpSelect) {
|
||||||
|
selection = mouseUpSelect();
|
||||||
|
}
|
||||||
if (selection && selection.length > 0) {
|
if (selection && selection.length > 0) {
|
||||||
let int = intersect(selection, selectRecurse);
|
let int = intersect(selection, selectRecurse);
|
||||||
if (mouseUp) {
|
if (mouseUp) {
|
||||||
|
|
@ -800,7 +802,9 @@
|
||||||
if (!refresh && platformClickAt) {
|
if (!refresh && platformClickAt) {
|
||||||
platformClick(platformClickAt);
|
platformClick(platformClickAt);
|
||||||
}
|
}
|
||||||
if (refresh) requestRefresh();
|
if (refresh) {
|
||||||
|
requestRefresh();
|
||||||
|
}
|
||||||
mouseStart = null;
|
mouseStart = null;
|
||||||
} else if (mouseDrag && mouseDragStart) {
|
} else if (mouseDrag && mouseDragStart) {
|
||||||
mouseDrag(null,null,true);
|
mouseDrag(null,null,true);
|
||||||
|
|
|
||||||
|
|
@ -343,6 +343,31 @@ th, tr, td, label {
|
||||||
background-color: white;
|
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 {
|
#app-acct {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,10 @@
|
||||||
<div>Kiri:Moto</div>
|
<div>Kiri:Moto</div>
|
||||||
<div id="app-info">version</div>
|
<div id="app-info">version</div>
|
||||||
</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 class="grow"></div>
|
||||||
<div id="app-acct">
|
<div id="app-acct">
|
||||||
<i class="fas fa-user-cog"></i>
|
<i class="fas fa-user-cog"></i>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue