threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
178 lines • 8.23 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var TransformControlsPlugin_1;
import { uiButton, uiConfig, uiPanelContainer, uiToggle } from 'uiconfig.js';
import { AViewerPluginSync } from '../../viewer';
import { TransformControls2 } from '../../three';
import { PickingPlugin } from './PickingPlugin';
import { onChange } from 'ts-browser-helpers';
import { TransformControls } from '../../three/controls/TransformControls';
import { UnlitLineMaterial, UnlitMaterial } from '../../core';
import { Euler, Vector3 } from 'three';
let TransformControlsPlugin = TransformControlsPlugin_1 = class TransformControlsPlugin extends AViewerPluginSync {
setDirty() {
if (!this._viewer)
return;
const picking = this._viewer.getPlugin(PickingPlugin);
const enabled = !this.isDisabled();
if (enabled && picking.widgetEnabled) {
picking.widgetEnabled = false;
this._pickingWidgetDisabled = true;
}
else if (!enabled && this._pickingWidgetDisabled) {
picking.widgetEnabled = true;
this._pickingWidgetDisabled = false;
}
if (this.transformControls) {
if (enabled && picking.getSelectedObject()?.isObject3D)
this.transformControls.attach(picking.getSelectedObject());
else
this.transformControls.detach();
}
this._viewer.setDirty();
}
constructor(enabled = true) {
super();
this.enabled = true;
this._pickingWidgetDisabled = false;
this.toJSON = undefined;
this.dependencies = [PickingPlugin];
this._isInteracting = false;
this._viewerListeners = {
postFrame: () => {
if (!this.transformControls || !this._viewer)
return;
// this._viewer.scene.mainCamera.setInteractions(!this._isInteracting, TransformControlsPlugin.PluginType)
},
};
this._transformState = {
obj: null,
position: new Vector3(),
rotation: new Euler(),
scale: new Vector3(),
};
this._mainCameraChange = () => {
if (!this.transformControls || !this._viewer)
return;
this.transformControls.camera = this._viewer.scene.mainCamera;
};
TransformControls.ObjectConstructors.MeshBasicMaterial = UnlitMaterial;
TransformControls.ObjectConstructors.LineBasicMaterial = UnlitLineMaterial;
this.enabled = enabled;
}
onAdded(viewer) {
super.onAdded(viewer);
this.setDirty();
this.transformControls = new TransformControls2(viewer.scene.mainCamera, viewer.canvas);
this._mainCameraChange = this._mainCameraChange.bind(this);
viewer.scene.addEventListener('mainCameraChange', this._mainCameraChange);
this.transformControls.addEventListener('dragging-changed', (event) => {
if (!this?._viewer)
return;
const controls = this._viewer.scene.mainCamera.controls;
if (typeof controls?.stopDamping === 'function' && controls?.enabled)
controls.stopDamping();
this._viewer.scene.mainCamera.setInteractions(!event.value, TransformControlsPlugin_1.PluginType);
// this._viewer.scene.mainCamera.autoNearFar = !event.value // todo: maintain state
});
this.transformControls.addEventListener('axis-changed', (event) => {
if (!this?._viewer)
return;
this._isInteracting = !!event.value;
const controls = this._viewer.scene.mainCamera.controls;
if (typeof controls?.stopDamping === 'function' && controls?.enabled)
controls.stopDamping();
this._viewer.setDirty(); // rerender for color change
});
viewer.scene.addObject(this.transformControls, { addToRoot: true });
const picking = viewer.getPlugin(PickingPlugin);
picking.addEventListener('selectedObjectChanged', (event) => {
if (!this.transformControls)
return;
if (this.isDisabled()) {
if (this.transformControls.object)
this.transformControls.detach();
return;
}
event.object ? this.transformControls.attach(event.object) : this.transformControls.detach();
});
viewer.forPlugin('UndoManagerPlugin', (um) => {
this.undoManager = um.undoManager;
}, () => this.undoManager = undefined);
// same logic for undo as three.js editor. todo It can be made better by syncing with the UI so it supports the hotkeys and other properties inside TransformControls2
this.transformControls.addEventListener('mouseDown', () => {
if (!this.transformControls)
return;
const object = this.transformControls.object;
if (!object)
return;
this._transformState.obj = object;
this._transformState.position = object.position.clone();
this._transformState.rotation = object.rotation.clone();
this._transformState.scale = object.scale.clone();
});
this.transformControls.addEventListener('mouseUp', () => {
if (!this.transformControls)
return;
const object = this.transformControls.object;
if (!object)
return;
if (this._transformState.obj !== object || !this.undoManager)
return;
const key = {
'translate': 'position',
'rotate': 'rotation',
'scale': 'scale',
}[this.transformControls.getMode()];
if (!key)
return;
if (this._transformState[key].equals(object[key]))
return;
const command = {
last: this._transformState[key].clone(), current: object[key].clone(),
set: (value) => {
object[key].copy(value);
object.updateMatrixWorld(true);
this.transformControls?.dispatchEvent({ type: 'change' });
this.transformControls?.dispatchEvent({ type: 'objectChange' });
},
undo: () => command.set(command.last),
redo: () => command.set(command.current),
};
this.undoManager.record(command);
});
}
onRemove(viewer) {
viewer.scene.removeEventListener('mainCameraChange', this._mainCameraChange);
if (this.transformControls) {
this.transformControls.detach();
viewer.scene.remove(this.transformControls);
this.transformControls.dispose();
}
this.transformControls = undefined;
super.onRemove(viewer);
}
centerAllMeshes() {
return this._viewer?.scene.centerAllGeometries(true);
}
};
TransformControlsPlugin.PluginType = 'TransformControlsPlugin';
__decorate([
uiToggle(),
onChange(TransformControlsPlugin.prototype.setDirty)
], TransformControlsPlugin.prototype, "enabled", void 0);
__decorate([
uiConfig(undefined, { expanded: true })
], TransformControlsPlugin.prototype, "transformControls", void 0);
__decorate([
uiButton('Center All Meshes')
], TransformControlsPlugin.prototype, "centerAllMeshes", null);
TransformControlsPlugin = TransformControlsPlugin_1 = __decorate([
uiPanelContainer('Transform Controls')
], TransformControlsPlugin);
export { TransformControlsPlugin };
//# sourceMappingURL=TransformControlsPlugin.js.map