Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1 from dominichamon/master

Adding dart
  • Loading branch information...
commit 990a0d13c448caca741da575989e9e907eab3656 2 parents b9df6fd + 1956f67
@joelgwebber authored
Showing with 18,457 additions and 0 deletions.
  1. +167 −0 dart/Bench2d.dart
  2. +6,004 −0 dart/Bench2d.dart.js
  3. +45 −0 dart/bench2d.html
  4. +97 −0 dart/dartbox2d/box2d.dart
  5. +199 −0 dart/dartbox2d/callbacks/CanvasDraw.dart
  6. +41 −0 dart/dartbox2d/callbacks/ContactFilter.dart
  7. +28 −0 dart/dartbox2d/callbacks/ContactImpulse.dart
  8. +64 −0 dart/dartbox2d/callbacks/ContactListener.dart
  9. +147 −0 dart/dartbox2d/callbacks/DebugDraw.dart
  10. +23 −0 dart/dartbox2d/callbacks/DestructionListener.dart
  11. +18 −0 dart/dartbox2d/callbacks/PairCallback.dart
  12. +25 −0 dart/dartbox2d/callbacks/QueryCallback.dart
  13. +22 −0 dart/dartbox2d/callbacks/TreeCallback.dart
  14. +115 −0 dart/dartbox2d/collision/AxisAlignedBox.dart
  15. +685 −0 dart/dartbox2d/collision/Collision.dart
  16. +60 −0 dart/dartbox2d/collision/ContactID.dart
  17. +206 −0 dart/dartbox2d/collision/Distance.dart
  18. +32 −0 dart/dartbox2d/collision/DistanceInput.dart
  19. +33 −0 dart/dartbox2d/collision/DistanceOutput.dart
  20. +82 −0 dart/dartbox2d/collision/DistanceProxy.dart
  21. +80 −0 dart/dartbox2d/collision/Features.dart
  22. +90 −0 dart/dartbox2d/collision/Manifold.dart
  23. +63 −0 dart/dartbox2d/collision/ManifoldPoint.dart
  24. +22 −0 dart/dartbox2d/collision/ManifoldType.dart
  25. +21 −0 dart/dartbox2d/collision/PointState.dart
  26. +354 −0 dart/dartbox2d/collision/Simplex.dart
  27. +53 −0 dart/dartbox2d/collision/SimplexCache.dart
  28. +45 −0 dart/dartbox2d/collision/SimplexVertex.dart
  29. +547 −0 dart/dartbox2d/collision/TimeOfImpact.dart
  30. +164 −0 dart/dartbox2d/collision/WorldManifold.dart
  31. +270 −0 dart/dartbox2d/collision/broadphase/BroadPhase.dart
  32. +445 −0 dart/dartbox2d/collision/broadphase/DynamicTree.dart
  33. +55 −0 dart/dartbox2d/collision/broadphase/DynamicTreeNode.dart
  34. +52 −0 dart/dartbox2d/collision/broadphase/Pair.dart
  35. +96 −0 dart/dartbox2d/collision/shapes/CircleShape.dart
  36. +50 −0 dart/dartbox2d/collision/shapes/MassData.dart
  37. +444 −0 dart/dartbox2d/collision/shapes/PolygonShape.dart
  38. +62 −0 dart/dartbox2d/collision/shapes/Shape.dart
  39. +26 −0 dart/dartbox2d/collision/shapes/ShapeType.dart
  40. +130 −0 dart/dartbox2d/common/CanvasViewportTransform.dart
  41. +41 −0 dart/dartbox2d/common/Color3.dart
  42. +81 −0 dart/dartbox2d/common/IViewportTransform.dart
  43. +55 −0 dart/dartbox2d/common/MathBox.dart
  44. +170 −0 dart/dartbox2d/common/Matrix22.dart
  45. +109 −0 dart/dartbox2d/common/Matrix33.dart
  46. +142 −0 dart/dartbox2d/common/Settings.dart
  47. +102 −0 dart/dartbox2d/common/Sweep.dart
  48. +97 −0 dart/dartbox2d/common/Transform.dart
  49. +217 −0 dart/dartbox2d/common/Vector.dart
  50. +121 −0 dart/dartbox2d/common/Vector3.dart
  51. +971 −0 dart/dartbox2d/dynamics/Body.dart
  52. +102 −0 dart/dartbox2d/dynamics/BodyDef.dart
  53. +32 −0 dart/dartbox2d/dynamics/BodyType.dart
  54. +239 −0 dart/dartbox2d/dynamics/ContactManager.dart
  55. +58 −0 dart/dartbox2d/dynamics/Filter.dart
  56. +153 −0 dart/dartbox2d/dynamics/Fixture.dart
  57. +72 −0 dart/dartbox2d/dynamics/FixtureDef.dart
  58. +328 −0 dart/dartbox2d/dynamics/Island.dart
  59. +41 −0 dart/dartbox2d/dynamics/TimeStep.dart
  60. +1,180 −0 dart/dartbox2d/dynamics/World.dart
  61. +28 −0 dart/dartbox2d/dynamics/contacts/CircleContact.dart
  62. +212 −0 dart/dartbox2d/dynamics/contacts/Contact.dart
  63. +77 −0 dart/dartbox2d/dynamics/contacts/ContactConstraint.dart
  64. +56 −0 dart/dartbox2d/dynamics/contacts/ContactConstraintPoint.dart
  65. +19 −0 dart/dartbox2d/dynamics/contacts/ContactCreator.dart
  66. +42 −0 dart/dartbox2d/dynamics/contacts/ContactEdge.dart
  67. +22 −0 dart/dartbox2d/dynamics/contacts/ContactRegister.dart
  68. +657 −0 dart/dartbox2d/dynamics/contacts/ContactSolver.dart
  69. +30 −0 dart/dartbox2d/dynamics/contacts/PolygonAndCircleContact.dart
  70. +30 −0 dart/dartbox2d/dynamics/contacts/PolygonContact.dart
  71. +51 −0 dart/dartbox2d/dynamics/contacts/TimeOfImpactConstraint.dart
  72. +231 −0 dart/dartbox2d/dynamics/contacts/TimeOfImpactSolver.dart
  73. +241 −0 dart/dartbox2d/dynamics/joints/ConstantVolumeJoint.dart
  74. +59 −0 dart/dartbox2d/dynamics/joints/ConstantVolumeJointDef.dart
  75. +214 −0 dart/dartbox2d/dynamics/joints/DistanceJoint.dart
  76. +70 −0 dart/dartbox2d/dynamics/joints/DistanceJointDef.dart
  77. +145 −0 dart/dartbox2d/dynamics/joints/Joint.dart
  78. +50 −0 dart/dartbox2d/dynamics/joints/JointDef.dart
  79. +47 −0 dart/dartbox2d/dynamics/joints/JointEdge.dart
  80. +31 −0 dart/dartbox2d/dynamics/joints/JointType.dart
  81. +21 −0 dart/dartbox2d/dynamics/joints/LimitState.dart
  82. +492 −0 dart/dartbox2d/dynamics/joints/RevoluteJoint.dart
  83. +100 −0 dart/dartbox2d/dynamics/joints/RevoluteJointDef.dart
  84. +59 −0 dart/dartbox2d/pooling/DefaultWorldPool.dart
View
167 dart/Bench2d.dart
@@ -0,0 +1,167 @@
+#library('Bench2d');
+#import('dart:dom');
+#import('dartbox2d/box2d.dart');
+
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+class Bench2d {
+ static final int CANVAS_WIDTH = 900;
+ static final int CANVAS_HEIGHT = 600;
+
+ static final int WARMUP = 64;
+ static final int FRAMES = 256;
+ static final int PYRAMID_SIZE = 40;
+
+ static final num _VIEWPORT_SCALE = 10;
+
+ static final num GRAVITY = -10;
+
+ static final num TIME_STEP = 1/60;
+ static final int VELOCITY_ITERATIONS = 3;
+ static final int POSITION_ITERATIONS = 3;
+
+ HTMLCanvasElement canvas;
+ CanvasRenderingContext2D ctx;
+ IViewportTransform viewport;
+ DebugDraw debugDraw;
+
+ World world;
+
+ Bench2d() {
+ final gravity = new Vector(0, GRAVITY);
+ bool doSleep = true;
+ world = new World(gravity, doSleep, new DefaultWorldPool());
+ }
+
+ /**
+ * Creates the canvas and readies the demo for animation. Must be called
+ * before calling runAnimation.
+ */
+ void initializeAnimation() {
+ // Setup the canvas.
+ canvas = document.createElement('canvas');
+ canvas.width = CANVAS_WIDTH;
+ canvas.height = CANVAS_HEIGHT;
+ document.body.appendChild(canvas);
+ ctx = canvas.getContext("2d");
+
+ // Create the viewport transform with the center at extents.
+ final extents = new Vector(CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2);
+ viewport = new CanvasViewportTransform(extents, extents);
+ viewport.scale = _VIEWPORT_SCALE;
+
+ // Create our canvas drawing tool to give to the world.
+ debugDraw = new CanvasDraw(viewport, ctx);
+
+ // Have the world draw itself for debugging purposes.
+ world.debugDraw = debugDraw;
+ }
+
+ void initialize() {
+ {
+ BodyDef bd = new BodyDef();
+ Body ground = world.createBody(bd);
+
+ PolygonShape shape = new PolygonShape();
+ shape.setAsEdge(new Vector(-40.0, 0), new Vector(40.0, 0));
+
+ final fixDef = new FixtureDef();
+ fixDef.shape = shape;
+ fixDef.density = 0;
+
+ ground.createFixture(fixDef);
+ }
+
+ {
+ num a = .5;
+ PolygonShape shape = new PolygonShape();
+ shape.setAsBox(a, a);
+
+ final fixDef = new FixtureDef();
+ fixDef.shape = shape;
+ fixDef.density = 5;
+
+ Vector x = new Vector(-7.0, 0.75);
+ Vector y = new Vector();
+ Vector deltaX = new Vector(0.5625, 1);
+ Vector deltaY = new Vector(1.125, 0.0);
+
+ for (int i = 0; i < PYRAMID_SIZE; ++i){
+ y.setFrom(x);
+
+ for (int j = i; j < PYRAMID_SIZE; ++j){
+ BodyDef bd = new BodyDef();
+ bd.type = BodyType.DYNAMIC;
+ bd.position.setFrom(y);
+ Body body = world.createBody(bd);
+ body.createFixture(fixDef);
+ y.addLocal(deltaY);
+ }
+
+ x.addLocal(deltaX);
+ }
+ }
+ }
+
+ void step() {
+ world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
+ }
+
+ void render() {
+ step();
+
+ ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
+ world.drawDebugData();
+ window.webkitRequestAnimationFrame((num time) {
+ render();
+ }, canvas);
+ }
+
+ void runAnimation() {
+ window.webkitRequestAnimationFrame((num time) {
+ render();
+ }, canvas);
+ }
+
+ void warmup() {
+ for (int i = 0; i < WARMUP; ++i) step();
+ }
+
+ void bench() {
+ Bench2d bench2d = new Bench2d();
+
+ final times = new List<int>(FRAMES);
+ for (int i = 0; i < FRAMES; ++i) {
+ final watch = new Stopwatch();
+ watch.start();
+ bench2d.step();
+ watch.stop();
+ times[i] = watch.elapsed() / watch.frequency();
+ print(times[i]);
+ }
+
+ int total = 0;
+ for (int i = 0; i < FRAMES; ++i) total += times[i];
+ print('Average: ${total / FRAMES}');
+ }
+}
+
+void main() {
+ final bench2d = new Bench2d();
+ bench2d.initialize();
+ bench2d.warmup();
+ bench2d.bench();
+}
+
View
6,004 dart/Bench2d.dart.js
6,004 additions, 0 deletions not shown
View
45 dart/bench2d.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Bench2d</title>
+
+ <!-- This tag improves the experience on mobile browsers. -->
+ <meta name="viewport" content="width=device-width, initial-scale=1.0,
+ maximum-scale=1.0, user-scalable=0">
+
+ <!-- These tags improve web apps on ios. -->
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ </head>
+ <body>
+ <script type="text/javascript">
+ function getQueryVariable(variable) {
+ var query = window.location.search.substring(1);
+ var vars = query.split('&');
+ for (var i = 0; i < vars.length; i++) {
+ var pair = vars[i].split('=');
+ if (pair[0] == variable) {
+ return unescape(pair[1]);
+ }
+ }
+ return null;
+ }
+
+ console.log('Loading Bench2d');
+ console.log('Checking for native dart support');
+ var script = document.createElement('script');
+ if(getQueryVariable('native') && document.implementation.hasFeature('dart')) {
+ // Browser has the goods, so kick it in to dart gear.
+ console.log('Dart supported.');
+ script.setAttribute('type', 'application/dart');
+ script.setAttribute('src', 'Bench2d.dart');
+ } else {
+ // If browser doesn't support dart, redirect to js version.
+ console.log('JS fallback.');
+ script.setAttribute('src', 'Bench2d.dart.js');
+ }
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html>
+
+
View
97 dart/dartbox2d/box2d.dart
@@ -0,0 +1,97 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#library('box2d');
+#import('dart:dom');
+
+#source('collision/AxisAlignedBox.dart');
+#source('collision/Collision.dart');
+#source('collision/ContactID.dart');
+#source('collision/Distance.dart');
+#source('collision/DistanceInput.dart');
+#source('collision/DistanceOutput.dart');
+#source('collision/DistanceProxy.dart');
+#source('collision/Features.dart');
+#source('collision/Manifold.dart');
+#source('collision/ManifoldPoint.dart');
+#source('collision/ManifoldType.dart');
+#source('collision/PointState.dart');
+#source('collision/Simplex.dart');
+#source('collision/SimplexCache.dart');
+#source('collision/SimplexVertex.dart');
+#source('collision/TimeOfImpact.dart');
+#source('collision/WorldManifold.dart');
+#source('collision/broadphase/BroadPhase.dart');
+#source('collision/broadphase/DynamicTree.dart');
+#source('collision/broadphase/DynamicTreeNode.dart');
+#source('collision/broadphase/Pair.dart');
+#source('collision/shapes/CircleShape.dart');
+#source('collision/shapes/MassData.dart');
+#source('collision/shapes/PolygonShape.dart');
+#source('collision/shapes/Shape.dart');
+#source('collision/shapes/ShapeType.dart');
+#source('callbacks/CanvasDraw.dart');
+#source('callbacks/PairCallback.dart');
+#source('callbacks/TreeCallback.dart');
+#source('callbacks/ContactListener.dart');
+#source('callbacks/ContactFilter.dart');
+#source('callbacks/ContactImpulse.dart');
+#source('callbacks/QueryCallback.dart');
+#source('callbacks/DebugDraw.dart');
+#source('callbacks/DestructionListener.dart');
+#source('dynamics/Body.dart');
+#source('dynamics/BodyDef.dart');
+#source('dynamics/BodyType.dart');
+#source('dynamics/ContactManager.dart');
+#source('dynamics/Filter.dart');
+#source('dynamics/Fixture.dart');
+#source('dynamics/FixtureDef.dart');
+#source('dynamics/Island.dart');
+#source('dynamics/TimeStep.dart');
+#source('dynamics/World.dart');
+#source('dynamics/contacts/Contact.dart');
+#source('dynamics/contacts/ContactConstraint.dart');
+#source('dynamics/contacts/ContactConstraintPoint.dart');
+#source('dynamics/contacts/ContactEdge.dart');
+#source('dynamics/contacts/ContactCreator.dart');
+#source('dynamics/contacts/CircleContact.dart');
+#source('dynamics/contacts/ContactRegister.dart');
+#source('dynamics/contacts/ContactSolver.dart');
+#source('dynamics/contacts/PolygonAndCircleContact.dart');
+#source('dynamics/contacts/PolygonContact.dart');
+#source('dynamics/contacts/TimeOfImpactSolver.dart');
+#source('dynamics/contacts/TimeOfImpactConstraint.dart');
+#source('dynamics/joints/Joint.dart');
+#source('dynamics/joints/JointEdge.dart');
+#source('dynamics/joints/JointDef.dart');
+#source('dynamics/joints/JointType.dart');
+#source('dynamics/joints/LimitState.dart');
+#source('dynamics/joints/ConstantVolumeJoint.dart');
+#source('dynamics/joints/ConstantVolumeJointDef.dart');
+#source('dynamics/joints/DistanceJoint.dart');
+#source('dynamics/joints/DistanceJointDef.dart');
+#source('dynamics/joints/RevoluteJoint.dart');
+#source('dynamics/joints/RevoluteJointDef.dart');
+#source('pooling/DefaultWorldPool.dart');
+#source('common/Color3.dart');
+#source('common/IViewportTransform.dart');
+#source('common/MathBox.dart');
+#source('common/CanvasViewportTransform.dart');
+#source('common/Matrix22.dart');
+#source('common/Matrix33.dart');
+#source('common/Settings.dart');
+#source('common/Sweep.dart');
+#source('common/Transform.dart');
+#source('common/Vector.dart');
+#source('common/Vector3.dart');
+
View
199 dart/dartbox2d/callbacks/CanvasDraw.dart
@@ -0,0 +1,199 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Draws using the given canvas context for debugging purposes.
+ */
+// TODO(gregbglw): Test all of these methods to make sure that they draw the
+// correct things.
+class CanvasDraw extends DebugDraw {
+ /**
+ * The canvas rendering context with which to draw.
+ */
+ CanvasRenderingContext2D ctx;
+
+ CanvasDraw(IViewportTransform viewport, this.ctx) : super(viewport) { }
+
+ /**
+ * Draw a closed polygon provided in CCW order. This implementation
+ * uses [drawSegment] to draw each side of the polygon.
+ */
+ void drawPolygon(List<Vector> vertices, int vertexCount, Color3 color) {
+ if(vertexCount == 1){
+ drawSegment(vertices[0], vertices[0], color);
+ return;
+ }
+
+ for(int i=0; i<vertexCount-1; i+=1){
+ drawSegment(vertices[i], vertices[i+1], color);
+ }
+
+ if(vertexCount > 2){
+ drawSegment(vertices[vertexCount-1], vertices[0], color);
+ }
+ }
+
+ /**
+ * Draws the given point with the given radius, in the given color.
+ */
+ void drawPoint(Vector argPoint, num argRadiusOnScreen, Color3 argColor) {
+ _color = argColor;
+ getWorldToScreenToOut(argPoint, argPoint);
+ ctx.beginPath();
+ ctx.arc(argPoint.x, argPoint.y, argRadiusOnScreen, 0, MathBox._2PI, true);
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ /**
+ * Draw a solid closed polygon provided in CCW order.
+ */
+ void drawSolidPolygon(List<Vector> vertices, int vertexCount,
+ Color3 color) {
+ // Set the color and convert to screen coordinates.
+ _color = color;
+ // TODO(gregbglw): Do a single ctx transform rather than convert all of
+ // these vectors.
+ for (int i = 0; i < vertexCount; i++) {
+ getWorldToScreenToOut(vertices[i], vertices[i]);
+ }
+ ctx.beginPath();
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+
+ // Draw lines to all of the remaining points.
+ for (int i = 1; i < vertexCount; i++) {
+ ctx.lineTo(vertices[i].x, vertices[i].y);
+ }
+
+ // Draw a line back to the starting point.
+ ctx.lineTo(vertices[0].x, vertices[0].y);
+
+ // Fill in the drawn polygon.
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ /**
+ * Draw a circle.
+ */
+ void drawCircle(Vector center, num radius, Color3 color) {
+ _color = color;
+ getWorldToScreenToOut(center, center);
+ radius *= viewportTransform.scale;
+
+ ctx.beginPath();
+ ctx.arc(center.x, center.y, radius, 0, MathBox._2PI, true);
+ ctx.closePath();
+ ctx.stroke();
+ }
+
+ /**
+ * Draw a solid circle.
+ */
+ void drawSolidCircle(Vector center, num radius, Vector axis,
+ Color3 color) {
+ _color = color;
+ getWorldToScreenToOut(center, center);
+ radius *= viewportTransform.scale;
+
+ ctx.beginPath();
+ ctx.arc(center.x, center.y, radius, 0, MathBox._2PI, true);
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ /**
+ * Draw a line segment.
+ */
+ void drawSegment(Vector p1, Vector p2, Color3 color) {
+ _color = color;
+ getWorldToScreenToOut(p1, p1);
+ getWorldToScreenToOut(p2, p2);
+ ctx.beginPath();
+ ctx.moveTo(p1.x, p1.y);
+ ctx.lineTo(p2.x, p2.y);
+ ctx.closePath();
+ ctx.stroke();
+ }
+
+ /**
+ * Draw a transform. Choose your own length scale.
+ */
+ void drawTransform(Transform xf) {
+ throw new NotImplementedException();
+ }
+
+ /**
+ * Draw a string.
+ */
+ void drawString(num x, num y, String s, Color3 color) {
+ _color = color;
+ ctx.strokeText(s, x, y);
+ }
+
+ /** Sets the rendering context stroke color based on the given color3. */
+ void set _color(Color3 color) {
+ int red = (color.x * 255).round().toInt();
+ int green = (color.y * 255).round().toInt();
+ int blue = (color.z * 255).round().toInt();
+
+ // TODO(dominich): Replace with the following when setStrokeColor works correctly.
+ //ctx.setStrokeColor(red, green, blue);
+ //ctx.setFillColor(red, green, blue);
+
+ final colorString = new StringBuffer("#");
+ String redString = _toHex(red);
+ String greenString = _toHex(green);
+ String blueString = _toHex(blue);
+
+ colorString.add(redString);
+ colorString.add(greenString);
+ colorString.add(blueString);
+
+ ctx.setStrokeColor(colorString.toString());
+ ctx.setFillColor(colorString.toString());
+ }
+
+ /**
+ * Conversion routines to get hex numbers from base 10 numbers. From
+ * en.wikipedia.org/wiki/Hexadecimal.
+ * TODO(gregbglw): Replace with toRadixString when that function is
+ * implemented.
+ */
+ String _toHex(int n) {
+ //TODO(gregbglw): Remove this once my vm is working again.
+ // Mysterious, non-reproducible bug.
+ n = n.toInt();
+ int r = (n % 16);
+ String result;
+ if (n - r == 0) {
+ result = _toChar(r);
+ } else {
+ result = _toHex((n - r) ~/ 16);
+ result.concat(_toChar(r));
+ }
+
+ return result;
+ }
+
+ //TODO(gregbglw): Override getScreenToWorld and getWorldToScreen and just do
+ //context transformations. This should be a performance boost over converting
+ //lots of vectors. Should just do this once and draw everything.
+
+ /** Returns the hex digit corresponding to the given value. */
+ String _toChar(int n) {
+ final alpha = "0123456789ABCDEF";
+ return alpha[n];
+ }
+}
View
41 dart/dartbox2d/callbacks/ContactFilter.dart
@@ -0,0 +1,41 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Implement a subtype of this class and pass it as the argument to
+ * world.setContactFilter to provide collision filtering.
+ * In other words, you can implement this class if you want finer control over
+ * contact creation.
+ */
+class ContactFilter {
+ ContactFilter() { }
+
+ /**
+ * Return true if contact calculations should be performed between these two
+ * shapes.
+ */
+ bool shouldCollide(Fixture fixtureA, Fixture fixtureB){
+ Filter filterA = fixtureA.filter;
+ Filter filterB = fixtureB.filter;
+
+ if (filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0){
+ return filterA.groupIndex > 0;
+ }
+
+ bool collide = (filterA.maskBits & filterB.categoryBits) != 0 &&
+ (filterA.categoryBits & filterB.maskBits) != 0;
+ return collide;
+ }
+}
+
View
28 dart/dartbox2d/callbacks/ContactImpulse.dart
@@ -0,0 +1,28 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Contact impulses for reporting. Impulses are used instead of forces because
+ * sub-step forces may approach infinity for rigid body collisions. These
+ * match up one-to-one with the contact points in [Manifold].
+ */
+class ContactImpulse {
+ List<num> normalImpulses;
+ List<num> tangentImpulses;
+
+ ContactImpulse() :
+ normalImpulses = new List<num>(Settings.MAX_MANIFOLD_POINTS),
+ tangentImpulses = new List<num>(Settings.MAX_MANIFOLD_POINTS) { }
+}
+
View
64 dart/dartbox2d/callbacks/ContactListener.dart
@@ -0,0 +1,64 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Implement this interface to get contact information. You can use these
+ * results for things like sounds and game logic. You can also get contact
+ * results by traversing the contact lists after the time step. However, you
+ * might miss some contacts because continuous physics leads to sub-stepping.
+ * Additionally you may receive multiple callbacks for the same contact in a
+ * single time step.
+ * You should strive to make your callbacks efficient because there may be
+ * many callbacks per time step.
+ *
+ * warning: You cannot create/destroy Box2D entities inside these callbacks.
+ *
+ */
+interface ContactListener {
+ /**
+ * Called when two fixtures begin to touch.
+ */
+ void beginContact(Contact contact);
+
+ /**
+ * Called when two fixtures cease to touch.
+ */
+ void endContact(Contact contact);
+
+ /**
+ * This is called after a contact is updated. This allows you to inspect a
+ * contact before it goes to the solver. If you are careful, you can modify
+ * the contact manifold (e.g. disable contact).
+ * A copy of the old manifold is provided so that you can detect changes.
+ * Note: this is called only for awake bodies.
+ * Note: this is called even when the number of contact points is zero.
+ * Note: this is not called for sensors.
+ * Note: if you set the number of contact points to zero, you will not
+ * get an EndContact callback. However, you may get a BeginContact callback
+ * the next step.
+ * Note: the oldManifold parameter is pooled, so it will be the same object
+ * for every callback for each thread.
+ */
+ void preSolve(Contact contact, Manifold oldManifold);
+
+ /**
+ * This lets you inspect a contact after the solver is finished. This is
+ * useful for inspecting impulses.
+ * Note: the contact manifold does not include time of impact impulses,
+ * which can be arbitrarily large if the sub-step is small. Hence the impulse
+ * is provided explicitly in a separate data structure.
+ * Note: this is only called for contacts that are touching, solid, and awake.
+ */
+ void postSolve(Contact contact, ContactImpulse impulse);
+}
View
147 dart/dartbox2d/callbacks/DebugDraw.dart
@@ -0,0 +1,147 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Implement this abstract class to allow JBox2d to
+ * automatically draw your physics for debugging purposes.
+ * Not intended to replace your own custom rendering
+ * routines! Draws shapes by default.
+ */
+class DebugDraw {
+ // TODO(gregbglw): Draw joints once have them implemented. Also draw other
+ // neat stuff described below.
+ static final int e_shapeBit = 0x0001; ///< draw shapes
+ static final int e_jointBit = 0x0002; ///< draw joint connections
+ static final int e_aabbBit = 0x0004; ///< draw core (TimeOfImpact) shapes
+ static final int e_pairBit = 0x0008; ///< draw axis aligned boxes
+ static final int e_centerOfMassBit = 0x0010; ///< draw center of mass
+ static final int e_dynamicTreeBit = 0x0020; ///< draw dynamic tree.
+
+ int drawFlags;
+ IViewportTransform viewportTransform;
+
+ DebugDraw(IViewportTransform viewport) :
+ drawFlags = e_shapeBit,
+ viewportTransform = viewport { }
+
+ void setFlags(int flags) {
+ drawFlags = flags;
+ }
+
+ int getFlags() {
+ return drawFlags;
+ }
+
+ void appendFlags(int flags) {
+ drawFlags |= flags;
+ }
+
+ void clearFlags(int flags) {
+ drawFlags &= ~flags;
+ }
+
+ /**
+ * Draw a closed polygon provided in CCW order. This implementation
+ * uses [drawSegment] to draw each side of the polygon.
+ */
+ void drawPolygon(List<Vector> vertices, int vertexCount, Color3 color){
+ if(vertexCount == 1){
+ drawSegment(vertices[0], vertices[0], color);
+ return;
+ }
+
+ for(int i=0; i<vertexCount-1; i+=1){
+ drawSegment(vertices[i], vertices[i+1], color);
+ }
+
+ if(vertexCount > 2){
+ drawSegment(vertices[vertexCount-1], vertices[0], color);
+ }
+ }
+
+ /**
+ * Draws the given point with the given radius and color.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawPoint(Vector argPoint, num argRadiusOnScreen, Color3 argColor) { }
+
+ /**
+ * Draw a solid closed polygon provided in CCW order.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawSolidPolygon(List<Vector> vertices, int vertexCount,
+ Color3 color) { }
+
+ /**
+ * Draw a circle.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawCircle(Vector center, num radius, Color3 color) { }
+
+ /**
+ * Draw a solid circle.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawSolidCircle(Vector center, num radius, Vector axis,
+ Color3 color) { }
+
+ /**
+ * Draw a line segment.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawSegment(Vector p1, Vector p2, Color3 color) { }
+
+ /**
+ * Draw a transform. Choose your own length scale
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawTransform(Transform xf) { }
+
+ /**
+ * Draw a string.
+ */
+ // TODO: abstract http://b/issue?id=5015671
+ void drawString(num x, num y, String s, Color3 color) { }
+
+ /**
+ * Returns the viewport transform.
+ */
+ IViewportTransform getViewportTranform(){
+ return viewportTransform;
+ }
+
+ /**
+ * Sets the center of the viewport to the given x and y values and the
+ * viewport scale to the given scale.
+ */
+ void setCamera(num x, num y, num scale){
+ viewportTransform.setCamera(x,y,scale);
+ }
+
+ /**
+ * Screen coordinates are specified in argScreen. These coordinates are
+ * converted to World coordinates and placed in the argWorld return vector.
+ */
+ void getScreenToWorldToOut(Vector argScreen, Vector argWorld) {
+ viewportTransform.getScreenToWorld(argScreen, argWorld);
+ }
+
+ /**
+ * World coordinates are specified in argWorld. These coordinates are
+ * converted to screen coordinates and placed in the argScreen return vector.
+ */
+ void getWorldToScreenToOut(Vector argWorld, Vector argScreen) {
+ viewportTransform.getWorldToScreen(argWorld, argScreen);
+ }
+}
View
23 dart/dartbox2d/callbacks/DestructionListener.dart
@@ -0,0 +1,23 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Joints and fixtures are destroyed when their associated
+ * body is destroyed. Implement this function type so that you
+ * may nullify references to these joints and shapes. It is called when any
+ * fixture is about to be destroyed due to the destruction of its parent body.
+ */
+typedef void FixtureDestructionListener(Fixture fixture);
+
+typedef void JointDestructionListener(Joint joint);
View
18 dart/dartbox2d/callbacks/PairCallback.dart
@@ -0,0 +1,18 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//TODO(gregbglw): Make a typedef. bug# 4534395
+interface PairCallback {
+ void addPair(userDataA, userDataB);
+}
View
25 dart/dartbox2d/callbacks/QueryCallback.dart
@@ -0,0 +1,25 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Callback class for AABB queries. See method "query" in [World].
+ */
+//TODO(gregbglw): Make a typedef. Bug: 4534395
+interface QueryCallback {
+ /**
+ * Called for each fixture found in the query AABB.
+ * return false to terminate the query.
+ */
+ bool reportFixture(Fixture fixture);
+}
View
22 dart/dartbox2d/callbacks/TreeCallback.dart
@@ -0,0 +1,22 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Callback from a tree query request. Returns true if the query should be
+ * continued.
+ */
+//TODO(gregbglw): Make a typedef. bug# 4534395
+interface TreeCallback {
+ bool treeCallback(DynamicTreeNode node);
+}
View
115 dart/dartbox2d/collision/AxisAlignedBox.dart
@@ -0,0 +1,115 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * An axis-aligned bounding box.
+ */
+class AxisAlignedBox {
+ /** Bottom left vertex of bounding box. */
+ Vector lowerBound;
+
+ /** Top right vertex of bounding box. */
+ Vector upperBound;
+
+ /**
+ * Constructs a new box with the given lower and upper bounds. If no bounds
+ * are specified, constructs the box with both bounds at the origin.
+ */
+ AxisAlignedBox([this.lowerBound = null, this.upperBound = null]) {
+ if (lowerBound == null) {
+ lowerBound = new Vector();
+ }
+ if (upperBound == null) {
+ upperBound = new Vector();
+ }
+ }
+
+ /**
+ * Sets this box to be a combination of the two given boxes.
+ * The combination is determined by picking and choosing the lowest x and y
+ * values from the lowerBounds to form a new lower bound and picking and
+ * choosing the largest x and y values from the upperBounds to form a new
+ * upperBound.
+ */
+ void setFromCombination(AxisAlignedBox boxOne, AxisAlignedBox boxTwo) {
+ lowerBound.x = Math.min(boxOne.lowerBound.x, boxTwo.lowerBound.x);
+ lowerBound.y = Math.min(boxOne.lowerBound.y, boxTwo.lowerBound.y);
+ upperBound.x = Math.max(boxOne.upperBound.x, boxTwo.upperBound.x);
+ upperBound.y = Math.max(boxOne.upperBound.y, boxTwo.upperBound.y);
+ }
+
+ /**
+ * Sets the bounds to the given values.
+ */
+ AxisAlignedBox setBounds(Vector lower, Vector upper) {
+ lowerBound.setFrom(lower);
+ upperBound.setFrom(upper);
+ return this;
+ }
+
+ /**
+ * Returns true if the given box overlaps with this box.
+ */
+ static bool testOverlap(AxisAlignedBox a, AxisAlignedBox b) {
+ if (b.lowerBound.x > a.upperBound.x || b.lowerBound.y > a.upperBound.y) {
+ return false;
+ }
+
+ if (a.lowerBound.x > b.upperBound.x || a.lowerBound.y > b.upperBound.y) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if the lower bound is strictly less than the upper bound and
+ * both bounds are themselves valid (Vector.isValid() returns true).
+ */
+ bool isValid() {
+ return lowerBound.isValid() && upperBound.isValid()
+ && lowerBound.x < upperBound.x && lowerBound.y < upperBound.y;
+ }
+
+ /**
+ * Returns the center of this box.
+ */
+ Vector get center() {
+ Vector c = new Vector.copy(lowerBound);
+ c.addLocal(upperBound);
+ c.mulLocal(.5);
+ return c;
+ }
+
+ /**
+ * Returns true if this box contains the given box.
+ */
+ bool contains(AxisAlignedBox aabb) {
+ return lowerBound.x > aabb.lowerBound.x &&
+ lowerBound.y > aabb.lowerBound.y && upperBound.y < aabb.upperBound.y
+ && upperBound.x < aabb.upperBound.x;
+ }
+
+ /**
+ * Sets this box to be a copy of the given box.
+ */
+ void setFrom(AxisAlignedBox other) {
+ lowerBound.setFrom(other.lowerBound);
+ upperBound.setFrom(other.upperBound);
+ }
+
+ String toString() {
+ return lowerBound.toString() + ", " + upperBound.toString();
+ }
+}
View
685 dart/dartbox2d/collision/Collision.dart
@@ -0,0 +1,685 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Functions used for computing contact points, distance
+ * queries, and time of impact (TimeOfImpact) queries. Collision methods are non-static
+ * for pooling speed, retrieve a collision object from the [SingletonPool].
+ */
+class Collision {
+ static final int NULL_FEATURE = Settings.MAX_INTEGER;
+
+ final DefaultWorldPool _pool;
+
+ /** Cache used to help warmstart distance. */
+ final SimplexCache cache;
+
+ /** Distance input and output. */
+ final DistanceInput input;
+ final DistanceOutput output;
+
+ /** A pool of already constructed objects. */
+ final EdgeResults results1;
+ final EdgeResults results2;
+ final List<ClipVertex> incidentEdge;
+ final Vector localTangent;
+ final Vector localNormal;
+ final Vector planePoint;
+ final Vector tangent;
+ final Vector normal;
+ final Vector normal1;
+ final Vector v11;
+ final Vector v12;
+ final List<ClipVertex> clipPoints1;
+ final List<ClipVertex> clipPoints2;
+
+ /**
+ * Constructs a new Collision object. Should only be constructed once (in the
+ * pool). Retrieve from the pool to use.
+ */
+ Collision._construct(DefaultWorldPool pool) :
+ _pool = pool,
+ input = new DistanceInput(),
+ cache = new SimplexCache(),
+ output = new DistanceOutput(),
+ results1 = new EdgeResults(),
+ results2 = new EdgeResults(),
+ incidentEdge = new List<ClipVertex>(2),
+ localTangent = new Vector(),
+ localNormal = new Vector(),
+ planePoint = new Vector(),
+ tangent = new Vector(),
+ normal = new Vector(),
+ normal1 = new Vector(),
+ v11 = new Vector(),
+ v12 = new Vector(),
+ clipPoints1 = new List<ClipVertex>(2),
+ clipPoints2 = new List<ClipVertex>(2) {
+ incidentEdge[0] = new ClipVertex();
+ incidentEdge[1] = new ClipVertex();
+ clipPoints1[0] = new ClipVertex();
+ clipPoints1[1] = new ClipVertex();
+ clipPoints2[0] = new ClipVertex();
+ clipPoints2[1] = new ClipVertex();
+ }
+
+ /**
+ * Returns true if the two given shapes overlap.
+ */
+ bool testOverlap(Shape shapeA, Shape shapeB, Transform transformA,
+ Transform transformB) {
+ input.proxyA.setFromShape(shapeA);
+ input.proxyB.setFromShape(shapeB);
+ input.transformA.setFrom(transformA);
+ input.transformB.setFrom(transformB);
+ input.useRadii = true;
+
+ cache.count = 0;
+
+ _pool.distance.distance(output, cache, input);
+ return output.distance < 10.0 * Settings.EPSILON;
+ }
+
+ /**
+ * Compute the point states given two manifolds. The states pertain to the
+ * transition from manifold1 to manifold2. So state1 is either persist or
+ * remove while state2 is either add or persist.
+ */
+ void getPointStates(List<int> state1, List<int> state2,
+ Manifold manifold1, Manifold manifold2) {
+ for (int i = 0; i < Settings.MAX_MANIFOLD_POINTS; i++) {
+ state1[i] = PointState.NULL_STATE;
+ state2[i] = PointState.NULL_STATE;
+ }
+
+ // Detect persists and removes.
+ for (int i = 0; i < manifold1.pointCount; i++) {
+ ContactID id = manifold1.points[i].id;
+
+ state1[i] = PointState.REMOVE_STATE;
+
+ for (int j = 0; j < manifold2.pointCount; j++) {
+ if (manifold2.points[j].id.isEqual(id)) {
+ state1[i] = PointState.PERSIST_STATE;
+ break;
+ }
+ }
+ }
+
+ // Detect persists and adds
+ for (int i = 0; i < manifold2.pointCount; i++) {
+ ContactID id = manifold2.points[i].id;
+
+ state2[i] = PointState.ADD_STATE;
+
+ for (int j = 0; j < manifold1.pointCount; j++) {
+ if (manifold1.points[j].id.isEqual(id)) {
+ state2[i] = PointState.PERSIST_STATE;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Clipping for contact manifolds.
+ * Sutherland-Hodgman clipping.
+ */
+ static int clipSegmentToLine(List<ClipVertex> vOut, List<ClipVertex> vIn,
+ Vector norm, num offset) {
+
+ // Start with no output points
+ int numOut = 0;
+
+ // Calculate the distance of end points to the line
+ num distance0 = Vector.dot(norm, vIn[0].v) - offset;
+ num distance1 = Vector.dot(norm, vIn[1].v) - offset;
+
+ // If the points are behind the plane
+ if (distance0 <= 0.0) {
+ vOut[numOut++].setFrom(vIn[0]);
+ }
+ if (distance1 <= 0.0) {
+ vOut[numOut++].setFrom(vIn[1]);
+ }
+
+ // If the points are on different sides of the plane
+ if (distance0 * distance1 < 0.0) {
+ // Find intersection point of edge and plane
+ num interp = distance0 / (distance0 - distance1);
+ // vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v);
+ vOut[numOut].v.setFrom(vIn[1].v).
+ subLocal(vIn[0].v).mulLocal(interp).addLocal(vIn[0].v);
+ if (distance0 > 0.0) {
+ vOut[numOut].id.setFrom(vIn[0].id);
+ } else {
+ vOut[numOut].id.setFrom(vIn[1].id);
+ }
+ ++numOut;
+ }
+
+ return numOut;
+ }
+
+ /**
+ * Compute the collision manifold between two circles.
+ *
+ */
+ //TODO(gregbglw): Consider introducing operator overloading for matrix/vector
+ //operations and then replace many of the inlined calculations with those.
+ void collideCircles(Manifold manifold, CircleShape circle1, Transform xfA,
+ CircleShape circle2, Transform xfB) {
+ manifold.pointCount = 0;
+
+ final Vector v = circle1.position;
+ final num pAy = xfA.position.y + xfA.rotation.col1.y *
+ v.x + xfA.rotation.col2.y * v.y;
+
+ final num pAx = xfA.position.x + xfA.rotation.col1.x *
+ v.x + xfA.rotation.col2.x * v.y;
+
+ final Vector v1 = circle2.position;
+ final num pBy = xfB.position.y + xfB.rotation.col1.y * v1.x +
+ xfB.rotation.col2.y * v1.y;
+ final num pBx = xfB.position.x + xfB.rotation.col1.x * v1.x +
+ xfB.rotation.col2.x * v1.y;
+
+ final num dx = pBx - pAx;
+ final num dy = pBy - pAy;
+
+ final num distSqr = dx * dx + dy * dy;
+
+ final num radius = circle1.radius + circle2.radius;
+ if (distSqr > radius * radius) {
+ return;
+ }
+
+ manifold.type = ManifoldType.CIRCLES;
+ manifold.localPoint.setFrom(circle1.position);
+ manifold.localNormal.setZero();
+ manifold.pointCount = 1;
+
+ manifold.points[0].localPoint.setFrom(circle2.position);
+ manifold.points[0].id.zero();
+ }
+
+ /**
+ * Compute the collision manifold between a polygon and a circle.
+ */
+ void collidePolygonAndCircle(Manifold manifold, PolygonShape polygon,
+ Transform xfA, CircleShape circle, Transform xfB) {
+ manifold.pointCount = 0;
+ Vector v = circle.position;
+
+ final num cy = xfB.position.y + xfB.rotation.col1.y * v.x +
+ xfB.rotation.col2.y * v.y;
+ final num cx = xfB.position.x + xfB.rotation.col1.x * v.x +
+ xfB.rotation.col2.x * v.y;
+ final num v1x = cx - xfA.position.x;
+ final num v1y = cy - xfA.position.y;
+ final Vector b = xfA.rotation.col1;
+ final Vector b1 = xfA.rotation.col2;
+ final num cLocaly = v1x * b1.x + v1y * b1.y;
+ final num cLocalx = v1x * b.x + v1y * b.y;
+
+ // Find the min separating edge.
+ int normalIndex = 0;
+ num separation = Settings.SMALL_NUMBER;
+ final num radius = polygon.radius + circle.radius;
+ final int vertexCount = polygon.vertexCount;
+
+ final List<Vector> vertices = polygon.vertices;
+ final List<Vector> normals = polygon.normals;
+
+ for (int i = 0; i < vertexCount; i++) {
+ final Vector vertex = vertices[i];
+ final num tempx = cLocalx - vertex.x;
+ final num tempy = cLocaly - vertex.y;
+ final Vector norm = normals[i];
+ final num s = norm.x * tempx + norm.y * tempy;
+
+ if (s > radius) {
+ // early out
+ return;
+ }
+
+ if (s > separation) {
+ separation = s;
+ normalIndex = i;
+ }
+ }
+
+ // Vertices that subtend the incident face.
+ final int vertIndex1 = normalIndex;
+ final int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
+ final Vector v1 = vertices[vertIndex1];
+ final Vector v2 = vertices[vertIndex2];
+
+ // If the center is inside the polygon ...
+ if (separation < Settings.EPSILON) {
+ manifold.pointCount = 1;
+ manifold.type = ManifoldType.FACE_A;
+
+ Vector norm = normals[normalIndex];
+ manifold.localNormal.x = norm.x;
+ manifold.localNormal.y = norm.y;
+ manifold.localPoint.x = (v1.x + v2.x) * .5;
+ manifold.localPoint.y = (v1.y + v2.y) * .5;
+ ManifoldPoint mpoint = manifold.points[0];
+ mpoint.localPoint.x = circle.position.x;
+ mpoint.localPoint.y = circle.position.y;
+ mpoint.id.zero();
+ return;
+ }
+
+ // Compute barycentric coordinates
+ final num tempX = cLocalx - v1.x;
+ final num tempY = cLocaly - v1.y;
+ final num temp2X = v2.x - v1.x;
+ final num temp2Y = v2.y - v1.y;
+ final num u1 = tempX * temp2X + tempY * temp2Y;
+
+ final num temp3X = cLocalx - v2.x;
+ final num temp3Y = cLocaly - v2.y;
+ final num temp4X = v1.x - v2.x;
+ final num temp4Y = v1.y - v2.y;
+ final num u2 = temp3X * temp4X + temp3Y * temp4Y;
+
+ if (u1 <= 0) {
+ final num dx = cLocalx - v1.x;
+ final num dy = cLocaly - v1.y;
+ if ( dx * dx + dy * dy > radius * radius) {
+ return;
+ }
+
+ manifold.pointCount = 1;
+ manifold.type = ManifoldType.FACE_A;
+ manifold.localNormal.x = cLocalx - v1.x;
+ manifold.localNormal.y = cLocaly - v1.y;
+ manifold.localNormal.normalize();
+ manifold.localPoint.setFrom(v1);
+ manifold.points[0].localPoint.setFrom(circle.position);
+ manifold.points[0].id.zero();
+ } else if (u2 <= 0.0) {
+ final num dx = cLocalx - v2.x;
+ final num dy = cLocaly - v2.y;
+ if ( dx * dx + dy * dy > radius * radius) {
+ return;
+ }
+
+ manifold.pointCount = 1;
+ manifold.type = ManifoldType.FACE_A;
+ manifold.localNormal.x = cLocalx - v2.x;
+ manifold.localNormal.y = cLocaly - v2.y;
+ manifold.localNormal.normalize();
+ manifold.localPoint.setFrom(v2);
+ manifold.points[0].localPoint.setFrom(circle.position);
+ manifold.points[0].id.zero();
+ } else {
+ // Vector faceCenter = 0.5 * (v1 + v2);
+ // (temp is faceCenter)
+ final num fcx = (v1.x + v2.x) * .5;
+ final num fcy = (v1.y + v2.y) * .5;
+
+ final num tx = cLocalx - fcx;
+ final num ty = cLocaly - fcy;
+ final Vector norm = normals[vertIndex1];
+ separation = tx * norm.x + ty * norm.y;
+ if(separation > radius){
+ return;
+ }
+
+ manifold.pointCount = 1;
+ manifold.type = ManifoldType.FACE_A;
+ manifold.localNormal.setFrom(normals[vertIndex1]);
+ manifold.localPoint.x = fcx;
+ manifold.localPoint.y = fcy;
+ manifold.points[0].localPoint.setFrom(circle.position);
+ manifold.points[0].id.zero();
+ }
+ }
+
+ /**
+ * Find the separation between poly1 and poly2 for a given edge normal on
+ * poly1.
+ */
+ num edgeSeparation(PolygonShape poly1, Transform xf1, int edge1,
+ PolygonShape poly2, Transform xf2) {
+ final int count1 = poly1.vertexCount;
+ final List<Vector> vertices1 = poly1.vertices;
+ final List<Vector> normals1 = poly1.normals;
+
+ final int count2 = poly2.vertexCount;
+ final List<Vector> vertices2 = poly2.vertices;
+
+ assert (0 <= edge1 && edge1 < count1);
+ // Convert normal from poly1's frame into poly2's frame.
+ final Matrix22 R = xf1.rotation;
+ final Vector v = normals1[edge1];
+ final num normal1Worldy = R.col1.y * v.x + R.col2.y * v.y;
+ final num normal1Worldx = R.col1.x * v.x + R.col2.x * v.y;
+ final Matrix22 R1 = xf2.rotation;
+ final num normal1x = normal1Worldx * R1.col1.x + normal1Worldy * R1.col1.y;
+ final num normal1y = normal1Worldx * R1.col2.x + normal1Worldy * R1.col2.y;
+ // end inline
+
+ // Find support vertex on poly2 for -normal.
+ int index = 0;
+ num minDot = Settings.BIG_NUMBER;
+
+ for (int i = 0; i < count2; ++i) {
+ final Vector a = vertices2[i];
+ final num dot = a.x * normal1x + a.y * normal1y;
+ if (dot < minDot) {
+ minDot = dot;
+ index = i;
+ }
+ }
+
+ final Vector v3 = vertices1[edge1];
+ final num v1y = xf1.position.y + R.col1.y * v3.x + R.col2.y * v3.y;
+ final num v1x = xf1.position.x + R.col1.x * v3.x + R.col2.x * v3.y;
+ final Vector v4 = vertices2[index];
+ final num v2y = xf2.position.y + R1.col1.y * v4.x + R1.col2.y * v4.y - v1y;
+ final num v2x = xf2.position.x + R1.col1.x * v4.x + R1.col2.x * v4.y - v1x;
+
+ return v2x * normal1Worldx + v2y * normal1Worldy;
+ }
+
+ /**
+ * Find the max separation between poly1 and poly2 using edge normals from
+ * poly1.
+ */
+ void findMaxSeparation(EdgeResults results, PolygonShape poly1, Transform xf1,
+ PolygonShape poly2, Transform xf2) {
+ int count1 = poly1.vertexCount;
+ final List<Vector> normals1 = poly1.normals;
+ Vector v = poly2.centroid;
+
+ final num predy = xf2.position.y + xf2.rotation.col1.y * v.x +
+ xf2.rotation.col2.y * v.y;
+ final num predx = xf2.position.x + xf2.rotation.col1.x * v.x +
+ xf2.rotation.col2.x * v.y;
+ final Vector v1 = poly1.centroid;
+ final num tempy = xf1.position.y + xf1.rotation.col1.y * v1.x +
+ xf1.rotation.col2.y * v1.y;
+ final num tempx = xf1.position.x + xf1.rotation.col1.x * v1.x +
+ xf1.rotation.col2.x * v1.y;
+ final num dx = predx - tempx;
+ final num dy = predy - tempy;
+
+ final Matrix22 R = xf1.rotation;
+ final num dLocal1x = dx * R.col1.x + dy * R.col1.y;
+ final num dLocal1y = dx * R.col2.x + dy * R.col2.y;
+
+ // Find edge normal on poly1 that has the largest projection onto d.
+ int edge = 0;
+ num dot;
+ num maxDot = Settings.SMALL_NUMBER;
+ for (int i = 0; i < count1; i++) {
+ final Vector norm = normals1[i];
+ dot = norm.x * dLocal1x + norm.y * dLocal1y;
+ if (dot > maxDot) {
+ maxDot = dot;
+ edge = i;
+ }
+ }
+
+ // Get the separation for the edge normal.
+ num s = edgeSeparation(poly1, xf1, edge, poly2, xf2);
+
+ // Check the separation for the previous edge normal.
+ int prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1;
+ num sPrev = edgeSeparation(poly1, xf1, prevEdge, poly2, xf2);
+
+ // Check the separation for the next edge normal.
+ int nextEdge = edge + 1 < count1 ? edge + 1 : 0;
+ num sNext = edgeSeparation(poly1, xf1, nextEdge, poly2, xf2);
+
+ // Find the best edge and the search direction.
+ int bestEdge;
+ num bestSeparation;
+ int increment;
+ if (sPrev > s && sPrev > sNext) {
+ increment = -1;
+ bestEdge = prevEdge;
+ bestSeparation = sPrev;
+ } else if (sNext > s) {
+ increment = 1;
+ bestEdge = nextEdge;
+ bestSeparation = sNext;
+ } else {
+ results.edgeIndex = edge;
+ results.separation = s;
+ return;
+ }
+
+ // Perform a local search for the best edge normal.
+ while (true) {
+ if (increment == -1) {
+ edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
+ } else {
+ edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;
+ }
+
+ s = edgeSeparation(poly1, xf1, edge, poly2, xf2);
+
+ if (s > bestSeparation) {
+ bestEdge = edge;
+ bestSeparation = s;
+ } else {
+ break;
+ }
+ }
+
+ results.edgeIndex = bestEdge;
+ results.separation = bestSeparation;
+ }
+
+ void findIncidentEdge(List<ClipVertex> c, PolygonShape poly1, Transform xf1,
+ int edge1, PolygonShape poly2, Transform xf2) {
+ int count1 = poly1.vertexCount;
+ final List<Vector> normals1 = poly1.normals;
+
+ int count2 = poly2.vertexCount;
+ final List<Vector> vertices2 = poly2.vertices;
+ final List<Vector> normals2 = poly2.normals;
+
+ assert (0 <= edge1 && edge1 < count1);
+
+ // Get the normal of the reference edge in poly2's frame.
+ Matrix22.mulMatrixAndVectorToOut(xf1.rotation, normals1[edge1], normal1);
+ Matrix22.mulTransMatrixAndVectorToOut(xf2.rotation, normal1, normal1);
+
+ // Find the incident edge on poly2.
+ int index = 0;
+ num minDot = Settings.BIG_NUMBER;
+ for (int i = 0; i < count2; ++i) {
+ num dot = Vector.dot(normal1, normals2[i]);
+ if (dot < minDot) {
+ minDot = dot;
+ index = i;
+ }
+ }
+
+ // Build the clip vertices for the incident edge.
+ int i1 = index;
+ int i2 = i1 + 1 < count2 ? i1 + 1 : 0;
+
+ Transform.mulToOut(xf2, vertices2[i1], c[0].v);
+ c[0].id.features.referenceEdge = edge1;
+ c[0].id.features.incidentEdge = i1;
+ c[0].id.features.incidentVertex = 0;
+
+ Transform.mulToOut(xf2, vertices2[i2], c[1].v);
+ c[1].id.features.referenceEdge = edge1;
+ c[1].id.features.incidentEdge = i2;
+ c[1].id.features.incidentVertex = 1;
+ }
+
+ /**
+ * Compute the collision manifold between two polygons.
+ */
+ void collidePolygons(Manifold manifold, PolygonShape polyA, Transform xfA,
+ PolygonShape polyB, Transform xfB) {
+
+ manifold.pointCount = 0;
+ num totalRadius = polyA.radius + polyB.radius;
+
+ findMaxSeparation(results1, polyA, xfA, polyB, xfB);
+ if (results1.separation > totalRadius) {
+ return;
+ }
+
+ findMaxSeparation(results2, polyB, xfB, polyA, xfA);
+ if (results2.separation > totalRadius) {
+ return;
+ }
+
+ PolygonShape poly1; // reference polygon
+ PolygonShape poly2; // incident polygon
+ Transform xf1, xf2;
+ int edge1; // reference edge
+ int flip;
+ num k_relativeTol = 0.98;
+ num k_absoluteTol = 0.001;
+
+ if (results2.separation > k_relativeTol * results1.separation +
+ k_absoluteTol) {
+ poly1 = polyB;
+ poly2 = polyA;
+ xf1 = xfB;
+ xf2 = xfA;
+ edge1 = results2.edgeIndex;
+ manifold.type = ManifoldType.FACE_B;
+ flip = 1;
+ } else {
+ poly1 = polyA;
+ poly2 = polyB;
+ xf1 = xfA;
+ xf2 = xfB;
+ edge1 = results1.edgeIndex;
+ manifold.type = ManifoldType.FACE_A;
+ flip = 0;
+ }
+
+ findIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
+
+ int count1 = poly1.vertexCount;
+ List<Vector> vertices1 = poly1.vertices;
+
+ v11.setFrom(vertices1[edge1]);
+ v12.setFrom(edge1 + 1 < count1 ? vertices1[edge1 + 1] : vertices1[0]);
+
+ localTangent.setFrom(v12).subLocal(v11);
+ localTangent.normalize();
+
+ // Vector localNormal = Cross(dv, 1.0);
+ Vector.crossVectorAndNumToOut(localTangent, 1, localNormal);
+
+ // Vector planePoint = 0.5 * (v11 + v12)
+ planePoint.setFrom(v11).addLocal(v12).mulLocal(.5);
+
+ // Vector sideNormal = Mul(xf1.rotation, v12 - v11);
+ Matrix22.mulMatrixAndVectorToOut(xf1.rotation, localTangent, tangent);
+
+ // Vector frontNormal = Cross(sideNormal, 1.0);
+ Vector.crossVectorAndNumToOut(tangent, 1, normal);
+
+ // v11 = Mul(xf1, v11);
+ // v12 = Mul(xf1, v12);
+ Transform.mulToOut(xf1, v11, v11);
+ Transform.mulToOut(xf1, v12, v12);
+
+ // Face offset
+ num frontOffset = Vector.dot(normal, v11);
+
+ // Side offsets, extended by polytope skin thickness.
+ num sideOffset1 = -Vector.dot(tangent, v11) + totalRadius;
+ num sideOffset2 = Vector.dot(tangent, v12) + totalRadius;
+
+ // Clip incident edge against extruded edge1 side edges.
+ // ClipVertex clipPoints1[2];
+ // ClipVertex clipPoints2[2];
+ int np;
+
+ // Clip to box side 1
+ // np = ClipSegmentToLine(clipPoints1, incidentEdge, -sideNormal,
+ // sideOffset1);
+ tangent.negateLocal();
+ np = clipSegmentToLine(clipPoints1, incidentEdge, tangent, sideOffset1);
+ tangent.negateLocal();
+
+ if (np < 2) {
+ return;
+ }
+
+ // Clip to negative box side 1
+ np = clipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2);
+
+ if (np < 2) {
+ return;
+ }
+
+ // Now clipPoints2 contains the clipped points.
+ manifold.localNormal.setFrom(localNormal);
+ manifold.localPoint.setFrom(planePoint);
+
+ int pointCount = 0;
+ for (int i = 0; i < Settings.MAX_MANIFOLD_POINTS; ++i) {
+ num separation = Vector.dot(normal, clipPoints2[i].v) - frontOffset;
+
+ if (separation <= totalRadius) {
+ ManifoldPoint cp = manifold.points[pointCount];
+ Transform.mulTransToOut(xf2, clipPoints2[i].v, cp.localPoint);
+ // cp.localPoint = MulT(xf2, clipPoints2[i].v);
+ cp.id.setFrom(clipPoints2[i].id);
+ cp.id.features.flip = flip;
+ ++pointCount;
+ }
+ }
+
+ manifold.pointCount = pointCount;
+ }
+}
+
+/**
+ * Used for computing contact manifolds.
+ */
+class ClipVertex {
+ Vector v;
+ ContactID id;
+
+ ClipVertex() {
+ v = new Vector();
+ id = new ContactID();
+ }
+
+ void setFrom(ClipVertex cv){
+ v.setFrom(cv.v);
+ id.setFrom(cv.id);
+ }
+}
+
+/**
+ * Class for returning edge results
+ */
+class EdgeResults {
+ num separation;
+ int edgeIndex;
+
+ EdgeResults()
+ :separation = 0,
+ edgeIndex = 0 { }
+}
View
60 dart/dartbox2d/collision/ContactID.dart
@@ -0,0 +1,60 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Contact ids to facilitate warm starting. Basically just containers for
+ * an individual Features object that contains interesting information.
+ */
+class ContactID {
+ /** The features that intersect to form the contact point */
+ final Features features;
+
+ /**
+ * Constructs a new ContactID. */
+ ContactID() : features = new Features() { }
+
+ /**
+ * Constructs a ContactID that is a copy of the given ContactID.
+ */
+ ContactID.copy(ContactID other) :
+ features = new Features.copy(other.features) { }
+
+ /**
+ * Returns true if this ContactID equals the given ContactID.
+ */
+ bool operator == (other) {
+ return other.features == features;
+ }
+
+ /**
+ * Sets this contactID to be equal to the given ContactID.
+ */
+ void setFrom(ContactID other) {
+ features.setFrom(other.features);
+ }
+
+ /**
+ * Returns true if this ContactID equals the given ContactID.
+ */
+ bool isEqual(ContactID other) {
+ return other.features == features;
+ }
+
+ /**
+ * Zeroes out the data.
+ */
+ void zero() {
+ features.zero();
+ }
+}
View
206 dart/dartbox2d/collision/Distance.dart
@@ -0,0 +1,206 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * This is non-static for faster pooling. To get an instance,
+ * use the SingletonPool rather than construct a distance object.
+ */
+class Distance {
+ int calls;
+ int iters;
+ int maxIters;
+
+ /** Pool variables for use in distance calculation. */
+ Simplex simplex;
+ List<int> saveA;
+ List<int> saveB;
+ Vector closestPoint;
+ Vector searchDirection;
+ Vector temp;
+ Vector normal;
+
+ /**
+ * Construct a new Distance object. For internal use only. Don't directly
+ * invoke.
+ */
+ Distance._construct() :
+ simplex = new Simplex(),
+ saveA = new List<int>(3),
+ saveB = new List<int>(3),
+ closestPoint = new Vector(),
+ searchDirection = new Vector(),
+ temp = new Vector(),
+ normal = new Vector(),
+ calls = 0,
+ iters = 0,
+ maxIters = 20 { }
+
+ /**
+ * Compute the closest points between two shapes. Supports any combination of:
+ * CircleShape and PolygonShape. The simplex cache is input/output.
+ * On the first call set SimplexCache.count to zero.
+ */
+ void distance(DistanceOutput output, SimplexCache cache,
+ DistanceInput input) {
+ calls++;
+
+ final DistanceProxy proxyA = input.proxyA;
+ final DistanceProxy proxyB = input.proxyB;
+
+ Transform transformA = input.transformA;
+ Transform transformB = input.transformB;
+
+ // Initialize the simplex.
+ simplex.readCache(cache, proxyA, transformA, proxyB, transformB);
+
+ // Get simplex vertices as an array.
+ List<SimplexVertex> vertices = simplex.vertices;
+
+ // These store the vertices of the last simplex so that we
+ // can check for duplicates and prevent cycling.
+ // (pooled above)
+ int saveCount = 0;
+
+ simplex.getClosestPoint(closestPoint);
+ num distanceSqr1 = closestPoint.lengthSquared;
+ num distanceSqr2 = distanceSqr1;
+
+ // Main iteration loop
+ int iter = 0;
+ while (iter < maxIters) {
+
+ // Copy simplex so we can identify duplicates.
+ saveCount = simplex.count;
+ for (int i = 0; i < saveCount; i++) {
+ saveA[i] = vertices[i].indexA;
+ saveB[i] = vertices[i].indexB;
+ }
+
+ switch (simplex.count) {
+ case 1 :
+ break;
+ case 2 :
+ simplex.solve2();
+ break;
+ case 3 :
+ simplex.solve3();
+ break;
+ default :
+ assert (false);
+ }
+
+ // If we have 3 points, then the origin is in the corresponding triangle.
+ if (simplex.count == 3) {
+ break;
+ }
+
+ // Compute closest point.
+ simplex.getClosestPoint(closestPoint);
+ distanceSqr2 = closestPoint.lengthSquared;
+
+ // ensure progress
+ if (distanceSqr2 >= distanceSqr1) {
+ // break;
+ }
+ distanceSqr1 = distanceSqr2;
+
+ // get search direction;
+ simplex.getSearchDirection(searchDirection);
+
+ // Ensure the search direction is numerically fit.
+ if (searchDirection.lengthSquared < Settings.EPSILON * Settings.EPSILON) {
+ // The origin is probably contained by a line segment
+ // or triangle. Thus the shapes are overlapped.
+
+ // We can't return zero here even though there may be overlap.
+ // In case the simplex is a point, segment, or triangle it is difficult
+ // to determine if the origin is contained in the CSO or very close to
+ // it.
+ break;
+ }
+
+ // Compute a tentative new simplex vertex using support points.
+ SimplexVertex vertex = vertices[simplex.count];
+
+ Matrix22.mulTransMatrixAndVectorToOut(transformA.rotation,
+ searchDirection.negateLocal(), temp);
+ vertex.indexA = proxyA.getSupport(temp);
+ Transform.mulToOut(transformA, proxyA.vertices[vertex.indexA],
+ vertex.wA);
+ // Vec2 wBLocal;
+ Matrix22.mulTransMatrixAndVectorToOut(transformB.rotation,
+ searchDirection.negateLocal(), temp);
+ vertex.indexB = proxyB.getSupport(temp);
+ Transform.mulToOut(transformB, proxyB.vertices[vertex.indexB],
+ vertex.wB);
+ vertex.w.setFrom(vertex.wB).subLocal(vertex.wA);
+
+ // Iteration count is equated to the number of support point calls.
+ ++iter;
+ ++iters;
+
+ // Check for duplicate support points. This is the main termination
+ // criteria.
+ bool duplicate = false;
+ for (int i = 0; i < saveCount; ++i) {
+ if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i]) {
+ duplicate = true;
+ break;
+ }
+ }
+
+ // If we found a duplicate support point we must exit to avoid cycling.
+ if (duplicate) {
+ break;
+ }
+
+ // New vertex is ok and needed.
+ ++simplex.count;
+ }
+
+ maxIters = Math.max(maxIters, iter);
+
+ // Prepare output.
+ simplex.getWitnessPoints(output.pointA, output.pointB);
+ output.distance = MathBox.distance(output.pointA, output.pointB);
+ output.iterations = iter;
+
+ // Cache the simplex.
+ simplex.writeCache(cache);
+
+ // Apply radii if requested.
+ if (input.useRadii) {
+ num rA = proxyA.radius;
+ num rB = proxyB.radius;
+
+ if (output.distance > rA + rB && output.distance > Settings.EPSILON) {
+ // Shapes are still no overlapped.
+ // Move the witness points to the outer surface.
+ output.distance -= rA + rB;
+ normal.setFrom(output.pointB).subLocal(output.pointA);
+ normal.normalize();
+ temp.setFrom(normal).mulLocal(rA);
+ output.pointA.addLocal(temp);
+ temp.setFrom(normal).mulLocal(rB);
+ output.pointB.subLocal(temp);
+ } else {
+ // Shapes are overlapped when radii are considered.
+ // Move the witness points to the middle.
+ output.pointA.addLocal(output.pointB).mulLocal(.5);
+ output.pointB.setFrom(output.pointA);
+ output.distance = 0.0;
+ }
+ }
+ }
+}
View
32 dart/dartbox2d/collision/DistanceInput.dart
@@ -0,0 +1,32 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Input for Distance.
+ * You have the option to use the shape radii in the computation.
+ */
+class DistanceInput {
+ DistanceProxy proxyA;
+ DistanceProxy proxyB;
+ Transform transformA;
+ Transform transformB;
+ bool useRadii;
+
+ DistanceInput() :
+ proxyA = new DistanceProxy(),
+ proxyB = new DistanceProxy(),
+ transformA = new Transform(),
+ transformB = new Transform(),
+ useRadii = false { }
+}
View
33 dart/dartbox2d/collision/DistanceOutput.dart
@@ -0,0 +1,33 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Output for Distance.
+ */
+class DistanceOutput {
+ /** Closest point on shapeA */
+ final Vector pointA;
+
+ /** Closest point on shapeB */
+ final Vector pointB;
+
+ num distance;
+
+ /** number of gjk iterations used */
+ int iterations;
+
+ DistanceOutput() :
+ pointA = new Vector(),
+ pointB = new Vector() { }
+}
View
82 dart/dartbox2d/collision/DistanceProxy.dart
@@ -0,0 +1,82 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/**
+ * A distance proxy is used by the GJK algorithm. It encapsulates any shape.
+ */
+class DistanceProxy {
+ final List<Vector> vertices;
+ int count;
+ num radius;
+
+ /**
+ * Constructs a new DistanceProxy.
+ */
+ DistanceProxy() :
+ vertices = new List<Vector>(Settings.MAX_POLYGON_VERTICES),
+ count = 0,
+ radius = 0 {
+
+ for(int i = 0; i < vertices.length; i++) {
+ vertices[i] = new Vector();
+ }
+ }
+
+ /**
+ * Initialize the proxy using the given shape. The shape
+ * must remain in scope while the proxy is in use.
+ */
+ void setFromShape(shape) {
+ // If the shape is a circle...
+ if (shape.type == ShapeType.CIRCLE) {
+ vertices[0].setFrom(shape.position);
+ count = 1;
+ radius = shape.radius;
+
+ // If the shape is a polygon...
+ } else if (shape.type == ShapeType.POLYGON) {
+ count = shape.vertexCount;
+ radius = shape.radius;
+ for(int i = 0; i < count; i++) {
+ vertices[i].setFrom(shape.vertices[i]);
+ }
+ } else {
+ // Should always be a circle or a polygon.
+ assert(false);
+ }
+ }
+
+ /**
+ * Get the supporting vertex index in the given direction.
+ */
+ int getSupport(Vector direction) {
+ int bestIndex = 0;
+ num bestValue = Vector.dot(vertices[0], direction);
+ for (int i = 1; i < count; i++) {
+ num value = Vector.dot(vertices[i], direction);
+ if(value > bestValue) {
+ bestIndex = i;
+ bestValue = value;
+ }
+ }
+
+ return bestIndex;
+ }
+
+ /**
+ * Get the supporting vertex in the given direction.
+ */
+ Vector getSupportVertex(Vector direction) {
+ return vertices[getSupport(direction)];
+ }
+}
View
80 dart/dartbox2d/collision/Features.dart
@@ -0,0 +1,80 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/** The features that intersect to form the contact point */
+class Features {
+ /** The edge that defines the outward contact normal. */
+ int referenceEdge;
+
+ /** The edge most anti-parallel to the reference edge. */
+ int incidentEdge;
+
+ /** The vertex (0 or 1) on the incident edge that was clipped. */
+ int incidentVertex;
+
+ /** A value of 1 indicates that the reference edge is on shape2. */
+ int flip;
+
+ /**
+ * Constructs a new features with zero values for all fields.
+ */
+ Features() :
+ referenceEdge = 0,
+ incidentEdge = 0,
+ incidentVertex = 0,
+ flip = 0 { }
+
+ // Constructs a new Features that is a copy of the given features.
+ Features.copy(Features f) :
+ referenceEdge = f.referenceEdge,
+ incidentEdge = f.incidentEdge,
+ incidentVertex = f.incidentVertex,
+ flip = f.flip { }
+
+ // Set this feature to be a copy of the given feature.
+ setFrom(Features f) {
+ referenceEdge = f.referenceEdge;
+ incidentEdge = f.incidentEdge;
+ incidentVertex = f.incidentVertex;
+ flip = f.flip;
+ }
+
+ /**
+ * Returns true if this Features object is equal to the given object.
+ */
+ bool operator == (other) {
+ return referenceEdge == other.referenceEdge &&
+ incidentEdge == other.incidentEdge &&
+ incidentVertex == other.incidentVertex && flip == other.flip;
+ }
+
+ /**
+ * Returns a String representation of this Features.
+ */
+ String toString() {
+ String s = "Features: (" + flip + " ," + incidentEdge +
+ " ," + incidentVertex + " ," + referenceEdge + ")";
+ return s;
+ }
+
+ /**
+ * Sets all features to 0.
+ */
+ zero() {
+ referenceEdge = 0;
+ incidentEdge = 0;
+ incidentVertex = 0;
+ flip = 0;
+ }
+}
View
90 dart/dartbox2d/collision/Manifold.dart
@@ -0,0 +1,90 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * A manifold for two touching convex shapes. Box2D has support for many kinds
+ * of contact, such as clip pont versus plain with radius and point versus point
+ * with radius (as with circles).
+ */
+class Manifold {
+ /** The points of contact. */
+ final List<ManifoldPoint> points;
+
+ /**
+ * The meaning of the localNormal depends on the type of this manifold. The
+ * different meanings (or lack thereof) are outlined below.
+ * Circles: not used.
+ * faceA: The normal on polygonA.
+ * faceB: The normal on polygonB.
+ */
+ final Vector localNormal;
+
+ /**
+ * The meaning of the localPoint depends on the type of this manifold. The
+ * different meanings (or lack thereof) are outlined below.
+ * Circles: The local center of circleA.
+ * faceA: The center of faceA.
+ * faceB: The center of faceB.
+ */
+ final Vector localPoint;
+
+ /** The type of manifold. See [ManifoldType]. */
+ int type;
+
+ /** The number of manifold points. */
+ int pointCount;
+
+ /**
+ * Creates a manifold with 0 points. It's point array should be full of
+ * already instantiated ManifoldPoints.
+ */
+ Manifold() :
+ points = new List<ManifoldPoint>(Settings.MAX_MANIFOLD_POINTS),
+ localNormal = new Vector(),
+ localPoint = new Vector(),
+ pointCount = 0 {
+
+ for (int i = 0; i < Settings.MAX_MANIFOLD_POINTS; i++) {
+ points[i] = new ManifoldPoint();
+ }
+ }
+
+ /**
+ * Creates a new manifold that is a copy of the given manifold.
+ */
+ Manifold.copy(Manifold other) :
+ points = new List<ManifoldPoint>(Settings.MAX_MANIFOLD_POINTS),
+ localNormal = new Vector.copy(other.localNormal),
+ localPoint = new Vector.copy(other.localPoint),
+ pointCount = other.pointCount,
+ type = other.type {
+ for (int i = 0; i < Settings.MAX_MANIFOLD_POINTS; i++) {
+ points[i] = new ManifoldPoint.copy(other.points[i]);
+ }
+ }
+
+ /**
+ * Sets this manifold to be a copy of the given manifold.
+ */
+ void setFrom(Manifold other) {
+ for (int i = 0; i < other.pointCount; i++) {
+ points[i].setFrom(other.points[i]);
+ }
+
+ type = other.type;
+ localNormal.setFrom(other.localNormal);
+ localPoint.setFrom(other.localPoint);
+ pointCount = other.pointCount;
+ }
+}
View
63 dart/dartbox2d/collision/ManifoldPoint.dart
@@ -0,0 +1,63 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * A manifold point is a contact point belonging to a contact manifold. It holds
+ * the details of the geometry and dynamics of the contact points.
+ */
+class ManifoldPoint {
+ /**
+ * Usage depends on manifold type. For circles, is the local center of
+ * circleB. For faceA, is the local center of CircleB or the clip point of
+ * polygonB. For faceB, is the clip point of polygonA.
+ */
+ final Vector localPoint;
+
+ /** The non-penetration impulse. */
+ num normalImpulse;
+
+ /** The friction impulse. */
+ num tangentImpulse;
+
+ /** Unique identifier for a contact point between two shapes. */
+ final ContactID id;
+
+ /**
+ * Constructs a new ManifoldPoint.
+ */
+ ManifoldPoint() :
+ localPoint = new Vector(),
+ tangentImpulse = 0,
+ normalImpulse = 0,
+ id = new ContactID() { }
+
+ /**
+ * Constructs a new ManifoldPoint that is a copy of the given point.
+ */
+ ManifoldPoint.copy(ManifoldPoint other) :
+ localPoint = new Vector.copy(other.localPoint),
+ normalImpulse = other.normalImpulse,
+ tangentImpulse = other.tangentImpulse,
+ id = new ContactID.copy(other.id) { }
+
+ /**
+ * Sets this ManifoldPoint to be equal to the given point.
+ */
+ void setFrom(ManifoldPoint other) {
+ localPoint.setFrom(other.localPoint);
+ normalImpulse = other.normalImpulse;
+ tangentImpulse = other.tangentImpulse;
+ id.setFrom(other.id);
+ }
+}
View
22 dart/dartbox2d/collision/ManifoldType.dart
@@ -0,0 +1,22 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * An enum class for the different kinds of Manifolds.
+ */
+class ManifoldType {
+ static final int CIRCLES = 0;
+ static final int FACE_A = 1;
+ static final int FACE_B = 2;
+}
View
21 dart/dartbox2d/collision/PointState.dart
@@ -0,0 +1,21 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/** An enum class for determining the state of contact points. */
+class PointState {
+ static final int NULL_STATE = 0;
+ static final int ADD_STATE = 1;
+ static final int PERSIST_STATE = 2;
+ static final int REMOVE_STATE = 3;
+}
View
354 dart/dartbox2d/collision/Simplex.dart
@@ -0,0 +1,354 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/** Class for internal use by Distance.dart. */
+class Simplex {
+ final SimplexVertex v1;
+ final SimplexVertex v2;
+ final SimplexVertex v3;
+ final List<SimplexVertex> vertices;
+ int count;
+
+ Simplex() :
+ count = 0,
+ v1 = new SimplexVertex(),
+ v2 = new SimplexVertex(),
+ v3 = new SimplexVertex(),
+ vertices = new List<SimplexVertex>(3),
+ e13 = new Vector(),
+ e12 = new Vector(),
+ e23 = new Vector(),
+ case2 = new Vector(),
+ case22 = new Vector(),
+ case3 = new Vector(),
+ case33 = new Vector() {
+
+ vertices[0] = v1;
+ vertices[1] = v2;
+ vertices[2] = v3;
+ }
+
+ /** Pooling. */
+ final Vector e13;
+ final Vector e23;
+ final Vector e12;
+ final Vector case2;
+ final Vector case22;
+ final Vector case3;
+ final Vector case33;
+
+ void readCache(SimplexCache cache, DistanceProxy proxyA,
+ Transform transformA, DistanceProxy proxyB,
+ Transform transformB) {
+ assert (cache.count <= 3);
+
+ // Copy data from cache.
+ count = cache.count;
+
+ for (int i = 0; i < count; ++i) {
+ SimplexVertex v = vertices[i];
+ v.indexA = cache.indexA[i];
+ v.indexB = cache.indexB[i];
+ Vector wALocal = proxyA.vertices[v.indexA];
+ Vector wBLocal = proxyB.vertices[v.indexB];
+ Transform.mulToOut(transformA, wALocal, v.wA);
+ Transform.mulToOut(transformB, wBLocal, v.wB);
+ v.w.setFrom(v.wB).subLocal(v.wA);
+ v.a = 0.0;
+ }
+
+ // Compute the new simplex metric, if it is substantially different than
+ // old metric then flush the simplex.
+ if (count > 1) {
+ num metric1 = cache.metric;
+ num metric2 = getMetric();
+ if (metric2 < 0.5 * metric1 || 2.0 * metric1 < metric2 || metric2 <
+ Settings.EPSILON) {
+ // Reset the simplex.
+ count = 0;
+ }
+ }
+
+ // If the cache is empty or invalid ...
+ if (count == 0) {
+ SimplexVertex v = vertices[0];
+ v.indexA = 0;
+ v.indexB = 0;
+ Vector wALocal = proxyA.vertices[0];
+ Vector wBLocal = proxyB.vertices[0];
+ Transform.mulToOut(transformA, wALocal, v.wA);
+ Transform.mulToOut(transformB, wBLocal, v.wB);
+ v.w.setFrom(v.wB).subLocal(v.wA);
+ count = 1;
+ }
+ }
+
+ void writeCache(SimplexCache cache) {
+ cache.metric = getMetric();
+ cache.count = count;
+
+ for (int i = 0; i < count; ++i) {
+ cache.indexA[i] = (vertices[i].indexA);
+ cache.indexB[i] = (vertices[i].indexB);
+ }
+ }
+
+ void getSearchDirection(Vector out) {
+ switch (count) {
+ case 1 :
+ out.setFrom(v1.w).negateLocal();
+ return;
+ case 2 :
+ e12.setFrom(v2.w).subLocal(v1.w);
+ // use out for a temp variable real quick
+ out.setFrom(v1.w).negateLocal();
+ num sgn = Vector.crossVectors(e12, out);
+
+ if (sgn > 0) {
+ // Origin is left of e12.
+ Vector.crossNumAndVectorToOut(1, e12, out);
+ return;
+ }
+ else {
+ // Origin is right of e12.
+ Vector.crossVectorAndNumToOut(e12, 1, out);
+ return;
+ }
+ default :
+ assert (false);
+ out.setZero();
+ return;
+ }
+ }
+
+
+ /**