diff --git a/demo/js/Demo.js b/demo/js/Demo.js index 248409bb..4469942e 100644 --- a/demo/js/Demo.js +++ b/demo/js/Demo.js @@ -244,21 +244,27 @@ Demo.reset(); - var bodyA = Bodies.rectangle(100, 200, 50, 50), + var bodyA = Bodies.rectangle(100, 200, 50, 50, { isStatic: true }), bodyB = Bodies.rectangle(200, 200, 50, 50), bodyC = Bodies.rectangle(300, 200, 50, 50), bodyD = Bodies.rectangle(400, 200, 50, 50), bodyE = Bodies.rectangle(550, 200, 50, 50), bodyF = Bodies.rectangle(700, 200, 50, 50), - bodyG = Bodies.circle(400, 100, 25); + bodyG = Bodies.circle(400, 100, 25), + partA = Bodies.rectangle(600, 200, 120, 50), + partB = Bodies.rectangle(660, 200, 50, 190), + compound = Body.create({ + parts: [partA, partB], + isStatic: true + }); - World.add(_world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG]); + World.add(_world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]); var counter = 0, scaleFactor = 1.01; _sceneEvents.push( - Events.on(_engine, 'tick', function(event) { + Events.on(_engine, 'beforeUpdate', function(event) { counter += 1; if (counter === 40) @@ -266,6 +272,7 @@ if (scaleFactor > 1) { Body.scale(bodyF, scaleFactor, scaleFactor); + Body.scale(compound, 0.995, 0.995); // modify bodyE vertices bodyE.vertices[0].x -= 0.2; @@ -277,7 +284,11 @@ // make bodyA move up and down and rotate constantly Body.setPosition(bodyA, { x: 100, y: 300 + 100 * Math.sin(_engine.timing.timestamp * 0.005) }); - Body.setAngularVelocity(bodyA, 0.02); + Body.rotate(bodyA, 0.02); + + // make compound body move up and down and rotate constantly + Body.setPosition(compound, { x: 600, y: 300 + 100 * Math.sin(_engine.timing.timestamp * 0.005) }); + Body.rotate(compound, 0.02); // every 1.5 sec if (counter >= 60 * 1.5) { @@ -293,9 +304,10 @@ ); var renderOptions = _engine.render.options; - renderOptions.wireframes = false; renderOptions.showAxes = true; renderOptions.showCollisions = true; + renderOptions.showPositions = true; + renderOptions.showConvexHulls = true; }; Demo.compositeManipulation = function() { diff --git a/src/body/Body.js b/src/body/Body.js index f6173b2c..9f3db17f 100644 --- a/src/body/Body.js +++ b/src/body/Body.js @@ -32,6 +32,7 @@ var Body = {}; id: Common.nextId(), type: 'body', label: 'Body', + parts: [], angle: 0, vertices: Vertices.fromPath('L 0 0 L 40 0 L 40 40 L 0 40'), position: { x: 0, y: 0 }, @@ -118,7 +119,7 @@ var Body = {}; isStatic: body.isStatic, isSleeping: body.isSleeping, parent: body.parent || body, - parts: body.parts || [body] + parts: body.parts }); Vertices.rotate(body.vertices, body.angle, body.position); @@ -313,26 +314,31 @@ var Body = {}; * @param {bool} [autoHull=true] */ Body.setParts = function(body, parts, autoHull) { - autoHull = typeof autoHull !== 'undefined' ? autoHull : true; + var i; - // ensure the body is always at index 0 - var index = Common.indexOf(parts, body); - if (index > -1) { - parts.splice(index, 1); - } + // add all the parts, ensuring that the first part is always the parent body + parts = parts.slice(0); + body.parts.length = 0; + body.parts.push(body); + body.parent = body; - parts.unshift(body); - body.parts = parts; + for (i = 0; i < parts.length; i++) { + var part = parts[i]; + if (part !== body) { + part.parent = body; + body.parts.push(part); + } + } - if (parts.length === 1) + if (body.parts.length === 1) return; - var i; + autoHull = typeof autoHull !== 'undefined' ? autoHull : true; // find the convex hull of all parts to set on the parent body if (autoHull) { var vertices = []; - for (i = 1; i < parts.length; i++) { + for (i = 0; i < parts.length; i++) { vertices = vertices.concat(parts[i].vertices); } @@ -342,36 +348,22 @@ var Body = {}; hullCentre = Vertices.centre(hull); Body.setVertices(body, hull); - Body.setPosition(body, hullCentre); - } - - // find the combined properties of all parts to set on the parent body - var mass = 0, - area = 0, - inertia = 0, - centroid = { x: 0, y: 0 }; - - for (i = 1; i < parts.length; i++) { - var part = parts[i]; - part.parent = body; - mass += part.mass; - area += part.area; - inertia += part.inertia; - Vector.add(centroid, part.position, centroid); + Vertices.translate(body.vertices, hullCentre); } - centroid = Vector.div(centroid, parts.length - 1); + // sum the properties of all compound parts of the parent body + var total = _totalProperties(body); - body.area = area; + body.area = total.area; body.parent = body; - body.position.x = centroid.x; - body.position.y = centroid.y; - body.positionPrev.x = centroid.x; - body.positionPrev.y = centroid.y; - - Body.setMass(body, mass); - Body.setInertia(body, inertia); - Body.setPosition(body, centroid); + body.position.x = total.centre.x; + body.position.y = total.centre.y; + body.positionPrev.x = total.centre.x; + body.positionPrev.y = total.centre.y; + + Body.setMass(body, total.mass); + Body.setInertia(body, total.inertia); + Body.setPosition(body, total.centre); }; /** @@ -382,14 +374,16 @@ var Body = {}; */ Body.setPosition = function(body, position) { var delta = Vector.sub(position, body.position); - - body.position.x = position.x; - body.position.y = position.y; body.positionPrev.x += delta.x; body.positionPrev.y += delta.y; - Vertices.translate(body.vertices, delta); - Bounds.update(body.bounds, body.vertices, body.velocity); + for (var i = 0; i < body.parts.length; i++) { + var part = body.parts[i]; + part.position.x += delta.x; + part.position.y += delta.y; + Vertices.translate(part.vertices, delta); + Bounds.update(part.bounds, part.vertices, body.velocity); + } }; /** @@ -400,13 +394,15 @@ var Body = {}; */ Body.setAngle = function(body, angle) { var delta = angle - body.angle; - - body.angle = angle; body.anglePrev += delta; - Vertices.rotate(body.vertices, delta, body.position); - Axes.rotate(body.axes, delta); - Bounds.update(body.bounds, body.vertices, body.velocity); + for (var i = 0; i < body.parts.length; i++) { + var part = body.parts[i]; + part.angle += delta; + Vertices.rotate(part.vertices, delta, body.position); + Axes.rotate(part.axes, delta); + Bounds.update(part.bounds, part.vertices, body.velocity); + } }; /** @@ -464,21 +460,35 @@ var Body = {}; * @param {vector} [point] */ Body.scale = function(body, scaleX, scaleY, point) { - // scale vertices - Vertices.scale(body.vertices, scaleX, scaleY, point); + for (var i = 0; i < body.parts.length; i++) { + var part = body.parts[i]; - // update properties - body.axes = Axes.fromVertices(body.vertices); - body.area = Vertices.area(body.vertices); - Body.setMass(body, body.density * body.area); + // scale vertices + Vertices.scale(part.vertices, scaleX, scaleY, body.position); - // update inertia (requires vertices to be at origin) - Vertices.translate(body.vertices, { x: -body.position.x, y: -body.position.y }); - Body.setInertia(body, Vertices.inertia(body.vertices, body.mass)); - Vertices.translate(body.vertices, { x: body.position.x, y: body.position.y }); + // update properties + part.axes = Axes.fromVertices(part.vertices); - // update bounds - Bounds.update(body.bounds, body.vertices, body.velocity); + if (!body.isStatic) { + part.area = Vertices.area(part.vertices); + Body.setMass(part, body.density * part.area); + + // update inertia (requires vertices to be at origin) + Vertices.translate(part.vertices, { x: -part.position.x, y: -part.position.y }); + Body.setInertia(part, Vertices.inertia(part.vertices, part.mass)); + Vertices.translate(part.vertices, { x: part.position.x, y: part.position.y }); + } + + // update bounds + Bounds.update(part.bounds, part.vertices, body.velocity); + } + + if (!body.isStatic) { + var total = _totalProperties(body); + body.area = total.area; + Body.setMass(body, total.mass); + Body.setInertia(body, total.inertia); + } }; /** @@ -545,6 +555,35 @@ var Body = {}; body.torque += (offset.x * force.y - offset.y * force.x) * body.inverseInertia; }; + /** + * Returns the sums of the properties of all compound parts of the parent body. + * @method _totalProperties + * @private + * @param {body} body + * @return {} + */ + var _totalProperties = function(body) { + var properties = { + mass: 0, + area: 0, + inertia: 0, + centre: { x: 0, y: 0 } + }; + + // 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++) { + var part = body.parts[i]; + properties.mass += part.mass; + properties.area += part.area; + properties.inertia += part.inertia; + Vector.add(properties.centre, part.position, properties.centre); + } + + properties.centre = Vector.div(properties.centre, body.parts.length - 1); + + return properties; + }; + /* * * Properties Documentation diff --git a/src/body/Composite.js b/src/body/Composite.js index 4429203f..d0a9affc 100644 --- a/src/body/Composite.js +++ b/src/body/Composite.js @@ -78,6 +78,12 @@ var Composite = {}; switch (obj.type) { case 'body': + // skip adding compound parts + if (obj.parent !== obj) { + Common.log('Composite.add: skipped adding a compound body part (you must add its parent instead)', 'warn'); + break; + } + Composite.addBody(composite, obj); break; case 'constraint': diff --git a/src/core/Sleeping.js b/src/core/Sleeping.js index 4f18a8f8..9b50e81c 100644 --- a/src/core/Sleeping.js +++ b/src/core/Sleeping.js @@ -67,8 +67,8 @@ var Sleeping = {}; continue; var collision = pair.collision, - bodyA = collision.bodyA, - bodyB = collision.bodyB; + bodyA = collision.bodyA.parent, + bodyB = collision.bodyB.parent; // don't wake if at least one body is static if ((bodyA.isSleeping && bodyB.isSleeping) || bodyA.isStatic || bodyB.isStatic) diff --git a/src/render/Render.js b/src/render/Render.js index 1eeb18f0..4c68b042 100644 --- a/src/render/Render.js +++ b/src/render/Render.js @@ -399,7 +399,7 @@ var Render = {}; var sprite = part.render.sprite, texture = _getTexture(render, sprite.texture); - if (options.showSleeping && part.isSleeping) + if (options.showSleeping && body.isSleeping) c.globalAlpha = 0.5; c.translate(part.position.x, part.position.y); @@ -412,7 +412,7 @@ var Render = {}; c.rotate(-part.angle); c.translate(-part.position.x, -part.position.y); - if (options.showSleeping && part.isSleeping) + if (options.showSleeping && body.isSleeping) c.globalAlpha = 1; } else { // part polygon @@ -429,7 +429,7 @@ var Render = {}; } if (!options.wireframes) { - if (options.showSleeping && part.isSleeping) { + if (options.showSleeping && body.isSleeping) { c.fillStyle = Common.shadeColor(part.render.fillStyle, 50); } else { c.fillStyle = part.render.fillStyle; @@ -442,7 +442,7 @@ var Render = {}; } else { c.lineWidth = 1; c.strokeStyle = '#bbb'; - if (options.showSleeping && part.isSleeping) + if (options.showSleeping && body.isSleeping) c.strokeStyle = 'rgba(255,255,255,0.2)'; c.stroke(); }