API Docs for: 1.4.0
Show:

File: src\components\Box.ts

/**
* 
* @module Kiwi
* @submodule Components 
* 
*/

module Kiwi.Components {

	/**
	* The Box Component is used to handle the various 'bounds' that each GameObject has. 
	* There are two main different types of bounds (Bounds and Hitbox) with each one having three variants (each one is a rectangle) depending on what you are wanting:
	*
	* RawBounds: The bounding box of the GameObject before rotation/scale.
	* 
	* RawHitbox: The hitbox of the GameObject before rotation/scale. This can be modified to be different than the normal bounds but if not specified it will be the same as the raw bounds.
	*
	* Bounds: The bounding box of the GameObject after rotation/scale.
	*
	* Hitbox: The hitbox of the GameObject after rotation/scale. If you modified the raw hitbox then this one will be modified as well, otherwise it will be the same as the normal bounds.
	*
	* WorldBounds: The bounding box of the Entity using its world coordinates and after rotation/scale.
	*
	* WorldHitbox: The hitbox of the Entity using its world coordinates and after rotation/scale.
	*
	* @class Box
	* @extends Kiwi.Component
	* @namespace Kiwi.Components
	* @constructor
	* @param parent {Kiwi.Entity} The entity that this box belongs to.
	* @param [x=0] {Number} Its position on the x axis
	* @param [y=0] {Number} Its position on the y axis
	* @param [width=0] {Number} The width of the box.
	* @param [height=0] {Number} The height of the box.
	* @return {Kiwi.Components.Box}
	*/
	export class Box extends Component {

		constructor(parent: Entity, x: number = 0, y: number = 0, width: number = 0, height: number = 0) {
			
			super(parent, 'Box');
			
			this.entity = parent;

			this._rawBounds = new Kiwi.Geom.Rectangle(x,y,width,height);
			this._rawCenter = new Kiwi.Geom.Point(x + width / 2, y + height / 2);
			this._rawHitbox = new Kiwi.Geom.Rectangle();

			this._hitboxOffset = new Kiwi.Geom.Point();

			this.hitbox = new Kiwi.Geom.Rectangle(0, 0, width, height); 
			this.autoUpdate = true;

			this._scratchMatrix = new Kiwi.Geom.Matrix();
		}


		/**
		* The entity that this box belongs to.
		* @property entity
		* @type Kiwi.Entity
		* @public
		*/
		public entity: Kiwi.Entity;


		/**
		* The type of object that this is.
		* @method objType
		* @return {string} "Box"
		* @public
		*/
		public objType() {
			return "Box";
		}


		/**
		* Controls whether the hitbox should update automatically to match the hitbox of the current cell on the entity this Box component is attached to (default behaviour).
		* Or if the hitbox shouldn't auto update. Which will mean it will stay the same as the last value it had. 
		* This property is automatically set to 'false' when you override the hitboxes width/height, but you can set this to true afterwards. 
		* 
		* @property autoUpdate
		* @type boolean
		* @default true
		* @private
		*/
		public autoUpdate: boolean = true;


		/**
		* Indicates whether or not this component needs re-rendering/updating or not.
		* @property dirty
		* @type boolean
		* @public
		* @deprecated in version 1.1.0 because the box always needed updating
		*/
		public dirty: boolean;



		/**
		* Contains offset point for the hitbox 
		* @property _hitboxOffset
		* @type Kiwi.Geom.Point
		* @private
		*/
		private _hitboxOffset: Kiwi.Geom.Point;



		/**
		* Returns the offset value of the hitbox as a point for the X/Y axis for the developer to use.
		* This is without rotation or scaling.
		* This is a READ ONLY property.
		* @property hitboxOffset
		* @type Kiwi.Geom.Point
		* @public
		*/
		public get hitboxOffset(): Kiwi.Geom.Point {

			if ( this.autoUpdate == true && this.entity.atlas !== null && this.entity.atlas.cells && this.entity.atlas.cells[ 0 ].hitboxes ) {
				this._hitboxOffset.x =
					this.entity.atlas.cells[this.entity.cellIndex].hitboxes[0].x || 0;
				this._hitboxOffset.y =
					this.entity.atlas.cells[this.entity.cellIndex].hitboxes[0].y || 0;

			}

			return this._hitboxOffset;
		}



		/**
		* Contains the offset rectangle for the raw hitbox. 
		* @property _rawHitbox
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _rawHitbox: Kiwi.Geom.Rectangle;


		/**
		* Returns the raw hitbox rectangle for the developer to use. 
		* 'Raw' means where it would be without rotation or scaling.
		* This is READ ONLY.
		* @property rawHitbox
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get rawHitbox(): Kiwi.Geom.Rectangle {

			this._rawHitbox.x = this.rawBounds.x + this.hitboxOffset.x;
			this._rawHitbox.y = this.rawBounds.y + this.hitboxOffset.y;

			//If the hitbox has not already been set, then update the width/height based upon the current cell that the entity has.
			if (this.autoUpdate == true) {
				var atlas = this.entity.atlas;

				if ( atlas !== null && atlas.cells && atlas.cells[ 0 ].hitboxes ) {
					this._rawHitbox.width = atlas.cells[ this.entity.cellIndex ].hitboxes[ 0 ].w;
					this._rawHitbox.height = atlas.cells[ this.entity.cellIndex ].hitboxes[ 0 ].h;
				} else {
					this._rawHitbox.width = this.entity.width;
					this._rawHitbox.height = this.entity.height;
				}
			}

			return this._rawHitbox;
		}



		/**
		* The transformed or 'normal' hitbox for the entity. This is its box after rotation/scale.
		* @property _transformedHitbox
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _transformedHitbox: Kiwi.Geom.Rectangle;


		/** 
		* The transformed 'world' hitbox for the entity. This is its box after rotation/scale.
		* @property _worldHitbox
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _worldHitbox: Kiwi.Geom.Rectangle;


		/**
		* The 'normal' or transformed hitbox for the entity. This is its box after rotation/Kiwi.Geom.Rectangle. 
		* @property hitbox
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get hitbox(): Kiwi.Geom.Rectangle {
			this._transformedHitbox = this._rotateHitbox(this.rawHitbox.clone());

			return this._transformedHitbox;
		}
		public set hitbox(value: Kiwi.Geom.Rectangle) {

			//Use custom hitbox defined by user.

			this._hitboxOffset.x = value.x;
			this._hitboxOffset.y = value.y;

			this._rawHitbox = value;

			this._rawHitbox.x += this._rawBounds.x;
			this._rawHitbox.y += this._rawBounds.y;

			this.autoUpdate = false;

		}



		/**
		* Returns the transformed hitbox for the entity using its 'world' coordinates.
		* This is READ ONLY.
		* @property worldHitbox
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get worldHitbox(): Kiwi.Geom.Rectangle {
			this._worldHitbox = this._rotateHitbox(this.rawHitbox.clone(), true);

			return this._worldHitbox;
		}



		/**
		* The 'raw' bounds of entity. This is its bounds before rotation/scale.
		* This for property is only for storage of the values and should be accessed via the getter 'rawBounds' so that it can update.
		* 
		* @property _rawBounds
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _rawBounds: Kiwi.Geom.Rectangle;



		/**
		* Returns the 'raw' bounds for this entity.
		* This is READ ONLY.
		* @property rawBounds
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get rawBounds(): Kiwi.Geom.Rectangle {
			this._rawBounds.x = this.entity.x;
			this._rawBounds.y = this.entity.y;
			this._rawBounds.width = this.entity.width;
			this._rawBounds.height = this.entity.height;

			return this._rawBounds;
		}



		/**
		* Contains the 'raw' center point for the bounds.
		* @property Kiwi.Geom.Point
		* @type Kiwi.Geom.Point
		* @private
		*/
		private _rawCenter: Kiwi.Geom.Point;


		/**
		* Returns the raw center point of the box.
		* This is READ ONLY.
		* @property rawCenter
		* @type Kiwi.Geom.Point
		* @public
		*/
		public get rawCenter(): Kiwi.Geom.Point {
			this._rawCenter.x = this.rawBounds.x + this.rawBounds.width / 2;
			this._rawCenter.y = this.rawBounds.y + this.rawBounds.height / 2;

			return this._rawCenter;
		}

		/**
		* Scratch matrix used in geometry calculations
		*
		* @property _scratchMatrix
		* @type Kiwi.Geom.Matrix
		* @private
		* @since 1.3.1
		*/
		private _scratchMatrix: Kiwi.Geom.Matrix;


		/**
		* Contains the center point after the box has been transformed.
		* @property _transformedCenter
		* @type Kiwi.Geom.Point
		* @private
		*/
		private _transformedCenter: Kiwi.Geom.Point;


		/**
		* Returns the center point for the box after it has been transformed.
		* World coordinates.
		* This is READ ONLY.
		* @property center
		* @type Kiwi.Geom.Point
		* @public
		*/
		public get center(): Kiwi.Geom.Point {
			var m: Kiwi.Geom.Matrix = this.entity.transform.getConcatenatedMatrix();

			this._transformedCenter = m.transformPoint(
				new Kiwi.Geom.Point(
					this.entity.width / 2 - this.entity.anchorPointX,
					this.entity.height / 2 - this.entity.anchorPointY ) );

			return this._transformedCenter;
		}



		/**
		* Contains the transformed or 'normal' bounds for this entity.
		* @property _transformedBounds
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _transformedBounds: Kiwi.Geom.Rectangle;


		/**
		* The 'world' transformed bounds for this entity. 
		* @property _worldBounds
		* @type Kiwi.Geom.Rectangle
		* @private
		*/
		private _worldBounds: Kiwi.Geom.Rectangle;


		/**
		* Returns the 'transformed' or 'normal' bounds for this box. 
		* This is READ ONLY.
		* @property bounds
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get bounds(): Kiwi.Geom.Rectangle {
			this._transformedBounds = this._rotateRect(this.rawBounds.clone()); 

			return this._transformedBounds;
		}

		/**
		* Returns the 'transformed' bounds for this entity using the world coodinates.
		* This is READ ONLY.
		* @property worldBounds
		* @type Kiwi.Geom.Rectangle
		* @public
		*/
		public get worldBounds(): Kiwi.Geom.Rectangle {
			this._worldBounds = this._rotateRect(this.rawBounds.clone(), true);

			return this._worldBounds;
		}

		/**
		* Private internal method only. Used to calculate the transformed bounds after rotation/scale.
		* @method _rotateRect
		* @param rect {Kiwi.Geom.Rectangle}
		* @param [useWorldCoords=false] {Boolean}
		* @return {Kiwi.Geom.Rectangle}
		* @private
		*/
		private _rotateRect(rect: Kiwi.Geom.Rectangle, useWorldCoords: boolean=false): Kiwi.Geom.Rectangle {
			var out: Kiwi.Geom.Rectangle = new Kiwi.Geom.Rectangle();
			var t: Kiwi.Geom.Transform = this.entity.transform;
			var m: Kiwi.Geom.Matrix = this._scratchMatrix.copyFrom(
				t.getConcatenatedMatrix() );

			// Use world coordinates?
			if( !useWorldCoords )
			{
				m.setTo(m.a, m.b, m.c, m.d, t.x + t.rotPointX, t.y + t.rotPointY);
			}

			out = this.extents(
				m.transformPoint({ x: - t.rotPointX, y: - t.rotPointY }),
				m.transformPoint({ x: - t.rotPointX + rect.width, y: - t.rotPointY }),
				m.transformPoint({ x: - t.rotPointX + rect.width, y: - t.rotPointY + rect.height }),
				m.transformPoint({ x: - t.rotPointX, y: - t.rotPointY + rect.height })
				);
			return out;
		}



		/**
		* A private method that is used to calculate the transformed hitbox's coordinates after rotation. 
		* @method _rotateHitbox
		* @param rect {Kiwi.Geom.Rectangle} 
		* @param [useWorldCoords=false] {Boolean}
		* @return {Kiwi.Geom.Rectangle}
		* @private
		*/
		private _rotateHitbox(rect: Kiwi.Geom.Rectangle, useWorldCoords: boolean=false): Kiwi.Geom.Rectangle {
			var out: Kiwi.Geom.Rectangle = new Kiwi.Geom.Rectangle();
			var t: Kiwi.Geom.Transform = this.entity.transform;
			var m: Kiwi.Geom.Matrix = this._scratchMatrix.copyFrom(
				t.getConcatenatedMatrix() );

			//Use world coordinates?
			if( !useWorldCoords )
			{
				m.setTo(m.a, m.b, m.c, m.d, t.x + t.rotPointX, t.y + t.rotPointY);
			}

			out = this.extents(
				m.transformPoint({ x: - t.rotPointX + this._hitboxOffset.x,              y: - t.rotPointY + this._hitboxOffset.y }),
				m.transformPoint({ x: - t.rotPointX + rect.width + this._hitboxOffset.x, y: - t.rotPointY +  this._hitboxOffset.y }),
				m.transformPoint({ x: - t.rotPointX + rect.width + this._hitboxOffset.x, y: - t.rotPointY + rect.height + this._hitboxOffset.y}),
				m.transformPoint({ x: - t.rotPointX + this._hitboxOffset.x,              y: - t.rotPointY + rect.height + this._hitboxOffset.y })
				);

			return out;
		}



		/**
		* Draws the various bounds on a context that is passed. Useful for debugging and using in combination with the debug canvas.
		* @method draw
		* @param ctx {CanvasRenderingContext2D} Context of the canvas that this box component is to be rendered on top of.
		* @param [camera] {Kiwi.Camera} A camera that should be taken into account before rendered. This is the default camera by default.
		* @public
		*/
		public draw(ctx: CanvasRenderingContext2D, camera: Kiwi.Camera = this.game.cameras.defaultCamera) {
			var t: Kiwi.Geom.Transform = this.entity.transform;
			var ct: Kiwi.Geom.Transform = camera.transform;

			// Draw raw bounds and raw center
			ctx.strokeStyle = "red";
			ctx.fillRect(this.rawCenter.x + ct.x - 1, this.rawCenter.y + ct.y - 1, 3, 3);
			ctx.strokeRect(t.x + ct.x + t.rotPointX - 3, t.y + ct.y + t.rotPointY - 3, 7, 7);

			// Draw bounds
			ctx.strokeStyle = "blue";
			ctx.strokeRect(this.bounds.x + ct.x, this.bounds.y + ct.y, this.bounds.width, this.bounds.height);

			// Draw hitbox
			ctx.strokeStyle = "green";
			ctx.strokeRect(this.hitbox.x + ct.x, this.hitbox.y + ct.y, this.hitbox.width, this.hitbox.height);

			// Draw raw hitbox
			ctx.strokeStyle = "white";
			ctx.strokeRect(this.rawHitbox.x + ct.x, this.rawHitbox.y + ct.y, this.rawHitbox.width, this.rawHitbox.height);

			// Draw world bounds
			ctx.strokeStyle = "purple";
			ctx.strokeRect(this.worldBounds.x, this.worldBounds.y, this.worldBounds.width, this.worldBounds.height);

			// Draw world hitbox
			ctx.strokeStyle = "cyan";
			ctx.strokeRect(this.worldHitbox.x, this.worldHitbox.y, this.worldHitbox.width, this.worldHitbox.height);
		}



		/** 
		* Method which takes four Points and then converts it into a Rectangle, which represents the area those points covered.
		* The points passed can be maybe in any order, as the are checked for validity first. 
		*
		* @method extents
		* @param topLeftPoint {Kiwi.Geom.Point} The top left Point that the Rectangle should have.
		* @param topRightPoint {Kiwi.Geom.Point} The top right Point that the Rectangle should have.
		* @param bottomRightPoint {Kiwi.Geom.Point} The bottom right Point that the Rectangle should have.
		* @param bottomLeftPoint {Kiwi.Geom.Point} The bottom left Point that the Rectangle should have.
		* @return {Kiwi.Geom.Rectangle} The new Rectangle that represents the area the points covered.
		* @return Rectangle
		*/
		public extents(topLeftPoint:Kiwi.Geom.Point, topRightPoint:Kiwi.Geom.Point, bottomRightPoint:Kiwi.Geom.Point, bottomLeftPoint:Kiwi.Geom.Point):Kiwi.Geom.Rectangle {
			var left: number = Math.min(topLeftPoint.x, topRightPoint.x, bottomRightPoint.x, bottomLeftPoint.x);
			var right: number = Math.max(topLeftPoint.x, topRightPoint.x, bottomRightPoint.x, bottomLeftPoint.x);
			var top: number = Math.min(topLeftPoint.y, topRightPoint.y, bottomRightPoint.y, bottomLeftPoint.y);
			var bottom: number = Math.max(topLeftPoint.y, topRightPoint.y, bottomRightPoint.y, bottomLeftPoint.y);

			return new Kiwi.Geom.Rectangle(left, top, right - left, bottom - top);

		}



		/**
		* Destroys this component and all of the links it may have to other objects.
		* @method destroy
		* @public
		*/
		public destroy() {
			super.destroy();
			delete this.entity;
		}

	}

}