From bda45b7b5c9f85a9ab47cb3548f4de27671a2a87 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Mon, 16 Jun 2025 00:21:05 -0400 Subject: [PATCH] add visible handles (not yet functional) to bounding box --- src2/mesh/handles.js | 195 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 src2/mesh/handles.js diff --git a/src2/mesh/handles.js b/src2/mesh/handles.js new file mode 100644 index 00000000..a07a1172 --- /dev/null +++ b/src2/mesh/handles.js @@ -0,0 +1,195 @@ +/** Copyright Stewart Allen -- All Rights Reserved */ + +import { THREE } from '../ext/three.js'; +import { broker } from '../moto/broker.js'; +import { space as motoSpace } from '../moto/space.js'; +import { api as meshApi } from './api.js'; +import { util as meshUtil } from './util.js'; + +const { + BoxGeometry, + EdgesGeometry, + LineSegments, + MeshBasicMaterial, + LineBasicMaterial, + Group, + Vector3, + Quaternion, + Mesh +} = THREE; + +class TransformTool { + constructor() { + this.group = new Group(); + this.handles = []; + this.mode = '2d'; // or '3d' + this.bounds = null; + this.handleSize = 2; // Fixed size in world units + this.handleColor = 0x0088ff; + this.handleOpacity = 0.5; + this.visible = false; + + // Create materials + this.handleMaterial = new MeshBasicMaterial({ + color: this.handleColor, + transparent: true, + opacity: this.handleOpacity + }); + + this.edgeMaterial = new LineBasicMaterial({ + color: this.handleColor, + transparent: true, + opacity: 0.8 + }); + + // Add to scene + motoSpace.world.add(this.group); + this.group.visible = false; + + // Listen for selection changes + broker.listeners({ + selection_update: () => this.update(), + sketch_selections: () => this.update(), + selection_drag: () => this.update(), + selection_move: () => this.update(), + selection_scale: () => this.update(), + selection_rotate: () => this.update(), + selection_qrotate: () => this.update(), + history_undo: () => this.update(), + history_redo: () => this.update(), + move: () => this.update(), + rotate: () => this.update(), + qrotate: () => this.update(), + scale: () => this.update(), + }); + } + + show() { + this.visible = true; + this.group.visible = true; + } + + hide() { + this.visible = false; + this.group.visible = false; + } + + update() { + // Get current selection + const selection = meshApi.selection; + const sketch = selection.sketch(); + const models = selection.models(); + + // Hide if no selection + if (!sketch && !models.length) { + this.hide(); + return; + } + + // Show transform tool + this.show(); + + // Get bounds based on selection type + let bounds; + if (sketch) { + // For sketches, use 2D mode + this.mode = '2d'; + bounds = { + min: new Vector3( + sketch.center.x - sketch.scale.x/2, + sketch.center.y - sketch.scale.y/2, + 0 + ), + max: new Vector3( + sketch.center.x + sketch.scale.x/2, + sketch.center.y + sketch.scale.y/2, + 0 + ) + }; + } else { + // For models/groups, use 3D mode + this.mode = '3d'; + const objects = models; + bounds = meshUtil.bounds(objects); + } + + this.setBounds(bounds); + } + + setBounds(bounds, mode = '3d') { + this.mode = mode; + this.bounds = bounds; + this.updateHandles(); + } + + updateHandles() { + // Clear existing handles + this.handles.forEach(handle => this.group.remove(handle)); + this.handles = []; + + if (!this.bounds) return; + + const { min, max } = this.bounds; + const corners = this.getCorners(min, max); + + // Create handles for each corner + corners.forEach(corner => { + const handle = this.createHandle(); + handle.position.copy(corner); + this.handles.push(handle); + this.group.add(handle); + }); + } + + getCorners(min, max) { + if (this.mode === '2d') { + // 2D mode - 4 corners in XY plane + return [ + new Vector3(min.x, min.y, 0), + new Vector3(max.x, min.y, 0), + new Vector3(max.x, max.y, 0), + new Vector3(min.x, max.y, 0) + ]; + } else { + // 3D mode - 8 corners of bounding box + return [ + new Vector3(min.x, min.y, min.z), + new Vector3(max.x, min.y, min.z), + new Vector3(max.x, max.y, min.z), + new Vector3(min.x, max.y, min.z), + new Vector3(min.x, min.y, max.z), + new Vector3(max.x, min.y, max.z), + new Vector3(max.x, max.y, max.z), + new Vector3(min.x, max.y, max.z) + ]; + } + } + + createHandle() { + // Create a small box for the handle + const geometry = new BoxGeometry( + this.handleSize, + this.handleSize, + this.handleSize + ); + + // Create the handle mesh + const handle = new Mesh(geometry, this.handleMaterial); + + // Add wireframe edges + const edges = new EdgesGeometry(geometry); + const line = new LineSegments(edges, this.edgeMaterial); + handle.add(line); + + return handle; + } + + get object() { + return this.group; + } +} + +// Create singleton instance +const handles = new TransformTool(); + +export { handles }; \ No newline at end of file