From ef0f4c3efdcd754186cc069c8a2c875e15d0554e Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Wed, 24 Jul 2024 15:57:32 -0400 Subject: [PATCH] add sketch scaling, selection persistence --- src/main/mesh.js | 13 +++-- src/mesh/api.js | 9 +++- src/mesh/sketch.js | 119 +++++++++++++++++++++++++++++++++++---------- src/moto/space.js | 2 + 4 files changed, 111 insertions(+), 32 deletions(-) diff --git a/src/main/mesh.js b/src/main/mesh.js index bf8f733e..fa2371af 100644 --- a/src/main/mesh.js +++ b/src/main/mesh.js @@ -131,8 +131,8 @@ async function restore_space() { db_space.remove(id); } } else if (data.type === 'sketch') { - claimed.push(data); - mesh.api.add.sketch(data); + claimed.push(id); + mesh.api.add.sketch({ id, ...data }); } } for (let id of claimed) { @@ -163,7 +163,8 @@ async function restore_space() { let tolist = space.tools || []; let tmodel = api.model.list().filter(m => tolist.contains(m.id)); let tgroup = api.group.list().filter(m => tolist.contains(m.id)); - api.selection.set([...smodel, ...sgroup], [...tmodel, ...tgroup]); + let sklist = api.sketch.list().filter(s => selist.contains(s.id)); + api.selection.set([...smodel, ...sgroup, ...sklist], [...tmodel, ...tgroup]); // restore edit mode api.mode[mode](); // restore dark mode @@ -326,10 +327,16 @@ function space_init(data) { // mouse hover/click handlers. required to enable model drag in space.js space.mouse.downSelect((int, event) => { + if (int?.object?.sketch) { + int.object.sketch.drag({ start: int.object }); + } return event && event.shiftKey ? api.objects() : undefined; }); space.mouse.upSelect((int, event) => { + if (int?.object?.sketch) { + int.object.sketch.drag({ end: int.object }); + } if (event && event.target.nodeName === "CANVAS") { const model = int?.object?.model; const sketch = int?.object?.sketch; diff --git a/src/mesh/api.js b/src/mesh/api.js index 3bb74c8c..d3f8846c 100644 --- a/src/mesh/api.js +++ b/src/mesh/api.js @@ -49,6 +49,10 @@ 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); @@ -81,6 +85,7 @@ const selection = { case 'sketch': selected = selected.filter(s => s.type === 'sketch'); tools.length = 0; + mesh.edges?.end(); break; case 'group': case 'model': @@ -191,7 +196,7 @@ const selection = { }, move(dx = 0, dy = 0, dz = 0) { - for (let s of selection.groups()) { + for (let s of [...selection.groups(), ...selection.sketches()]) { s.move(dx, dy, dz); } return selection; @@ -1144,7 +1149,7 @@ const api = exports({ // return model objects suitable for finding ray intersections return [ ...group.list().map(o => o.models).flat().map(o => o.mesh), - ...sketches.map(s => s.object) + ...sketches.map(s => s.meshes).flat() ]; }, diff --git a/src/mesh/sketch.js b/src/mesh/sketch.js index 361f004e..3afe991c 100644 --- a/src/mesh/sketch.js +++ b/src/mesh/sketch.js @@ -13,7 +13,7 @@ gapp.register("mesh.sketch", [], (root, exports) => { const { MeshBasicMaterial, LineBasicMaterial, LineSegments, DoubleSide } = THREE; -const { PlaneGeometry, EdgesGeometry, SphereGeometry, Vector3, Mesh } = THREE; +const { PlaneGeometry, EdgesGeometry, SphereGeometry, Vector3, Mesh, Group } = THREE; const { mesh, moto } = root; const { space } = moto; @@ -21,11 +21,9 @@ const mapp = mesh; const worker = moto.client.fn; const material = { - plane: new MeshBasicMaterial({ color: 0x888888, side: DoubleSide, transparent: true, opacity: 0.25 }), - outline: new LineBasicMaterial({ color: 0x88dddd, side: DoubleSide, transparent: true, opacity: 0.25 }), - handle: new MeshBasicMaterial({ color: 0x88dddd, side: DoubleSide, transparent: true, opacity: 0.25 }), - highlight: new LineBasicMaterial({ color: 0x88ffdd, side: DoubleSide, transparent: true, opacity: 0.5 }), - selected: new MeshBasicMaterial({ color: 0x88aa88, side: DoubleSide, transparent: true, opacity: 0.25 }), + normal: new MeshBasicMaterial({ color: 0x888888, side: DoubleSide, transparent: true, opacity: 0.25 }), + selected: new MeshBasicMaterial({ color: 0x889988, side: DoubleSide, transparent: true, opacity: 0.25 }), + highlight: new LineBasicMaterial({ color: 0x88ffdd, side: DoubleSide, transparent: true, opacity: 0.50 }), }; /** 2D plane containing open and closed polygons which can be extruded **/ @@ -34,45 +32,64 @@ mesh.sketch = class MeshSketch extends mesh.object { super(opt.id); this.file = opt.file || this.id; - this.scale = opt.scale || { x: 10, y: 10 }; + this.scale = opt.scale || { x: 10, y: 10, z: 0 }; this.center = opt.center || { x: 0, y: 0, z: 0 }; this.normal = opt.normal || { x: 0, y: 0, z: 1 }; - const planeGeometry = new PlaneGeometry(1, 1); - const planeMaterial = material.plane; - const plane = this.plane = new Mesh(planeGeometry, planeMaterial); + const group = this.group = new Group(); + group.sketch = this; - const outlineGeometry = new EdgesGeometry(planeGeometry); - const outlineMaterial = material.outline; - const outline = this.outline = new LineSegments(outlineGeometry, outlineMaterial); - plane.add(outline); + const planeGeometry = new PlaneGeometry(1, 1); + const planeMaterial = material.normal; + const plane = this.plane = new Mesh(planeGeometry, planeMaterial); plane.sketch = this; - const handleGeometry = new SphereGeometry(0.05, 16, 16); - const handleMaterial = material.handle; + const outlineGeometry = new EdgesGeometry(planeGeometry); + const outlineMaterial = material.normal; + const outline = this.outline = new LineSegments(outlineGeometry, outlineMaterial); - const handles = []; - const corners = [ [-0.5, 0.5, 0], [0.5, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0] ]; + const handleGeometry = new SphereGeometry(0.5, 16, 16); + const handleMaterial = material.normal; + + const handles = this.handles = []; + const corners = this.corners = [ [-0.5, 0.5, 0], [0.5, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0] ]; for (let corner of corners) { const handle = new Mesh(handleGeometry, handleMaterial); handle.position.set(...corner); - plane.add(handle); handles.push(handle); + group.add(handle); + handle.sketch = this; } + group.add(outline); + group.add(plane); + this.update(); } update() { - const { plane, center, normal, scale, type } = this; + const { plane, outline, center, handles, corners, normal, scale } = this; + // group.rotation.x = -Math.PI/2; plane.scale.set(scale.x, scale.y, 1); plane.position.set(center.x, center.y, center.z); + outline.scale.set(scale.x, scale.y, 1); + outline.position.set(center.x, center.y, center.z); - const normalVector = new Vector3(normal.x, normal.y, normal.z); - const targetPoint = new Vector3().addVectors(plane.position, normalVector); - plane.lookAt(targetPoint); + for (let i=0; i<4; i++) { + const handle = handles[i]; + const corner = corners[i]; + handle.position.set( + (corner[0] * scale.x) + center.x, + (corner[1] * scale.y) + center.y, + (corner[2] * scale.z) + center.z + ); + } + + // const nv = new Vector3(normal.x, normal.y, normal.z); + // const tp = new Vector3().addVectors(group.position, nv); + // group.lookAt(tp); this.#db_save(); } @@ -92,7 +109,11 @@ mesh.sketch = class MeshSketch extends mesh.object { } get object() { - return this.plane; + return this.group; + } + + get meshes() { + return this.group.children.filter(c => c.sketch ? c : undefined); } rename(newname) { @@ -109,17 +130,61 @@ mesh.sketch = class MeshSketch extends mesh.object { } unhighlight() { - this.outline.material = material.outline; + this.outline.material = material.normal; } select(bool) { + const { plane, handles } = this; if (bool === undefined) { - return this.plane.material === material.selected; + return plane.material === material.selected; } if (bool.toggle) { return this.select(!this.select()); } - return this.plane.material = (bool ? material.selected : material.plane); + plane.material = (bool ? material.selected : material.normal); + for (let handle of handles) { + handle.material = bool ? material.highlight : plane.material; + } + return bool; + } + + move(x, y, z = 0) { + const { center, scale, plane, handles, dragging } = this; + const handle = handles.indexOf(dragging); + if (dragging === plane) { + center.x += x; + center.y += y; + center.z += z; + this.update(); + } else if (handle >= 0) { + const sf = [ + [-1, 1, 1], + [ 1, 1, 1], + [-1,-1, 1], + [ 1,-1, 1], + ][handle]; + center.x += x / 2; + center.y += y / 2; + center.z += z / 2; + scale.x += x * sf[0]; + scale.y += y * sf[1]; + scale.z += z * sf[2]; + this.update(); + } else { + console.log({ sketch_move: [...arguments] }); + } +} + + drag(opt = {}) { + const { plane, handles, dragging } = this; + if (opt.start) { + // console.log({ drag_start: opt.start, plane, same: opt.start === plane }); + this.dragging = opt.start; + } else if (opt.end) { + console.log({ drag_end: opt.end }); + } else { + console.log({ invalid_sketch_drag: opt }); + } } } diff --git a/src/moto/space.js b/src/moto/space.js index 7bd05832..3104db4c 100644 --- a/src/moto/space.js +++ b/src/moto/space.js @@ -876,6 +876,8 @@ gapp.register("moto.space", [], (root, exports) => { isVis = trackTo.visible; if (mouseDownSelect) selection = mouseDownSelect(undefined, event); if (selection && selection.length > 0) { + // selection = selection.map(o => o.isGroup ? o.children : o).flat(); + // console.log({ selection }); trackTo.visible = true; let int = intersect(selection.slice().append(trackTo), false); trackTo.visible = isVis;