import Vector3 from './Vector3';
import mat4 from '../glmatrix/mat4';
import vec3 from '../glmatrix/vec3';
import vec4 from '../glmatrix/vec4';
/**
* @constructor
* @alias clay.Plane
* @param {clay.Vector3} [normal]
* @param {number} [distance]
*/
var Plane = function(normal, distance) {
/**
* Normal of the plane
* @type {clay.Vector3}
*/
this.normal = normal || new Vector3(0, 1, 0);
/**
* Constant of the plane equation, used as distance to the origin
* @type {number}
*/
this.distance = distance || 0;
};
Plane.prototype = {
constructor: Plane,
/**
* Distance from a given point to the plane
* @param {clay.Vector3} point
* @return {number}
*/
distanceToPoint: function(point) {
return vec3.dot(point.array, this.normal.array) - this.distance;
},
/**
* Calculate the projection point on the plane
* @param {clay.Vector3} point
* @param {clay.Vector3} out
* @return {clay.Vector3}
*/
projectPoint: function(point, out) {
if (!out) {
out = new Vector3();
}
var d = this.distanceToPoint(point);
vec3.scaleAndAdd(out.array, point.array, this.normal.array, -d);
out._dirty = true;
return out;
},
/**
* Normalize the plane's normal and calculate the distance
*/
normalize: function() {
var invLen = 1 / vec3.len(this.normal.array);
vec3.scale(this.normal.array, invLen);
this.distance *= invLen;
},
/**
* If the plane intersect a frustum
* @param {clay.Frustum} Frustum
* @return {boolean}
*/
intersectFrustum: function(frustum) {
// Check if all coords of frustum is on plane all under plane
var coords = frustum.vertices;
var normal = this.normal.array;
var onPlane = vec3.dot(coords[0].array, normal) > this.distance;
for (var i = 1; i < 8; i++) {
if ((vec3.dot(coords[i].array, normal) > this.distance) != onPlane) {
return true;
}
}
},
/**
* Calculate the intersection point between plane and a given line
* @function
* @param {clay.Vector3} start start point of line
* @param {clay.Vector3} end end point of line
* @param {clay.Vector3} [out]
* @return {clay.Vector3}
*/
intersectLine: (function() {
var rd = vec3.create();
return function(start, end, out) {
var d0 = this.distanceToPoint(start);
var d1 = this.distanceToPoint(end);
if ((d0 > 0 && d1 > 0) || (d0 < 0 && d1 < 0)) {
return null;
}
// Ray intersection
var pn = this.normal.array;
var d = this.distance;
var ro = start.array;
// direction
vec3.sub(rd, end.array, start.array);
vec3.normalize(rd, rd);
var divider = vec3.dot(pn, rd);
// ray is parallel to the plane
if (divider === 0) {
return null;
}
if (!out) {
out = new Vector3();
}
var t = (vec3.dot(pn, ro) - d) / divider;
vec3.scaleAndAdd(out.array, ro, rd, -t);
out._dirty = true;
return out;
};
})(),
/**
* Apply an affine transform matrix to plane
* @function
* @return {clay.Matrix4}
*/
applyTransform: (function() {
var inverseTranspose = mat4.create();
var normalv4 = vec4.create();
var pointv4 = vec4.create();
pointv4[3] = 1;
return function(m4) {
m4 = m4.array;
// Transform point on plane
vec3.scale(pointv4, this.normal.array, this.distance);
vec4.transformMat4(pointv4, pointv4, m4);
this.distance = vec3.dot(pointv4, this.normal.array);
// Transform plane normal
mat4.invert(inverseTranspose, m4);
mat4.transpose(inverseTranspose, inverseTranspose);
normalv4[3] = 0;
vec3.copy(normalv4, this.normal.array);
vec4.transformMat4(normalv4, normalv4, inverseTranspose);
vec3.copy(this.normal.array, normalv4);
};
})(),
/**
* Copy from another plane
* @param {clay.Vector3} plane
*/
copy: function(plane) {
vec3.copy(this.normal.array, plane.normal.array);
this.normal._dirty = true;
this.distance = plane.distance;
},
/**
* Clone a new plane
* @return {clay.Plane}
*/
clone: function() {
var plane = new Plane();
plane.copy(this);
return plane;
}
};
export default Plane;