diff --git a/src/mesh/api.js b/src/mesh/api.js index 0117e6fe..50b4c21a 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -443,11 +443,12 @@ let sketch = { }, extrude(opt = {}) { - if (!selection.sketch()) { + let sketch = selection.sketch(); + if (!sketch) { return log('select a sketch to extrude'); } api.modal.dialog({ - title: "gear generator", + title: "extrude", body: [ h.div({ class: "addgear" }, [ h.label('height'), h.input({ value: opt.height || 10, size: 5, id: "_height" }), @@ -460,7 +461,8 @@ let sketch = { h.input({ value: opt.chamfer_bottom || 0, size: 5, id: "_chambot" }), h.button({ _: "create", onclick() { const { _height, _chamtop, _chambot } = api.modal.bound; - selection.sketch()?.extrude({ + sketch.extrude({ + selection: sketch.selection.count() > 0, ...opt, height: parseFloat(_height.value), chamfer_top: parseFloat(_chamtop.value), @@ -473,6 +475,27 @@ let sketch = { api.modal.bound._height.focus(); }, + arrange: { + group() { + let one = sketch.selection.one; + let items = one?.items.filter(i => i.selected); + if (items.length > 1) { + let group = Date.now().toString(36); + items.forEach(i => i.group = group); + log('grouped', items.length, 'sketch items'); + } + }, + + ungroup() { + let one = sketch.selection.one; + let items = one?.items.filter(i => i.selected) + if (items.length > 1) { + items.forEach(i => delete i.group); + log('ungrouped', items.length, 'sketch items'); + } + } + }, + selection: { get one() { return selection.sketch(); @@ -502,6 +525,10 @@ let sketch = { sketch.selection.one?.boolean.flatten(); }, + evenodd() { + sketch.selection.one?.boolean.evenodd(); + }, + union() { sketch.selection.one?.boolean.union(); }, diff --git a/src/mesh/build.js b/src/mesh/build.js index b68a4876..1f13eae8 100644 --- a/src/mesh/build.js +++ b/src/mesh/build.js @@ -425,6 +425,14 @@ function ui_build() { h.button({ _: 'difference', onclick: tool.difference }), ]) ]), + h.div({ class: "sketch-on" }, [ + h.div({ _: "arrange", class: "head" }), + h.div({ class: "back"}), + h.div({ class: "pop"}, [ + h.button({ _: 'group', onclick: sketch.arrange.group }), + h.button({ _: 'ungroup', onclick: sketch.arrange.ungroup }), + ]) + ]), h.div({ class: "sketch-on" }, [ h.div({ _: "boolean", class: "head" }), h.div({ class: "back"}), @@ -433,6 +441,7 @@ function ui_build() { h.button({ _: 'intersect', onclick: sketch.boolean.intersect }), h.button({ _: 'difference', onclick: sketch.boolean.difference }), h.hr(), + h.button({ _: 'even/odd', onclick: sketch.boolean.evenodd }), h.button({ _: 'nest', onclick: sketch.boolean.nest }), h.button({ _: 'flatten', onclick: sketch.boolean.flatten }), ]) diff --git a/src/mesh/sketch.js b/src/mesh/sketch.js index 01fb654b..7c9684eb 100644 --- a/src/mesh/sketch.js +++ b/src/mesh/sketch.js @@ -141,6 +141,14 @@ mesh.sketch = class MeshSketch extends mesh.object { return sketch.group.children.filter(c => c.sketch_item && c.sketch_item.selected); }, + items() { + return sketch.selection.mesh_items().map(i => c.sketch_item.item); + }, + + count() { + return sketch.selection.mesh_items().length; + }, + all() { sketch.items.forEach(i => i.selected = true); sketch.render(); @@ -229,6 +237,18 @@ mesh.sketch = class MeshSketch extends mesh.object { } }, + evenodd() { + let items = sketch.selection.mesh_items(); + // if (items.length < 2) return log('operation requires at least 2 items'); + let polys = items.map(i => i.sketch_item.poly); + let even = POLYS.nest(POLYS.flatten(polys.clone(true), [], true)); + let odd = POLYS.nest(POLYS.flatten(even.clone(true), [], true).filter(p => p.depth > 0)); + sketch.selection.delete(); + for (let poly of [...even, ...odd]) { + sketch.add_polygon({ poly }); + } + }, + flatten() { let items = sketch.selection.mesh_items(); let polys = items.map(i => i.sketch_item.poly).clone(true); @@ -346,6 +366,7 @@ mesh.sketch = class MeshSketch extends mesh.object { log(this.file || this.id, '| add circle'); this.items.push({ type: "circle", + selected: true, ...Object.assign({}, { center: {x:0, y:0, z:0}, radius:5 }, opt) }); this.render(); @@ -355,6 +376,7 @@ mesh.sketch = class MeshSketch extends mesh.object { log(this.file || this.id, '| add rectangle'); this.items.push({ type: "rectangle", + selected: true, ...Object.assign({}, { center: {x:0, y:0, z:0}, width:15, height:10 }, opt) }); this.render(); @@ -366,6 +388,7 @@ mesh.sketch = class MeshSketch extends mesh.object { delete opt.poly; this.items.push({ type: "polygon", + selected: true, ...Object.assign({}, { center: {x:0, y:0, z:0} }, opt), ...poly.toObject() }); @@ -386,6 +409,7 @@ mesh.sketch = class MeshSketch extends mesh.object { } extrude(opt = {}) { + console.log('extrude',opt); let { selection, height, chamfer, chamfer_top, chamfer_bottom } = opt; let models = []; let items = this.group.children @@ -427,9 +451,15 @@ class SketchItem { } toggle() { - this.item.selected = !this.item.selected; + let { sketch, item } = this; + item.selected = !item.selected; + if (item.group) { + sketch.items + .filter(i => i.group === item.group) + .forEach(i => i.selected = item.selected); + } this.update(); - this.sketch.render(); + sketch.render(); broker.publish('sketch_selections'); }