UNPKG

@pmndrs/handle

Version:

framework agnostic expandable handle implementation for threejs

142 lines (141 loc) 6.01 kB
import { Group, Vector3 } from 'three'; import { PivotAxisScaleHandle } from './scale.js'; import { HandlesContext } from '../context.js'; import { computeHandlesScale } from '../utils.js'; import { PivotAxisRotationHandle } from './rotate.js'; import { AxisTranslateHandle } from '../translate/axis.js'; import { PlaneTranslateHandle } from '../translate/plane.js'; const vectorHelper = new Vector3(); export class PivotHandlesHandles extends Group { context; size; fixed; scaleX; scaleY; scaleZ; rotationX; rotationY; rotationZ; translationX; translationY; translationZ; translationXY; translationYZ; translationXZ; xAxis = new Vector3(); yAxis = new Vector3(); zAxis = new Vector3(); xRotationAxis = new Vector3(); yRotationAxis = new Vector3(); zRotationAxis = new Vector3(); constructor(context, size, fixed) { super(); this.context = context; this.size = size; this.fixed = fixed; this.scaleX = new PivotAxisScaleHandle(context, 'x', 's'); this.add(this.scaleX); this.scaleY = new PivotAxisScaleHandle(context, 'y', 's'); this.scaleY.rotation.z = Math.PI / 2; this.add(this.scaleY); this.scaleZ = new PivotAxisScaleHandle(context, 'z', 's'); this.scaleZ.rotation.y = -Math.PI / 2; this.add(this.scaleZ); this.rotationX = new PivotAxisRotationHandle(context, 'x', 'r', this.xRotationAxis); this.add(this.rotationX); this.rotationY = new PivotAxisRotationHandle(context, 'y', 'r', this.yRotationAxis); this.rotationY.rotation.z = -Math.PI / 2; this.add(this.rotationY); this.rotationZ = new PivotAxisRotationHandle(context, 'z', 'r', this.zRotationAxis); this.rotationZ.rotation.y = Math.PI / 2; this.add(this.rotationZ); this.translationX = new AxisTranslateHandle(context, 'x', 'ta', this.xAxis); this.add(this.translationX); this.translationY = new AxisTranslateHandle(context, 'y', 'ta', this.yAxis); this.translationY.rotation.z = Math.PI / 2; this.add(this.translationY); this.translationZ = new AxisTranslateHandle(context, 'z', 'ta', this.zAxis); this.translationZ.rotation.y = -Math.PI / 2; this.add(this.translationZ); this.translationXY = new PlaneTranslateHandle(context, 'xy', 'tp', [this.xAxis, this.yAxis]); this.add(this.translationXY); this.translationYZ = new PlaneTranslateHandle(context, 'yz', 'tp', [this.yAxis, this.zAxis]); this.translationYZ.rotation.y = -Math.PI / 2; this.add(this.translationYZ); this.translationXZ = new PlaneTranslateHandle(context, 'xz', 'tp', [this.xAxis, this.zAxis]); this.translationXZ.rotation.x = Math.PI / 2; this.add(this.translationXZ); } update(camera) { this.updateWorldMatrix(true, false); const target = this.context.getTarget(); if (target != null) { computeLocalAxis(this.xAxis, target, undefined, 1, 0, 0); computeLocalAxis(this.yAxis, target, undefined, 0, 1, 0); computeLocalAxis(this.zAxis, target, undefined, 0, 0, 1); computeLocalAxis(this.xRotationAxis, target, this.rotationX.store, 1, 0, 0); computeLocalAxis(this.yRotationAxis, target, this.rotationY.store, 0, 1, 0); computeLocalAxis(this.zRotationAxis, target, this.rotationZ.store, 0, 0, 1); } this.scale.setScalar(1); if (target != null) { target.getWorldScale(vectorHelper); this.scale.divide(vectorHelper); } this.scale.multiplyScalar(computeHandlesScale(this, camera, this.fixed ?? true, this.size ?? 1)); } bind(translation, rotation, scale) { const unbindScaleX = this.scaleX.bind(0xff2060, scale); const unbindScaleY = this.scaleY.bind(0x20df80, scale); const unbindScaleZ = this.scaleZ.bind(0x2080ff, scale); const unbindRotationX = this.rotationX.bind(0xff2060, rotation); const unbindRotationY = this.rotationY.bind(0x20df80, rotation); const unbindRotationZ = this.rotationZ.bind(0x2080ff, rotation); const unbindTranslationX = this.translationX.bind(0xff2060, 0xffff40, translation); const unbindTranslationY = this.translationY.bind(0x20df80, 0xffff40, translation); const unbindTranslationZ = this.translationZ.bind(0x2080ff, 0xffff40, translation); const unbindTranslationXY = this.translationXY.bind(0xff2060, 0xffff40, translation); const unbindTranslationYZ = this.translationYZ.bind(0x2080ff, 0xffff40, translation); const unbindTranslationXZ = this.translationXZ.bind(0x20df80, 0xffff40, translation); return () => { unbindScaleX?.(); unbindScaleY?.(); unbindScaleZ?.(); unbindRotationX?.(); unbindRotationY?.(); unbindRotationZ?.(); unbindTranslationX?.(); unbindTranslationY?.(); unbindTranslationZ?.(); unbindTranslationXY?.(); unbindTranslationYZ?.(); unbindTranslationXZ?.(); }; } } export class PivotHandles extends Group { handles; context; constructor(getOptions) { super(); this.context = new HandlesContext(this, getOptions); this.handles = new PivotHandlesHandles(this.context); this.add(this.handles); } update(time, camera) { this.context.update(time); this.handles.update(camera); } bind(translation, rotation, scale) { return this.handles.bind(translation, rotation, scale); } } function computeLocalAxis(target, object, handleStore, ...[x, y, z]) { if (handleStore?.getState() != null) { return; } target.set(x, y, z); target.applyQuaternion(object.quaternion); } export * from './rotate.js'; export * from './scale.js';