UNPKG

planck-js

Version:

2D physics engine for JavaScript/HTML5 game development

246 lines (213 loc) 7.43 kB
/* * Copyright (c) 2016 Ali Shakiba http://shakiba.me/planck.js * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ module.exports = ChainShape; var create = require('../util/create'); var options = require('../util/options'); var Settings = require('../Settings'); var Shape = require('../Shape'); var Math = require('../common/Math'); var Transform = require('../common/Transform'); var Rot = require('../common/Rot'); var Vec2 = require('../common/Vec2'); var AABB = require('../collision/AABB'); var EdgeShape = require('./EdgeShape'); ChainShape._super = Shape; ChainShape.prototype = create(ChainShape._super.prototype); ChainShape.TYPE = 'chain'; /** * A chain shape is a free form sequence of line segments. The chain has * two-sided collision, so you can use inside and outside collision. Therefore, * you may use any winding order. Connectivity information is used to create * smooth collisions. * * WARNING: The chain will not collide properly if there are self-intersections. */ function ChainShape(vertices, loop) { if (!(this instanceof ChainShape)) { return new ChainShape(vertices, loop); } ChainShape._super.call(this); this.m_type = ChainShape.TYPE; this.m_radius = Settings.polygonRadius; this.m_vertices = []; this.m_count = 0; this.m_prevVertex = null; this.m_nextVertex = null; this.m_hasPrevVertex = false; this.m_hasNextVertex = false; if (vertices && vertices.length) { if (loop) { this.CreateLoop(vertices); } else { this.CreateChain(vertices); } } } // ChainShape.Clear = function() { // this.m_vertices.length = 0; // this.m_count = 0; // } /** * Create a loop. This automatically adjusts connectivity. * * @param vertices an array of vertices, these are copied * @param count the vertex count */ ChainShape.prototype.CreateLoop = function(vertices) { Assert(this.m_vertices.length == 0 && this.m_count == 0); Assert(vertices.length >= 3); for (var i = 1; i < vertices.length; ++i) { var v1 = vertices[i - 1]; var v2 = vertices[i]; // If the code crashes here, it means your vertices are too close together. Assert(Vec2.DistanceSquared(v1, v2) > Settings.linearSlopSquared); } this.m_vertices.length = 0; this.m_count = vertices.length + 1; for (var i = 0; i < vertices.length; ++i) { this.m_vertices[i] = vertices[i].Clone(); } this.m_vertices[vertices.length] = vertices[0].Clone(); this.m_prevVertex = this.m_vertices[this.m_count - 2]; this.m_nextVertex = this.m_vertices[1]; this.m_hasPrevVertex = true; this.m_hasNextVertex = true; return this; } /** * Create a chain with isolated end vertices. * * @param vertices an array of vertices, these are copied * @param count the vertex count */ ChainShape.prototype.CreateChain = function(vertices) { Assert(this.m_vertices.length == 0 && this.m_count == 0); Assert(vertices.length >= 2); for (var i = 1; i < vertices.length; ++i) { // If the code crashes here, it means your vertices are too close together. var v1 = vertices[i - 1]; var v2 = vertices[i]; Assert(Vec2.DistanceSquared(v1, v2) > Settings.linearSlopSquared); } this.m_count = vertices.length; for (var i = 0; i < vertices.length; ++i) { this.m_vertices[i] = vertices[i].Clone(); } this.m_hasPrevVertex = false; this.m_hasNextVertex = false; this.m_prevVertex = null; this.m_nextVertex = null; return this; } /** * Establish connectivity to a vertex that precedes the first vertex. Don't call * this for loops. */ ChainShape.prototype.SetPrevVertex = function(prevVertex) { this.m_prevVertex = prevVertex; this.m_hasPrevVertex = true; } /** * Establish connectivity to a vertex that follows the last vertex. Don't call * this for loops. */ ChainShape.prototype.SetNextVertex = function(nextVertex) { this.m_nextVertex = nextVertex; this.m_hasNextVertex = true; } ChainShape.prototype.Clone = function() { var clone = new ChainShape(); clone.CreateChain(this.m_vertices); clone.m_type = this.m_type; clone.m_radius = this.m_radius; clone.m_prevVertex = this.m_prevVertex; clone.m_nextVertex = this.m_nextVertex; clone.m_hasPrevVertex = this.m_hasPrevVertex; clone.m_hasNextVertex = this.m_hasNextVertex; return clone; } ChainShape.prototype.GetChildCount = function() { // edge count = vertex count - 1 return this.m_count - 1; } // Get a child edge. ChainShape.prototype.GetChildEdge = function(edge, childIndex) { Assert(0 <= childIndex && childIndex < this.m_count - 1); edge.m_type = EdgeShape.TYPE; edge.m_radius = this.m_radius; edge.m_vertex1 = this.m_vertices[childIndex]; edge.m_vertex2 = this.m_vertices[childIndex + 1]; if (childIndex > 0) { edge.m_vertex0 = this.m_vertices[childIndex - 1]; edge.m_hasVertex0 = true; } else { edge.m_vertex0 = this.m_prevVertex; edge.m_hasVertex0 = this.m_hasPrevVertex; } if (childIndex < this.m_count - 2) { edge.m_vertex3 = this.m_vertices[childIndex + 2]; edge.m_hasVertex3 = true; } else { edge.m_vertex3 = this.m_nextVertex; edge.m_hasVertex3 = this.m_hasNextVertex; } } ChainShape.prototype.GetVertex = function(index) { Assert(0 <= index && index <= this.m_count); if (index < this.m_count) { return this.m_vertices[index]; } else { return this.m_vertices[0]; } } /** * This always return false. */ ChainShape.prototype.TestPoint = function(xf, p) { return false; } ChainShape.prototype.RayCast = function(output, input, xf, childIndex) { Assert(0 <= childIndex && childIndex < this.m_count); var edgeShape = new EdgeShape(); edgeShape.m_vertex1 = this.GetVertex(childIndex); edgeShape.m_vertex2 = this.GetVertex(childIndex + 1); return edgeShape.RayCast(output, input, xf, 0); } ChainShape.prototype.ComputeAABB = function(aabb, xf, childIndex) { Assert(0 <= childIndex && childIndex < this.m_count); var v1 = Transform.Mul(xf, this.GetVertex(childIndex)); var v2 = Transform.Mul(xf, this.GetVertex(childIndex + 1)); aabb.CombinePoints(v1, v2); } /** * Chains have zero mass. */ ChainShape.prototype.ComputeMass = function(massData, density) { massData.mass = 0.0; massData.center = Vec(); massData.I = 0.0; } ChainShape.prototype.ComputeDistanceProxy = function(proxy, childIndex) { Assert(0 <= childIndex && childIndex < this.m_count); proxy.m_buffer[0] = this.GetVertex(childIndex); proxy.m_buffer[1] = this.GetVertex(childIndex + 1); proxy.m_vertices = proxy.m_buffer; proxy.m_count = 2; proxy.m_radius = this.m_radius; };