123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- module BABYLON {
- /**
- * Stores 2D Bounding Information.
- * This class handles a circle area and a bounding rectangle one.
- */
- export class BoundingInfo2D {
- /**
- * The coordinate of the center of the bounding info
- */
- public center: Vector2;
- /**
- * The radius of the bounding circle, from the center of the bounded object
- */
- public radius: number;
- /**
- * The extent of the bounding rectangle, from the center of the bounded object.
- * This is an absolute value in both X and Y of the vector which describe the right/top corner of the rectangle, you can easily reconstruct the whole rectangle by negating X &| Y.
- */
- public extent: Vector2;
- constructor() {
- this.radius = 0;
- this.center = Vector2.Zero();
- this.extent = Vector2.Zero();
- this._worldAABBDirty = false;
- this._worldAABBDirtyObservable = null;
- this._worldAABB = Vector4.Zero();
- }
- /**
- * Create a BoundingInfo2D object from a given size
- * @param size the size that will be used to set the extend, radius will be computed from it.
- */
- public static CreateFromSize(size: Size): BoundingInfo2D {
- let r = new BoundingInfo2D();
- BoundingInfo2D.CreateFromSizeToRef(size, r);
- return r;
- }
- /**
- * Create a BoundingInfo2D object from a given radius
- * @param radius the radius to use, the extent will be computed from it.
- */
- public static CreateFromRadius(radius: number): BoundingInfo2D {
- let r = new BoundingInfo2D();
- BoundingInfo2D.CreateFromRadiusToRef(radius, r);
- return r;
- }
- /**
- * Create a BoundingInfo2D object from a list of points.
- * The resulted object will be the smallest bounding area that includes all the given points.
- * @param points an array of points to compute the bounding object from.
- */
- public static CreateFromPoints(points: Vector2[]): BoundingInfo2D {
- let r = new BoundingInfo2D();
- BoundingInfo2D.CreateFromPointsToRef(points, r);
- return r;
- }
- /**
- * Update a BoundingInfo2D object using the given Size as input
- * @param size the bounding data will be computed from this size.
- * @param b must be a valid/allocated object, it will contain the result of the operation
- */
- public static CreateFromSizeToRef(size: Size, b: BoundingInfo2D) {
- if (!size) {
- size = Size.Zero();
- }
- b.center.x = +size.width / 2;
- b.center.y = +size.height / 2;
- b.extent.x = b.center.x;
- b.extent.y = b.center.y;
- b.radius = b.extent.length();
- b._worldAABBDirty = true;
- }
- /**
- * Update a BoundingInfo2D object using the given radius as input
- * @param radius the bounding data will be computed from this radius
- * @param b must be a valid/allocated object, it will contain the result of the operation
- */
- public static CreateFromRadiusToRef(radius: number, b: BoundingInfo2D) {
- b.center.x = b.center.y = 0;
- let r = +radius;
- b.extent.x = r;
- b.extent.y = r;
- b.radius = r;
- b._worldAABBDirty = true;
- }
- /**
- * Update a BoundingInfo2D object using the given points array as input
- * @param points the point array to use to update the bounding data
- * @param b must be a valid/allocated object, it will contain the result of the operation
- */
- public static CreateFromPointsToRef(points: Vector2[], b: BoundingInfo2D) {
- let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = Number.MIN_VALUE, ymax = Number.MIN_VALUE;
- for (let p of points) {
- xmin = Math.min(p.x, xmin);
- xmax = Math.max(p.x, xmax);
- ymin = Math.min(p.y, ymin);
- ymax = Math.max(p.y, ymax);
- }
- BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, b);
- }
- /**
- * Update a BoundingInfo2D object using the given min/max values as input
- * @param xmin the smallest x coordinate
- * @param xmax the biggest x coordinate
- * @param ymin the smallest y coordinate
- * @param ymax the buggest y coordinate
- * @param b must be a valid/allocated object, it will contain the result of the operation
- */
- public static CreateFromMinMaxToRef(xmin: number, xmax: number, ymin: number, ymax: number, b: BoundingInfo2D) {
- let w = xmax - xmin;
- let h = ymax - ymin;
- b.center = new Vector2(xmin + w / 2, ymin + h / 2);
- b.extent = new Vector2(xmax - b.center.x, ymax - b.center.y);
- b.radius = b.extent.length();
- b._worldAABBDirty = true;
- }
- public toString(): string {
- return `Center: ${this.center}, Extent: ${this.extent}, Radius: ${this.radius}`;
- }
- /**
- * Duplicate this instance and return a new one
- * @return the duplicated instance
- */
- public clone(): BoundingInfo2D {
- let r = new BoundingInfo2D();
- r.center = this.center.clone();
- r.radius = this.radius;
- r.extent = this.extent.clone();
- return r;
- }
- public clear() {
- this.center.copyFromFloats(0, 0);
- this.radius = 0;
- this.extent.copyFromFloats(0, 0);
- this._worldAABBDirty = true;
- }
- public copyFrom(src: BoundingInfo2D) {
- this.center.copyFrom(src.center);
- this.radius = src.radius;
- this.extent.copyFrom(src.extent);
- this._worldAABBDirty = true;
- }
- public equals(other: BoundingInfo2D): boolean {
- if (!other) {
- return false;
- }
- return other.center.equals(this.center) && other.extent.equals(this.extent);
- }
- /**
- * return the max extend of the bounding info
- */
- public max(): Vector2 {
- let r = Vector2.Zero();
- this.maxToRef(r);
- return r;
- }
- /**
- * return the min/max extend of the bounding info.
- * x, y, z, w are left, bottom, right and top
- */
- public minMax(): Vector4 {
- let r = Vector4.Zero();
- this.minMaxToRef(r);
- return r;
- }
- /**
- * Update a vector2 with the max extend of the bounding info
- * @param result must be a valid/allocated vector2 that will contain the result of the operation
- */
- public maxToRef(result: Vector2) {
- result.x = this.center.x + this.extent.x;
- result.y = this.center.y + this.extent.y;
- }
- /**
- * Update a vector4 with the min/max extend of the bounding info
- * x, y, z, w are left, bottom, right and top
- * @param result must be a valid/allocated vector4 that will contain the result of the operation
- */
- public minMaxToRef(result: Vector4) {
- result.x = this.center.x - this.extent.x;
- result.y = this.center.y - this.extent.y;
- result.z = this.center.x + this.extent.x;
- result.w = this.center.y + this.extent.y;
- }
- /**
- * Return the size of the boundingInfo rect surface
- */
- public size(): Size {
- let r = Size.Zero();
- this.sizeToRef(r);
- return r;
- }
- /**
- * Stores in the result object the size of the boundingInfo rect surface
- * @param result
- */
- public sizeToRef(result: Size) {
- result.width = this.extent.x * 2;
- result.height = this.extent.y * 2;
- }
- /**
- * Inflate the boundingInfo with the given vector
- * @param offset the extent will be incremented with offset and the radius will be computed again
- */
- public inflate(offset: Vector2) {
- this.extent.addInPlace(offset);
- this.radius = this.extent.length();
- }
- /**
- * Apply a transformation matrix to this BoundingInfo2D and return a new instance containing the result
- * @param matrix the transformation matrix to apply
- * @return the new instance containing the result of the transformation applied on this BoundingInfo2D
- */
- public transform(matrix: Matrix2D): BoundingInfo2D {
- var r = new BoundingInfo2D();
- this.transformToRef(matrix, r);
- return r;
- }
- /**
- * Compute the union of this BoundingInfo2D with a given one, returns a new BoundingInfo2D as a result
- * @param other the second BoundingInfo2D to compute the union with this one
- * @return a new instance containing the result of the union
- */
- public union(other: BoundingInfo2D): BoundingInfo2D {
- var r = new BoundingInfo2D();
- this.unionToRef(other, r);
- return r;
- }
- public worldAABBIntersectionTest(other: BoundingInfo2D): boolean {
- let a = this.worldAABB;
- let b = other.worldAABB;
- return b.z >= a.x && b.x <= a.z && b.w >= a.y && b.y <= a.w;
- }
- private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
- /**
- * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
- * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
- * @param matrix The matrix to use to compute the transformation
- * @param result A VALID (i.e. allocated) BoundingInfo2D object where the result will be stored
- */
- public transformToRef(matrix: Matrix2D, result: BoundingInfo2D) {
- // Construct a bounding box based on the extent values
- let p = BoundingInfo2D._transform;
- p[0].x = this.center.x + this.extent.x;
- p[0].y = this.center.y + this.extent.y;
- p[1].x = this.center.x + this.extent.x;
- p[1].y = this.center.y - this.extent.y;
- p[2].x = this.center.x - this.extent.x;
- p[2].y = this.center.y - this.extent.y;
- p[3].x = this.center.x - this.extent.x;
- p[3].y = this.center.y + this.extent.y;
- // Transform the four points of the bounding box with the matrix
- for (let i = 0; i < 4; i++) {
- matrix.transformPointToRef(p[i], p[i]);
- }
- BoundingInfo2D.CreateFromPointsToRef(p, result);
- }
- private _updateWorldAABB(worldMatrix: Matrix2D) {
- // Construct a bounding box based on the extent values
- let p = BoundingInfo2D._transform;
- p[0].x = this.center.x + this.extent.x;
- p[0].y = this.center.y + this.extent.y;
- p[1].x = this.center.x + this.extent.x;
- p[1].y = this.center.y - this.extent.y;
- p[2].x = this.center.x - this.extent.x;
- p[2].y = this.center.y - this.extent.y;
- p[3].x = this.center.x - this.extent.x;
- p[3].y = this.center.y + this.extent.y;
- // Transform the four points of the bounding box with the matrix
- for (let i = 0; i < 4; i++) {
- worldMatrix.transformPointToRef(p[i], p[i]);
- }
- this._worldAABB.x = Math.min(Math.min(p[0].x, p[1].x), Math.min(p[2].x, p[3].x));
- this._worldAABB.y = Math.min(Math.min(p[0].y, p[1].y), Math.min(p[2].y, p[3].y));
- this._worldAABB.z = Math.max(Math.max(p[0].x, p[1].x), Math.max(p[2].x, p[3].x));
- this._worldAABB.w = Math.max(Math.max(p[0].y, p[1].y), Math.max(p[2].y, p[3].y));
- }
- public worldMatrixAccess: () => Matrix2D;
- public get worldAABBDirtyObservable(): Observable<BoundingInfo2D> {
- if (!this._worldAABBDirtyObservable) {
- this._worldAABBDirtyObservable = new Observable<BoundingInfo2D>();
- }
- return this._worldAABBDirtyObservable;
- }
- public get isWorldAABBDirty() {
- return this._worldAABBDirty;
- }
- public dirtyWorldAABB() {
- if (this._worldAABBDirty) {
- return;
- }
- this._worldAABBDirty = true;
- if (this._worldAABBDirtyObservable && this._worldAABBDirtyObservable.hasObservers()) {
- this._worldAABBDirtyObservable.notifyObservers(this);
- }
- }
- /**
- * Retrieve the world AABB, the Vector4's data is x=xmin, y=ymin, z=xmax, w=ymax
- */
- public get worldAABB(): Vector4 {
- if (this._worldAABBDirty) {
- if (!this.worldMatrixAccess) {
- throw new Error("you must set the worldMatrixAccess function first");
- }
- this._updateWorldAABB(this.worldMatrixAccess());
- this._worldAABBDirty = false;
- }
- return this._worldAABB;
- }
- /**
- * Compute the union of this BoundingInfo2D with another one and store the result in a third valid BoundingInfo2D object
- * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
- * @param other the second object used to compute the union
- * @param result a VALID BoundingInfo2D instance (i.e. allocated) where the result will be stored
- */
- public unionToRef(other: BoundingInfo2D, result: BoundingInfo2D) {
- let xmax = Math.max(this.center.x + this.extent.x, other.center.x + other.extent.x);
- let ymax = Math.max(this.center.y + this.extent.y, other.center.y + other.extent.y);
- let xmin = Math.min(this.center.x - this.extent.x, other.center.x - other.extent.x);
- let ymin = Math.min(this.center.y - this.extent.y, other.center.y - other.extent.y);
- BoundingInfo2D.CreateFromMinMaxToRef(xmin, xmax, ymin, ymax, result);
- }
- /**
- * Check if the given point is inside the BoundingInfo.
- * The test is first made on the radius, then inside the rectangle described by the extent
- * @param pickPosition the position to test
- * @return true if the point is inside, false otherwise
- */
- public doesIntersect(pickPosition: Vector2): boolean {
- // is it inside the radius?
- let pickLocal = pickPosition.subtract(this.center);
- if (pickLocal.lengthSquared() <= (this.radius * this.radius)) {
- // is it inside the rectangle?
- return ((Math.abs(pickLocal.x) <= this.extent.x) && (Math.abs(pickLocal.y) <= this.extent.y));
- }
- return false;
- }
- private _worldAABBDirtyObservable: Observable<BoundingInfo2D>;
- private _worldAABBDirty: boolean;
- private _worldAABB: Vector4;
- }
- }
|