diff --git a/bin/build-upload-all b/bin/build-upload-all index 3ca5ce0f..abb928f1 100755 --- a/bin/build-upload-all +++ b/bin/build-upload-all @@ -1,5 +1,5 @@ #!/bin/bash tag=${1:-latest} -./bin/build-upload mac ${tag} && \ ./bin/build-upload win ${tag} && \ -./bin/build-upload linux ${tag} +./bin/build-upload linux ${tag} && \ +./bin/build-upload mac ${tag} diff --git a/src/mesh/api.js b/src/mesh/api.js index 317edd87..9c855c8e 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -8,6 +8,9 @@ // dep: moto.space // dep: mesh.util // dep: mesh.edges +// dep: mesh.group +// dep: mesh.model +// dep: mesh.sketch // dep: add.three // use: add.array gapp.register("mesh.api", [], (root, exports) => { @@ -20,6 +23,7 @@ const { newPolygon } = base; const worker = moto.client.fn const groups = []; +const sketches = []; let selected = []; let tools = []; @@ -30,7 +34,7 @@ const selection = { }, // @returns {MeshObject[]} or all groups if not strict and no selection - list(strict = false) { + list(strict) { return selected.length || strict ? selected.slice() : groups.slice(); }, @@ -156,6 +160,9 @@ const selection = { opacity: prefs.map.wireframe.opacity} ); } + for (let sketch of sketches) { + sketch.select(false); + } api.updateFog(); // prevent selection of model and its group let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group); @@ -377,7 +384,30 @@ let model = { } }; +let sketch = { + add(sk) { + sketches.addOnce(sk); + space.world.add(sk.object); + api.selection.update(); + return sk; + }, + + remove(sk) { + sketches.remove(sk); + space.world.remove(sk.object); + space.update();s + }, + + list() { + return sketches; + } +}; + let add = { + sketch() { + sketch.add(new mesh.sketch()); + }, + input() { api.modal.dialog({ title: "vertices", @@ -695,7 +725,7 @@ const tool = { } }, - rename(models) { + rename(models, type = 'model') { models = fallback(models, true); let model = models[0]; if (!model) { @@ -712,7 +742,7 @@ const tool = { selection.update(); api.modal.hide(); }; - let { tempedit } = api.modal.show(`rename model`, h.div({ class: "rename"}, [ + let { tempedit } = api.modal.show(`rename ${type}`, h.div({ class: "rename"}, [ h.input({ id: "tempedit", value: model.file, onkeydown }), h.button({ _: 'ok', onclick }) ])); @@ -883,28 +913,28 @@ const mode = { }, object() { - if (mode.is([ modes.face, modes.surface ])) { + if (!mode.is([ modes.object, modes.tool ])) { selection.clear(); } mode.set(modes.object); }, tool() { - if (mode.is([ modes.face, modes.surface ])) { + if (!mode.is([ modes.object, modes.tool ])) { selection.clear(); } mode.set(modes.tool); }, face() { - if (mode.is([ modes.object, modes.tool ])) { + if (!mode.is([ modes.face, modes.surface ])) { selection.clear(); } mode.set(modes.face); }, surface() { - if (mode.is([ modes.object, modes.tool ])) { + if (!mode.is([ modes.face, modes.surface ])) { selection.clear(); } mode.set(modes.surface); @@ -1089,6 +1119,8 @@ const api = exports({ model, + sketch, + add, file, diff --git a/src/mesh/build.js b/src/mesh/build.js index 3bc23795..9f3a38fd 100644 --- a/src/mesh/build.js +++ b/src/mesh/build.js @@ -387,6 +387,8 @@ function ui_build() { h.button({ _: 'cylinder', onclick() { add.cylinder() } }), h.button({ _: 'cube', onclick() { add.cube() } }), h.button({ _: 'gear', onclick() { add.gear() } }), + h.hr(), + h.button({ _: 'sketch', onclick() { add.sketch() } }), devel ? h.button({ _: 'input', onclick: add.input }) : undefined ]) ]), @@ -488,7 +490,10 @@ function ui_build() { .map(g => h.div([ h.button({ _: g.name || `group`, title: g.id, class: [ "group", selHas(g) ? 'selected' : undefined ], - onclick(e) { selection.toggle(g) } + onclick(e) { + api.mode.object(); + selection.toggle(g); + } }), h.div({ class: "models"}, // map models to buttons @@ -499,18 +504,17 @@ function ui_build() { if (e.shiftKey) { tool.rename([ m ]); } else { + if (!api.mode.is([ api.modes.object, api.modes.tool ])) { + api.mode.object(); + } selection.toggle(m); } }, onmouseover(e) { - if (!m.wireframe().enabled) { - m.opacity({temp: 0.5}); - } + m.highlight(); }, onmouseleave(e) { - if (!m.wireframe().enabled) { - m.opacity({restore: true}); - } + m.unhighlight(); } }), h.button({ @@ -523,6 +527,42 @@ function ui_build() { ]) ) ])); + let sketches = api.sketch.list(); + if (sketches.length) { + let sg = h.div([ + h.button({ _: 'sketch', + class: [ "group" ], + }), + sketches.map(sketch => h.div({ class: "models"}, [ + h.button({ _: sketch.file || sketch.id, + class: [ selHas(sketch) ? 'selected' : undefined ], + onclick(e) { + if (e.shiftKey) { + tool.rename([ sketch ], 'sketch'); + } else { + selection.toggle(sketch); + } + }, + onmouseover(e) { + sketch.highlight(); + }, + onmouseleave(e) { + sketch.unhighlight(); + } + }), + h.button({ + class: [ 'square' ], + onclick(e) { + sketch.visible({ toggle: true }); + update_selector(); + } + }, [ + h.raw(sketch.visible() ? eye_open : eye_closed) + ]) + ])) + ]); + groups.push(sg); + } h.bind(grouplist, groups); } @@ -564,8 +604,8 @@ function ui_build() { // update model information dashboard (bottom) function update_selection() { let map = { fixed: 2 }; - let s_grp = selection.groups(); - let s_mdl = selection.models(); + let s_grp = selection.groups(true); + let s_mdl = selection.models(true); if (s_mdl.length === 0) { return h.bind(selectlist, []); } diff --git a/src/mesh/edges.js b/src/mesh/edges.js index 31328fc3..88406480 100644 --- a/src/mesh/edges.js +++ b/src/mesh/edges.js @@ -89,6 +89,7 @@ let edges = { }, async add() { + console.log({ add: selected.slice() }); if (selected.length === 2 && selected[0].object === selected[1].object) { let verts = selected.map(s => [ s.side.p0, s.side.p1 ]).flat(); // clean up floating point cruft for comparison @@ -113,7 +114,7 @@ let edges = { point.z !== arr[index - 1].z; }); let model = selected[0].object.model; - if (verts.length === 3) { + if (sort.length === 3) { await model.duplicate({ append: [ sort[0].x, -sort[0].z, sort[0].y, sort[1].x, -sort[1].z, sort[1].y, @@ -125,8 +126,8 @@ let edges = { verts[0].x, -verts[0].z, verts[0].y, verts[1].x, -verts[1].z, verts[1].y, verts[2].x, -verts[2].z, verts[2].y, - verts[1].x, -verts[1].z, verts[1].y, verts[2].x, -verts[2].z, verts[2].y, + verts[1].x, -verts[1].z, verts[1].y, verts[3].x, -verts[3].z, verts[3].y, ]}); model.remove(); @@ -144,6 +145,7 @@ let edges = { select() { space.scene.remove(edges.selected); + if (!hovered) return; const { object, face, side } = hovered; let add = true; diff --git a/src/mesh/model.js b/src/mesh/model.js index 6fdb0f22..aafa7332 100644 --- a/src/mesh/model.js +++ b/src/mesh/model.js @@ -281,17 +281,6 @@ mesh.model = class MeshModel extends mesh.object { return this.vertices / 3; } - // get, set, or toggle visibility of model and wireframe - visible(bool) { - if (bool === undefined) { - return this.mesh.visible; - } - if (bool.toggle) { - return this.visible(!this.mesh.visible); - } - this.mesh.visible = bool; - } - // get, set, or toggle selection of model (coloring) select(bool, stool) { const cmat = this.mesh.material[0]; @@ -344,6 +333,18 @@ mesh.model = class MeshModel extends mesh.object { space.update(); } + highlight() { + if (!this.wireframe().enabled) { + this.opacity({temp: 0.5}); + } + } + + unhighlight() { + if (!this.wireframe().enabled) { + this.opacity({restore: true}); + } + } + wireframe(bool, opt = {}) { let was = this._wire ? true : false; if (bool === undefined) { diff --git a/src/mesh/object.js b/src/mesh/object.js index d2c79dcf..7c57b576 100644 --- a/src/mesh/object.js +++ b/src/mesh/object.js @@ -64,7 +64,11 @@ mesh.object = class MeshObject { } select() { - mesh.api.selection.set([this]); + throw "select() requires implementation"; + } + + remove() { + throw "remove() requires implementation"; } focus() {