improve sketch bounds and handles

This commit is contained in:
Stewart Allen 2025-06-16 00:56:58 -04:00
commit d947963c28
2 changed files with 34 additions and 31 deletions

View file

@ -29,14 +29,14 @@ class TransformTool {
this.handleColor = 0x0088ff; this.handleColor = 0x0088ff;
this.handleOpacity = 0.5; this.handleOpacity = 0.5;
this.visible = false; this.visible = false;
// Create materials // Create materials
this.handleMaterial = new MeshBasicMaterial({ this.handleMaterial = new MeshBasicMaterial({
color: this.handleColor, color: this.handleColor,
transparent: true, transparent: true,
opacity: this.handleOpacity opacity: this.handleOpacity
}); });
this.edgeMaterial = new LineBasicMaterial({ this.edgeMaterial = new LineBasicMaterial({
color: this.handleColor, color: this.handleColor,
transparent: true, transparent: true,
@ -56,6 +56,7 @@ class TransformTool {
selection_scale: () => this.update(), selection_scale: () => this.update(),
selection_rotate: () => this.update(), selection_rotate: () => this.update(),
selection_qrotate: () => this.update(), selection_qrotate: () => this.update(),
sketch_render: () => this.update(),
history_undo: () => this.update(), history_undo: () => this.update(),
history_redo: () => this.update(), history_redo: () => this.update(),
move: () => this.update(), move: () => this.update(),
@ -95,18 +96,7 @@ class TransformTool {
if (sketch) { if (sketch) {
// For sketches, use 2D mode // For sketches, use 2D mode
this.mode = '2d'; this.mode = '2d';
bounds = { bounds = sketch.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 { } else {
// For models/groups, use 3D mode // For models/groups, use 3D mode
this.mode = '3d'; this.mode = '3d';
@ -134,7 +124,7 @@ class TransformTool {
const { min, max } = this.bounds; const { min, max } = this.bounds;
const corners = this.getCorners(min, max); const corners = this.getCorners(min, max);
// Create handles for each corner // Create handles for each corner
corners.forEach(corner => { corners.forEach(corner => {
const handle = this.createHandle(); const handle = this.createHandle();
@ -207,12 +197,13 @@ class TransformTool {
getCorners(min, max) { getCorners(min, max) {
if (this.mode === '2d') { if (this.mode === '2d') {
// 2D mode - 4 corners in XY plane // 2D mode - 4 corners in XY plane at z midline
const zMid = (min.z + max.z) / 2;
return [ return [
new Vector3(min.x, min.y, 0), new Vector3(min.x, min.y, zMid),
new Vector3(max.x, min.y, 0), new Vector3(max.x, min.y, zMid),
new Vector3(max.x, max.y, 0), new Vector3(max.x, max.y, zMid),
new Vector3(min.x, max.y, 0) new Vector3(min.x, max.y, zMid)
]; ];
} else { } else {
// 3D mode - 8 corners of bounding box // 3D mode - 8 corners of bounding box
@ -236,15 +227,15 @@ class TransformTool {
this.handleSize, this.handleSize,
this.handleSize this.handleSize
); );
// Create the handle mesh // Create the handle mesh
const handle = new Mesh(geometry, this.handleMaterial); const handle = new Mesh(geometry, this.handleMaterial);
// Add wireframe edges // Add wireframe edges
const edges = new EdgesGeometry(geometry); const edges = new EdgesGeometry(geometry);
const line = new LineSegments(edges, this.edgeMaterial); const line = new LineSegments(edges, this.edgeMaterial);
handle.add(line); handle.add(line);
return handle; return handle;
} }

View file

@ -2,7 +2,7 @@
import { client as motoClient } from '../moto/client.js'; import { client as motoClient } from '../moto/client.js';
import { space as motoSpace } from '../moto/space.js'; import { space as motoSpace } from '../moto/space.js';
import { broker as motoBroker } from '../moto/broker.js'; import { broker } from '../moto/broker.js';
import { object as meshObject } from './object.js'; import { object as meshObject } from './object.js';
import { model as meshModel, materials } from './model.js'; import { model as meshModel, materials } from './model.js';
import { util as meshUtil } from './util.js'; import { util as meshUtil } from './util.js';
@ -11,7 +11,7 @@ import { newPolygon } from '../geo/polygon.js';
import { polygons } from '../geo/polygons.js'; import { polygons } from '../geo/polygons.js';
import { THREE } from '../ext/three.js'; import { THREE } from '../ext/three.js';
const { BufferGeometry, BufferAttribute, Quaternion, MeshBasicMaterial, LineBasicMaterial, LineSegments, DoubleSide, PlaneGeometry, EdgesGeometry, SphereGeometry, Vector3, Mesh, Group } = THREE; const { BufferGeometry, BufferAttribute, Quaternion, MeshBasicMaterial, LineBasicMaterial, LineSegments, DoubleSide, PlaneGeometry, EdgesGeometry, SphereGeometry, Vector3, Box3, Mesh, Group } = THREE;
const worker = motoClient.fn; const worker = motoClient.fn;
const drag = {}; const drag = {};
const hpos = [ const hpos = [
@ -119,8 +119,19 @@ class MeshSketch extends meshObject {
} }
get bounds() { get bounds() {
this.plane.geometry.computeBoundingBox(); const { scale, center } = this;
return this.plane.geometry.boundingBox.clone(); return new Box3(
new Vector3(
center.x - scale.x/2,
center.y - scale.y/2,
center.z
),
new Vector3(
center.x + scale.x/2,
center.y + scale.y/2,
center.z
)
);
} }
get meshes() { get meshes() {
@ -152,7 +163,7 @@ class MeshSketch extends meshObject {
all() { all() {
sketch.items.forEach(i => i.selected = true); sketch.items.forEach(i => i.selected = true);
sketch.render(); sketch.render();
motoBroker.publish('sketch_selections'); broker.publish('sketch_selections');
return sketch.items.length; return sketch.items.length;
}, },
@ -162,7 +173,7 @@ class MeshSketch extends meshObject {
if (sel.length) { if (sel.length) {
sel.forEach(s => s.selected = false); sel.forEach(s => s.selected = false);
sketch.render(); sketch.render();
motoBroker.publish('sketch_selections'); broker.publish('sketch_selections');
} }
return sel.length; return sel.length;
}, },
@ -173,7 +184,7 @@ class MeshSketch extends meshObject {
if (sel.length) { if (sel.length) {
sketch.items = sketch.items.filter(i => !i.selected); sketch.items = sketch.items.filter(i => !i.selected);
sketch.render(); sketch.render();
motoBroker.publish('sketch_selections'); broker.publish('sketch_selections');
} }
return sel.length; return sel.length;
}, },
@ -524,6 +535,7 @@ class MeshSketch extends meshObject {
} }
this.update(); this.update();
motoSpace.refresh(); motoSpace.refresh();
broker.publish('sketch_render', this);
} }
render_defer() { render_defer() {
@ -589,7 +601,7 @@ class SketchItem {
} }
this.update(); this.update();
sketch.render(); sketch.render();
motoBroker.publish('sketch_selections'); broker.publish('sketch_selections');
} }
extrude(z, opt = {}) { extrude(z, opt = {}) {