ami.js
Version:
<p align="center"> <img src="https://cloud.githubusercontent.com/assets/214063/23213764/78ade038-f90c-11e6-8208-4fcade5f3832.png" width="60%"> </p>
194 lines (170 loc) • 5.69 kB
JavaScript
/** * Imports ***/
import coreIntersections from '../core/core.intersections';
import {Matrix4, Vector3} from 'three';
/**
*
* It is typically used for creating an irregular 3D planar shape given a box and the cut-plane.
*
* Demo: {@link https://fnndsc.github.io/vjs#geometry_slice}
*
* @module geometries/slice
*
* @param {Vector3} halfDimensions - Half-dimensions of the box to be sliced.
* @param {Vector3} center - Center of the box to be sliced.
* @param {Vector3<Vector3>} orientation - Orientation of the box to be sliced. (might not be necessary..?)
* @param {Vector3} position - Position of the cutting plane.
* @param {Vector3} direction - Cross direction of the cutting plane.
*
* @example
* // Define box to be sliced
* let halfDimensions = new THREE.Vector(123, 45, 67);
* let center = new Vector3(0, 0, 0);
* let orientation = new Vector3(
* new Vector3(1, 0, 0),
* new Vector3(0, 1, 0),
* new Vector3(0, 0, 1)
* );
*
* // Define slice plane
* let position = center.clone();
* let direction = new Vector3(-0.2, 0.5, 0.3);
*
* // Create the slice geometry & materials
* let sliceGeometry = new VJS.geometries.slice(halfDimensions, center, orientation, position, direction);
* let sliceMaterial = new THREE.MeshBasicMaterial({
* 'side': THREE.DoubleSide,
* 'color': 0xFF5722
* });
*
* // Create mesh and add it to the scene
* let slice = new THREE.Mesh(sliceGeometry, sliceMaterial);
* scene.add(slice);
*/
export default class GeometriesSlice extends THREE.ShapeGeometry {
constructor(halfDimensions, center, position, direction, toAABB = new Matrix4()) {
//
// prepare data for the shape!
//
let aabb = {
halfDimensions,
center,
toAABB,
};
let plane = {
position,
direction,
};
// BOOM!
let intersections = coreIntersections.aabbPlane(aabb, plane);
// can not exist before calling the constructor
if (intersections.length < 3) {
window.console.log('WARNING: Less than 3 intersections between AABB and Plane.');
window.console.log('AABB');
window.console.log(aabb);
window.console.log('Plane');
window.console.log(plane);
window.console.log('exiting...');
// or throw error?
throw 'geometries.slice has less than 3 intersections, can not create a valid geometry.';
}
let orderedIntersections = GeometriesSlice.orderIntersections(intersections, direction);
let sliceShape = GeometriesSlice.shape(orderedIntersections);
//
// Generate Geometry from shape
// It does triangulation for us!
//
super(sliceShape);
this.type = 'SliceGeometry';
// update real position of each vertex! (not in 2d)
this.vertices = orderedIntersections;
this.verticesNeedUpdate = true;
}
static shape(points) {
//
// Create Shape
//
let shape = new THREE.Shape();
// move to first point!
shape.moveTo(points[0].xy.x, points[0].xy.y);
// loop through all points!
for (let l = 1; l < points.length; l++) {
// project each on plane!
shape.lineTo(points[l].xy.x, points[l].xy.y);
}
// close the shape!
shape.lineTo(points[0].xy.x, points[0].xy.y);
return shape;
}
/**
*
* Convenience function to extract center of mass from list of points.
*
* @private
*
* @param {Array<Vector3>} points - Set of points from which we want to extract the center of mass.
*
* @returns {Vector3} Center of mass from given points.
*/
static centerOfMass(points) {
let centerOfMass = new Vector3(0, 0, 0);
for (let i = 0; i < points.length; i++) {
centerOfMass.x += points[i].x;
centerOfMass.y += points[i].y;
centerOfMass.z += points[i].z;
}
centerOfMass.divideScalar(points.length);
return centerOfMass;
}
/**
*
* Order 3D planar points around a refence point.
*
* @private
*
* @param {Array<Vector3>} points - Set of planar 3D points to be ordered.
* @param {Vector3} direction - Direction of the plane in which points and reference are sitting.
*
* @returns {Array<Object>} Set of object representing the ordered points.
*/
static orderIntersections(points, direction) {
let reference = GeometriesSlice.centerOfMass(points);
// direction from first point to reference
let referenceDirection = new Vector3(
points[0].x - reference.x,
points[0].y - reference.y,
points[0].z - reference.z
).normalize();
let base = new Vector3(0, 0, 0)
.crossVectors(referenceDirection, direction)
.normalize();
let orderedpoints = [];
// other lines // if inter, return location + angle
for (let j = 0; j < points.length; j++) {
let point = new Vector3(
points[j].x,
points[j].y,
points[j].z);
point.direction = new Vector3(
points[j].x - reference.x,
points[j].y - reference.y,
points[j].z - reference.z).normalize();
let x = referenceDirection.dot(point.direction);
let y = base.dot(point.direction);
point.xy = {x, y};
let theta = Math.atan2(y, x) * (180 / Math.PI);
point.angle = theta;
orderedpoints.push(point);
}
orderedpoints.sort(function(a, b) {
return a.angle - b.angle;
});
let noDups = [orderedpoints[0]];
let epsilon = 0.0001;
for (let i=1; i<orderedpoints.length; i++) {
if (Math.abs(orderedpoints[i-1].angle - orderedpoints[i].angle) > epsilon) {
noDups.push(orderedpoints[i]);
}
}
return noDups;
}
}