refactor lay flat, focus, hover pop tool menus

This commit is contained in:
Stewart Allen 2024-06-05 12:10:33 -07:00
commit 9a02408e7b
8 changed files with 202 additions and 391 deletions

View file

@ -178,7 +178,6 @@ kiri.load(() => {
if (steps !== 1) {
playButton.style.display = 'none';
pauseButton.style.display = '';
// $('render-hide').onclick();
}
kiri.client.animate({
speed,
@ -192,7 +191,6 @@ kiri.load(() => {
updateSpeed(1);
playButton.style.display = 'none';
pauseButton.style.display = '';
// $('render-hide').onclick();
kiri.client.animate({
speed,
steps: steps || Infinity,

View file

@ -72,7 +72,6 @@ kiri.load(() => {
api.event.emit('animate', 'CAM');
api.alerts.hide(alert);
// moto.space.platform.showGridBelow(false);
$('render-hide').onclick();
});
}
@ -147,7 +146,6 @@ kiri.load(() => {
if (steps !== 1) {
playButton.style.display = 'none';
pauseButton.style.display = '';
$('render-hide').onclick();
}
kiri.client.animate2({
speed,
@ -161,7 +159,6 @@ kiri.load(() => {
updateSpeed(1);
playButton.style.display = 'none';
pauseButton.style.display = '';
$('render-hide').onclick();
kiri.client.animate2({
speed,
steps: steps || Infinity,

View file

@ -1559,6 +1559,7 @@ CAM.init = function(kiri, api) {
}
api.event.on('tool.mesh.face-normal', normal => {
console.log({ poppedRec });
poppedRec.degrees = (Math.atan2(normal.y, normal.z) * RAD2DEG).round(2);
poppedRec.absolute = true;
func.opRender();

View file

@ -462,9 +462,9 @@ gapp.register("kiri.init", [], (root, exports) => {
case cca('e'): // device
showDevices();
break;
case cca('w'): // scale
api.event.emit("tool.next");
break;
// case cca('w'): // scale
// api.event.emit("tool.next");
// break;
case cca('o'): // tools
showTools();
break;
@ -516,13 +516,6 @@ gapp.register("kiri.init", [], (root, exports) => {
return false;
}
function setFocus() {
let int = contextInt[0];
if (int && int.object && int.object.widget) {
api.space.set_focus(undefined, int.point);
}
}
function duplicateSelection() {
api.selection.duplicate();
}
@ -1595,7 +1588,6 @@ gapp.register("kiri.init", [], (root, exports) => {
speedbar: $('speedbar'),
context: $('context-menu'),
back: $('lt-back'),
ltsetup: $('lt-setup'),
ltfile: $('lt-file'),
ltview: $('lt-view'),
@ -1940,12 +1932,12 @@ gapp.register("kiri.init", [], (root, exports) => {
camFirstZMax: newBoolean(LANG.ou_z1st_s, onBooleanClick, {title:LANG.ou_z1st_l}),
camForceZMax: newBoolean(LANG.ou_forz_s, onBooleanClick, {title:LANG.ou_forz_l}),
camOrigin: newGroup(LANG.co_menu, $('cam-origin'), { modes:CAM, driven, separator }),
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
separator: newBlank({ class:"set-sep", driven }),
camOriginOffX: newInput(LANG.co_offx_s, {title:LANG.co_offx_l, convert:toFloat, units:true}),
camOriginOffY: newInput(LANG.co_offy_s, {title:LANG.co_offy_l, convert:toFloat, units:true}),
camOriginOffZ: newInput(LANG.co_offz_s, {title:LANG.co_offz_l, convert:toFloat, units:true}),
separator: newBlank({ class:"set-sep", driven }),
camOriginTop: newBoolean(LANG.or_topp_s, onBooleanClick, {title:LANG.or_topp_l}),
camOriginCenter: newBoolean(LANG.or_cntr_s, onBooleanClick, {title:LANG.or_cntr_l}),
camExpert: newGroup(LANG.op_xprt_s, $('cam-expert'), { group: "cam_expert", modes:CAM, marker: false, driven, separator }),
camExpertFast: newBoolean(LANG.cx_fast_s, onBooleanClick, {title:LANG.cx_fast_l, show: () => !ui.camTrueShadow.checked }),
camTrueShadow: newBoolean(LANG.cx_true_s, onBooleanClick, {title:LANG.cx_true_l, show: () => !ui.camExpertFast.checked }),
@ -2401,29 +2393,29 @@ gapp.register("kiri.init", [], (root, exports) => {
}
};
space.mouse.up((event, int) => {
// context menu
if (event.button === 2) {
let et = event.target;
if (et.tagName != 'CANVAS' && et.id != 'context-menu') {
return;
}
let full = api.view.is_arrange();
for (let key of ["layflat","mirror","duplicate"]) {
$(`context-${key}`).disabled = !full;
}
let style = ui.context.style;
style.display = 'flex';
style.left = `${event.clientX-3}px`;
style.top = `${event.clientY-3}px`;
ui.context.onmouseleave = () => {
style.display = '';
};
event.preventDefault();
event.stopPropagation();
contextInt = int;
}
});
// space.mouse.up((event, int) => {
// // context menu
// if (event.button === 2) {
// let et = event.target;
// if (et.tagName != 'CANVAS' && et.id != 'context-menu') {
// return;
// }
// let full = api.view.is_arrange();
// for (let key of ["layflat","mirror","duplicate"]) {
// $(`context-${key}`).disabled = !full;
// }
// let style = ui.context.style;
// style.display = 'flex';
// style.left = `${event.clientX-3}px`;
// style.top = `${event.clientY-3}px`;
// ui.context.onmouseleave = () => {
// style.display = '';
// };
// event.preventDefault();
// event.stopPropagation();
// contextInt = int;
// }
// });
space.mouse.downSelect((int, event) => {
if (api.feature.on_mouse_down) {
@ -2520,8 +2512,6 @@ gapp.register("kiri.init", [], (root, exports) => {
function init_two() {
api.event.emit('init.two');
// api.space.set_focus();
// call driver initializations, if present
Object.values(kiri.driver).forEach(driver => {
if (driver.init) try {
@ -2710,7 +2700,6 @@ gapp.register("kiri.init", [], (root, exports) => {
api.widgets.for(w => w.unrotate());
api.selection.update_info();
};
$('lay-flat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
// rotation buttons
let d = (Math.PI / 180) * 5;
$('rot_x_lt').onclick = () => { api.selection.rotate(-d,0,0) };
@ -2727,17 +2716,15 @@ gapp.register("kiri.init", [], (root, exports) => {
$('mesh-swap').onclick = () => { api.widgets.replace() };
$('mesh-export-stl').onclick = () => { objectsExport('stl') };
$('mesh-export-obj').onclick = () => { objectsExport('obj') };
// context menu
$('context-export-workspace').onclick = () => { profileExport(true) };
$('context-clear-workspace').onclick = () => {
api.view.set(VIEWS.ARRANGE);
api.platform.clear();
ui.context.onmouseleave();
};
$('context-duplicate').onclick = duplicateSelection;
$('context-mirror').onclick = mirrorSelection;
$('context-layflat').onclick = () => { api.event.emit("tool.mesh.lay-flat") };
$('context-setfocus').onclick = setFocus;
$('context-setfocus').onclick = () => {
api.event.emit(
"tool.camera.focus",
ev => api.space.set_focus(undefined, ev.object.point)
);
};
ui.modal.onclick = api.modal.hide;
ui.modalBox.onclick = (ev) => { ev.stopPropagation() };
@ -2748,112 +2735,6 @@ gapp.register("kiri.init", [], (root, exports) => {
// show topline separator when iframed
try { if (WIN.self !== WIN.top) $('top-sep').style.display = 'flex' } catch (e) { console.log(e) }
// bind tictac buttons to panel show/hide
let groups = {};
for (let tt of [...document.getElementsByClassName('tictac')]) {
let sid = tt.getAttribute('select');
let gid = tt.getAttribute('group') || 'default';
let grp = groups[gid] = groups[gid] || [];
let target = $(sid);
target.style.display = 'none';
target.control = tt;
grp.push({button: tt, div: target});
tt.onclick = () => {
for (let rec of grp) {
let {button, div} = rec;
if (div === target) {
div.style.display = '';
button.classList.add('selected');
} else {
div.style.display = 'none';
button.classList.remove('selected');
}
}
let lid = tt.getAttribute('label');
if (lid) {
$(lid).innerText = tt.getAttribute('title');
}
};
}
// bind closer X to hiding parent action
for (let tt of [...document.getElementsByClassName('closer')]) {
let tid = tt.getAttribute('target');
let target = tid ? $(tid) : parentWithClass(tt, 'movable');
target.classList.add('hide');
let close = tt.onmousedown = (ev) => {
target.classList.add('hide');
if (ev) {
ev.preventDefault();
ev.stopPropagation();
}
};
api.event.on('key.esc', close);
}
// add drag behavior to movers
[...document.getElementsByClassName('mover')].forEach(mover => {
mover.onmousedown = (ev) => {
let moving = parentWithClass(mover, 'movable');
let mpos = {
x: moving.offsetLeft,
y: moving.offsetTop
};
ev.preventDefault();
ev.stopPropagation();
let origin = {
x: ev.screenX,
y: ev.screenY
};
let tracker = ui.tracker;
tracker.style.display = 'block';
tracker.onmousemove = (ev) => {
ev.preventDefault();
ev.stopPropagation();
let pos = {
x: ev.screenX,
y: ev.screenY
};
let delta = {
x: pos.x - origin.x,
y: pos.y - origin.y
};
moving.style.left = `${mpos.x + delta.x}px`;
moving.style.top = `${mpos.y + delta.y}px`;
};
tracker.onmouseup = (ev) => {
ui.tracker.style.display = 'none';
ev.preventDefault();
ev.stopPropagation();
tracker.onmouseup = null;
tracker.onmouseout = null;
tracker.onmousemove = null;
};
};
});
// bind tool buttons
{
let { event } = api;
let next = 0;
let list = ['rotate','scale','mesh'];
event.on("tool.next", () => {
event.emit("tool.show", list[next++ % list.length]);
});
event.on("tool.show", tictac => {
if (typeof(tictac) === 'string') {
next = list.indexOf(tictac)+1;
tictac = $(`ft-${tictac}`);
}
let mover = parentWithClass(tictac.control, 'movable');
mover.classList.remove('hide');
tictac.control.onclick();
});
$('tool-rotate').onclick = event.bind("tool.show", "rotate");
$('tool-scale').onclick = event.bind("tool.show", "scale");
$('tool-mesh').onclick = event.bind("tool.show", "mesh");
}
// warn users they are running a beta release
if (kiri.beta && kiri.beta > 0 && sdb.kiri_beta != kiri.beta) {
api.show.alert("this is a beta / development release");

View file

@ -5,7 +5,7 @@
gapp.register("kiri.tools", [], (root, exports) => {
const { Vector3, Quaternion } = THREE;
const { kiri } = root;
const { kiri, moto } = root;
const { api } = kiri;
const XAXIS = new THREE.Vector3(1,0,0);
@ -13,7 +13,7 @@ const ZAXIS = new THREE.Vector3(0,0,1);
// circle
let pgeo = new THREE.CircleGeometry(8, 30);
let pmat = new THREE.MeshBasicMaterial({color: 0xff0000, opacity: 0.5, transparent: true});
let pmat = new THREE.MeshBasicMaterial({color: 0xddeeff, opacity: 0.6, transparent: true});
let pmesh = new THREE.Mesh(pgeo, pmat);
// circle outline
@ -34,7 +34,7 @@ for (let i=0; i<pi.length; i++) {
let wgeo = new THREE.BufferGeometry();
wgeo.setAttribute('position', new THREE.BufferAttribute(np.toFloat32(), 3));
let wmat = new THREE.LineBasicMaterial({ color: 0x883333 });
let wmat = new THREE.LineBasicMaterial({ color: 0x6699cc });
let wmesh = new THREE.LineSegments(wgeo, wmat);
pmesh.add(wmesh);
@ -48,12 +48,12 @@ let onDone = onLayFlatSelect;
function onLayFlatSelect() {
let q = new Quaternion().setFromUnitVectors(lastface.normal, new Vector3(0,0,-1));
api.selection.rotate(q);
endIt();
// endIt();
}
function onFaceUpSelect() {
api.event.emit('tool.mesh.face-normal', lastface.normal);
endIt();
// endIt();
}
function startIt() {
@ -91,6 +91,12 @@ function cleanup() {
api.event.on('key.esc', endIt);
api.event.on('tool.camera.focus', (fn) => {
opName = 'camera focus';
onDone = fn;
startIt();
});
api.event.on('tool.mesh.face-up', () => {
opName = 'face select';
onDone = onFaceUpSelect;
@ -103,6 +109,16 @@ api.event.on('tool.mesh.lay-flat', () => {
startIt();
});
function scale() {
let cam = moto.space.internals().camera;
let dist = cam.position.distanceTo(pmesh.position);
let scale = opName === 'camera focus' ?
Math.min(0.25, 100 / dist) : dist / 100;
pmesh.scale.set(scale,scale,scale);
}
api.event.on("space.view.zoom", scale);
api.event.on('mouse.hover', (ev) => {
if (!enabled) {
return;
@ -134,6 +150,7 @@ api.event.on('mouse.hover', (ev) => {
new Vector3(norm.x,norm.y,norm.z)
);
pmesh.setRotationFromQuaternion(q);
scale();
}
});
@ -145,7 +162,8 @@ api.event.on('mouse.hover.up', (ev) => {
if (!object) {
return;
}
onDone();
onDone(ev);
endIt();
});
});

View file

@ -712,6 +712,12 @@ gapp.register("moto.space", [], (root, exports) => {
m.renderOrder = 3;
});
Space.scene.add(group);
const scale = grid.origin.scale = () => {
const dist = camera.position.distanceTo(group.position);
const scale = Math.min(dist / 100, 0.5);
group.scale.set(scale, scale, scale);
};
scale();
updateDraws();
}
@ -1369,11 +1375,15 @@ gapp.register("moto.space", [], (root, exports) => {
updateLastAction();
updateFocus();
}, (val) => {
if (camera && grid?.origin?.scale) {
grid.origin.scale();
}
if (camera && viewControl) {
// increase intersect line precision on zoom
const dist = camera.position.distanceTo(viewControl.target);
raycaster.params.Line.threshold = Math.min(1, dist / 100);
}
moto.broker.publish("space.view.zoom", camera);
updateLastAction();
if (slider) slider(val);
});

View file

@ -255,6 +255,7 @@ th, tr, td, label {
{
background-color: #555 !important;
}
.dark #render-tools .pop,
.dark #render-tools svg,
.dark #lang-pop,
.dark .pop-tics > div,
@ -265,7 +266,6 @@ th, tr, td, label {
.dark .set2-group .var-row,
.dark .set2-sep,
.dark .var-row,
.dark .movable .body,
.dark #layers .var-row,
.dark #slider-zero,
.dark #slider-max,
@ -276,6 +276,7 @@ th, tr, td, label {
background-color: #222;
color: white;
}
.dark #render-tools .pop,
.dark #render-tools svg,
.dark .cam-pop-op {
border-color: #555;
@ -596,6 +597,9 @@ th, tr, td, label {
.gap5 {
gap: 5px;
}
.gap10 {
gap: 10px;
}
.grid {
display: grid;
}
@ -928,31 +932,6 @@ th, tr, td, label {
max-height: 1px;
background-image: var(--gradbar2);
}
.movable {
margin: 5px;
}
.movable .title {
color: white;
padding: 2px 3px 1px 3px;
background-color: var(--main-color);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
font-size: smaller;
text-align: center;
}
.movable .body {
margin: 0;
padding: 0 5px 0 0;
background-color: #fff;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
}
.movable .footer {
height: 10px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
background-color: var(--main-color);
}
.closer {
position: absolute;
@ -1016,12 +995,62 @@ th, tr, td, label {
color: white;
}
/* top center toolbar */
#render-tools {
pointer-events: all;
position: absolute;
gap: 2px;
top: 3px;
left: 0;
right: 0;
align-items: center;
justify-content: center;
flex-direction: row;
}
#render-tools svg {
font-size: 20px;
aspect-ratio: 1/1;
background-color: #fff;
border: 1px solid #aaa;
border-radius: 3px;
padding: 8px;
}
#render-tools svg:hover {
border: 1px solid blue;
background-color: var(--blue-4);
}
/** tool pop menus */
#render-tools .pop:before {
position: absolute;
top: -10px;
left: -20px;
width: calc(100% + 40px);
height: calc(100% + 20px);
/* background-color: red; */
content: "";
}
#render-tools .pop {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 3px;
position: absolute;
display: none;
margin-top: 3px;
padding: 3px;
top: 100%;
}
#render-tools > span:hover .pop {
display: grid;
}
/** selection rotation */
#ft-rotate {
grid-gap: 2px;
grid-template-rows: auto auto auto 1fr;
grid-template-columns: auto auto auto auto;
}
#ft-rotate label, #ft-rotate svg {
#ft-rotate label,
#ft-rotate svg
{
text-align: center;
}
#ft-rotate svg {
@ -1044,6 +1073,7 @@ th, tr, td, label {
align-items: stretch;
min-width: 150px;
}
/** selection scaling */
#ft-scale {
grid-gap: 2px;
grid-template-rows: auto auto auto 1fr;
@ -1058,6 +1088,7 @@ th, tr, td, label {
margin-left: 0;
margin-right: 0;
}
/** selection mesh actions */
#ft-mesh label {
font-size: smaller;
}
@ -1068,36 +1099,6 @@ th, tr, td, label {
#ft-mesh {
min-width: 200px;
}
#render-tools {
pointer-events: all;
position: absolute;
gap: 2px;
top: 3px;
left: 0;
right: 0;
align-items: center;
justify-content: center;
flex-direction: row;
z-index: 102;
}
#render-tools svg {
font-size: 20px;
aspect-ratio: 1/1;
background-color: #fff;
border: 1px solid var(--blue-4);
border-radius: 3px;
padding: 8px;
}
#render-tools svg:hover {
border: 1px solid blue;
background-color: var(--blue-4);
}
#render-hide > a {
border: 0.5px dashed black;
border-radius: 2px;
width: 17px;
height: 17px;
}
#mesh-info {
grid-gap: 2px;
grid-template-rows: auto auto auto;
@ -1112,54 +1113,10 @@ th, tr, td, label {
background-color: var(--main-color);
}
#lt-back, #lt-options {
flex-direction: row;
position: absolute;
display: none;
gap: 5px;
top: 0;
}
#lt-trash:hover, #lt-w-enable:hover, #lt-w-disable:hover {
color: var(--main-color);
}
#act-sep, #lt-sep {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
background-color: var(--main-color);
height: 10px;
left: -5px;
}
#lt-end {
height: 10px;
border-top-right-radius: 1px;
border-bottom-right-radius: 10px;
background-color: var(--main-color);
left: -5px;
}
#act-animate.disabled {
color: #aaa !important;
}
.lt-menu {
flex-direction: column;
align-items: center;
margin: 2px 0 2px 0;
padding: 5px 5px 5px 10px;
border: 1px solid rgba(0,0,0,0);
border-radius: 3px;
color: #ccc;
left: -5px;
background-color: rgba(255,255,255,0.75);
}
.lt-menu svg {
font-size: 40px;
text-align: center;
}
.lt-menu label {
font-size: small;
}
.dev-buttons {
margin: 1px -1px 0 -1px;
}
@ -1485,28 +1442,6 @@ th, tr, td, label {
#mod-tools .set-header {
margin: 0;
}
/* .devlist {
background-color: #fdfdfd;
}
.devlist button {
margin: 1px;
padding: 2px;
border: 0;
border-radius: 0;
font-size: 11px;
font-family: sans-serif;
background-color: #f8f8f8;
text-align: left;
min-height: 1.5em;
}
.devlist button:hover {
background-color: var(--main-color);
color: white;
}
.devlist button.selected {
background-color: var(--highlight);
border: 1px solid var(--highlight);
} */
#mod-help svg {
width: 36px;
height: 36px;
@ -1842,14 +1777,6 @@ th, tr, td, label {
.t-inset {
padding: 3px;
}
.t-pad {
width: 5px;
height: 5px;
}
.t-pad2 {
width: 10px;
height: 10px;
}
#devices {
width: 225px;

View file

@ -211,6 +211,70 @@
<!-- line betwwen menu bar and main application windows -->
<div id="progress"><div id="progbar"><div id="progtxt">loading</div></div></div>
<!-- toolbar with mesh and rendering options -->
<div>
<div id="render-tools">
<span id="tool-rotate"><i class="fas fa-sync-alt" title="rotate selection"></i>
<div id="ft-rotate" class="grid pop">
<div id="rot_x_lt"><i class="fas fa-chevron-left"></i></div>
<label>X</label>
<div id="rot_x_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_x" class="value center" size="6" value="0">
<div id="rot_y_lt"><i class="fas fa-chevron-left"></i></div>
<label>Y</label>
<div id="rot_y_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_y" class="value center" size="6" value="0">
<div id="rot_z_lt"><i class="fas fa-chevron-left"></i></div>
<label>Z</label>
<div id="rot_z_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_z" class="value center" size="6" value="0">
<div class="buttons f-row">
<button id="unrotate" class="grow" lk="reset">reset</button>
</div>
</div>
</span>
<span id="tool-scale"><i class="fas fa-expand" title="scale selection"></i>
<div id="ft-scale" class="grid pop">
<div><label class="middle">X</label><input id="lock_x" type="checkbox" checked></div>
<div><label class="middle">Y</label><input id="lock_y" type="checkbox" checked></div>
<div><label class="middle">Z</label><input id="lock_z" type="checkbox" checked></div>
<div><label id="lab-axis" lk="axis">axis</label></div>
<div><input id="size_x" size="8" class="value"></div>
<div><input id="size_y" size="8" class="value"></div>
<div><input id="size_z" size="8" class="value"></div>
<div><label id="lab-size" lk="size">size</label></div>
<div><input id="scale_x" size="8" class="value" value="1"></div>
<div><input id="scale_y" size="8" class="value" value="1"></div>
<div><input id="scale_z" size="8" class="value" value="1"></div>
<div><label id="lab-scale" lk="scale">scale</label></div>
<button id="scale-reset" class="gr4">reset</button>
</div>
</span>
<span id="tool-mesh"><i class="fas fa-dice-d20" title="selected mesh tool"></i>
<div id="ft-mesh" class="grow f-col pop">
<div id="mesh-info" class="grid">
<label>object:</label>
<span id="mesh-name" class="value">[0]</span>
<label>points:</label>
<span id="mesh-points" class="value">-</span>
<label>faces:</label>
<span id="mesh-faces" class="value">-</span>
</div>
<button id="mesh-export-obj" lk="rc_xobj">export OBJ</button>
<button id="mesh-export-stl" lk="rc_xstl">export STL</button>
<button id="mesh-swap" lk="rc_swap">replace vertices</button>
</div>
</span>
<span id="context-mirror" xlk="rc_mirr"><i class="fas fa-arrows-left-right-to-line" title="mirror selection"></i></span>
<span id="context-duplicate" xlk="rc_dupl"><i class="fas fa-copy" title="duplicate selection"></i></span>
<span id="context-layflat" xlk="rc_lafl"><i class="fas fa-arrows-down-to-line" title="lay flat: choose a face to place on the work surface"></i></span>
<span id="context-setfocus" xlk="rc_focs"><i class="fas fa-eye" title="choose focal point, camera orbit center"></i></span>
<span id="render-solid" class="j-center" title="view solid"><i class="fas fa-square"></i></span>
<span id="render-wire" class="j-center" title="view wireframe"><i class="fas fa-border-all"></i></span>
<span id="render-ghost" class="a-center" title="view transparent"><i class="fas fa-border-none"></i></span>
</div>
</div>
<div id="mid" class="grow f-row">
<!-- left settings menu -->
@ -254,17 +318,6 @@
<!-- main workspace -->
<div id="mid-mid" class="grow">
<div id="render-tools">
<span id="tool-rotate"><i class="fas fa-sync-alt" title="rotate"></i></span>
<span id="tool-scale"><i class="fas fa-expand" title="scale"></i></span>
<span id="tool-mesh"><i class="fas fa-dice-d20" title="mesh tool"></i></span>
<span id="context-mirror" xlk="rc_mirr"><i class="fas fa-arrows-left-right-to-line" title="mirror"></i></span>
<span id="context-duplicate" xlk="rc_dupl"><i class="fas fa-copy" title="duplicate"></i></span>
<span id="context-layflat" xlk="rc_lafl"><i class="fas fa-arrows-down-to-line" title="lay flat"></i></span>
<span id="render-solid" class="j-center" title="view solid"><i class="fas fa-square"></i></span>
<span id="render-wire" class="j-center" title="view wireframe"><i class="fas fa-border-all"></i></span>
<span id="render-ghost" class="a-center" title="view transparent"><i class="fas fa-border-none"></i></span>
</div>
<div id="layer-slider" class="f-col a-stretch">
<div id="layers" class="f-row j-center gap2"></div>
<div id="slider" class="f-row russo">
@ -446,13 +499,15 @@
<div id="tool-view" class="f-col t-33 t-body"></div>
</div>
<div class="set-sep"></div>
<div id="tool-action" class="f-row j-center">
<div id="tool-action" class="f-row j-center gap10">
<div class="f-row">
<button id="tool-add"><i class="fas fa-plus"></i></button>
<button id="tool-del"><i class="fas fa-minus"></i></button>
<div class="t-pad2"></div>
</div>
<div class="f-row">
<button id="tools-save" lk="save">save</button>
<button id="tools-close" lk="done">done</button>
<div class="t-pad2"></div>
</div>
<button id="tools-export" lk="export">export</button>
</div>
</div>
@ -462,19 +517,13 @@
<div id="mod-prefs" class="mdialog f-col a-center">
<label lk="dm_appp" class="menu-title">Application Preferences</label>
<hr width="80%">
<div class="f-row">
<div class="f-row gap10">
<div id="prefs-gen1" class="f-col"></div>
<div class="t-pad2"></div>
<div id="prefs-gen2" class="f-col"></div>
<div class="t-pad2"></div>
<div id="prefs-lay" class="f-col"></div>
<div class="t-pad2"></div>
<div id="prefs-xpo" class="f-col"></div>
<div class="t-pad2"></div>
<div id="prefs-prt" class="f-col"></div>
<div class="t-pad2"></div>
<div id="prefs-add" class="f-col"></div>
<div class="t-pad2"></div>
</div>
</div>
@ -494,14 +543,6 @@
<button id="gotit" class="f-col j-center">got it</button>
</div>
<!-- right click pop-up menu -->
<div id="context-menu">
<button id="context-export-workspace" lk="rc_xpws">export workspace</button>
<button id="context-clear-workspace" lk="rc_clws">clear workspace</button>
<div class="sep"></div>
<button id="context-setfocus" lk="rc_focs">set focus</button>
</div>
<!-- developer debugging -->
<div id="stats">
<div id="rms"></div>
@ -525,68 +566,6 @@
<input id="anim-slider" type="range" min="0" max="1000" value="0"></input>
</div>
<!-- movable tool tray for mesh tools -->
<div id="float-tools" class="f-col movable hide">
<div id="float-tools-title" class="title mover">
<label id="ft-label" class="grow">title bar</label>
<div id="float-tools-close" class="closer"><i class="fas fa-times"></i></div>
</div>
<div class="body f-row">
<div class="f-col menu">
<div lkt="rotate" class="tictac" select="ft-rotate" label="ft-label"><i class="fas fa-sync-alt"></i></div>
<div lkt="scale" class="tictac" select="ft-scale" label="ft-label"><i class="fas fa-expand"></i></div>
<div lkt="mesh" class="tictac" select="ft-mesh" label="ft-label"><i class="fas fa-dice-d20"></i></div>
</div>
<div id="ft-scale" class="grow grid">
<div><label class="middle">X</label><input id="lock_x" type="checkbox" checked></div>
<div><label class="middle">Y</label><input id="lock_y" type="checkbox" checked></div>
<div><label class="middle">Z</label><input id="lock_z" type="checkbox" checked></div>
<div><label id="lab-axis" lk="axis">axis</label></div>
<div><input id="size_x" size="8" class="value"></div>
<div><input id="size_y" size="8" class="value"></div>
<div><input id="size_z" size="8" class="value"></div>
<div><label id="lab-size" lk="size">size</label></div>
<div><input id="scale_x" size="8" class="value" value="1"></div>
<div><input id="scale_y" size="8" class="value" value="1"></div>
<div><input id="scale_z" size="8" class="value" value="1"></div>
<div><label id="lab-scale" lk="scale">scale</label></div>
<button id="scale-reset" class="gr4">reset</button>
</div>
<div id="ft-rotate" class="grow grid">
<div id="rot_x_lt"><i class="fas fa-chevron-left"></i></div>
<label>X</label>
<div id="rot_x_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_x" class="value center" size="6" value="0">
<div id="rot_y_lt"><i class="fas fa-chevron-left"></i></div>
<label>Y</label>
<div id="rot_y_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_y" class="value center" size="6" value="0">
<div id="rot_z_lt"><i class="fas fa-chevron-left"></i></div>
<label>Z</label>
<div id="rot_z_gt"><i class="fas fa-chevron-right"></i></div>
<input id="rot_z" class="value center" size="6" value="0">
<div class="buttons f-row">
<button id="lay-flat" class="grow">lay flat</button>
<button id="unrotate" class="grow" lk="reset">reset</button>
</div>
</div>
<div id="ft-mesh" class="grow f-col">
<div id="mesh-info" class="grid">
<label>object:</label>
<span id="mesh-name" class="value">[0]</span>
<label>points:</label>
<span id="mesh-points" class="value">-</span>
<label>faces:</label>
<span id="mesh-faces" class="value">-</span>
</div>
<button id="mesh-export-obj" lk="rc_xobj">export OBJ</button>
<button id="mesh-export-stl" lk="rc_xstl">export STL</button>
<button id="mesh-swap" lk="rc_swap">replace vertices</button>
</div>
</div>
<div class="footer"></div>
</div>
</div>
</body>
</html>