refactor menu to use helpers, add hot key display

This commit is contained in:
Stewart Allen 2024-07-29 17:37:27 -04:00
commit 0346393f7c
4 changed files with 180 additions and 174 deletions

View file

@ -297,7 +297,7 @@ const pattern = {
* @param {int} count number of entities
* @param {float[]} center [x,y,z]
*/
circle(count, center) {
circle() {
async function doit(count = 3, center = [0,0], zdelta = 0) {
api.modal.hide();
let models = selection.models();
@ -319,7 +319,6 @@ const pattern = {
await api.tool.regroup(modnu);
}
}
if (!count) {
api.modal.dialog({
title: "circle pattern",
body: [ h.div({ class: "addgear" }, [
@ -340,12 +339,9 @@ const pattern = {
]) ]
});
api.modal.bound._count.focus();
} else {
doit(count, origin);
}
},
grid(x, y, xs, ys) {
grid() {
async function doit(x = 3, y = 3, xs = 10, ys = 10) {
api.modal.hide();
let models = selection.models();
@ -364,7 +360,6 @@ const pattern = {
await api.tool.regroup(modnu);
}
}
if (!(x && y)) {
api.modal.dialog({
title: "grid pattern",
body: [ h.div({ class: "addgear" }, [
@ -387,9 +382,6 @@ const pattern = {
]) ]
});
api.modal.bound._x.focus();
} else {
doit(x, y, xs, ys);
}
}
};
@ -706,7 +698,7 @@ let add = {
api.modal.dialog({
title: "vertices",
body: [ h.div({ class: "addact" }, [
h.textarea({ value: '', size: 5, id: "genvrt" }),
h.textarea({ value: '', cols:30, rows:4, id: "genvrt" }),
h.button({ _: "create", onclick() {
const vert = (api.modal.bound.genvrt.value)
.split(',').map(v => parseFloat(v)).toFloat32();
@ -725,10 +717,11 @@ let add = {
const nmdl = new mesh.model({ file: "box", mesh: vert });
const ngrp = group.new([ nmdl ]);
ngrp.scale(10, 10, 10).floor();
selection.set([ nmdl ]);
return ngrp;
},
cylinder(opt = {}) {
cylinder() {
function gencyl(opt = {}) {
let { diameter, height, facets, chamfer, bore } = opt;
api.modal.hide();
@ -743,26 +736,24 @@ let add = {
const vert = cyl.extrude(height, { chamfer }).toFloat32();
const nmdl = new mesh.model({ file: "cylinder", mesh: vert });
const ngrp = group.new([ nmdl ]);
selection.set([ nmdl ]);
ngrp.floor();
}
}
if (opt.facets && opt.diameter && opt.height) {
return gencyl(opt.diameter, opt.height, opt.facets);
} else {
api.modal.dialog({
title: "cylinder",
body: [ h.div({ class: "addgear" }, [
h.label('diameter'),
h.input({ value: opt.diameter || 20, size: 5, id: "_diameter" }),
h.input({ value: 20, size: 5, id: "_diameter" }),
h.label('height'),
h.input({ value: opt.height || 10, size: 5, id: "_height" }),
h.input({ value: 10, size: 5, id: "_height" }),
h.hr(),
h.label('bore'),
h.input({ value: opt.facets || 0, size: 5, id: "_bore" }),
h.input({ value: 0, size: 5, id: "_bore" }),
h.label('facets'),
h.input({ value: opt.facets || 0, size: 5, id: "_facets" }),
h.input({ value: 0, size: 5, id: "_facets" }),
h.label('chamfer'),
h.input({ value: opt.chamfer || 0, size: 5, id: "_chamfer" }),
h.input({ value: 0, size: 5, id: "_chamfer" }),
h.button({ _: "create", onclick() {
const { _diameter, _facets, _height, _chamfer, _bore } = api.modal.bound;
gencyl({
@ -776,10 +767,9 @@ let add = {
]) ]
});
api.modal.bound._diameter.focus();
}
},
gear(opt = {}) {
gear() {
function gengear(bound) {
const { _teeth, _module, _angle, _twist, _shaft, _offset, _height, _chamfer } = bound;
api.modal.hide();
@ -796,6 +786,7 @@ let add = {
}).then(gear => {
const nmdl = new mesh.model({ file: "gear", mesh: gear.toFloat32() });
const ngrp = group.new([ nmdl ]);
selection.set([ nmdl ]);
ngrp.floor();
Object.assign(last, params);
api.prefs.save();
@ -806,25 +797,25 @@ let add = {
title: "gear generator",
body: [ h.div({ class: "addgear" }, [
h.label('number of teeth'),
h.input({ value: opt.teeth || last.teeth || 20, size: 5, id: "_teeth" }),
h.input({ value: last.teeth || 20, size: 5, id: "_teeth" }),
h.label('shaft diameter'),
h.input({ value: opt.shaft || last.shaft || 5, size: 5, id: "_shaft" }),
h.input({ value: last.shaft || 5, size: 5, id: "_shaft" }),
h.label('z height'),
h.input({ value: opt.height || last.height || 15, size: 5, id: "_height" }),
h.input({ value: last.height || 15, size: 5, id: "_height" }),
h.label({ _:'chamfer', title:"negative values apply chamfer to the bottom only" }),
h.input({ value: opt.chamfer || last.chamfer || 0, size: 5, id: "_chamfer" }),
h.input({ value: last.chamfer || 0, size: 5, id: "_chamfer" }),
h.hr(),
h.code("all other settings must"),
h.code("match for gears to mesh"),
h.hr(),
h.label('module (diam / teeth)'),
h.input({ value: opt.module || last.module || 3, size: 5, id: "_module" }),
h.input({ value: last.module || 3, size: 5, id: "_module" }),
h.label('pressure angle'),
h.input({ value: opt.angle || last.angle || 20, size: 5, id: "_angle" }),
h.input({ value: last.angle || 20, size: 5, id: "_angle" }),
h.label('twist angle (helix)'),
h.input({ value: opt.twist || last.twist || 0, size: 5, id: "_twist" }),
h.input({ value: last.twist || 0, size: 5, id: "_twist" }),
h.label('tooth offset (play)'),
h.input({ value: opt.offset || last.offset || 0.1, size: 5, id: "_offset" }),
h.input({ value: last.offset || 0.1, size: 5, id: "_offset" }),
h.button({ _: "create", onclick() { gengear(api.modal.bound) } })
]) ]
});

View file

@ -8,8 +8,9 @@
gapp.register("mesh.build", [], (root, exports) => {
const { broker } = gapp;
const { mesh } = root;
const { mesh, moto } = root;
const { api, util } = mesh;
const { space } = moto;
const devel = api.isDebug;
let call = broker.send;
@ -339,6 +340,20 @@ api.settings = function() {
] ));
}
function menu_item(text, fn, short) {
if (short) {
short = Array.isArray(short) ? short : [ short ];
short = h.div({ class: "short" }, short.map(s => s.startsWith('bi-') ?
h.i({ class: ["bi", s ]}) :
h.div(s)
));
}
return h.div({ onclick: fn }, [
h.div({ _: text, class: "grow" }),
short
].filter(o => o));
}
// create html elements
function ui_build() {
let { file, selection, mode, tool, sketch, prefs, add } = api;
@ -351,52 +366,54 @@ function ui_build() {
h.div({ class: "menu" }, [
h.div('File'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Import', onclick: file.import }, [
h.input({
id: "import", type: "file", class: ["hide"], multiple: true,
onchange(evt) { broker.send.load_files(evt.target.files) }
})
]),
h.div({ _: 'Export', onclick: file.export }),
}),
menu_item('Import', file.import, 'I'),
menu_item('Export', file.export, 'X'),
h.hr(),
h.div({ _: "Slicer", onclick: api.kirimoto }),
menu_item('Slicer', api.kirimoto),
h.hr(),
h.div({ _: 'Close', onclick: window.close }),
menu_item('Close', window.close),
])
]),
h.div({ class: "menu sketch-on" }, [
h.div('Edit'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Add Circle', onclick() { sketch.selected.one?.add.circle() } }),
h.div({ _: 'Add Rectangle', onclick() { sketch.selected.one?.add.rectangle() } }),
menu_item('Add Circle', () => { sketch.selected.one?.add.circle() }),
menu_item('Add Rectangle', () => { sketch.selected.one?.add.rectangle() }),
h.hr(),
h.div({ _: 'Add Sketch', onclick: add.sketch }),
menu_item('Add Sketch', add.sketch),
h.hr(),
h.div({ _: 'Delete', onclick: api.selection.delete }),
menu_item('Delete', api.selection.delete, 'bi-backspace'),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Edit'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Add Cylinder', onclick() { add.cylinder() } }),
h.div({ _: 'Add Cube', onclick() { add.cube() } }),
h.div({ _: 'Add Gear', onclick() { add.gear() } }),
menu_item('Add Cylinder', add.cylinder),
menu_item('Add Cube', add.cube),
menu_item('Add Gear', add.gear),
h.hr(),
h.div({ _: 'Add Sketch', onclick: add.sketch }),
devel ? h.div({ _: 'Add Vertices', onclick: add.input }) : undefined,
menu_item('Add Sketch', add.sketch),
devel ? menu_item('Add Vertices', add.input) : undefined,
h.hr(),
h.div({ _: 'Delete', onclick: api.selection.delete }),
menu_item('Delete', api.selection.delete, 'bi-backspace'),
])
]),
h.div({ class: "menu" }, [
h.div('View'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Bounds', onclick() { selection.boundsBox({ toggle: true }) } }),
h.div({ _: 'Normals', onclick() { api.normals() } }),
h.div({ _: 'Wireframes', onclick() { api.wireframe() } }),
menu_item('Bounds', () => { selection.boundsBox({ toggle: true }) }, 'B'),
menu_item('Normals', () => { api.normals() }, 'N'),
menu_item('Wireframes', () => { api.wireframe() }, 'W'),
h.hr(),
h.div({ _: 'Gridlines', onclick() { api.grid() } }),
h.div({ _: 'Messages', onclick() { api.log.toggle({ spinner: false }) } }),
menu_item('Gridlines', () => { api.grid() }, 'G'),
menu_item('Messages', () => { api.log.toggle({ spinner: false }) }, 'L'),
h.hr(),
menu_item('Home', space.view.home, 'H'),
menu_item('Top', space.view.top, 'T'),
])
]),
h.div({ class: "menu" }, [
@ -415,75 +432,75 @@ function ui_build() {
h.div({ class: "menu sketch-on" }, [
h.div('Object'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Group', onclick: sketch.arrange.group }),
h.div({ _: 'Ungroup', onclick: sketch.arrange.ungroup }),
menu_item('Group', sketch.arrange.group),
menu_item('Ungroup', sketch.arrange.ungroup),
h.hr(),
h.div({ _: 'Center', onclick: sketch.arrange.center }),
menu_item('Center', sketch.arrange.center),
h.hr(),
h.div({ _: 'Flip Horizontal', onclick: sketch.arrange.fliph }),
h.div({ _: 'Flip Vertical', onclick: sketch.arrange.flipv }),
menu_item('Flip Horizontal', sketch.arrange.fliph),
menu_item('Flip Vertical', sketch.arrange.flipv),
h.hr(),
h.div({ _: 'Raise', onclick: sketch.arrange.up }),
h.div({ _: 'Lower', onclick: sketch.arrange.down }),
h.div({ _: 'Raise to Top', onclick: sketch.arrange.top }),
h.div({ _: 'Lower to Bottom', onclick: sketch.arrange.bottom }),
menu_item('Raise', sketch.arrange.up),
menu_item('Lower', sketch.arrange.down),
menu_item('To Top', sketch.arrange.top),
menu_item('To Bottom', sketch.arrange.bottom),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Object'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Regroup', onclick: tool.regroup }),
h.div({ _: 'Isolate', onclick: tool.isolate }),
menu_item('Regroup', tool.regroup),
menu_item('Isolate', tool.isolate),
h.hr(),
h.div({ _: 'Duplicate', onclick: tool.duplicate }),
h.div({ _: 'Mirror', onclick: tool.mirror }),
h.div({ _: 'Merge', onclick: tool.merge }),
h.div({ _: 'Split', onclick: call.edit_split }),
menu_item('Duplicate', tool.duplicate, ['bi-shift','D']),
menu_item('Mirror', tool.mirror, 'M'),
menu_item('Merge', tool.merge),
menu_item('Split', mesh.split.start, 'S'),
h.hr(),
h.div({ _: 'Union', onclick: tool.union }),
h.div({ _: 'Subtract', onclick: tool.subtract }),
h.div({ _: 'Intersect', onclick: tool.intersect }),
h.div({ _: 'Difference', onclick: tool.difference }),
menu_item('Union', tool.union),
menu_item('Subtract', tool.subtract),
menu_item('Intersect', tool.intersect),
menu_item('Difference', tool.difference),
h.hr(),
h.div({ _: 'Copy Circle', onclick: api.pattern.circle }),
h.div({ _: 'Copy Grid', onclick: api.pattern.grid }),
menu_item('Copy Circle', api.pattern.circle),
menu_item('Copy Grid', api.pattern.grid),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Faces'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Flip Normals', onclick: tool.invert }),
h.div({ _: 'Analyze', onclick: tool.analyze }),
menu_item('Flip Normals', tool.invert),
menu_item('Analyze', tool.analyze),
// h.div({ _: 'Patch', onclick: tool.repair }),
// h.div({ _: 'rebuild', onclick: tool.rebuild }),
h.div({ _: 'Clean', onclick: tool.clean }),
menu_item('Clean', tool.clean),
])
]),
h.div({ class: "menu sketch-on" }, [
h.div('Shape'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Duplicate', onclick: api.tool.duplicate }),
h.div({ _: 'Extrude', onclick: sketch.extrude }),
menu_item('Duplicate', tool.duplicate, ['bi-shift','D']),
menu_item('Extrude', sketch.extrude, ['bi-shift','E']),
h.hr(),
h.div({ _: 'Union', onclick: sketch.boolean.union }),
h.div({ _: 'Intersect', onclick: sketch.boolean.intersect }),
h.div({ _: 'Difference', onclick: sketch.boolean.difference }),
menu_item('Union', sketch.boolean.union),
menu_item('Intersect', sketch.boolean.intersect),
menu_item('Difference', sketch.boolean.difference),
h.hr(),
h.div({ _: 'Nest', onclick: sketch.boolean.nest }),
h.div({ _: 'Flatten', onclick: sketch.boolean.flatten }),
h.div({ _: 'Even Odd', onclick: sketch.boolean.evenodd }),
menu_item('Nest', sketch.boolean.nest),
menu_item('Flatten', sketch.boolean.flatten),
menu_item('Even Odd', sketch.boolean.evenodd),
])
]),
h.div({ class: "menu" }, [
h.div('Help'),
h.div({ class: "menu-items" }, [
h.div({ _: 'About', onclick() { api.welcome(mesh.version) } }),
menu_item('About', () => { api.welcome(mesh.version) }),
h.hr(),
h.div({ _: 'Documentation', onclick: api.help }),
h.div({ _: 'Official Site', onclick: api.gridspace }),
h.div({ _: "Donate", onclick: api.donate }),
menu_item('Documentation', api.help),
menu_item('Official Site', api.gridspace),
menu_item('Donate', api.donate),
h.hr(),
h.div({ _: 'Versions', onclick: api.version }),
menu_item('Versions', api.version),
])
]),
]);

View file

@ -28,9 +28,6 @@ let split = {
return;
}
let { api, util } = mesh;
// highlight button
let button = event.target;
button.classList.add('selected');
// create split plane visual
let geo, mat, obj = new Mesh(
geo = new PlaneGeometry(1,1),
@ -45,7 +42,7 @@ let split = {
// hide until first hover
obj.visible = false;
// enable temp mode
let state = split.state = { button, obj };
let state = split.state = { obj };
let models = state.models = api.selection.models();
let meshes = models.map(m => m.mesh);
// for split and lay flat modes
@ -92,8 +89,7 @@ let split = {
return;
}
let space = moto.space;
let { button, obj } = split.state;
button.classList.remove('selected');
let { obj } = split.state;
space.scene.remove(obj);
space.mouse.onHover(undefined);
isActive = split.state = undefined;

View file

@ -173,11 +173,11 @@ button:hover {
padding: 0 !important;
}
.menu {
.menu, #top-right > div {
cursor: default;
align-self: stretch;
align-items: center;
padding: 5px 12px 5px 12px;
padding: 6px 12px 6px 12px;
border-radius: 6px;
white-space: nowrap;
user-select: none;
@ -214,10 +214,19 @@ button:hover {
}
.menu-items > div {
gap: 15px;
border-radius: 6px;
padding: 4px 8px 4px 8px;
}
.menu-items .grow {
flex-grow: 1;
}
.menu-items .short {
gap: 1px;
}
#top-mid {
flex-grow: 1;
}
@ -230,13 +239,6 @@ button:hover {
#top-right > div {
gap: 8px;
align-self: stretch;
align-items: center;
padding: 5px 12px 5px 12px;
border-radius: 6px;
cursor: default;
user-select: none;
white-space: nowrap;
}
/** specific modal dialogs */
@ -357,11 +359,11 @@ button:hover {
/** welcome dialog **/
.welcome {
font-size: smaller;
flex-direction: column;
align-items: center;
padding: 5px;
gap: 5px;
font-size: smaller;
}
.welcome a {
@ -469,9 +471,9 @@ button:hover {
background-color: rgba(0,255,0,0.8);
}
.dark .head {
/* .dark .head {
color: #000 !important;
}
} */
/** slide in/out logging window **/