Merge remote-tracking branch 'origin/master'

This commit is contained in:
Pavle Goloskokovic 2017-11-27 17:22:32 +01:00
commit c253e01cac
44 changed files with 671 additions and 107 deletions

View file

@ -3,8 +3,8 @@ var CalculateFacesWithin = require('./CalculateFacesWithin');
/** /**
* Copies the tiles in the source rectangular area to a new destination (all specified in tile * Copies the tiles in the source rectangular area to a new destination (all specified in tile
* coordinates) within the layer. This copies all tile properties & recalculates interesting tile * coordinates) within the layer. This copies all tile properties & recalculates collision
* faces in the destination region. * information in the destination region.
* *
* @param {number} srcTileX - [description] * @param {number} srcTileX - [description]
* @param {number} srcTileY - [description] * @param {number} srcTileY - [description]
@ -16,7 +16,6 @@ var CalculateFacesWithin = require('./CalculateFacesWithin');
* @param {boolean} [recalculateFaces=true] - [description] * @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description] * @param {LayerData} layer - [description]
*/ */
var Copy = function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, layer) var Copy = function (srcTileX, srcTileY, width, height, destTileX, destTileY, recalculateFaces, layer)
{ {
if (srcTileX < 0) { srcTileX = 0; } if (srcTileX < 0) { srcTileX = 0; }

View file

@ -3,7 +3,7 @@
* internally. * internally.
* *
* @param {LayerData} layer - [description] * @param {LayerData} layer - [description]
* @param {Camera} camera - [description] * @param {Camera} [camera=main camera] - [description]
* @param {array} [outputArray] - [description] * @param {array} [outputArray] - [description]
* @returns {array} * @returns {array}
*/ */

View file

@ -4,13 +4,13 @@ var CalculateFacesWithin = require('./CalculateFacesWithin');
/** /**
* Sets the tiles in the given rectangular area (in tile coordinates) of the layer with the * Sets the tiles in the given rectangular area (in tile coordinates) of the layer with the
* specified index. Tiles will be set to collide if the given index is a colliding index. * specified index. Tiles will be set to collide if the given index is a colliding index.
* Interesting tile faces in the region will be recalculated. * Collision information in the region will be recalculated.
* *
* @param {number} index - [description] * @param {number} index - [description]
* @param {number} tileX - [description] * @param {number} [tileX=0] - [description]
* @param {number} tileY - [description] * @param {number} [tileY=0] - [description]
* @param {number} width - [description] * @param {number} [width=max width based on tileX] - [description]
* @param {number} height - [description] * @param {number} [height=max height based on tileY] - [description]
* @param {boolean} [recalculateFaces=true] - [description] * @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description] * @param {LayerData} layer - [description]
*/ */

View file

@ -12,7 +12,6 @@
* @param {LayerData} layer - [description] * @param {LayerData} layer - [description]
* @return {Tile|null} The first (or n skipped) tile with the matching index. * @return {Tile|null} The first (or n skipped) tile with the matching index.
*/ */
var FindByIndex = function (findIndex, skip, reverse, layer) var FindByIndex = function (findIndex, skip, reverse, layer)
{ {
if (skip === undefined) { skip = 0; } if (skip === undefined) { skip = 0; }

View file

@ -1,26 +1,25 @@
var GetTilesWithin = require('./GetTilesWithin'); var GetTilesWithin = require('./GetTilesWithin');
/** /**
* For each tile in the given rectangular area (in tile coordinates) of the layer, run the given * For each tile in the given rectangular area (in tile coordinates) of the layer, run the given
* callback. * callback.
* *
* @param {number} callback - The callback. Each tile in the given area will be passed to this * @param {number} callback - The callback. Each tile in the given area will be passed to this
* callback as the first and only parameter. * callback as the first and only parameter.
* @param {number} context - The context under which the callback should be run. * @param {number} context - The context under which the callback should be run.
* @param {number} [tileX=0] * @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] * @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description] * @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description] * @param {number} [height=max height based on tileY] - [description]
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles. * @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have * @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index. * -1 for an index.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on * @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on
* at least one side. * at least one side.
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that * @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face. * have at least one interesting face.
* @param {LayerData} layer - [description] * @param {LayerData} layer - [description]
*/ */
var ForEachTile = function (callback, context, tileX, tileY, width, height, filteringOptions, layer) var ForEachTile = function (callback, context, tileX, tileY, width, height, filteringOptions, layer)
{ {
var tiles = GetTilesWithin(tileX, tileY, width, height, filteringOptions, layer); var tiles = GetTilesWithin(tileX, tileY, width, height, filteringOptions, layer);

View file

@ -1,5 +1,16 @@
var IsInLayerBounds = require('./IsInLayerBounds'); var IsInLayerBounds = require('./IsInLayerBounds');
/**
* Gets a tile at the given tile coordinates from the given layer.
*
* @param {number} tileX - X position to get the tile from (given in tile units, not pixels)
* @param {number} tileY - Y position to get the tile from (given in tile units, not pixels)
* @param {boolean} [nonNull=false] - If true getTile won't return null for empty tiles, but a Tile
* object with an index of -1.
* @param {LayerData} layer - [description]
* @return {Tile} The tile at the given coordinates or null if no tile was found or the coordinates
* were invalid.
*/
var GetTileAt = function (tileX, tileY, nonNull, layer) var GetTileAt = function (tileX, tileY, nonNull, layer)
{ {
if (nonNull === undefined) { nonNull = false; } if (nonNull === undefined) { nonNull = false; }

View file

@ -2,6 +2,18 @@ var GetTileAt = require('./GetTileAt');
var WorldToTileX = require('./WorldToTileX'); var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
/**
* Gets a tile at the given world coordinates from the given layer.
*
* @param {number} worldX - X position to get the tile from (given in pixels)
* @param {number} worldY - Y position to get the tile from (given in pixels)
* @param {boolean} [nonNull=false] - If true, function won't return null for empty tiles, but a Tile
* object with an index of -1.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The tile at the given coordinates or null if no tile was found or the coordinates
* were invalid.
*/
var GetTileAtWorldXY = function (worldX, worldY, nonNull, camera, layer) var GetTileAtWorldXY = function (worldX, worldY, nonNull, camera, layer)
{ {
var tileX = WorldToTileX(worldX, true, camera, layer); var tileX = WorldToTileX(worldX, true, camera, layer);

View file

@ -2,14 +2,23 @@
var GetFastValue = require('../../../utils/object/GetFastValue'); var GetFastValue = require('../../../utils/object/GetFastValue');
// Get tiles within the rectangular area specified. Note: this clips the x, y, w & h to the map's /**
// boundries. * Gets the tiles in the given rectangular area (in tile coordinates) of the layer.
// Options: *
// { * @param {number} [tileX=0] - [description]
// isNotEmpty: false, * @param {number} [tileY=0] - [description]
// isColliding: false, * @param {number} [width=max width based on tileX] - [description]
// hasInterestingFace: false * @param {number} [height=max height based on tileY] - [description]
// } * @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on
* at least one side.
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
*/
var GetTilesWithin = function (tileX, tileY, width, height, filteringOptions, layer) var GetTilesWithin = function (tileX, tileY, width, height, filteringOptions, layer)
{ {
if (tileX === undefined) { tileX = 0; } if (tileX === undefined) { tileX = 0; }

View file

@ -13,9 +13,25 @@ var TriangleToRectangle = function (triangle, rect)
return Intersects.RectangleToTriangle(rect, triangle); return Intersects.RectangleToTriangle(rect, triangle);
}; };
// Circle, Line, Rect, Triangle in world coordinates.
// Note: Could possibly be optimized by copying the shape and shifting it into tilemapLayer // Note: Could possibly be optimized by copying the shape and shifting it into tilemapLayer
// coordinates instead of shifting the tiles. // coordinates instead of shifting the tiles.
/**
* Gets the tiles that overlap with the given shape in the given layer. The shape must be a Circle,
* Line, Rectangle or Triangle. The shape should be in world coordinates.
*
* @param {Circle|Line|Rectangle|Triangle} shape - A shape in world (pixel) coordinates
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on
* at least one side.
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
*/
var GetTilesWithinShape = function (shape, filteringOptions, camera, layer) var GetTilesWithinShape = function (shape, filteringOptions, camera, layer)
{ {
if (shape === undefined) { return []; } if (shape === undefined) { return []; }

View file

@ -2,6 +2,24 @@ var GetTilesWithin = require('./GetTilesWithin');
var WorldToTileX = require('./WorldToTileX'); var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
/**
* Gets the tiles in the given rectangular area (in world coordinates) of the layer.
*
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {number} width - [description]
* @param {number} height - [description]
* @param {object} [filteringOptions] - Optional filters to apply when getting the tiles.
* @param {boolean} [filteringOptions.isNotEmpty=false] - If true, only return tiles that don't have
* -1 for an index.
* @param {boolean} [filteringOptions.isColliding=false] - If true, only return tiles that collide on
* at least one side.
* @param {boolean} [filteringOptions.hasInterestingFace=false] - If true, only return tiles that
* have at least one interesting face.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {array} Array of Tile objects.
*/
var GetTilesWithinWorldXY = function (worldX, worldY, width, height, filteringOptions, camera, layer) var GetTilesWithinWorldXY = function (worldX, worldY, width, height, filteringOptions, camera, layer)
{ {
// Top left corner of the rect, rounded down to include partial tiles // Top left corner of the rect, rounded down to include partial tiles

View file

@ -1,5 +1,14 @@
var IsInLayerBounds = require('./IsInLayerBounds'); var IsInLayerBounds = require('./IsInLayerBounds');
/**
* Checks if there is a tile at the given location (in tile coordinates) in the given layer. Returns
* false if there is no tile or if the tile at that location has an index of -1.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {LayerData} layer - [description]
* @return {boolean}
*/
var HasTileAt = function (tileX, tileY, layer) var HasTileAt = function (tileX, tileY, layer)
{ {
if (IsInLayerBounds(tileX, tileY, layer)) if (IsInLayerBounds(tileX, tileY, layer))

View file

@ -2,6 +2,16 @@ var HasTileAt = require('./HasTileAt');
var WorldToTileX = require('./WorldToTileX'); var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
/**
* Checks if there is a tile at the given location (in world coordinates) in the given layer. Returns
* false if there is no tile or if the tile at that location has an index of -1.
*
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {boolean}
*/
var HasTileAtWorldXY = function (worldX, worldY, camera, layer) var HasTileAtWorldXY = function (worldX, worldY, camera, layer)
{ {
var tileX = WorldToTileX(worldX, true, camera, layer); var tileX = WorldToTileX(worldX, true, camera, layer);

View file

@ -1,3 +1,11 @@
/**
* Checks if the given tile coordinates are within the bounds of the layer.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {LayerData} layer - [description]
* @return {boolean}
*/
var IsInLayerBounds = function (tileX, tileY, layer) var IsInLayerBounds = function (tileX, tileY, layer)
{ {
return (tileX >= 0 && tileX < layer.width && tileY >= 0 && tileY < layer.height); return (tileX >= 0 && tileX < layer.width && tileY >= 0 && tileY < layer.height);

View file

@ -2,8 +2,19 @@ var Tile = require('../Tile');
var IsInLayerBounds = require('./IsInLayerBounds'); var IsInLayerBounds = require('./IsInLayerBounds');
var RecalculateFacesAt = require('./RecalculateFacesAt'); var RecalculateFacesAt = require('./RecalculateFacesAt');
// Put Phaser.Tile|number. Note: does not place a reference to tile, it copies the tile or creates a /**
// new one. * Puts a tile at the given tile coordinates in the specified layer. You can pass in either an index
* or a Tile object. If you pass in a Tile, all attributes will be copied over to the specified
* location. If you pass in an index, only the index at the specified location will be changed.
* Collision information will be recalculated at the specified location.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The Tile object that was created or added to this map.
*/
var PutTileAt = function (tile, tileX, tileY, recalculateFaces, layer) var PutTileAt = function (tile, tileX, tileY, recalculateFaces, layer)
{ {
if (!IsInLayerBounds(tileX, tileY, layer)) { return null; } if (!IsInLayerBounds(tileX, tileY, layer)) { return null; }
@ -18,11 +29,8 @@ var PutTileAt = function (tile, tileX, tileY, recalculateFaces, layer)
{ {
layer.data[tileY][tileX] = new Tile(layer, tile.index, tileX, tileY, tile.width, tile.height); layer.data[tileY][tileX] = new Tile(layer, tile.index, tileX, tileY, tile.width, tile.height);
} }
else
{
layer.data[tileY][tileX].copy(tile); layer.data[tileY][tileX].copy(tile);
} }
}
else else
{ {
var index = tile; var index = tile;

View file

@ -2,6 +2,20 @@ var PutTileAt = require('./PutTileAt');
var WorldToTileX = require('./WorldToTileX'); var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
/**
* Puts a tile at the given world coordinates (pixels) in the specified layer. You can pass in either
* an index or a Tile object. If you pass in a Tile, all attributes will be copied over to the
* specified location. If you pass in an index, only the index at the specified location will be
* changed. Collision information will be recalculated at the specified location.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {boolean} [recalculateFaces=true] - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The Tile object that was created or added to this map.
*/
var PutTileAtWorldXY = function (tile, worldX, worldY, recalculateFaces, camera, layer) var PutTileAtWorldXY = function (tile, worldX, worldY, recalculateFaces, camera, layer)
{ {
var tileX = WorldToTileX(worldX, true, camera, layer); var tileX = WorldToTileX(worldX, true, camera, layer);

View file

@ -1,7 +1,20 @@
var GetTilesWithin = require('./GetTilesWithin'); var GetTilesWithin = require('./GetTilesWithin');
var GetRandomElement = require('../../../utils/array/GetRandomElement'); var GetRandomElement = require('../../../utils/array/GetRandomElement');
// Randomizes indices, not other properties. Does not modify collisions. Matches v2 functionality. /**
* Randomizes the indices of a rectangular region of tiles (in tile coordinates) within the
* specified layer. Each tile will recieve a new index. If an array of indices is passed in, then
* those will be used for randomly assigning new tile indices. If an array is not provided, the
* indices found within the region (excluding -1) will be used for randomly assigning new tile
* indices. This method only modifies tile indexes and does not change collision information.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {array} [indices] - An array of indices to randomly draw from during randomization.
* @param {LayerData} layer - [description]
*/
var Randomize = function (tileX, tileY, width, height, indices, layer) var Randomize = function (tileX, tileY, width, height, indices, layer)
{ {
var i; var i;

View file

@ -1,8 +1,14 @@
var GetTileAt = require('./GetTileAt'); var GetTileAt = require('./GetTileAt');
// Recalculate the faces, assuming only one tile location has been changed /**
// Used internally to update faces quickly for PutTileAt/RemoveTileAt/etc. Alternate approach to * Calculates interesting faces at the given tile coordinates of the specified layer. Interesting
// 951. * faces are used internally for optimizing collisions against tiles. This method is mostly used
* internally to optimize recalculating faces when only one tile has been changed.
*
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {LayerData} layer - [description]
*/
var RecalculateFacesAt = function (tileX, tileY, layer) var RecalculateFacesAt = function (tileX, tileY, layer)
{ {
var tile = GetTileAt(tileX, tileY, true, layer); var tile = GetTileAt(tileX, tileY, true, layer);

View file

@ -2,7 +2,19 @@ var Tile = require('../Tile');
var IsInLayerBounds = require('./IsInLayerBounds'); var IsInLayerBounds = require('./IsInLayerBounds');
var RecalculateFacesAt = require('./RecalculateFacesAt'); var RecalculateFacesAt = require('./RecalculateFacesAt');
// Remove and return Tile with option for placing a -1 index tile or null. /**
* Removes the tile at the given tile coordinates in the specified layer and updates the layer's
* collision information.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} tileX - [description]
* @param {number} tileY - [description]
* @param {boolean} [replaceWithNull=true] - If true, this will replace the tile at the specified
* location with null instead of a Tile with an index of -1.
* @param {boolean} [recalculateFaces=true] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The Tile object that was removed.
*/
var RemoveTileAt = function (tileX, tileY, replaceWithNull, recalculateFaces, layer) var RemoveTileAt = function (tileX, tileY, replaceWithNull, recalculateFaces, layer)
{ {
if (replaceWithNull === undefined) { replaceWithNull = false; } if (replaceWithNull === undefined) { replaceWithNull = false; }

View file

@ -2,6 +2,20 @@ var RemoveTileAt = require('./RemoveTileAt');
var WorldToTileX = require('./WorldToTileX'); var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
/**
* Removes the tile at the given world coordinates in the specified layer and updates the layer's
* collision information.
*
* @param {number|Tile} tile - The index of this tile to set or a Tile object.
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {boolean} [replaceWithNull=true] - If true, this will replace the tile at the specified
* location with null instead of a Tile with an index of -1.
* @param {boolean} [recalculateFaces=true] - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @return {Tile} The Tile object that was removed.
*/
var RemoveTileAtWorldXY = function (worldX, worldY, replaceWithNull, recalculateFaces, camera, layer) var RemoveTileAtWorldXY = function (worldX, worldY, replaceWithNull, recalculateFaces, camera, layer)
{ {
var tileX = WorldToTileX(worldX, true, camera, layer); var tileX = WorldToTileX(worldX, true, camera, layer);

View file

@ -1,6 +1,18 @@
var GetTilesWithin = require('./GetTilesWithin'); var GetTilesWithin = require('./GetTilesWithin');
// Replaces indices, not other properties. Does not modify collisions. Matches v2 functionality. /**
* Scans the given rectangular area (given in tile coordinates) for tiles with an index matching
* `findIndex` and updates their index to match `newIndex`. This only modifies the index and does
* not change collision information.
*
* @param {number} findIndex - [description]
* @param {number} newIndex - [description]
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var ReplaceByIndex = function (findIndex, newIndex, tileX, tileY, width, height, layer) var ReplaceByIndex = function (findIndex, newIndex, tileX, tileY, width, height, layer)
{ {
var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer); var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);

View file

@ -2,6 +2,18 @@ var SetTileCollision = require('./SetTileCollision');
var CalculateFacesWithin = require('./CalculateFacesWithin'); var CalculateFacesWithin = require('./CalculateFacesWithin');
var SetLayerCollisionIndex = require('./SetLayerCollisionIndex'); var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
/**
* Sets collision on the given tile or tiles within a layer by index. You can pass in either a
* single numeric index or an array of indexes: [2, 3, 15, 20]. The `collides` parameter controls if
* collision will be enabled (true) or disabled (false).
*
* @param {number|array} indexes - Either a single tile index, or an array of tile indexes.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the
* update.
* @param {LayerData} layer - [description]
*/
var SetCollision = function (indexes, collides, recalculateFaces, layer) var SetCollision = function (indexes, collides, recalculateFaces, layer)
{ {
if (collides === undefined) { collides = true; } if (collides === undefined) { collides = true; }

View file

@ -2,6 +2,20 @@ var SetTileCollision = require('./SetTileCollision');
var CalculateFacesWithin = require('./CalculateFacesWithin'); var CalculateFacesWithin = require('./CalculateFacesWithin');
var SetLayerCollisionIndex = require('./SetLayerCollisionIndex'); var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
/**
* Sets collision on a range of tiles in a layer whose index is between the specified `start` and
* `stop` (inclusive). Calling this with a start value of 10 and a stop value of 14 would set
* collision for tiles 10, 11, 12, 13 and 14. The `collides` parameter controls if collision will be
* enabled (true) or disabled (false).
*
* @param {number} start - The first index of the tile to be set for collision.
* @param {number} stop - The last index of the tile to be set for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the
* update.
* @param {LayerData} layer - [description]
*/
var SetCollisionBetween = function (start, stop, collides, recalculateFaces, layer) var SetCollisionBetween = function (start, stop, collides, recalculateFaces, layer)
{ {
if (collides === undefined) { collides = true; } if (collides === undefined) { collides = true; }

View file

@ -2,13 +2,25 @@ var SetTileCollision = require('./SetTileCollision');
var CalculateFacesWithin = require('./CalculateFacesWithin'); var CalculateFacesWithin = require('./CalculateFacesWithin');
var SetLayerCollisionIndex = require('./SetLayerCollisionIndex'); var SetLayerCollisionIndex = require('./SetLayerCollisionIndex');
// Note: this only updates layer.collideIndexes for tile indexes found currently in the layer /**
* Sets collision on all tiles in the given layer, except for tiles that have an index specified in
* the given array. The `collides` parameter controls if collision will be enabled (true) or
* disabled (false).
*
* @param {array} indexes - An array of the tile indexes to not be counted for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear
* collision.
* @param {boolean} [recalculateFaces=true] - Whether or not to recalculate the tile faces after the
* update.
* @param {LayerData} layer - [description]
*/
var SetCollisionByExclusion = function (indexes, collides, recalculateFaces, layer) var SetCollisionByExclusion = function (indexes, collides, recalculateFaces, layer)
{ {
if (collides === undefined) { collides = true; } if (collides === undefined) { collides = true; }
if (recalculateFaces === undefined) { recalculateFaces = true; } if (recalculateFaces === undefined) { recalculateFaces = true; }
if (!Array.isArray(indexes)) { indexes = [ indexes ]; } if (!Array.isArray(indexes)) { indexes = [ indexes ]; }
// Note: this only updates layer.collideIndexes for tile indexes found currently in the layer
for (var ty = 0; ty < layer.height; ty++) for (var ty = 0; ty < layer.height; ty++)
{ {
for (var tx = 0; tx < layer.width; tx++) for (var tx = 0; tx < layer.width; tx++)

View file

@ -1,5 +1,11 @@
// Internal method /**
// Update layer.collideIndexes to either contain or not contain tileIndex * Internally used method to keep track of the tile indexes that collide within a layer. This
* updates LayerData.collideIndexes to either contain or not contain the given `tileIndex`.
*
* @param {number} tileIndex - [description]
* @param {boolean} [collides=true] - [description]
* @param {LayerData} layer - [description]
*/
var SetLayerCollisionIndex = function (tileIndex, collides, layer) var SetLayerCollisionIndex = function (tileIndex, collides, layer)
{ {
var loc = layer.collideIndexes.indexOf(tileIndex); var loc = layer.collideIndexes.indexOf(tileIndex);

View file

@ -1,3 +1,9 @@
/**
* Internally used method to set the colliding state of a tile.
*
* @param {Tile} tile - [description]
* @param {boolean} [collides=true] - [description]
*/
var SetTileCollision = function (tile, collides) var SetTileCollision = function (tile, collides)
{ {
if (collides) if (collides)

View file

@ -1,7 +1,18 @@
var GetTilesWithin = require('./GetTilesWithin'); var GetTilesWithin = require('./GetTilesWithin');
var ShuffleArray = require('../../../utils/array/Shuffle'); var ShuffleArray = require('../../../utils/array/Shuffle');
// Shuffles indices, not other properties. Does not modify collisions. Matches v2 functionality. /**
* Shuffles the tiles in a rectangular region (specified in tile coordinates) within the given
* layer. It will only randomize the tiles in that area, so if they're all the same nothing will
* appear to have changed! This method only modifies tile indexes and does not change collision
* information.
*
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var Shuffle = function (tileX, tileY, width, height, layer) var Shuffle = function (tileX, tileY, width, height, layer)
{ {
var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer); var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);

View file

@ -1,6 +1,18 @@
var GetTilesWithin = require('./GetTilesWithin'); var GetTilesWithin = require('./GetTilesWithin');
// Swaps indices, not other properties. Does not modify collisions. Matches v2 functionality. /**
* Scans the given rectangular area (given in tile coordinates) for tiles with an index matching
* `indexA` and swaps then with `indexB`. This only modifies the index and does not change collision
* information.
*
* @param {number} tileA - First tile index.
* @param {number} tileB - Second tile index.
* @param {number} [tileX=0] - [description]
* @param {number} [tileY=0] - [description]
* @param {number} [width=max width based on tileX] - [description]
* @param {number} [height=max height based on tileY] - [description]
* @param {LayerData} layer - [description]
*/
var SwapByIndex = function (indexA, indexB, tileX, tileY, width, height, layer) var SwapByIndex = function (indexA, indexB, tileX, tileY, width, height, layer)
{ {
var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer); var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);

View file

@ -1,3 +1,12 @@
/**
* Internally used method to convert from tile X coordinates to world X coordinates, factoring in
* layer position, scale and scroll.
*
* @param {number} tileX - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number}
*/
var TileToWorldX = function (tileX, camera, layer) var TileToWorldX = function (tileX, camera, layer)
{ {
var tileWidth = layer.tileWidth; var tileWidth = layer.tileWidth;

View file

@ -1,3 +1,12 @@
/**
* Internally used method to convert from tile Y coordinates to world Y coordinates, factoring in
* layer position, scale and scroll.
*
* @param {number} tileY - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number}
*/
var TileToWorldY = function (tileY, camera, layer) var TileToWorldY = function (tileY, camera, layer)
{ {
var tileHeight = layer.tileHeight; var tileHeight = layer.tileHeight;

View file

@ -1,3 +1,14 @@
/**
* Converts from world X coordinates (pixels) to tile X coordinates (tile units), factoring in the
* layer's position, scale and scroll.
*
* @param {number} worldX - [description]
* @param {boolean} [snapToFloor=true] - Whether or not to round the tile coordinate down to the
* nearest integer.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number} The X location in tile units.
*/
var WorldToTileX = function (worldX, snapToFloor, camera, layer) var WorldToTileX = function (worldX, snapToFloor, camera, layer)
{ {
if (snapToFloor === undefined) { snapToFloor = true; } if (snapToFloor === undefined) { snapToFloor = true; }

View file

@ -2,6 +2,20 @@ var WorldToTileX = require('./WorldToTileX');
var WorldToTileY = require('./WorldToTileY'); var WorldToTileY = require('./WorldToTileY');
var Vector2 = require('../../../math/Vector2'); var Vector2 = require('../../../math/Vector2');
/**
* Converts from world XY coordinates (pixels) to tile XY coordinates (tile units), factoring in the
* layer's position, scale and scroll. This will return a new Vector2 object or update the given
* `point` object.
*
* @param {number} worldX - [description]
* @param {number} worldY - [description]
* @param {boolean} [snapToFloor=true] - Whether or not to round the tile coordinate down to the
* nearest integer.
* @param {Vector2} [point] - [description]
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {Vector2} The XY location in tile units.
*/
var WorldToTileXY = function (worldX, worldY, snapToFloor, point, camera, layer) var WorldToTileXY = function (worldX, worldY, snapToFloor, point, camera, layer)
{ {
if (point === undefined) { point = new Vector2(0, 0); } if (point === undefined) { point = new Vector2(0, 0); }

View file

@ -1,3 +1,14 @@
/**
* Converts from world Y coordinates (pixels) to tile Y coordinates (tile units), factoring in the
* layer's position, scale and scroll.
*
* @param {number} worldY - [description]
* @param {boolean} [snapToFloor=true] - Whether or not to round the tile coordinate down to the
* nearest integer.
* @param {Camera} [camera=main camera] - [description]
* @param {LayerData} layer - [description]
* @returns {number} The Y location in tile units.
*/
var WorldToTileY = function (worldY, snapToFloor, camera, layer) var WorldToTileY = function (worldY, snapToFloor, camera, layer)
{ {
if (snapToFloor === undefined) { snapToFloor = true; } if (snapToFloor === undefined) { snapToFloor = true; }

View file

@ -5,7 +5,17 @@ var LayerData = new Class({
initialize: initialize:
function MapLayerData (config) /**
* A class for representing data about about a layer in a map. Maps are parsed from CSV, Tiled,
* etc. into this format. Tilemap, StaticTilemapLayer and DynamicTilemapLayer have a reference
* to this data and use it to look up and perform operations on tiles.
*
* @class LayerData
* @constructor
*
* @param {object} [config] - [description]
*/
function LayerData (config)
{ {
if (config === undefined) { config = {}; } if (config === undefined) { config = {}; }

View file

@ -5,6 +5,16 @@ var MapData = new Class({
initialize: initialize:
/**
* A class for representing data about a map. Maps are parsed from CSV, Tiled, etc. into this
* format. A Tilemap object get a copy of this data and then unpacks the needed properties into
* itself.
*
* @class MapData
* @constructor
*
* @param {object} [config] - [description]
*/
function MapData (config) function MapData (config)
{ {
if (config === undefined) { config = {}; } if (config === undefined) { config = {}; }

View file

@ -4,20 +4,34 @@ var ParseCSV = require('./ParseCSV');
var ParseTiledJSON = require('./parsetiledjson/'); var ParseTiledJSON = require('./parsetiledjson/');
var Formats = require('../Formats'); var Formats = require('../Formats');
var Parse = function (key, mapFormat, mapData, tileWidth, tileHeight, insertNull) /**
* Parses raw data of a given Tilemap format into a new MapData object.
*
* @param {string} name - The name of the tilemap, used to set the name on the MapData.
* @param {number} mapFormat - See ../Formats.js.
* @param {array|string|object} data - 2D array, CSV string or Tiled JSON object
* @param {number} tileWidth - Required for 2D array and CSV, but ignored for Tiled JSON.
* @param {number} tileHeight - Required for 2D array and CSV, but ignored for Tiled JSON.
* @param {boolean} [insertNull=false] - If true, instead of placing empty tiles at locations where
* the tile index is -1, this will place null. If you've a large sparsely populated map and the tile
* data doesn't need to change then setting this value to `true` will help with memory consumption.
* However if your map is small, or you need to update the tiles (perhaps the map dynamically
* changes during the game) then leave the default value set.
*/
var Parse = function (name, mapFormat, data, tileWidth, tileHeight, insertNull)
{ {
var newMap; var newMap;
switch(mapFormat) switch(mapFormat)
{ {
case (Formats.TILEMAP_2D_ARRAY): case (Formats.TILEMAP_2D_ARRAY):
newMap = Parse2DArray(key, mapData, tileWidth, tileHeight, insertNull); newMap = Parse2DArray(name, data, tileWidth, tileHeight, insertNull);
break; break;
case (Formats.TILEMAP_CSV): case (Formats.TILEMAP_CSV):
newMap = ParseCSV(key, mapData, tileWidth, tileHeight, insertNull); newMap = ParseCSV(name, data, tileWidth, tileHeight, insertNull);
break; break;
case (Formats.TILEMAP_TILED_JSON): case (Formats.TILEMAP_TILED_JSON):
newMap = ParseTiledJSON(key, mapData, insertNull); newMap = ParseTiledJSON(name, data, insertNull);
break; break;
default: default:
console.warn('Unrecognized tilemap data format: ' + mapFormat); console.warn('Unrecognized tilemap data format: ' + mapFormat);

View file

@ -18,6 +18,7 @@ var MatterImage = new Class({
Components.Gravity, Components.Gravity,
Components.Mass, Components.Mass,
Components.Sensor, Components.Sensor,
Components.SetBody,
Components.Sleep, Components.Sleep,
Components.Static, Components.Static,
Components.Transform, Components.Transform,
@ -35,29 +36,27 @@ var MatterImage = new Class({
this.setSizeToFrame(); this.setSizeToFrame();
this.setOrigin(); this.setOrigin();
this._tempVec2 = new Vector2(); this.world = world;
var isCircle = GetFastValue(options, 'isCircle', false); this._tempVec2 = new Vector2(x, y);
if (isCircle) var shape = GetFastValue(options, 'shape', null);
{
var radius = GetFastValue(options, 'radius', Math.max(this.width, this.height) / 2);
this.body = Bodies.circle(x, y, radius, options); if (!shape)
}
else
{ {
this.body = Bodies.rectangle(x, y, this.width, this.height, options); this.body = Bodies.rectangle(x, y, this.width, this.height, options);
}
this.body.gameObject = this; this.body.gameObject = this;
this.world = world;
if (GetFastValue(options, 'addToWorld', true)) if (GetFastValue(options, 'addToWorld', true))
{ {
world.add(this.body); world.add(this.body);
} }
}
else
{
this.setBody(shape, options);
}
this.setPosition(x, y); this.setPosition(x, y);
} }

View file

@ -19,6 +19,7 @@ var MatterSprite = new Class({
Components.Gravity, Components.Gravity,
Components.Mass, Components.Mass,
Components.Sensor, Components.Sensor,
Components.SetBody,
Components.Sleep, Components.Sleep,
Components.Static, Components.Static,
Components.Transform, Components.Transform,
@ -38,29 +39,27 @@ var MatterSprite = new Class({
this.setSizeToFrame(); this.setSizeToFrame();
this.setOrigin(); this.setOrigin();
this._tempVec2 = new Vector2(); this.world = world;
var isCircle = GetFastValue(options, 'isCircle', false); this._tempVec2 = new Vector2(x, y);
if (isCircle) var shape = GetFastValue(options, 'shape', null);
{
var radius = GetFastValue(options, 'radius', Math.max(this.width, this.height) / 2);
this.body = Bodies.circle(x, y, radius, options); if (!shape)
}
else
{ {
this.body = Bodies.rectangle(x, y, this.width, this.height, options); this.body = Bodies.rectangle(x, y, this.width, this.height, options);
}
this.body.gameObject = this; this.body.gameObject = this;
this.world = world;
if (GetFastValue(options, 'addToWorld', true)) if (GetFastValue(options, 'addToWorld', true))
{ {
world.add(this.body); world.add(this.body);
} }
}
else
{
this.setBody(shape, options);
}
this.setPosition(x, y); this.setPosition(x, y);
} }

View file

@ -2,6 +2,7 @@
var Bodies = require('./lib/factory/Bodies'); var Bodies = require('./lib/factory/Bodies');
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
var Composite = require('./lib/body/Composite');
var Engine = require('./lib/core/Engine'); var Engine = require('./lib/core/Engine');
var EventDispatcher = require('../../events/EventDispatcher'); var EventDispatcher = require('../../events/EventDispatcher');
var GetFastValue = require('../../utils/object/GetFastValue'); var GetFastValue = require('../../utils/object/GetFastValue');
@ -61,6 +62,26 @@ var World = new Class({
} }
} }
this.isPaused = GetValue(config, 'isPaused', false);
this.drawDebug = GetValue(config, 'debug', false);
this.debugGraphic;
this.defaults = {
debugShowBody: GetValue(config, 'debugShowBody', true),
debugShowStaticBody: GetValue(config, 'debugShowStaticBody', true),
debugShowVelocity: GetValue(config, 'debugShowVelocity', true),
bodyDebugColor: GetValue(config, 'debugBodyColor', 0xff00ff),
staticBodyDebugColor: GetValue(config, 'debugBodyColor', 0x0000ff),
velocityDebugColor: GetValue(config, 'debugVelocityColor', 0x00ff00)
};
if (this.drawDebug)
{
this.createDebugGraphic();
}
this.setEventsProxy(); this.setEventsProxy();
}, },
@ -153,7 +174,7 @@ var World = new Class({
x += (width / 2); x += (width / 2);
y += (height / 2); y += (height / 2);
this.walls[position] = this.create(x, y, width, height, { isStatic: true }); this.walls[position] = this.create(x, y, width, height, { isStatic: true, friction: 0, frictionStatic: 0 });
} }
else else
{ {
@ -166,6 +187,28 @@ var World = new Class({
} }
}, },
createDebugGraphic: function ()
{
var graphic = this.scene.sys.add.graphics({ x: 0, y: 0 });
graphic.setZ(Number.MAX_SAFE_INTEGER);
this.debugGraphic = graphic;
this.drawDebug = true;
return graphic;
},
disableGravity: function ()
{
this.localWorld.gravity.x = 0;
this.localWorld.gravity.y = 0;
this.localWorld.gravity.scale = 0;
return this;
},
setGravity: function (x, y, scale) setGravity: function (x, y, scale)
{ {
this.localWorld.gravity.x = x; this.localWorld.gravity.x = x;
@ -188,10 +231,17 @@ var World = new Class({
return body; return body;
}, },
// body can be single or an array // object can be single or an array, and can be a body, composite or constraint
add: function (body) add: function (object)
{ {
MatterWorld.add(this.localWorld, body); MatterWorld.add(this.localWorld, object);
return this;
},
remove: function (object, deep)
{
MatterWorld.remove(this.localWorld, object, deep);
return this; return this;
}, },
@ -208,6 +258,11 @@ var World = new Class({
update: function (time, delta) update: function (time, delta)
{ {
if (this.isPaused)
{
return;
}
var correction = 1; var correction = 1;
Engine.update(this.engine, delta, correction); Engine.update(this.engine, delta, correction);
@ -215,7 +270,51 @@ var World = new Class({
postUpdate: function () postUpdate: function ()
{ {
// NOOP if (this.drawDebug)
{
var graphics = this.debugGraphic;
var bodies = Composite.allBodies(this.localWorld);
graphics.clear();
graphics.lineStyle(1, this.defaults.bodyDebugColor);
for (var i = 0; i < bodies.length; i++)
{
body = bodies[i];
var vertices = body.vertices;
graphics.moveTo(vertices[0].x, vertices[0].y);
for (var j = 1; j < vertices.length; j++)
{
graphics.lineTo(vertices[j].x, vertices[j].y);
}
graphics.lineTo(vertices[0].x, vertices[0].y);
graphics.strokePath();
// if (body.willDrawDebug())
// {
// body.drawDebug(graphics);
// }
}
}
},
fromPath: function (path, points)
{
if (points === undefined) { points = []; }
var pathPattern = /L?\s*([\-\d\.e]+)[\s,]*([\-\d\.e]+)*/ig;
path.replace(pathPattern, function(match, x, y)
{
points.push({ x: parseFloat(x), y: parseFloat(y) });
});
return points;
}, },
shutdown: function () shutdown: function ()

View file

@ -1,9 +1,19 @@
var Friction = { var Friction = {
setFriction: function (value) setFriction: function (value, air, static)
{ {
this.body.friction = value; this.body.friction = value;
if (air !== undefined)
{
this.body.frictionAir = air;
}
if (static !== undefined)
{
this.body.frictionStatic = static;
}
return this; return this;
}, },

View file

@ -0,0 +1,110 @@
var Bodies = require('../lib/factory/Bodies');
var Body = require('../lib/body/Body');
var GetFastValue = require('../../../utils/object/GetFastValue');
var SetBody = {
// Calling any of these methods resets previous properties you may have set on the body, including plugins, mass, etc
setRectangle: function (width, height, options)
{
return this.setBody({ type: 'rectangle', width: width, height: height }, options);
},
setCircle: function (radius, options)
{
return this.setBody({ type: 'circle', radius: radius }, options);
},
setPolygon: function (radius, sides, options)
{
return this.setBody({ type: 'polygon', sides: sides, radius: radius }, options);
},
setTrapezoid: function (width, height, slope, options)
{
return this.setBody({ type: 'trapezoid', width: width, height: height, slope: slope }, options);
},
setBody: function (config, options)
{
// Existing body? Remove it.
if (this.body)
{
this.world.remove(this.body);
}
if (!config)
{
return this;
}
else
{
// Allow them to do: shape: 'circle' instead of shape: { type: 'circle' }
if (typeof config === 'string')
{
// Using defaults
config = { type: config };
}
var shapeType = GetFastValue(config, 'type', 'rectangle');
var bodyX = GetFastValue(config, 'x', this._tempVec2.x);
var bodyY = GetFastValue(config, 'y', this._tempVec2.y);
var bodyWidth = GetFastValue(config, 'width', this.width);
var bodyHeight = GetFastValue(config, 'height', this.height);
switch (shapeType)
{
case 'rectangle':
this.body = Bodies.rectangle(bodyX, bodyY, bodyWidth, bodyHeight, options);
break;
case 'circle':
var radius = GetFastValue(config, 'radius', Math.max(bodyWidth, bodyHeight) / 2);
var maxSides = GetFastValue(config, 'maxSides', 25);
this.body = Bodies.circle(bodyX, bodyY, radius, options, maxSides);
break;
case 'trapezoid':
var slope = GetFastValue(config, 'slope', 0.5);
this.body = Bodies.trapezoid(bodyX, bodyY, bodyWidth, bodyHeight, slope, options);
break;
case 'polygon':
var sides = GetFastValue(config, 'sides', 5);
var pradius = GetFastValue(config, 'radius', Math.max(bodyWidth, bodyHeight) / 2);
this.body = Bodies.polygon(bodyX, bodyY, sides, pradius, options);
break;
case 'fromVertices':
case 'fromVerts':
var verts = GetFastValue(config, 'verts', []);
if (this.body)
{
Body.setVertices(this.body, verts);
}
else
{
var flagInternal = GetFastValue(config, 'flagInternal', false);
var removeCollinear = GetFastValue(config, 'removeCollinear', 0.01);
var minimumArea = GetFastValue(config, 'minimumArea', 10);
this.body = Bodies.fromVertices(bodyX, bodyY, verts, options, flagInternal, removeCollinear, minimumArea);
}
break;
}
}
this.body.gameObject = this;
if (GetFastValue(config, 'addToWorld', true))
{
this.world.add(this.body);
}
return this;
}
};
module.exports = SetBody;

View file

@ -10,6 +10,7 @@ module.exports = {
Mass: require('./Mass'), Mass: require('./Mass'),
Static: require('./Static'), Static: require('./Static'),
Sensor: require('./Sensor'), Sensor: require('./Sensor'),
SetBody: require('./SetBody'),
Sleep: require('./Sleep'), Sleep: require('./Sleep'),
Transform: require('./Transform'), Transform: require('./Transform'),
Velocity: require('./Velocity') Velocity: require('./Velocity')

View file

@ -40,6 +40,7 @@ var Axes = require('../geometry/Axes');
id: Common.nextId(), id: Common.nextId(),
type: 'body', type: 'body',
label: 'Body', label: 'Body',
gameObject: null,
parts: [], parts: [],
plugin: {}, plugin: {},
angle: 0, angle: 0,
@ -292,19 +293,23 @@ var Axes = require('../geometry/Axes');
}; };
/** /**
* Sets the mass of the body. Inverse mass and density are automatically updated to reflect the change. * Sets the mass of the body. Inverse mass, density and inertia are automatically updated to reflect the change.
* @method setMass * @method setMass
* @param {body} body * @param {body} body
* @param {number} mass * @param {number} mass
*/ */
Body.setMass = function(body, mass) { Body.setMass = function(body, mass) {
var moment = body.inertia / (body.mass / 6);
body.inertia = moment * (mass / 6);
body.inverseInertia = 1 / body.inertia;
body.mass = mass; body.mass = mass;
body.inverseMass = 1 / body.mass; body.inverseMass = 1 / body.mass;
body.density = body.mass / body.area; body.density = body.mass / body.area;
}; };
/** /**
* Sets the density of the body. Mass is automatically updated to reflect the change. * Sets the density of the body. Mass and inertia are automatically updated to reflect the change.
* @method setDensity * @method setDensity
* @param {body} body * @param {body} body
* @param {number} density * @param {number} density
@ -538,11 +543,17 @@ var Axes = require('../geometry/Axes');
* @param {vector} [point] * @param {vector} [point]
*/ */
Body.scale = function(body, scaleX, scaleY, point) { Body.scale = function(body, scaleX, scaleY, point) {
point = point || body.position;
for (var i = 0; i < body.parts.length; i++) { for (var i = 0; i < body.parts.length; i++) {
var part = body.parts[i]; var part = body.parts[i];
// scale position
part.position.x = point.x + (part.position.x - point.x) * scaleX;
part.position.y = point.y + (part.position.y - point.y) * scaleY;
// scale vertices // scale vertices
Vertices.scale(part.vertices, scaleX, scaleY, body.position); Vertices.scale(part.vertices, scaleX, scaleY, point);
// update properties // update properties
part.axes = Axes.fromVertices(part.vertices); part.axes = Axes.fromVertices(part.vertices);
@ -671,16 +682,16 @@ var Axes = require('../geometry/Axes');
// sum the properties of all compound parts of the parent body // sum the properties of all compound parts of the parent body
for (var i = body.parts.length === 1 ? 0 : 1; i < body.parts.length; i++) { for (var i = body.parts.length === 1 ? 0 : 1; i < body.parts.length; i++) {
var part = body.parts[i]; var part = body.parts[i],
mass = part.mass !== Infinity ? part.mass : 1;
properties.mass += part.mass; properties.mass += part.mass;
properties.area += part.area; properties.area += part.area;
properties.inertia += part.inertia; properties.inertia += part.inertia;
properties.centre = Vector.add(properties.centre, properties.centre = Vector.add(properties.centre, Vector.mult(part.position, mass));
Vector.mult(part.position, part.mass !== Infinity ? part.mass : 1));
} }
properties.centre = Vector.div(properties.centre, properties.centre = Vector.div(properties.centre, properties.mass);
properties.mass !== Infinity ? properties.mass : body.parts.length);
return properties; return properties;
}; };

View file

@ -275,7 +275,12 @@ var Common = require('../core/Common');
* @param {number} qualityMax * @param {number} qualityMax
*/ */
Vertices.chamfer = function(vertices, radius, quality, qualityMin, qualityMax) { Vertices.chamfer = function(vertices, radius, quality, qualityMin, qualityMax) {
if (typeof radius === 'number') {
radius = [radius];
} else {
radius = radius || [8]; radius = radius || [8];
}
if (!radius.length) if (!radius.length)
radius = [radius]; radius = [radius];

View file

@ -10,7 +10,7 @@ var GetFastValue = function (source, key, defaultValue)
{ {
return defaultValue; return defaultValue;
} }
else if (source.hasOwnProperty(key)) else if (source.hasOwnProperty(key) && source[key] !== undefined)
{ {
return source[key]; return source[key];
} }