Working clipping functionality
This commit is contained in:
parent
13be63c40a
commit
20d99b5815
4 changed files with 313 additions and 50 deletions
|
|
@ -2,6 +2,7 @@ import {
|
|||
DoubleSide,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
Object3DEventMap,
|
||||
PerspectiveCamera,
|
||||
Plane,
|
||||
PlaneGeometry,
|
||||
|
|
@ -10,60 +11,304 @@ import {
|
|||
} from "three";
|
||||
import { DragControls, OrbitControls } from "three/examples/jsm/Addons.js";
|
||||
import { Extent } from "./build-scene";
|
||||
import { getCenter3D } from "./utils";
|
||||
|
||||
export function createClippingPlane(
|
||||
enum Orientation {
|
||||
X = "x",
|
||||
Y = "y",
|
||||
Z = "z",
|
||||
}
|
||||
|
||||
type PlaneMesh = Mesh<PlaneGeometry, MeshBasicMaterial, Object3DEventMap>;
|
||||
type PlaneMeshMap = {
|
||||
[key in Orientation]: PlaneMesh;
|
||||
};
|
||||
|
||||
export function createClippingPlanes(
|
||||
renderer: WebGLRenderer,
|
||||
camera: PerspectiveCamera,
|
||||
orbitControls: OrbitControls,
|
||||
extent: Extent
|
||||
) {
|
||||
const center = getCenter3D(extent);
|
||||
const planesData = [
|
||||
{
|
||||
normal: new Vector3(1, 0, 0),
|
||||
d: -extent.xmin,
|
||||
orientation: Orientation.X,
|
||||
},
|
||||
{
|
||||
normal: new Vector3(0, 1, 0),
|
||||
d: -extent.ymin,
|
||||
orientation: Orientation.Y,
|
||||
},
|
||||
{
|
||||
normal: new Vector3(0, 0, -1),
|
||||
d: extent.zmax,
|
||||
orientation: Orientation.Z,
|
||||
},
|
||||
];
|
||||
|
||||
const width = extent.xmax - extent.xmin;
|
||||
const height = extent.ymax - extent.ymin;
|
||||
const d = extent.zmax;
|
||||
const planeMeshes: Mesh<
|
||||
PlaneGeometry,
|
||||
MeshBasicMaterial,
|
||||
Object3DEventMap
|
||||
>[] = [];
|
||||
const planes: Plane[] = [];
|
||||
let planeMeshMap = {} as Partial<PlaneMeshMap>;
|
||||
for (let p of planesData) {
|
||||
let name;
|
||||
let planeCenter;
|
||||
let width;
|
||||
let height;
|
||||
if (p.orientation === Orientation.X) {
|
||||
name = Orientation.X;
|
||||
width = extent.ymax - extent.ymin;
|
||||
height = extent.zmax - extent.zmin;
|
||||
planeCenter = new Vector3(
|
||||
-p.d,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
} else if (p.orientation === Orientation.Y) {
|
||||
name = Orientation.Y;
|
||||
width = extent.xmax - extent.xmin;
|
||||
height = extent.zmax - extent.zmin;
|
||||
planeCenter = new Vector3(
|
||||
extent.xmax - width / 2,
|
||||
-p.d,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
} else {
|
||||
name = Orientation.Z;
|
||||
width = extent.xmax - extent.xmin;
|
||||
height = extent.ymax - extent.ymin;
|
||||
planeCenter = new Vector3(
|
||||
extent.xmax - width / 2,
|
||||
extent.ymax - height / 2,
|
||||
p.d
|
||||
);
|
||||
}
|
||||
|
||||
// Visual representation of the clipping Plane
|
||||
// Plane is given in Hesse normal form
|
||||
const normalVector = new Vector3(0, 0, -1);
|
||||
const plane = new Plane(normalVector, d);
|
||||
// Visual representation of the clipping plane
|
||||
// Plane is given in Hesse normal form
|
||||
const plane = new Plane(p.normal, p.d);
|
||||
|
||||
// Dragging Mechanism
|
||||
const planeMesh = new Mesh(
|
||||
new PlaneGeometry(width, height),
|
||||
new MeshBasicMaterial({
|
||||
visible: true,
|
||||
color: 0xff0000,
|
||||
transparent: true,
|
||||
opacity: 0.1,
|
||||
side: DoubleSide,
|
||||
})
|
||||
);
|
||||
planeMesh.position.set(center.x, center.y, d);
|
||||
// Dragging Mechanism
|
||||
const planeMesh = new Mesh(
|
||||
new PlaneGeometry(width, height),
|
||||
new MeshBasicMaterial({
|
||||
visible: true,
|
||||
color: 0xff0000,
|
||||
transparent: true,
|
||||
opacity: 0.1,
|
||||
side: DoubleSide,
|
||||
clipIntersection: false,
|
||||
})
|
||||
);
|
||||
planeMesh.name = name;
|
||||
planeMesh.userData.plane = plane;
|
||||
|
||||
planeMesh.position.set(planeCenter.x, planeCenter.y, planeCenter.z);
|
||||
if (p.orientation === Orientation.X) {
|
||||
planeMesh.rotateY(Math.PI / 2);
|
||||
planeMesh.rotateZ(Math.PI / 2);
|
||||
} else if (p.orientation === Orientation.Y) {
|
||||
planeMesh.rotateX(Math.PI / 2);
|
||||
}
|
||||
planeMeshes.push(planeMesh);
|
||||
planes.push(plane);
|
||||
|
||||
planeMeshMap[p.orientation] = planeMesh;
|
||||
}
|
||||
|
||||
for (let pm of planeMeshes) {
|
||||
// Let clipping planes clip each other
|
||||
const clippingPlanes = planes.filter(
|
||||
(p) => !p.normal.equals(pm.userData.plane.normal)
|
||||
);
|
||||
|
||||
pm.material.clippingPlanes = clippingPlanes;
|
||||
}
|
||||
|
||||
// Enable DragControls for the clipping planes
|
||||
const dragControls = new DragControls(
|
||||
[planeMesh],
|
||||
planeMeshes,
|
||||
camera,
|
||||
renderer.domElement
|
||||
);
|
||||
|
||||
// Disable OrbitControls when dragging starts
|
||||
dragControls.addEventListener("dragstart", () => {
|
||||
// Disable OrbitControls when dragging starts
|
||||
orbitControls.enabled = false;
|
||||
});
|
||||
|
||||
// Re-enable OrbitControls when dragging ends
|
||||
dragControls.addEventListener("dragend", () => {
|
||||
// Reenable OrbitControls when dragging ends
|
||||
orbitControls.enabled = true;
|
||||
});
|
||||
|
||||
dragControls.addEventListener("drag", (event) => {
|
||||
const newZ = event.object.position.z;
|
||||
plane.constant = newZ;
|
||||
planeMesh.position.x = center.x;
|
||||
planeMesh.position.y = center.y;
|
||||
const object = event.object as PlaneMesh;
|
||||
const plane = event.object.userData.plane;
|
||||
const width = object.geometry.parameters.width;
|
||||
const height = object.geometry.parameters.height;
|
||||
if (object.name === Orientation.Z) {
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(0, 0, 0);
|
||||
|
||||
let newZ;
|
||||
if (event.object.position.z > extent.zmax) {
|
||||
newZ = extent.zmax;
|
||||
} else if (event.object.position.z < extent.zmin) {
|
||||
newZ = extent.zmin;
|
||||
} else {
|
||||
newZ = event.object.position.z;
|
||||
}
|
||||
|
||||
// Reset position of plane
|
||||
plane.constant = newZ;
|
||||
|
||||
// Set position of dragged mesh
|
||||
object.position.x = extent.xmax - width / 2;
|
||||
object.position.y = extent.ymax - height / 2;
|
||||
object.position.z = newZ;
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.Z, newZ, planeMeshMap as PlaneMeshMap, extent);
|
||||
} else if (object.name === Orientation.Y) {
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(Math.PI / 2, 0, 0);
|
||||
|
||||
let newY;
|
||||
if (event.object.position.y > extent.ymax) {
|
||||
newY = extent.ymax;
|
||||
} else if (event.object.position.y < extent.ymin) {
|
||||
newY = extent.ymin;
|
||||
} else {
|
||||
newY = event.object.position.y;
|
||||
}
|
||||
|
||||
// Reset position of plane
|
||||
plane.constant = -newY;
|
||||
|
||||
// Set position of dragged mesh
|
||||
object.position.x = extent.xmax - width / 2;
|
||||
object.position.y = newY;
|
||||
object.position.z = extent.zmax - height / 2;
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.Y, newY, planeMeshMap as PlaneMeshMap, extent);
|
||||
} else {
|
||||
// Fix rotation of dragged mesh
|
||||
event.object.rotation.set(0, Math.PI / 2, Math.PI / 2);
|
||||
|
||||
let newX;
|
||||
if (event.object.position.x > extent.xmax) {
|
||||
newX = extent.xmax;
|
||||
} else if (event.object.position.x < extent.xmin) {
|
||||
newX = extent.xmin;
|
||||
} else {
|
||||
newX = event.object.position.x;
|
||||
}
|
||||
|
||||
// Reset position of plane
|
||||
plane.constant = -newX;
|
||||
|
||||
// Set position of dragged mesh
|
||||
object.position.x = newX;
|
||||
object.position.y = extent.ymax - width / 2;
|
||||
object.position.z = extent.zmax - height / 2;
|
||||
|
||||
// Resize other meshes
|
||||
resizeMeshes(Orientation.X, newX, planeMeshMap as PlaneMeshMap, extent);
|
||||
}
|
||||
});
|
||||
|
||||
return { planeMesh, plane };
|
||||
return { planeMeshes, planes };
|
||||
}
|
||||
|
||||
function resizeMeshes(
|
||||
orientation: Orientation,
|
||||
newCoordinate: number,
|
||||
planeMeshes: PlaneMeshMap,
|
||||
extent: Extent
|
||||
) {
|
||||
if (orientation === Orientation.X) {
|
||||
// Resize y-clipping plane
|
||||
let planeMesh = planeMeshes[Orientation.Y];
|
||||
let width = extent.xmax - newCoordinate;
|
||||
let height = planeMesh.geometry.parameters.height;
|
||||
const y = planeMesh.position.y;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
y,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
|
||||
// Resize z-clipping-plane
|
||||
planeMesh = planeMeshes[Orientation.Z];
|
||||
width = extent.xmax - newCoordinate;
|
||||
height = planeMesh.geometry.parameters.height;
|
||||
const z = planeMesh.position.z;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
extent.ymax - height / 2,
|
||||
z
|
||||
);
|
||||
} else if (orientation === Orientation.Y) {
|
||||
// Resize x-clipping plane
|
||||
let planeMesh = planeMeshes[Orientation.X];
|
||||
let width = extent.ymax - newCoordinate;
|
||||
let height = planeMesh.geometry.parameters.height;
|
||||
const x = planeMesh.position.x;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
x,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
|
||||
// Resize z-clipping-plane
|
||||
planeMesh = planeMeshes[Orientation.Z];
|
||||
width = planeMesh.geometry.parameters.width;
|
||||
height = extent.ymax - newCoordinate;
|
||||
const z = planeMesh.position.z;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
extent.ymax - height / 2,
|
||||
z
|
||||
);
|
||||
} else if (orientation === Orientation.Z) {
|
||||
// Resize x-clipping-plane
|
||||
let planeMesh = planeMeshes[Orientation.X];
|
||||
let width = planeMesh.geometry.parameters.width;
|
||||
let height = newCoordinate - extent.zmin;
|
||||
const x = planeMesh.position.x;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
x,
|
||||
extent.ymax - width / 2,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
|
||||
// Resize y-clipping plane
|
||||
planeMesh = planeMeshes[Orientation.Y];
|
||||
width = planeMesh.geometry.parameters.width;
|
||||
height = newCoordinate - extent.zmin;
|
||||
const y = planeMesh.position.y;
|
||||
planeMesh.geometry.dispose();
|
||||
planeMesh.geometry = new PlaneGeometry(width, height);
|
||||
planeMesh.position.set(
|
||||
extent.xmax - width / 2,
|
||||
y,
|
||||
extent.zmax - height / 2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue