@awayjs/core
Version:
AwayJS core classes
792 lines (791 loc) • 31.7 kB
JavaScript
import { Vector3D } from './Vector3D';
/**
* A Box object is an area defined by its position, as indicated by its
* top-left-front corner point(<i>x</i>, <i>y</i>, <i>z</i>) and by its width,
* height and depth.
*
*
* <p>The <code>x</code>, <code>y</code>, <code>z</code>, <code>width</code>,
* <code>height</code> <code>depth</code> properties of the Box export class are
* independent of each other; changing the value of one property has no effect
* on the others. However, the <code>right</code>, <code>bottom</code> and
* <code>back</code> properties are integrally related to those six
* properties. For example, if you change the value of the <code>right</code>
* property, the value of the <code>width</code> property changes; if you
* change the <code>bottom</code> property, the value of the
* <code>height</code> property changes. </p>
*
* <p>The following methods and properties use Box objects:</p>
*
* <ul>
* <li>The <code>bounds</code> property of the DisplayObject class</li>
* </ul>
*
* <p>You can use the <code>new Box()</code> constructor to create a
* Box object.</p>
*
* <p><b>Note:</b> The Box export class does not define a cubic Shape
* display object.
*/
var Box = /** @class */ (function () {
function Box(x, y, z, width, height, depth) {
if (x === void 0) { x = 0; }
if (y === void 0) { y = 0; }
if (z === void 0) { z = 0; }
if (width === void 0) { width = 0; }
if (height === void 0) { height = 0; }
if (depth === void 0) { depth = 0; }
if (x instanceof Float32Array) {
this._rawData = x;
}
else {
var raw = this._rawData = new Float32Array(6);
raw[0] = x;
raw[1] = y;
raw[2] = z;
raw[3] = width;
raw[4] = height;
raw[5] = depth;
}
}
Object.defineProperty(Box.prototype, "height", {
/**
* The height of the box, in pixels. Changing the <code>height</code> value
* of a Box object has no effect on the <code>x</code>, <code>y</code>,
* <code>z</code>, <code>depth</code> and <code>width</code> properties.
*/
get: function () {
return this._rawData[4];
},
set: function (value) {
this._rawData[4] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "width", {
/**
* The width of the box, in pixels. Changing the <code>width</code> value
* of a Box object has no effect on the <code>x</code>, <code>y</code>,
* <code>z</code>, <code>depth</code> and <code>height</code> properties.
*/
get: function () {
return this._rawData[3];
},
set: function (value) {
this._rawData[3] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "depth", {
/**
* The deoth of the box, in pixels. Changing the <code>depth</code> value
* of a Box object has no effect on the <code>x</code>, <code>y</code>,
* <code>z</code>, <code>width</code> and <code>height</code> properties.
*/
get: function () {
return this._rawData[5];
},
set: function (value) {
this._rawData[5] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "x", {
/**
* The <i>x</i> coordinate of the top-left-front corner of the box.
* Changing the value of the <code>x</code> property of a Box object has no
* effect on the <code>y</code>, <code>z</code>, <code>width</code>,
* <code>height</code> and <code>depth</code> properties.
*
* <p>The value of the <code>x</code> property is equal to the value of the
* <code>left</code> property.</p>
*/
get: function () {
return this._rawData[0];
},
set: function (value) {
this._rawData[0] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "y", {
/**
* The <i>y</i> coordinate of the top-left-front corner of the box.
* Changing the value of the <code>y</code> property of a Box object has no
* effect on the <code>x</code>, <code>z</code>, <code>width</code>,
* <code>height</code> and <code>depth</code> properties.
*
* <p>The value of the <code>y</code> property is equal to the value of the
* <code>top</code> property.</p>
*/
get: function () {
return this._rawData[1];
},
set: function (value) {
this._rawData[1] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "z", {
/**
* The <i>y</i> coordinate of the top-left-front corner of the box.
* Changing the value of the <code>z</code> property of a Box object has no
* effect on the <code>x</code>, <code>y</code>, <code>width</code>,
* <code>height</code> and <code>depth</code> properties.
*
* <p>The value of the <code>z</code> property is equal to the value of the
* <code>front</code> property.</p>
*/
get: function () {
return this._rawData[2];
},
set: function (value) {
this._rawData[2] = value;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "back", {
/**
* The sum of the <code>z</code> and <code>height</code> properties.
*/
get: function () {
return this.z + this.depth;
},
set: function (val) {
this.depth = val - this.z;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "bottom", {
/**
* The sum of the <code>y</code> and <code>height</code> properties.
*/
get: function () {
return this.y + this.height;
},
set: function (val) {
this.height = val - this.y;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "bottomRightBack", {
/**
* The location of the Box object's bottom-right corner, determined by the
* values of the <code>right</code> and <code>bottom</code> properties.
*/
get: function () {
if (this._bottomRightBack == null)
this._bottomRightBack = new Vector3D();
this._bottomRightBack.x = this.x + this.width;
this._bottomRightBack.y = this.y + this.height;
this._bottomRightBack.z = this.z + this.depth;
return this._bottomRightBack;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "front", {
/**
* The <i>z</i> coordinate of the top-left-front corner of the box. Changing
* the <code>front</code> property of a Box object has no effect on the
* <code>x</code>, <code>y</code>, <code>width</code> and <code>height</code>
* properties. However it does affect the <code>depth</code> property,
* whereas changing the <code>z</code> value does <i>not</i> affect the
* <code>depth</code> property.
*
* <p>The value of the <code>left</code> property is equal to the value of
* the <code>x</code> property.</p>
*/
get: function () {
return this.z;
},
set: function (val) {
this.depth += this.z - val;
this.z = val;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "left", {
/**
* The <i>x</i> coordinate of the top-left corner of the box. Changing the
* <code>left</code> property of a Box object has no effect on the
* <code>y</code> and <code>height</code> properties. However it does affect
* the <code>width</code> property, whereas changing the <code>x</code> value
* does <i>not</i> affect the <code>width</code> property.
*
* <p>The value of the <code>left</code> property is equal to the value of
* the <code>x</code> property.</p>
*/
get: function () {
return this.x;
},
set: function (val) {
this.width += this.x - val;
this.x = val;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "right", {
/**
* The sum of the <code>x</code> and <code>width</code> properties.
*/
get: function () {
return this.x + this.width;
},
set: function (val) {
this.width = val - this.x;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "size", {
/**
* The size of the Box object, expressed as a Vector3D object with the
* values of the <code>width</code>, <code>height</code> and
* <code>depth</code> properties.
*/
get: function () {
if (this._size == null)
this._size = new Vector3D();
this._size.x = this.width;
this._size.y = this.height;
this._size.z = this.depth;
return this._size;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "top", {
/**
* The <i>y</i> coordinate of the top-left-front corner of the box. Changing
* the <code>top</code> property of a Box object has no effect on the
* <code>x</code> and <code>width</code> properties. However it does affect
* the <code>height</code> property, whereas changing the <code>y</code>
* value does <i>not</i> affect the <code>height</code> property.
*
* <p>The value of the <code>top</code> property is equal to the value of the
* <code>y</code> property.</p>
*/
get: function () {
return this.y;
},
set: function (val) {
this.height += (this.y - val);
this.y = val;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Box.prototype, "topLeftFront", {
/**
* The location of the Box object's top-left-front corner, determined by the
* <i>x</i>, <i>y</i> and <i>z</i> coordinates of the point.
*/
get: function () {
if (this._topLeftFront == null)
this._topLeftFront = new Vector3D();
this._topLeftFront.x = this.x;
this._topLeftFront.y = this.y;
this._topLeftFront.z = this.z;
return this._topLeftFront;
},
enumerable: false,
configurable: true
});
/**
* Returns a new Box object with the same values for the <code>x</code>,
* <code>y</code>, <code>z</code>, <code>width</code>, <code>height</code>
* and <code>depth</code> properties as the original Box object.
*
* @return A new Box object with the same values for the <code>x</code>,
* <code>y</code>, <code>z</code>, <code>width</code>,
* <code>height</code> and <code>depth</code> properties as the
* original Box object.
*/
Box.prototype.clone = function () {
return new Box(this._rawData);
};
/**
* Determines whether the specified position is contained within the cubic
* region defined by this Box object.
*
* @param x The <i>x</i> coordinate(horizontal component) of the position.
* @param y The <i>y</i> coordinate(vertical component) of the position.
* @param z The <i>z</i> coordinate(longitudinal component) of the position.
* @return A value of <code>true</code> if the Box object contains the
* specified position; otherwise <code>false</code>.
*/
Box.prototype.contains = function (x, y, z) {
return (this.x <= x && this.x + this.width >= x
&& this.y <= y && this.y + this.height >= y
&& this.z <= z && this.z + this.depth >= z);
};
/**
* Determines whether the specified position is contained within the cubic
* region defined by this Box object. This method is similar to the
* <code>Box.contains()</code> method, except that it takes a Vector3D
* object as a parameter.
*
* @param position The position, as represented by its <i>x</i>, <i>y</i> and
* <i>z</i> coordinates.
* @return A value of <code>true</code> if the Box object contains the
* specified position; otherwise <code>false</code>.
*/
Box.prototype.containsPoint = function (position) {
return (this.x <= position.x && this.x + this.width >= position.x
&& this.y <= position.y && this.y + this.height >= position.y
&& this.z <= position.z && this.z + this.depth >= position.z);
};
/**
* Determines whether the Box object specified by the <code>box</code>
* parameter is contained within this Box object. A Box object is said to
* contain another if the second Box object falls entirely within the
* boundaries of the first.
*
* @param box The Box object being checked.
* @return A value of <code>true</code> if the Box object that you specify
* is contained by this Box object; otherwise <code>false</code>.
*/
Box.prototype.containsBox = function (box) {
return (this.x <= box.x && this.x + this.width >= box.x + box.width
&& this.y <= box.y && this.y + this.height >= box.y + box.height
&& this.z <= box.z && this.z + this.depth >= box.z + box.depth);
};
/**
* Copies all of box data from the source Box object into the calling
* Box object.
*
* @param sourceBox The Box object from which to copy the data.
*/
Box.prototype.copyFrom = function (sourceBox) {
this.x = sourceBox.x;
this.y = sourceBox.y;
this.z = sourceBox.z;
this.width = sourceBox.width;
this.height = sourceBox.height;
this.depth = sourceBox.depth;
};
/**
* Determines whether the object specified in the <code>toCompare</code>
* parameter is equal to this Box object. This method compares the
* <code>x</code>, <code>y</code>, <code>z</code>, <code>width</code>,
* <code>height</code> and <code>depth</code> properties of an object against
* the same properties of this Box object.
*
* @param toCompare The box to compare to this Box object.
* @return A value of <code>true</code> if the object has exactly the same
* values for the <code>x</code>, <code>y</code>, <code>z</code>,
* <code>width</code>, <code>height</code> and <code>depth</code>
* properties as this Box object; otherwise <code>false</code>.
*/
Box.prototype.equals = function (toCompare) {
return (this.x == toCompare.x && this.y == toCompare.y && this.z == toCompare.z
&& this.width == toCompare.width && this.height == toCompare.height && this.depth == toCompare.depth);
};
/**
* Increases the size of the Box object by the specified amounts, in
* pixels. The center point of the Box object stays the same, and its
* size increases to the left and right by the <code>dx</code> value, to
* the top and the bottom by the <code>dy</code> value, and to
* the front and the back by the <code>dz</code> value.
*
* @param dx The value to be added to the left and the right of the Box
* object. The following equation is used to calculate the new
* width and position of the box:
* @param dy The value to be added to the top and the bottom of the Box
* object. The following equation is used to calculate the new
* height and position of the box:
* @param dz The value to be added to the front and the back of the Box
* object. The following equation is used to calculate the new
* depth and position of the box:
*/
Box.prototype.inflate = function (dx, dy, dz) {
this.x -= dx / 2;
this.y -= dy / 2;
this.z -= dz / 2;
this.width += dx / 2;
this.height += dy / 2;
this.depth += dz / 2;
};
/**
* Increases the size of the Box object. This method is similar to the
* <code>Box.inflate()</code> method except it takes a Vector3D object as
* a parameter.
*
* <p>The following two code examples give the same result:</p>
*
* @param delta The <code>x</code> property of this Vector3D object is used to
* increase the horizontal dimension of the Box object.
* The <code>y</code> property is used to increase the vertical
* dimension of the Box object.
* The <code>z</code> property is used to increase the
* longitudinal dimension of the Box object.
*/
Box.prototype.inflatePoint = function (delta) {
this.x -= delta.x / 2;
this.y -= delta.y / 2;
this.z -= delta.z / 2;
this.width += delta.x / 2;
this.height += delta.y / 2;
this.depth += delta.z / 2;
};
/**
* If the Box object specified in the <code>toIntersect</code> parameter
* intersects with this Box object, returns the area of intersection
* as a Box object. If the boxes do not intersect, this method returns an
* empty Box object with its properties set to 0.
*
* @param toIntersect The Box object to compare against to see if it
* intersects with this Box object.
* @return A Box object that equals the area of intersection. If the
* boxes do not intersect, this method returns an empty Box
* object; that is, a box with its <code>x</code>, <code>y</code>,
* <code>z</code>, <code>width</code>, <code>height</code>, and
* <code>depth</code> properties set to 0.
*/
Box.prototype.intersection = function (toIntersect) {
if (this.intersects(toIntersect)) {
var i = new Box();
if (this.x > toIntersect.x) {
i.x = this.x;
i.width = toIntersect.x - this.x + toIntersect.width;
if (i.width > this.width)
i.width = this.width;
}
else {
i.x = toIntersect.x;
i.width = this.x - toIntersect.x + this.width;
if (i.width > toIntersect.width)
i.width = toIntersect.width;
}
if (this.y > toIntersect.y) {
i.y = this.y;
i.height = toIntersect.y - this.y + toIntersect.height;
if (i.height > this.height)
i.height = this.height;
}
else {
i.y = toIntersect.y;
i.height = this.y - toIntersect.y + this.height;
if (i.height > toIntersect.height)
i.height = toIntersect.height;
}
if (this.z > toIntersect.z) {
i.z = this.z;
i.depth = toIntersect.z - this.z + toIntersect.depth;
if (i.depth > this.depth)
i.depth = this.depth;
}
else {
i.z = toIntersect.z;
i.depth = this.z - toIntersect.z + this.depth;
if (i.depth > toIntersect.depth)
i.depth = toIntersect.depth;
}
return i;
}
return new Box();
};
/**
* Determines whether the object specified in the <code>toIntersect</code>
* parameter intersects with this Box object. This method checks the
* <code>x</code>, <code>y</code>, <code>z</code>, <code>width</code>,
* <code>height</code>, and <code>depth</code> properties of the specified
* Box object to see if it intersects with this Box object.
*
* @param toIntersect The Box object to compare against this Box object.
* @return A value of <code>true</code> if the specified object intersects
* with this Box object; otherwise <code>false</code>.
*/
Box.prototype.intersects = function (toIntersect) {
return (this.x + this.width >= toIntersect.x && this.x <= toIntersect.x + toIntersect.width
&& this.y + this.height >= toIntersect.y && this.y <= toIntersect.y + toIntersect.height
&& this.z + this.depth >= toIntersect.z && this.z <= toIntersect.z + toIntersect.depth);
};
Box.prototype.rayIntersection = function (position, direction, targetNormal) {
if (targetNormal === void 0) { targetNormal = null; }
if (this.containsPoint(position))
return 0;
var halfExtentsX = this.width / 2;
var halfExtentsY = this.height / 2;
var halfExtentsZ = this.depth / 2;
var centerX = this.x + halfExtentsX;
var centerY = this.y + halfExtentsY;
var centerZ = this.z + halfExtentsZ;
var px = position.x - centerX;
var py = position.y - centerY;
var pz = position.z - centerZ;
var vx = direction.x;
var vy = direction.y;
var vz = direction.z;
var ix;
var iy;
var iz;
var rayEntryDistance;
// ray-plane tests
var intersects;
if (vx < 0) {
rayEntryDistance = (halfExtentsX - px) / vx;
if (rayEntryDistance > 0) {
iy = py + rayEntryDistance * vy;
iz = pz + rayEntryDistance * vz;
if (iy > -halfExtentsY && iy < halfExtentsY && iz > -halfExtentsZ && iz < halfExtentsZ) {
if (targetNormal) {
targetNormal.x = 1;
targetNormal.y = 0;
targetNormal.z = 0;
}
intersects = true;
}
}
}
if (!intersects && vx > 0) {
rayEntryDistance = (-halfExtentsX - px) / vx;
if (rayEntryDistance > 0) {
iy = py + rayEntryDistance * vy;
iz = pz + rayEntryDistance * vz;
if (iy > -halfExtentsY && iy < halfExtentsY && iz > -halfExtentsZ && iz < halfExtentsZ) {
if (targetNormal) {
targetNormal.x = -1;
targetNormal.y = 0;
targetNormal.z = 0;
}
intersects = true;
}
}
}
if (!intersects && vy < 0) {
rayEntryDistance = (halfExtentsY - py) / vy;
if (rayEntryDistance > 0) {
ix = px + rayEntryDistance * vx;
iz = pz + rayEntryDistance * vz;
if (ix > -halfExtentsX && ix < halfExtentsX && iz > -halfExtentsZ && iz < halfExtentsZ) {
if (targetNormal) {
targetNormal.x = 0;
targetNormal.y = 1;
targetNormal.z = 0;
}
intersects = true;
}
}
}
if (!intersects && vy > 0) {
rayEntryDistance = (-halfExtentsY - py) / vy;
if (rayEntryDistance > 0) {
ix = px + rayEntryDistance * vx;
iz = pz + rayEntryDistance * vz;
if (ix > -halfExtentsX && ix < halfExtentsX && iz > -halfExtentsZ && iz < halfExtentsZ) {
if (targetNormal) {
targetNormal.x = 0;
targetNormal.y = -1;
targetNormal.z = 0;
}
intersects = true;
}
}
}
if (!intersects && vz < 0) {
rayEntryDistance = (halfExtentsZ - pz) / vz;
if (rayEntryDistance > 0) {
ix = px + rayEntryDistance * vx;
iy = py + rayEntryDistance * vy;
if (iy > -halfExtentsY && iy < halfExtentsY && ix > -halfExtentsX && ix < halfExtentsX) {
if (targetNormal) {
targetNormal.x = 0;
targetNormal.y = 0;
targetNormal.z = 1;
}
intersects = true;
}
}
}
if (!intersects && vz > 0) {
rayEntryDistance = (-halfExtentsZ - pz) / vz;
if (rayEntryDistance > 0) {
ix = px + rayEntryDistance * vx;
iy = py + rayEntryDistance * vy;
if (iy > -halfExtentsY && iy < halfExtentsY && ix > -halfExtentsX && ix < halfExtentsX) {
if (targetNormal) {
targetNormal.x = 0;
targetNormal.y = 0;
targetNormal.z = -1;
}
intersects = true;
}
}
}
return intersects ? rayEntryDistance : -1;
};
/**
* Finds the closest point on the Box to another given point. This can be
* used for maximum error calculations for content within a given Box.
*
* @param point The point for which to find the closest point on the Box
* @param target An optional Vector3D to store the result to prevent
* creating a new object.
* @return
*/
Box.prototype.closestPointToPoint = function (point, target) {
if (target === void 0) { target = null; }
var p;
if (target == null)
target = new Vector3D();
p = point.x;
if (p < this.x)
p = this.x;
if (p > this.x + this.width)
p = this.x + this.width;
target.x = p;
p = point.y;
if (p < this.y + this.height)
p = this.y + this.height;
if (p > this.y)
p = this.y;
target.y = p;
p = point.z;
if (p < this.z)
p = this.z;
if (p > this.z + this.depth)
p = this.z + this.depth;
target.z = p;
return target;
};
/**
* Adjusts the location of the Box object, as determined by its
* top-left-front corner, by the specified amounts.
*
* @param dx Moves the <i>x</i> value of the Box object by this amount.
* @param dy Moves the <i>y</i> value of the Box object by this amount.
* @param dz Moves the <i>z</i> value of the Box object by this amount.
*/
Box.prototype.offset = function (dx, dy, dz) {
this.x += dx;
this.y += dy;
this.z += dz;
};
/**
* Adjusts the location of the Box object using a Vector3D object as a
* parameter. This method is similar to the <code>Box.offset()</code>
* method, except that it takes a Vector3D object as a parameter.
*
* @param position A Vector3D object to use to offset this Box object.
*/
Box.prototype.offsetPosition = function (position) {
this.x += position.x;
this.y += position.y;
this.z += position.z;
};
/**
* Sets all of the Box object's properties to 0. A Box object is empty if its
* width, height or depth is less than or equal to 0.
*
* <p> This method sets the values of the <code>x</code>, <code>y</code>,
* <code>z</code>, <code>width</code>, <code>height</code>, and
* <code>depth</code> properties to 0.</p>
*
*/
Box.prototype.identity = function () {
this.x = 0;
this.y = 0;
this.z = 0;
this.width = 0;
this.height = 0;
this.depth = 0;
};
/**
* Sets the members of Box to the specified values
*
* @param xa The <i>x</i> coordinate of the top-left-front corner of the
* box.
* @param ya The <i>y</i> coordinate of the top-left-front corner of the
* box.
* @param yz The <i>z</i> coordinate of the top-left-front corner of the
* box.
* @param widtha The width of the box, in pixels.
* @param heighta The height of the box, in pixels.
* @param deptha The depth of the box, in pixels.
*/
Box.prototype.setTo = function (xa, ya, za, widtha, heighta, deptha) {
this.x = xa;
this.y = ya;
this.z = za;
this.width = widtha;
this.height = heighta;
this.depth = deptha;
};
/**
* Builds and returns a string that lists the horizontal, vertical and
* longitudinal positions and the width, height and depth of the Box object.
*
* @return A string listing the value of each of the following properties of
* the Box object: <code>x</code>, <code>y</code>, <code>z</code>,
* <code>width</code>, <code>height</code>, and <code>depth</code>.
*/
Box.prototype.toString = function () {
return '[Box] (x=' + this.x + ', y=' + this.y + ', z='
+ this.z + ', width=' + this.width + ', height='
+ this.height + ', depth=' + this.depth + ')';
};
/**
* Adds two boxes together to create a new Box object, by filling
* in the horizontal, vertical and longitudinal space between the two boxes.
*
* @param toUnion A Box object to add to this Box object.
* @return A new Box object that is the union of the two boxes.
*/
Box.prototype.union = function (toUnion, target) {
if (target === void 0) { target = null; }
var width;
var height;
var depth;
if (target == null)
target = new Box();
if (toUnion == null) {
target.copyFrom(this);
return target;
}
if (this.x < toUnion.x) {
width = toUnion.x - this.x + toUnion.width;
target.x = this.x;
target.width = (width < this.width) ? this.width : width;
}
else {
width = this.x - toUnion.x + this.width;
target.x = toUnion.x;
target.width = (width < toUnion.width) ? toUnion.width : width;
}
if (this.y < toUnion.y) {
height = toUnion.y - this.y + toUnion.height;
target.y = this.y;
target.height = (height < this.height) ? this.height : height;
}
else {
height = this.y - toUnion.y + this.height;
target.y = toUnion.y;
target.height = (height < toUnion.height) ? toUnion.height : height;
}
if (this.z < toUnion.z) {
depth = toUnion.z - this.z + toUnion.depth;
target.z = this.z;
target.depth = (depth < this.depth) ? this.depth : depth;
}
else {
depth = this.z - toUnion.z + this.depth;
target.z = toUnion.z;
target.depth = (depth < toUnion.depth) ? toUnion.depth : depth;
}
return target;
};
return Box;
}());
export { Box };