web-ifc-viewer
Version:
189 lines • 8.31 kB
JavaScript
import { Float32BufferAttribute, Line, Line3, MathUtils, Matrix4, PerspectiveCamera, Vector2 } from 'three';
import { ShapeCaster } from './shape-caster';
export var SelectionWindowMode;
(function (SelectionWindowMode) {
SelectionWindowMode[SelectionWindowMode["lasso"] = 0] = "lasso";
SelectionWindowMode[SelectionWindowMode["box"] = 1] = "box";
})(SelectionWindowMode || (SelectionWindowMode = {}));
export class SelectionWindow {
constructor(context) {
this.context = context;
this.toolMode = SelectionWindowMode.box;
this.selectionShape = new Line();
this.dragging = false;
this.selectionPoints = [];
this.selectionShapeNeedsUpdate = false;
this.selectionNeedsUpdate = false;
this.startX = -Infinity;
this.startY = -Infinity;
this.prevX = -Infinity;
this.prevY = -Infinity;
this.tempVec0 = new Vector2();
this.tempVec1 = new Vector2();
this.tempVec2 = new Vector2();
this.toScreenSpaceMatrix = new Matrix4();
this.lassoSegments = [];
this.caster = new ShapeCaster(this.toScreenSpaceMatrix, this.lassoSegments);
this.setupSelectionShape();
this.updateAll();
}
setupSelectionShape() {
this.selectionShape = new Line();
const mat = this.selectionShape.material;
mat.depthTest = false;
mat.color.set(0xff9800).convertSRGBToLinear();
this.selectionShape.renderOrder = 1;
this.selectionShape.position.z = -0.2;
this.selectionShape.scale.setScalar(1);
this.selectionShape.frustumCulled = false;
this.context.getCamera().add(this.selectionShape);
}
onDragStarted() {
this.prevX = this.context.mouse.rawPosition.x;
this.prevY = this.context.mouse.rawPosition.y;
this.startX = this.context.mouse.position.x;
this.startY = this.context.mouse.position.y;
this.selectionPoints.length = 0;
this.dragging = true;
const camera = this.context.getCamera();
if (!camera.parent) {
this.context.getScene().add(camera);
}
if (camera instanceof PerspectiveCamera) {
const tan = Math.tan((MathUtils.DEG2RAD * camera.fov) / 2);
const yScale = tan * this.selectionShape.position.z;
this.selectionShape.scale.set(-yScale * camera.aspect, -yScale, 1);
}
}
onDragFinished() {
this.dragging = false;
this.selectionShape.visible = false;
if (this.selectionPoints.length) {
this.selectionNeedsUpdate = true;
}
this.updateAll();
}
onDrag() {
if (!this.dragging)
return;
const ex = this.context.mouse.rawPosition.x;
const ey = this.context.mouse.rawPosition.y;
const nx = this.context.mouse.position.x;
const ny = this.context.mouse.position.y;
if (this.toolMode === SelectionWindowMode.box) {
// set points for the corner of the box
this.selectionPoints.length = 3 * 5;
this.selectionPoints[0] = this.startX;
this.selectionPoints[1] = this.startY;
this.selectionPoints[2] = 0;
this.selectionPoints[3] = nx;
this.selectionPoints[4] = this.startY;
this.selectionPoints[5] = 0;
this.selectionPoints[6] = nx;
this.selectionPoints[7] = ny;
this.selectionPoints[8] = 0;
this.selectionPoints[9] = this.startX;
this.selectionPoints[10] = ny;
this.selectionPoints[11] = 0;
this.selectionPoints[12] = this.startX;
this.selectionPoints[13] = this.startY;
this.selectionPoints[14] = 0;
if (ex !== this.prevX || ey !== this.prevY) {
this.selectionShapeNeedsUpdate = true;
}
this.prevX = ex;
this.prevY = ey;
this.selectionShape.visible = true;
}
else {
// If the mouse hasn't moved a lot since the last point
const mouseDidntMuchMuch = Math.abs(ex - this.prevX) >= 3 || Math.abs(ey - this.prevY) >= 3;
if (mouseDidntMuchMuch) {
// Check if the mouse moved in roughly the same direction as the previous point
// and replace it if so.
const i = this.selectionPoints.length / 3 - 1;
const i3 = i * 3;
let doReplace = false;
if (this.selectionPoints.length > 3) {
// prev segment direction
this.tempVec0.set(this.selectionPoints[i3 - 3], this.selectionPoints[i3 - 3 + 1]);
this.tempVec1.set(this.selectionPoints[i3], this.selectionPoints[i3 + 1]);
this.tempVec1.sub(this.tempVec0).normalize();
// this segment direction
this.tempVec0.set(this.selectionPoints[i3], this.selectionPoints[i3 + 1]);
this.tempVec2.set(nx, ny);
this.tempVec2.sub(this.tempVec0).normalize();
const dot = this.tempVec1.dot(this.tempVec2);
doReplace = dot > 0.99;
}
if (doReplace) {
this.selectionPoints[i3] = nx;
this.selectionPoints[i3 + 1] = ny;
}
else {
this.selectionPoints.push(nx, ny, 0);
}
this.selectionShapeNeedsUpdate = true;
this.selectionShape.visible = true;
this.prevX = ex;
this.prevY = ey;
}
}
this.updateSelectionLasso();
}
updateSelectionLasso() {
if (this.selectionShapeNeedsUpdate) {
if (this.toolMode === SelectionWindowMode.lasso) {
const ogLength = this.selectionPoints.length;
this.selectionPoints.push(this.selectionPoints[0], this.selectionPoints[1], this.selectionPoints[2]);
this.selectionShape.geometry.setAttribute('position', new Float32BufferAttribute(this.selectionPoints, 3, false));
this.selectionPoints.length = ogLength;
}
else {
this.selectionShape.geometry.setAttribute('position', new Float32BufferAttribute(this.selectionPoints, 3, false));
}
this.selectionShapeNeedsUpdate = false;
}
}
updateAll() {
const models = this.context.items.pickableIfcModels;
models.forEach((model) => {
this.update(model);
});
this.selectionNeedsUpdate = false;
}
update(model) {
if (this.selectionNeedsUpdate && this.selectionPoints.length > 0) {
this.updateSelection(model);
}
}
updateSelection(model) {
// TODO: Possible improvements
// - Correctly handle the camera near clip
// - Improve line line intersect performance?
const camera = this.context.getCamera();
this.toScreenSpaceMatrix
.copy(model.matrixWorld)
.premultiply(camera.matrixWorldInverse)
.premultiply(camera.projectionMatrix);
// create scratch points and lines to use for selection
while (this.lassoSegments.length < this.selectionPoints.length) {
this.lassoSegments.push(new Line3());
}
this.lassoSegments.length = this.selectionPoints.length;
for (let s = 0, l = this.selectionPoints.length; s < l; s += 3) {
const line = this.lassoSegments[s];
const sNext = (s + 3) % l;
line.start.x = this.selectionPoints[s];
line.start.y = this.selectionPoints[s + 1];
line.end.x = this.selectionPoints[sNext];
line.end.y = this.selectionPoints[sNext + 1];
}
const indices = [];
this.caster.shapeCast(model, indices);
if (this.onSelected) {
this.onSelected(model, indices);
}
}
}
//# sourceMappingURL=selection-window.js.map