import Vector3 from './Vector3';
import vec3 from '../glmatrix/vec3';
var vec3Set = vec3.set;
var vec3Copy = vec3.copy;
/**
* Axis aligned bounding box
* @constructor
* @alias clay.BoundingBox
* @param {clay.Vector3} [min]
* @param {clay.Vector3} [max]
*/
var BoundingBox = function (min, max) {
/**
* Minimum coords of bounding box
* @type {clay.Vector3}
*/
this.min = min || new Vector3(Infinity, Infinity, Infinity);
/**
* Maximum coords of bounding box
* @type {clay.Vector3}
*/
this.max = max || new Vector3(-Infinity, -Infinity, -Infinity);
this.vertices = null;
};
BoundingBox.prototype = {
constructor: BoundingBox,
/**
* Update min and max coords from a vertices array
* @param {array} vertices
*/
updateFromVertices: function (vertices) {
if (vertices.length > 0) {
var min = this.min;
var max = this.max;
var minArr = min.array;
var maxArr = max.array;
vec3Copy(minArr, vertices[0]);
vec3Copy(maxArr, vertices[0]);
for (var i = 1; i < vertices.length; i++) {
var vertex = vertices[i];
if (vertex[0] < minArr[0]) { minArr[0] = vertex[0]; }
if (vertex[1] < minArr[1]) { minArr[1] = vertex[1]; }
if (vertex[2] < minArr[2]) { minArr[2] = vertex[2]; }
if (vertex[0] > maxArr[0]) { maxArr[0] = vertex[0]; }
if (vertex[1] > maxArr[1]) { maxArr[1] = vertex[1]; }
if (vertex[2] > maxArr[2]) { maxArr[2] = vertex[2]; }
}
min._dirty = true;
max._dirty = true;
}
},
/**
* Union operation with another bounding box
* @param {clay.BoundingBox} bbox
*/
union: function (bbox) {
var min = this.min;
var max = this.max;
vec3.min(min.array, min.array, bbox.min.array);
vec3.max(max.array, max.array, bbox.max.array);
min._dirty = true;
max._dirty = true;
return this;
},
/**
* Intersection operation with another bounding box
* @param {clay.BoundingBox} bbox
*/
intersection: function (bbox) {
var min = this.min;
var max = this.max;
vec3.max(min.array, min.array, bbox.min.array);
vec3.min(max.array, max.array, bbox.max.array);
min._dirty = true;
max._dirty = true;
return this;
},
/**
* If intersect with another bounding box
* @param {clay.BoundingBox} bbox
* @return {boolean}
*/
intersectBoundingBox: function (bbox) {
var _min = this.min.array;
var _max = this.max.array;
var _min2 = bbox.min.array;
var _max2 = bbox.max.array;
return ! (_min[0] > _max2[0] || _min[1] > _max2[1] || _min[2] > _max2[2]
|| _max[0] < _min2[0] || _max[1] < _min2[1] || _max[2] < _min2[2]);
},
/**
* If contain another bounding box entirely
* @param {clay.BoundingBox} bbox
* @return {boolean}
*/
containBoundingBox: function (bbox) {
var _min = this.min.array;
var _max = this.max.array;
var _min2 = bbox.min.array;
var _max2 = bbox.max.array;
return _min[0] <= _min2[0] && _min[1] <= _min2[1] && _min[2] <= _min2[2]
&& _max[0] >= _max2[0] && _max[1] >= _max2[1] && _max[2] >= _max2[2];
},
/**
* If contain point entirely
* @param {clay.Vector3} point
* @return {boolean}
*/
containPoint: function (p) {
var _min = this.min.array;
var _max = this.max.array;
var _p = p.array;
return _min[0] <= _p[0] && _min[1] <= _p[1] && _min[2] <= _p[2]
&& _max[0] >= _p[0] && _max[1] >= _p[1] && _max[2] >= _p[2];
},
/**
* If bounding box is finite
*/
isFinite: function () {
var _min = this.min.array;
var _max = this.max.array;
return isFinite(_min[0]) && isFinite(_min[1]) && isFinite(_min[2])
&& isFinite(_max[0]) && isFinite(_max[1]) && isFinite(_max[2]);
},
/**
* Apply an affine transform matrix to the bounding box
* @param {clay.Matrix4} matrix
*/
applyTransform: function (matrix) {
this.transformFrom(this, matrix);
},
/**
* Get from another bounding box and an affine transform matrix.
* @param {clay.BoundingBox} source
* @param {clay.Matrix4} matrix
*/
transformFrom: (function () {
// http://dev.theomader.com/transform-bounding-boxes/
var xa = vec3.create();
var xb = vec3.create();
var ya = vec3.create();
var yb = vec3.create();
var za = vec3.create();
var zb = vec3.create();
return function (source, matrix) {
var min = source.min.array;
var max = source.max.array;
var m = matrix.array;
xa[0] = m[0] * min[0]; xa[1] = m[1] * min[0]; xa[2] = m[2] * min[0];
xb[0] = m[0] * max[0]; xb[1] = m[1] * max[0]; xb[2] = m[2] * max[0];
ya[0] = m[4] * min[1]; ya[1] = m[5] * min[1]; ya[2] = m[6] * min[1];
yb[0] = m[4] * max[1]; yb[1] = m[5] * max[1]; yb[2] = m[6] * max[1];
za[0] = m[8] * min[2]; za[1] = m[9] * min[2]; za[2] = m[10] * min[2];
zb[0] = m[8] * max[2]; zb[1] = m[9] * max[2]; zb[2] = m[10] * max[2];
min = this.min.array;
max = this.max.array;
min[0] = Math.min(xa[0], xb[0]) + Math.min(ya[0], yb[0]) + Math.min(za[0], zb[0]) + m[12];
min[1] = Math.min(xa[1], xb[1]) + Math.min(ya[1], yb[1]) + Math.min(za[1], zb[1]) + m[13];
min[2] = Math.min(xa[2], xb[2]) + Math.min(ya[2], yb[2]) + Math.min(za[2], zb[2]) + m[14];
max[0] = Math.max(xa[0], xb[0]) + Math.max(ya[0], yb[0]) + Math.max(za[0], zb[0]) + m[12];
max[1] = Math.max(xa[1], xb[1]) + Math.max(ya[1], yb[1]) + Math.max(za[1], zb[1]) + m[13];
max[2] = Math.max(xa[2], xb[2]) + Math.max(ya[2], yb[2]) + Math.max(za[2], zb[2]) + m[14];
this.min._dirty = true;
this.max._dirty = true;
return this;
};
})(),
/**
* Apply a projection matrix to the bounding box
* @param {clay.Matrix4} matrix
*/
applyProjection: function (matrix) {
var min = this.min.array;
var max = this.max.array;
var m = matrix.array;
// min in min z
var v10 = min[0];
var v11 = min[1];
var v12 = min[2];
// max in min z
var v20 = max[0];
var v21 = max[1];
var v22 = min[2];
// max in max z
var v30 = max[0];
var v31 = max[1];
var v32 = max[2];
if (m[15] === 1) { // Orthographic projection
min[0] = m[0] * v10 + m[12];
min[1] = m[5] * v11 + m[13];
max[2] = m[10] * v12 + m[14];
max[0] = m[0] * v30 + m[12];
max[1] = m[5] * v31 + m[13];
min[2] = m[10] * v32 + m[14];
}
else {
var w = -1 / v12;
min[0] = m[0] * v10 * w;
min[1] = m[5] * v11 * w;
max[2] = (m[10] * v12 + m[14]) * w;
w = -1 / v22;
max[0] = m[0] * v20 * w;
max[1] = m[5] * v21 * w;
w = -1 / v32;
min[2] = (m[10] * v32 + m[14]) * w;
}
this.min._dirty = true;
this.max._dirty = true;
return this;
},
updateVertices: function () {
var vertices = this.vertices;
if (!vertices) {
// Cube vertices
vertices = [];
for (var i = 0; i < 8; i++) {
vertices[i] = vec3.fromValues(0, 0, 0);
}
/**
* Eight coords of bounding box
* @type {Float32Array[]}
*/
this.vertices = vertices;
}
var min = this.min.array;
var max = this.max.array;
//--- min z
// min x
vec3Set(vertices[0], min[0], min[1], min[2]);
vec3Set(vertices[1], min[0], max[1], min[2]);
// max x
vec3Set(vertices[2], max[0], min[1], min[2]);
vec3Set(vertices[3], max[0], max[1], min[2]);
//-- max z
vec3Set(vertices[4], min[0], min[1], max[2]);
vec3Set(vertices[5], min[0], max[1], max[2]);
vec3Set(vertices[6], max[0], min[1], max[2]);
vec3Set(vertices[7], max[0], max[1], max[2]);
return this;
},
/**
* Copy values from another bounding box
* @param {clay.BoundingBox} bbox
*/
copy: function (bbox) {
var min = this.min;
var max = this.max;
vec3Copy(min.array, bbox.min.array);
vec3Copy(max.array, bbox.max.array);
min._dirty = true;
max._dirty = true;
return this;
},
/**
* Clone a new bounding box
* @return {clay.BoundingBox}
*/
clone: function () {
var boundingBox = new BoundingBox();
boundingBox.copy(this);
return boundingBox;
}
};
export default BoundingBox;