@shopware-ag/dive
Version:
Shopware Spatial Framework
144 lines (122 loc) • 4.26 kB
text/typescript
import { Euler, Object3D, Vector3 } from 'three';
import {
AxesColorBlue,
AxesColorGreen,
AxesColorRed,
} from '../../constant/AxisHelperColors';
import DIVEOrbitControls from '../../controls/OrbitControls';
import { DIVERadialHandle } from '../handles/RadialHandle';
import { DIVEGizmo, DIVEGizmoAxis } from '../Gizmo';
import { DraggableEvent } from '../../toolbox/BaseTool';
import { DIVEMath } from '../../math';
export class DIVERotateGizmo extends Object3D {
public children: DIVERadialHandle[];
private _controller: DIVEOrbitControls;
public set debug(value: boolean) {
this.children.forEach((child) => {
child.debug = value;
});
}
private _startRot: Euler | null;
constructor(controller: DIVEOrbitControls) {
super();
this.name = 'DIVERotateGizmo';
this.children = [];
this._startRot = null;
this._controller = controller;
this.add(
new DIVERadialHandle(
'x',
1,
Math.PI / 2,
new Vector3(1, 0, 0),
AxesColorRed,
),
);
this.add(
new DIVERadialHandle(
'y',
1,
-Math.PI / 2,
new Vector3(0, 1, 0),
AxesColorGreen,
),
);
this.add(
new DIVERadialHandle(
'z',
1,
Math.PI / 2,
new Vector3(0, 0, 1),
AxesColorBlue,
),
);
}
public reset(): void {
this.children.forEach((child) => {
child.reset();
});
}
private handleHighlight(
axis: DIVEGizmoAxis,
value: boolean,
dragged: boolean,
): void {
// Set highlight state for all handles.
this.children.forEach((child) => {
if (dragged) {
// Dragging has priority when it comes to highlighting.
child.highlight = child.axis === axis && dragged;
} else {
// If nothing is dragged, decide on hovered state.
child.highlight = child.axis === axis && value;
}
});
}
public onHandleHover(handle: DIVERadialHandle, value: boolean): void {
// If _startRot is set, it means there is a drag operation in progress.
// While dragging, we don't want to change the hover state.
if (this._startRot) return;
if (!this.parent) return;
if (!this.parent.parent) return;
(this.parent.parent as DIVEGizmo).onHover('rotate', handle.axis, value);
this.handleHighlight(handle.axis, value, false);
}
public onHandleDragStart(handle: DIVERadialHandle): void {
if (!this.parent) return;
if (!this.parent.parent) return;
const object = (this.parent.parent as DIVEGizmo).object;
if (!object) return;
this._startRot = object.rotation.clone();
this.handleHighlight(handle.axis, true, true);
}
public onHandleDrag(handle: DIVERadialHandle, e: DraggableEvent): void {
if (!this._startRot) return;
if (!this.parent) return;
if (!this.parent.parent) return;
if (!('onChange' in this.parent.parent)) return;
const currentVector = e.dragCurrent
.clone()
.sub(this.parent.parent.position)
.normalize();
const startVector = e.dragStart
.clone()
.sub(this.parent.parent.position)
.normalize();
const signedAngle = DIVEMath.signedAngleTo(
startVector,
currentVector,
handle.forwardVector,
);
const euler = new Euler(
this._startRot.x + handle.forwardVector.x * signedAngle,
this._startRot.y + handle.forwardVector.y * signedAngle,
this._startRot.z + handle.forwardVector.z * signedAngle,
);
(this.parent.parent as DIVEGizmo).onChange(undefined, euler);
}
public onHandleDragEnd(handle: DIVERadialHandle): void {
this._startRot = null;
this.handleHighlight(handle.axis, false, false);
}
}