complete parametric scaling with axis grouping. angles in degrees

This commit is contained in:
Stewart Allen 2022-01-20 17:01:56 -05:00
commit 03a9eb396f
4 changed files with 79 additions and 38 deletions

View file

@ -440,6 +440,11 @@ let prefs = {
map: { map: {
info: {}, info: {},
mode: modes.object, mode: modes.object,
edit: {
scale_group_X: true,
scale_group_Y: true,
scale_group_Z: true
},
space: { space: {
wire: false, wire: false,
grid: true, grid: true,

View file

@ -16,6 +16,7 @@ if (mesh.build) return;
let broker = gapp.broker; let broker = gapp.broker;
let call = broker.send; let call = broker.send;
let { api, util } = mesh; let { api, util } = mesh;
let rad = 180 / Math.PI;
let und = undefined; let und = undefined;
broker.listeners({ broker.listeners({
@ -297,14 +298,14 @@ function ui_build() {
let [ pos, rot ] = side; let [ pos, rot ] = side;
return h.div({ class: "grid"}, [ return h.div({ class: "grid"}, [
h.div({ _: "" }), h.div({ _: "" }),
h.div({ _: X, class: "top", id: root ? [ root, l, X ] : und }), h.div({ _: X, class: "top noselect", id: root ? [ root, l, X ] : und }),
h.div({ _: Y, class: "top", id: root ? [ root, l, Y ] : und }), h.div({ _: Y, class: "top noselect", id: root ? [ root, l, Y ] : und }),
h.div({ _: Z, class: "top", id: root ? [ root, l, Z ] : und }), h.div({ _: Z, class: "top noselect", id: root ? [ root, l, Z ] : und }),
h.div({ _: side[0], class: "side" }), h.div({ _: side[0], class: "side noselect" }),
h.label({ _: v1[0], id: root ? [ root, v, X, pos ] : und }), h.label({ _: v1[0], id: root ? [ root, v, X, pos ] : und }),
h.label({ _: v1[1], id: root ? [ root, v, Y, pos ] : und }), h.label({ _: v1[1], id: root ? [ root, v, Y, pos ] : und }),
h.label({ _: v1[2], id: root ? [ root, v, Z, pos ] : und }), h.label({ _: v1[2], id: root ? [ root, v, Z, pos ] : und }),
h.div({ _: side[1], class: "side" }), h.div({ _: side[1], class: "side noselect" }),
h.label({ _: v2[0], id: root ? [ root, v, X, rot ] : und }), h.label({ _: v2[0], id: root ? [ root, v, X, rot ] : und }),
h.label({ _: v2[1], id: root ? [ root, v, Y, rot ] : und }), h.label({ _: v2[1], id: root ? [ root, v, Y, rot ] : und }),
h.label({ _: v2[2], id: root ? [ root, v, Z, rot ] : und }), h.label({ _: v2[2], id: root ? [ root, v, Z, rot ] : und }),
@ -350,7 +351,7 @@ function ui_build() {
// return function bound to field editing clicks // return function bound to field editing clicks
// builds a modal dialog that updates the object and field // builds a modal dialog that updates the object and field
function field_edit(title, get, set) { function field_edit(title, set) {
return function(ev) { return function(ev) {
let onclick = (ev) => { let onclick = (ev) => {
let tempval = parseFloat($('tempval').value); let tempval = parseFloat($('tempval').value);
@ -359,8 +360,7 @@ function ui_build() {
return; return;
} }
for (let g of api.selection.groups()) { for (let g of api.selection.groups()) {
let old = get(g); set(g, tempval);
set(g, tempval, old);
g.floor(mesh.group); g.floor(mesh.group);
defer_selection(); // update ui defer_selection(); // update ui
} }
@ -410,7 +410,7 @@ function ui_build() {
let g_id = s_grp.map(g => g.id).join(' '); let g_id = s_grp.map(g => g.id).join(' ');
let h_grp = sblock('group', g_id, grid( let h_grp = sblock('group', g_id, grid(
util.extract(g_pos, map), util.extract(g_pos, map),
util.extract(g_rot, map), util.extract(g_rot).map(r => (r * rad).round(2).toFixed(map.fixed)),
und, und, 'group' und, und, 'group'
)); ));
@ -461,44 +461,68 @@ function ui_build() {
[ group_val_X_rot, group_val_Y_rot, group_val_Z_rot ].forEach(e => { [ group_val_X_rot, group_val_Y_rot, group_val_Z_rot ].forEach(e => {
e.classList.add('editable'); e.classList.add('editable');
}); });
group_val_X_rot.onclick = field_edit('x rotation', group => { group_val_X_rot.onclick = field_edit('x rotation', (group, val) => {
return group.rotation(); let r = group.rotation();
}, (group, val, old) => { group.rotation(val/rad, r.y, r.z);
group.rotation(val, old.y, old.z);
}); });
group_val_Y_rot.onclick = field_edit('y rotation', group => { group_val_Y_rot.onclick = field_edit('y rotation', (group, val) => {
return group.rotation(); let r = group.rotation();
}, (group, val, old) => { group.rotation(r.x, val/rad, r.z);
group.rotation(old.x, val, old.z);
}); });
group_val_Z_rot.onclick = field_edit('z rotation', group => { group_val_Z_rot.onclick = field_edit('z rotation', (group, val) => {
return group.rotation(); let r = group.rotation();
}, (group, val, old) => { group.rotation(r.x, r.y, val/rad);
group.rotation(old.x, old.y, val);
}); });
// bind scale editable fields // bind scale editable fields
let pedit = prefs.map.edit;
let { span_val_X_size, span_val_Y_size, span_val_Z_size } = bound; let { span_val_X_size, span_val_Y_size, span_val_Z_size } = bound;
[ span_val_X_size, span_val_Y_size, span_val_Z_size ].forEach(e => { [ span_val_X_size, span_val_Y_size, span_val_Z_size ].forEach(e => {
e.classList.add('editable'); e.classList.add('editable');
}); });
span_val_X_size.onclick = field_edit('x size', group => { let axgrp = {};
return group.scale(); // activate axis grouping toggles
}, (group, val, old) => { [ "X", "Y", "Z" ].forEach(axis => {
val = val / group.bounds.dim.x; let prefkey = `scale_group_${axis}`;
group.scale(val, old.y, old.z); let agv = axgrp[axis] = pedit[prefkey];
let lbl = bound[`span_lbl_${axis}`];
lbl.classList.add('editable');
if (agv) {
lbl.classList.add('grouped');
} else {
bound[`span_lbl_${axis}`].classList.add('ungrouped');
}
lbl.onclick = () => {
pedit[prefkey] = !agv;
defer_selection(); // update ui
};
}); });
span_val_Y_size.onclick = field_edit('y size', group => { span_val_X_size.onclick = field_edit('x size', (group, val) => {
return group.scale(); let dim = group.bounds.dim;
}, (group, val, old) => { let rel = val / dim.x;
val = val / group.bounds.dim.y; if (axgrp.X) {
group.scale(old.x, val, old.z); group.scale(rel, axgrp.Y ? rel : 1, axgrp.Z ? rel : 1);
} else {
group.scale(rel, 1, 1);
}
}); });
span_val_Z_size.onclick = field_edit('z size', group => { span_val_Y_size.onclick = field_edit('y size', (group, val) => {
return group.scale(); let dim = group.bounds.dim;
}, (group, val, old) => { let rel = val / dim.y;
val = val / group.bounds.dim.z; if (axgrp.Y) {
group.scale(old.x, old.y, val); group.scale(axgrp.X ? rel : 1, rel, axgrp.Z ? rel : 1);
} else {
group.scale(1, rel, 1);
}
});
span_val_Z_size.onclick = field_edit('z size', (group, val) => {
let dim = group.bounds.dim;
let rel = val / dim.z;
if (axgrp.Z) {
group.scale(axgrp.X ? rel : 1, axgrp.Y ? rel : 1, rel);
} else {
group.scale(1, 1, rel);
}
}); });
let pmap = prefs.map.info; let pmap = prefs.map.info;

View file

@ -138,6 +138,7 @@ let util = mesh.util = {
} }
}, },
// extract object fields into an array with optional rounding
extract(object, opt = {}) { extract(object, opt = {}) {
let field = opt.fields || ['x', 'y', 'z']; let field = opt.fields || ['x', 'y', 'z'];
let array = []; let array = [];
@ -150,6 +151,7 @@ let util = mesh.util = {
return array; return array;
}, },
// for an array of maps, return the average of all named fields in a new map
average(array, opt = {}) { average(array, opt = {}) {
let fields = opt.fields || ['x', 'y', 'z']; let fields = opt.fields || ['x', 'y', 'z'];
let avg = {}; let avg = {};

View file

@ -505,6 +505,14 @@ button.selected:hover {
grid: min-content 1fr / min-content 1fr; grid: min-content 1fr / min-content 1fr;
} }
.grid .ungrouped {
background-color: rgba(128,128,128,0.5) !important;
}
.grid .ungrouped:hover {
color: #fff !important;
}
.tempedit { .tempedit {
gap: 3px; gap: 3px;
} }
@ -514,9 +522,11 @@ button.selected:hover {
} }
.editable { .editable {
background-color: rgba(0,128,255,0.2) !important; color: #05a;
cursor: default;
} }
.editable:hover { .editable:hover {
color: #000;
background-color: rgba(0,128,255,0.5) !important; background-color: rgba(0,128,255,0.5) !important;
} }