diff --git a/src/main/mesh.js b/src/main/mesh.js index ddd9998b..9cd8f85f 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -329,22 +329,34 @@ function space_init(data) { ]); // mouse hover/click handlers. required to enable model drag in space.js - space.mouse.downSelect((int, event) => { + // called two ways: + // without args to return a list of selectable targets + // with args when a selection is made + space.mouse.downSelect((int, event, ints) => { if (int?.object?.sketch) { int.object.sketch.drag({ start: int.object }); + } else if (int?.object?.sketch_item) { + int.object.sketch_item.sketch.drag({ start: int.object }); + } + if (event?.shiftKey) { + return api.objects(); + } else { + return undefined; } - return event && event.shiftKey ? api.objects() : undefined; }); + // called two ways: + // without args to return a list of selectable targets + // with args when a selection is made space.mouse.upSelect((int, event, ints) => { if (ints?.length > 1) { // ensure sketch items are returned before sketch plane / handles - let polys = ints.filter(i => i.object.poly); - int = polys.length ? polys[0] : int; + let els = ints.filter(i => i.object.sketch_item); + int = els.length ? els[0] : int; } - if (event && event.target.nodeName === "CANVAS") { - const model = int?.object?.model; - const sketch = int?.object?.sketch; + if (event?.target?.nodeName === "CANVAS") { + // a selection was made + const { model, sketch, sketch_item } = int?.object || {}; const { altKey, ctrlKey, metaKey, shiftKey } = event; if (mesh.split.active()) { return mesh.split.select(model); @@ -400,21 +412,25 @@ function space_init(data) { } else { selection.toggle(sketch); } + } else if (sketch_item) { + sketch_item.toggle(); } } else { - let sketches = api.selection.sketches(true); + // return objects upSelect can choose from + let visible = api.objects().filter(o => o.visible); + let sketches = api.selection.sketches(); if (sketches.length) { - // when sketch selected, return sketch items, too - return sketches[0].object.children; + // when sketch selected, append sketch items, too + visible.appendAll(sketches[0].object.children); }; - return api.objects().filter(o => o.visible); + return visible; } }); space.mouse.onDrag((delta, offset, up = false) => { - const { mode, modes } = api; - if (delta && delta.event.shiftKey) { - if (selection.count()) { + let { mode, modes } = api; + if (delta) { + if (delta.event?.shiftKey && selection.count()) { selection.move(delta.x, delta.y, 0); } } else if (up) { @@ -422,7 +438,8 @@ function space_init(data) { selection.drag({ end: true }); } } else if (mode.is([ modes.object, modes.tool, modes.sketch ])) { - return api.objects().length > 0; + // return true if there are draggable elements + return api.objects().length > 0 || api.selection.sketches().length; } }); } diff --git a/src/mesh/api.js b/src/mesh/api.js index 8b7e7f6f..ceda8275 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -49,10 +49,6 @@ const selection = { return grp; }, - sketches(strict) { - return selection.list(strict).filter(s => s.type === 'sketch'); - }, - // return selected models + models from selected groups models(strict) { let all = selection.list(strict); @@ -66,8 +62,12 @@ const selection = { return mdl; }, - sketches(strict) { - return selection.list(strict).filter(s => s.type === 'sketch'); + sketches() { + return selection.list(true).filter(s => s.type === 'sketch'); + }, + + sketch_items() { + return selection.list(true).filter(s => s.type === 'sketch_item') }, contains(object) { @@ -174,6 +174,7 @@ const selection = { // update selection and wireframe for all objects update() { + // de-select all for (let group of groups) { group.select(false); group.normals(prefs.map.space.norm || false); diff --git a/src/mesh/sketch.js b/src/mesh/sketch.js index 5a59e682..ab146261 100644 --- a/src/mesh/sketch.js +++ b/src/mesh/sketch.js @@ -126,7 +126,9 @@ mesh.sketch = class MeshSketch extends mesh.object { } get meshes() { - return this.group.children.filter(c => c.sketch ? c : undefined); + return this.group.children.filter(c => { + return c.sketch || (c.sketch_item && c.sketch_item.selected) ? c : undefined + }).reverse(); } rename(newname) { @@ -158,6 +160,7 @@ mesh.sketch = class MeshSketch extends mesh.object { for (let handle of handles) { handle.material = bool ? material.highlight : plane.material; } + this.render(); return bool; } @@ -183,6 +186,14 @@ mesh.sketch = class MeshSketch extends mesh.object { scale.y += y * sf[1]; scale.z += z * sf[2]; this.update(); + } else if (Array.isArray(dragging)) { + for (let item of dragging) { + let { center } = item; + center.x += x; + center.y += y; + center.z += z; + } + this.render(); } else { this.center = {x, y, z}; this.update(); @@ -190,9 +201,10 @@ mesh.sketch = class MeshSketch extends mesh.object { } drag(opt = {}) { - const { plane, handles, dragging } = this; + let { items } = this; if (opt.start) { - this.dragging = opt.start; + let selected = items.filter(i => i.selected); + this.dragging = opt.start.sketch_item ? selected : opt.start; } else if (opt.end) { this.dragging = undefined; } else { @@ -218,44 +230,79 @@ mesh.sketch = class MeshSketch extends mesh.object { render() { let { group } = this; // remove previous item/poly-based children of group - group.children.filter(c => c.poly).forEach(c => group.remove(c)); + group.children.filter(c => c.sketch_item).forEach(c => group.remove(c)); // mapy items into polys into meshes to add to group - this.items.map(item => { - let { type, center, width, height, radius, spacing, poly } = item; - if (type === 'circle') { - let circumference = 2 * Math.PI * radius; - let points = Math.floor(circumference / (spacing || 1)); - poly = newPolygon().centerCircle(center, radius, points).annotate({ item } ); - } else if (type === 'rectangle') { - poly = newPolygon().centerRectangle(center, width, height).annotate({ item }); - } - return poly; - }).filter(o => o).forEach(poly => { - let vrt = poly.extrude(0).toFloat32(); - let geo = new BufferGeometry(); - geo.setAttribute('position', new BufferAttribute(vrt, 3)); - let meh = new Mesh(geo, mesh.material.normal); - meh.renderOrder = -1; - meh.poly = poly; - group.add(meh); - }); + for (let si of this.items.map(i => new SketchItem(this, i))) { + group.add(si.mesh); + } this.update(); } extrude(opt = {}) { let { z } = opt; let models = []; - for (let item of this.group.children.filter(c => c.poly)) { - let vert = item.poly.extrude(z || 10); - let nmdl = new mesh.model({ file: "poly", mesh: vert.toFloat32() }); + console.log({ extrude: this }); + for (let item of this.group.children.filter(c => c.sketch_item)) { + let vert = item.sketch_item.extrude(z || 10); + let nmdl = new mesh.model({ file: "item", mesh: vert.toFloat32() }); models.push(nmdl); } if (models.length) { log('extrude', this.file || this.id, 'into', models.length, 'solid(s)'); + let { center } = this; let ngrp = api.group.new(models); ngrp.floor(); + ngrp.move(center.x, center.y, center.z + 0.001); } } } +class SketchItem { + constructor(sketch, item) { + this.sketch = sketch; + this.item = item; + this.update(); + } + + get type() { + return "sketch_item"; + } + + get selected() { + return this.item.selected; + } + + toggle() { + this.item.selected = !this.item.selected; + this.update(); + this.sketch.render(); + } + + extrude(opt = {}) { + return this.poly.extrude(opt); + } + + update() { + let { item, sketch } = this; + let { material } = mesh; + let { type, center, width, height, radius, spacing, poly, selected } = item; + if (type === 'circle') { + let circumference = 2 * Math.PI * radius; + let points = Math.floor(circumference / (spacing || 1)); + poly = newPolygon().centerCircle(center, radius, points).annotate({ item } ); + } else if (type === 'rectangle') { + poly = newPolygon().centerRectangle(center, width, height).annotate({ item }); + } else { + throw `invalid sketch type: ${type}`; + } + this.poly = poly; + let vrt = poly.extrude(0).toFloat32(); + let geo = new BufferGeometry(); + geo.setAttribute('position', new BufferAttribute(vrt, 3)); + let meh = this.mesh = new Mesh(geo, selected && sketch.select() ? material.select : material.normal); + meh.renderOrder = -1; + meh.sketch_item = this; + } +} + }); \ No newline at end of file diff --git a/src/moto/space.js b/src/moto/space.js index 0c9a48d4..9a646a7e 100644 --- a/src/moto/space.js +++ b/src/moto/space.js @@ -896,7 +896,7 @@ gapp.register("moto.space", [], (root, exports) => { viewControl.enabled = false; } if (selectInt) { - mouseDownSelect(selectInt, event); + mouseDownSelect(selectInt, event, int); } if (mouseDown) mouseDown(event, int); }