@pmndrs/handle
Version:
framework agnostic expandable handle implementation for threejs
253 lines (241 loc) • 9.57 kB
JavaScript
import { Group, Vector3 } from 'three';
import { computeHandlesScale } from '../utils.js';
import { AxisScaleHandle } from './axis.js';
import { PlaneScaleHandle } from './plane.js';
import { UniformAxisScaleHandle } from './uniform.js';
const vectorHelper = new Vector3();
export class ScaleHandles extends Group {
context;
size;
fixed;
scaleX;
scaleY;
scaleZ;
scaleNegX;
scaleNegY;
scaleNegZ;
translationX;
translationY;
translationZ;
translationNegX;
translationNegY;
translationNegZ;
translationXY;
translationYZ;
translationXZ;
constructor(context, size, fixed) {
super();
this.context = context;
this.size = size;
this.fixed = fixed;
this.scaleX = new UniformAxisScaleHandle(this.context, undefined, 'x');
this.add(this.scaleX);
this.scaleY = new UniformAxisScaleHandle(this.context, undefined, 'y');
this.scaleY.rotation.z = Math.PI / 2;
this.add(this.scaleY);
this.scaleZ = new UniformAxisScaleHandle(this.context, undefined, 'z');
this.scaleZ.rotation.y = -Math.PI / 2;
this.add(this.scaleZ);
this.scaleNegX = new UniformAxisScaleHandle(this.context, undefined, 'x', true);
this.add(this.scaleNegX);
this.scaleNegY = new UniformAxisScaleHandle(this.context, undefined, 'y', true);
this.scaleNegY.rotation.z = Math.PI / 2;
this.add(this.scaleNegY);
this.scaleNegZ = new UniformAxisScaleHandle(this.context, undefined, 'z', true);
this.scaleNegZ.rotation.y = -Math.PI / 2;
this.add(this.scaleNegZ);
this.translationX = new AxisScaleHandle(this.context, 'x');
this.add(this.translationX);
this.translationY = new AxisScaleHandle(this.context, 'y');
this.translationY.rotation.z = Math.PI / 2;
this.add(this.translationY);
this.translationZ = new AxisScaleHandle(this.context, 'z');
this.translationZ.rotation.y = -Math.PI / 2;
this.add(this.translationZ);
this.translationNegX = new AxisScaleHandle(this.context, 'x', undefined, true, false);
this.add(this.translationNegX);
this.translationNegY = new AxisScaleHandle(this.context, 'y', undefined, true, false);
this.translationNegY.rotation.z = Math.PI / 2;
this.add(this.translationNegY);
this.translationNegZ = new AxisScaleHandle(this.context, 'z', undefined, true, false);
this.translationNegZ.rotation.y = -Math.PI / 2;
this.add(this.translationNegZ);
this.translationXY = new PlaneScaleHandle(this.context, 'xy');
this.add(this.translationXY);
this.translationXZ = new PlaneScaleHandle(this.context, 'xz');
this.translationXZ.rotation.x = Math.PI / 2;
this.add(this.translationXZ);
this.translationYZ = new PlaneScaleHandle(this.context, 'yz');
this.translationYZ.rotation.y = -Math.PI / 2;
this.add(this.translationYZ);
}
update(camera) {
this.updateWorldMatrix(true, false);
this.scale.setScalar(1);
const target = this.context.getTarget();
if (target != null) {
target.getWorldScale(vectorHelper);
this.scale.divide(vectorHelper);
}
this.scale.multiplyScalar(computeHandlesScale(this, camera, this.fixed ?? true, this.size ?? 1));
}
bind(options) {
const unbindScaleX = this.scaleX.bind(0xffffff, 0xffff00, options);
const unbindScaleY = this.scaleY.bind(0xffffff, 0xffff00, options);
const unbindScaleZ = this.scaleZ.bind(0xffffff, 0xffff00, options);
const unbindScaleNegX = this.scaleNegX.bind(0xffffff, 0xffff00, options);
const unbindScaleNegY = this.scaleNegY.bind(0xffffff, 0xffff00, options);
const unbindScaleNegZ = this.scaleNegZ.bind(0xffffff, 0xffff00, options);
const unbindTranslationX = this.translationX.bind(0xff0000, 0xffff00, options);
const unbindTranslationY = this.translationY.bind(0x00ff00, 0xffff00, options);
const unbindTranslationZ = this.translationZ.bind(0x0000ff, 0xffff00, options);
const unbindTranslationNegX = this.translationNegX.bind(0xff0000, 0xffff00, options);
const unbindTranslationNegY = this.translationNegY.bind(0x00ff00, 0xffff00, options);
const unbindTranslationNegZ = this.translationNegZ.bind(0x0000ff, 0xffff00, options);
const unbindTranslationXY = this.translationXY.bind(0x0000ff, 0xffff00, options);
const unbindTranslationYZ = this.translationYZ.bind(0xff0000, 0xffff00, options);
const unbindTranslationXZ = this.translationXZ.bind(0x00ff00, 0xffff00, options);
return () => {
unbindTranslationX?.();
unbindTranslationY?.();
unbindTranslationZ?.();
unbindTranslationNegX?.();
unbindTranslationNegY?.();
unbindTranslationNegZ?.();
unbindTranslationXY?.();
unbindTranslationYZ?.();
unbindTranslationXZ?.();
unbindScaleX?.();
unbindScaleY?.();
unbindScaleZ?.();
unbindScaleNegX?.();
unbindScaleNegY?.();
unbindScaleNegZ?.();
};
}
}
/*
import {
ForwardRefExoticComponent,
PropsWithoutRef,
RefAttributes,
forwardRef,
useImperativeHandle,
useRef,
} from 'react'
import { Group, Vector3Tuple } from 'three'
import { HandlesContext } from '../context.js'
import { HandleOptions, HandleTransformOptions } from '@pmndrs/handle'
import { ThreeElements } from '@react-three/fiber'
import { FreeScaleControl } from './free.js'
import { HandlesAxisHighlight } from '../axis.js'
import { AxisScaleHande } from './axis.js'
import { PlaneScaleHandle } from './plane.js'
import { HandlesSize } from '../size.js'
export type ScaleHandlesProperties = ThreeElements['group'] &
Pick<HandleOptions<any>, 'alwaysUpdate' | 'apply' | 'stopPropagation'> & {
enabled?: Exclude<HandleTransformOptions, Array<Vector3Tuple>>
size?: number
fixed?: boolean
}
export const ScaleHandles: ForwardRefExoticComponent<PropsWithoutRef<ScaleHandlesProperties> & RefAttributes<Group>> =
forwardRef<Group, ScaleHandlesProperties>(
({ children, alwaysUpdate, apply, stopPropagation, enabled, size, fixed, ...props }, ref) => {
const groupRef = useRef<Group>(null)
useImperativeHandle(ref, () => groupRef.current!, [])
return (
<HandlesContext
alwaysUpdate={alwaysUpdate}
apply={apply}
stopPropagation={stopPropagation}
targetRef={groupRef}
>
<group {...props}>
<HandlesSize size={size} fixed={fixed}>
<PlaneScaleHandle
enabled={enabled}
color={0x0000ff}
hoverColor={0xffff00}
opacity={0.5}
hoverOpacity={1}
tag="xy"
/>
<PlaneScaleHandle
enabled={enabled}
rotation-x={Math.PI / 2}
color={0x00ff00}
hoverColor={0xffff00}
opacity={0.5}
hoverOpacity={1}
tag="xz"
/>
<PlaneScaleHandle
enabled={enabled}
rotation-y={-Math.PI / 2}
color={0xff0000}
hoverColor={0xffff00}
opacity={0.5}
hoverOpacity={1}
tag="yz"
/>
<AxisScaleHande enabled={enabled} color={0xff0000} hoverColor={0xffff00} opacity={1} tag="x" />
<AxisScaleHande
enabled={enabled}
color={0xff0000}
hoverColor={0xffff00}
opacity={1}
invert
showHandleLine={false}
tag="x"
/>
<HandlesAxisHighlight tag="x" enabled={enabled} />
<AxisScaleHande
enabled={enabled}
rotation-z={Math.PI / 2}
color={0x00ff00}
hoverColor={0xffff00}
opacity={1}
tag="y"
/>
<AxisScaleHande
enabled={enabled}
rotation-z={Math.PI / 2}
color={0x00ff00}
hoverColor={0xffff00}
opacity={1}
invert
showHandleLine={false}
tag="y"
/>
<HandlesAxisHighlight enabled={enabled} rotation-z={Math.PI / 2} tag="y" />
<AxisScaleHande
rotation-y={-Math.PI / 2}
enabled={enabled}
color={0x0000ff}
hoverColor={0xffff00}
opacity={1}
tag="z"
/>
<AxisScaleHande
rotation-y={-Math.PI / 2}
enabled={enabled}
color={0x0000ff}
hoverColor={0xffff00}
opacity={1}
invert
showHandleLine={false}
tag="z"
/>
<HandlesAxisHighlight rotation-y={-Math.PI / 2} enabled={enabled} tag="z" />
<FreeScaleControl enabled={enabled} />
</HandlesSize>
<group ref={groupRef}>{children}</group>
</group>
</HandlesContext>
)
},
)
export * from './free.js'
export * from './axis.js'
export * from './plane.js'
*/