diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..496ee2c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
\ No newline at end of file
diff --git a/0.0.0.18/pom.xml b/0.0.0.18/pom.xml
new file mode 100644
index 0000000..c14b2d6
--- /dev/null
+++ b/0.0.0.18/pom.xml
@@ -0,0 +1,28 @@
+
+ 4.0.0
+ platform.clients.fp9.libraries
+ AlternativaPhysics
+ swc
+ 0.0.0.18
+
+ platform.tools.maven
+ FlashBasePom
+ 1.0
+
+
+
+ platform.clients.fp9.libraries
+ AlternativaTypes
+ [1.1.0.1,2.0.0.0)
+ swc
+ external
+
+
+ platform.clients.fp9.libraries
+ AlternativaUtils
+ [1.2.0.0,2.0.0.0)
+ swc
+ external
+
+
+
\ No newline at end of file
diff --git a/0.0.0.18/src/.svn/all-wcprops b/0.0.0.18/src/.svn/all-wcprops
new file mode 100644
index 0000000..b7f99f9
--- /dev/null
+++ b/0.0.0.18/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 74
+/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src
+END
diff --git a/0.0.0.18/src/.svn/entries b/0.0.0.18/src/.svn/entries
new file mode 100644
index 0000000..b621a67
--- /dev/null
+++ b/0.0.0.18/src/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-17T11:38:42.040147Z
+5193
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+alternativa
+dir
+
diff --git a/0.0.0.18/src/.svn/format b/0.0.0.18/src/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/.svn/all-wcprops b/0.0.0.18/src/alternativa/.svn/all-wcprops
new file mode 100644
index 0000000..5a876f1
--- /dev/null
+++ b/0.0.0.18/src/alternativa/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 86
+/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa
+END
diff --git a/0.0.0.18/src/alternativa/.svn/entries b/0.0.0.18/src/alternativa/.svn/entries
new file mode 100644
index 0000000..18c2959
--- /dev/null
+++ b/0.0.0.18/src/alternativa/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-17T11:38:42.040147Z
+5193
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+physics
+dir
+
diff --git a/0.0.0.18/src/alternativa/.svn/format b/0.0.0.18/src/alternativa/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/.svn/all-wcprops
new file mode 100644
index 0000000..9ec4fdf
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 94
+/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics
+END
+altphysics.as
+K 25
+svn:wc:ra_dav:version-url
+V 108
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/altphysics.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/.svn/entries b/0.0.0.18/src/alternativa/physics/.svn/entries
new file mode 100644
index 0000000..5db5d29
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/.svn/entries
@@ -0,0 +1,55 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-17T11:38:42.040147Z
+5193
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+particle
+dir
+
+altphysics.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+44fa0a3979af103ba658ab05e4428212
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+registry
+dir
+
+collision
+dir
+
+force
+dir
+
+rigid
+dir
+
diff --git a/0.0.0.18/src/alternativa/physics/.svn/format b/0.0.0.18/src/alternativa/physics/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base b/0.0.0.18/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
new file mode 100644
index 0000000..8252b4f
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
diff --git a/0.0.0.18/src/alternativa/physics/altphysics.as b/0.0.0.18/src/alternativa/physics/altphysics.as
new file mode 100644
index 0000000..8252b4f
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/altphysics.as
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/collision/.svn/all-wcprops
new file mode 100644
index 0000000..390e0e6
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 104
+/!svn/ver/4398/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision
+END
+CollisionBox.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionBox.as
+END
+CollisionPrimitive.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionPrimitive.as
+END
+CollisionData.as
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionData.as
+END
+CollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/!svn/ver/4398/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionDetector.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/entries b/0.0.0.18/src/alternativa/physics/collision/.svn/entries
new file mode 100644
index 0000000..a0d5b41
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/entries
@@ -0,0 +1,76 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision
+http://svndev.alternativaplatform.com
+
+
+
+2008-11-26T15:04:50.319495Z
+4398
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+CollisionBox.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+777502bd075948da11606f32afba6679
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+CollisionPrimitive.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+1e73f056a418e5c664977b4fafad70e5
+2008-11-26T12:04:03.108808Z
+4375
+mike
+
+CollisionData.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+a8943fcaceb0836d59e7f4c2b2d3f4df
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+CollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+ca727662c3fe996a487323977c4a67bb
+2008-11-26T15:04:50.319495Z
+4398
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/format b/0.0.0.18/src/alternativa/physics/collision/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionBox.as.svn-base b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionBox.as.svn-base
new file mode 100644
index 0000000..be77bc3
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionBox.as.svn-base
@@ -0,0 +1,15 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ public class CollisionBox extends CollisionPrimitive {
+
+ public var halfSize:Point3D = new Point3D();
+
+ public function CollisionBox(body:RigidBody, halfSize:Point3D) {
+ super(body);
+ this.halfSize.copy(halfSize);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionData.as.svn-base b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionData.as.svn-base
new file mode 100644
index 0000000..ce02511
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionData.as.svn-base
@@ -0,0 +1,18 @@
+package alternativa.physics.collision {
+ import alternativa.types.Point3D;
+
+ public class CollisionData {
+
+ public var primitive1:CollisionPrimitive;
+ public var primitive2:CollisionPrimitive;
+
+ public var collisionNormal:Point3D = new Point3D();
+ public var collisionPoint:Point3D = new Point3D();
+ public var penetration:Number;
+
+ public var pointAxisCode1:int;
+ public var pointAxisCode2:int;
+
+ public var timeStamp:int;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionDetector.as.svn-base b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionDetector.as.svn-base
new file mode 100644
index 0000000..9d9de35
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionDetector.as.svn-base
@@ -0,0 +1,342 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class CollisionDetector {
+
+ private var epsilon:Number;
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var vectorToBox1:Point3D = new Point3D();
+ private var axis:Point3D = new Point3D();
+ private var axis10:Point3D = new Point3D();
+ private var axis11:Point3D = new Point3D();
+ private var axis12:Point3D = new Point3D();
+ private var axis20:Point3D = new Point3D();
+ private var axis21:Point3D = new Point3D();
+ private var axis22:Point3D = new Point3D();
+ private var collisionAxis:Point3D = new Point3D();
+ private var tmpAxis:Point3D = new Point3D();
+ private var point1:Point3D = new Point3D();
+ private var point2:Point3D = new Point3D();
+ private var vector:Point3D = new Point3D();
+
+ public function CollisionDetector(epsilon:Number = 0.01) {
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Проверяет столкновение двух боксов, используя теорему о разделяющих плоскостях.
+ *
+ * @param box1
+ * @param box2
+ * @param collisionData
+ *
+ * @return
+ */
+ public function boxToBoxCollision(box1:CollisionBox, box2:CollisionBox, collisionData:CollisionData):Boolean {
+ if (box1.body.inverseMass == 0 && box2.body.inverseMass == 0) {
+ // Нет необходимости проверять столкновения между неподвижными объектами
+ return false;
+ }
+ minOverlap = Number.MAX_VALUE;
+ // Вектор из центра второго бокса в центр первого
+ vectorToBox1.difference(box1.position, box2.position);
+
+ // Проверка пересечения по основным осям
+ box1.transform.getAxis(0, axis10);
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+ box1.transform.getAxis(1, axis11);
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+ box1.transform.getAxis(2, axis12);
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+ box2.transform.getAxis(0, axis20);
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+ box2.transform.getAxis(1, axis21);
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+ box2.transform.getAxis(2, axis22);
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Сохраняем ось с наименьшим перекрытием на случай обнаружения контакта ребро-грань
+ var bestMainAxis:int = bestAxisIndex;
+
+ // Проверка производных осей
+ axis.cross2(axis10, axis20);
+ if (!testAxis(box1, box2, axis, 6, vectorToBox1)) return false;
+ axis.cross2(axis10, axis21);
+ if (!testAxis(box1, box2, axis, 7, vectorToBox1)) return false;
+ axis.cross2(axis10, axis22);
+ if (!testAxis(box1, box2, axis, 8, vectorToBox1)) return false;
+
+ axis.cross2(axis11, axis20);
+ if (!testAxis(box1, box2, axis, 9, vectorToBox1)) return false;
+ axis.cross2(axis11, axis21);
+ if (!testAxis(box1, box2, axis, 10, vectorToBox1)) return false;
+ axis.cross2(axis11, axis22);
+ if (!testAxis(box1, box2, axis, 11, vectorToBox1)) return false;
+
+ axis.cross2(axis12, axis20);
+ if (!testAxis(box1, box2, axis, 12, vectorToBox1)) return false;
+ axis.cross2(axis12, axis21);
+ if (!testAxis(box1, box2, axis, 13, vectorToBox1)) return false;
+ axis.cross2(axis12, axis22);
+ if (!testAxis(box1, box2, axis, 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 3) {
+ // Пересечение вершины второго бокса с гранью первого
+ fillPointBoxPenetrationData(box1, box2, vectorToBox1, bestAxisIndex, minOverlap, collisionData);
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ return true;
+ } else if (bestAxisIndex < 6) {
+ // Пересечение вершины первого бокса с гранью второго
+ vectorToBox1.invert();
+ fillPointBoxPenetrationData(box2, box1, vectorToBox1, bestAxisIndex - 3, minOverlap, collisionData);
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ collisionData.collisionNormal.invert();
+ var tmp:Number = collisionData.pointAxisCode1;
+ collisionData.pointAxisCode1 = collisionData.pointAxisCode2;
+ collisionData.pointAxisCode2 = tmp;
+ return true;
+ } else {
+ // Пересечение рёбер
+ bestAxisIndex -= 6;
+ var axisIndex1:int = int(bestAxisIndex/3);
+ var axisIndex2:int = bestAxisIndex%3;
+
+ box1.transform.getAxis(axisIndex1, axis10);
+ box2.transform.getAxis(axisIndex2, axis20);
+ collisionAxis.cross2(axis10, axis20);
+ collisionAxis.normalize();
+ // Разворот оси в сторону первого бокса
+ if (collisionAxis.dot(vectorToBox1) < 0) {
+ collisionAxis.invert();
+ }
+
+ var edgeHalfLength1:Number;
+ var edgeHalfLength2:Number;
+ var pointEdgeCode1:int;
+ var pointEdgeCode2:int;
+
+ point1.copy(box1.halfSize);
+ point2.copy(box2.halfSize);
+ // x
+ if (axisIndex1 == 0) {
+ point1.x = 0;
+ edgeHalfLength1 = box1.halfSize.x;
+ pointEdgeCode1 |= 1;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.x = -point1.x;
+ pointEdgeCode1 |= 1;
+ }
+ }
+ if (axisIndex2 == 0) {
+ point2.x = 0;
+ edgeHalfLength2 = box2.halfSize.x;
+ pointEdgeCode2 |= 1;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.x = -point2.x;
+ pointEdgeCode2 |= 1;
+ }
+ }
+ // y
+ if (axisIndex1 == 1) {
+ point1.y = 0;
+ edgeHalfLength1 = box1.halfSize.y;
+ pointEdgeCode1 |= 2;
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.y = -point1.y;
+ pointEdgeCode1 |= 2;
+ }
+ }
+ if (axisIndex2 == 1) {
+ point2.y = 0;
+ edgeHalfLength2 = box2.halfSize.y;
+ pointEdgeCode2 |= 2;
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.y = -point2.y;
+ pointEdgeCode2 |= 2;
+ }
+ }
+ // z
+ if (axisIndex1 == 2) {
+ point1.z = 0;
+ edgeHalfLength1 = box1.halfSize.z;
+ pointEdgeCode1 |= 4;
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.z = -point1.z;
+ pointEdgeCode1 |= 4;
+ }
+ }
+ if (axisIndex2 == 2) {
+ point2.z = 0;
+ edgeHalfLength2 = box2.halfSize.z;
+ pointEdgeCode2 |= 4;
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.z = -point2.z;
+ pointEdgeCode2 |= 4;
+ }
+ }
+
+ point1.transform(box1.transform);
+ point2.transform(box2.transform);
+
+ // Находим точку пересечения рёбер
+ var k:Number = axis10.dot(axis20);
+ var det:Number = k*k - 1;
+ if (det > -0.0001 && det < 0.0001) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ vector.difference(point2, point1);
+ var c1:Number = axis10.dot(vector);
+ var c2:Number = axis20.dot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ if (t1 < -edgeHalfLength1 || t1 > edgeHalfLength1) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ var t2:Number = (c2 - c1*k)/det;
+ if (t2 < -edgeHalfLength2 || t2 > edgeHalfLength2) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ vector.x = 0.5*(point1.x + point2.x + axis10.x*t1 + axis20.x*t2);
+ vector.y = 0.5*(point1.y + point2.y + axis10.y*t1 + axis20.y*t2);
+ vector.z = 0.5*(point1.z + point2.z + axis10.z*t1 + axis20.z*t2);
+ }
+ }
+ }
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ collisionData.penetration = minOverlap;
+ collisionData.collisionPoint.copy(vector);
+ collisionData.collisionNormal.copy(collisionAxis);
+ collisionData.pointAxisCode1 = pointEdgeCode1 | (axisIndex1 << 3) | 32;
+ collisionData.pointAxisCode2 = pointEdgeCode2 | (axisIndex2 << 3) | 32;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return
+ *
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, axisIndex:int, vectorToBox1:Point3D):Boolean {
+ if (axis.lengthSqr < 0.0001) {
+ return true;
+ }
+ axis.normalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap <= 0) {
+ return false;
+ }
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param vectorToBox1
+ * @return
+ *
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, vectorToBox1:Point3D):Number {
+ var m:Matrix3D = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.halfSize.x;
+ var projection:Number = d < 0 ? -d : d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.halfSize.y;
+ projection += d < 0 ? -d : d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.halfSize.z;
+ projection += d < 0 ? -d : d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.halfSize.x;
+ projection += d < 0 ? -d : d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.halfSize.y;
+ projection += d < 0 ? -d : d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.halfSize.z;
+ projection += d < 0 ? -d : d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) {
+ d = -d;
+ }
+
+ return projection - d;
+ }
+
+ /**
+ * Заполняет данные о контакте грани первого бокса с вершиной второго.
+ *
+ * @param box1
+ * @param box2
+ * @param vectorToBox1
+ * @param axisIndex
+ * @param overlap
+ * @param collisionData
+ * @param swapBoxes поменять примитивы местами при записи данных
+ */
+ private function fillPointBoxPenetrationData(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Point3D, axisIndex:int, overlap:Number, collisionData:CollisionData):void {
+ box1.transform.getAxis(axisIndex, collisionData.collisionNormal);
+ if (collisionData.collisionNormal.dot(vectorToBox1) < 0) {
+ collisionData.collisionNormal.invert();
+ }
+
+ var pointCode:int;
+ axis10.copy(box2.halfSize);
+ box2.transform.getAxis(0, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.x = -axis10.x;
+ pointCode |= 1;
+ }
+ box2.transform.getAxis(1, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.y = -axis10.y;
+ pointCode |= 2;
+ }
+ box2.transform.getAxis(2, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.z = -axis10.z;
+ pointCode |= 4;
+ }
+ axis10.transform(box2.transform);
+
+ collisionData.collisionPoint.copy(axis10);
+ collisionData.penetration = overlap;
+ collisionData.pointAxisCode1 = (axisIndex << 3) | 32;
+ collisionData.pointAxisCode2 = pointCode;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionPrimitive.as.svn-base b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionPrimitive.as.svn-base
new file mode 100644
index 0000000..d473792
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/.svn/text-base/CollisionPrimitive.as.svn-base
@@ -0,0 +1,31 @@
+package alternativa.physics.collision {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class CollisionPrimitive {
+
+ public var position:Point3D = new Point3D();
+ public var transform:Matrix3D = new Matrix3D();
+ public var body:RigidBody;
+
+ public function CollisionPrimitive(body:RigidBody) {
+ this.body = body;
+ }
+
+ public function updateTransform():void {
+ transform.copy(body.transformMatrix);
+ position.x = body.position.x;
+ position.y = body.position.y;
+ position.z = body.position.z;
+ transform.d = position.x;
+ transform.h = position.y;
+ transform.l = position.z;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/CollisionBox.as b/0.0.0.18/src/alternativa/physics/collision/CollisionBox.as
new file mode 100644
index 0000000..be77bc3
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/CollisionBox.as
@@ -0,0 +1,15 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ public class CollisionBox extends CollisionPrimitive {
+
+ public var halfSize:Point3D = new Point3D();
+
+ public function CollisionBox(body:RigidBody, halfSize:Point3D) {
+ super(body);
+ this.halfSize.copy(halfSize);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/CollisionData.as b/0.0.0.18/src/alternativa/physics/collision/CollisionData.as
new file mode 100644
index 0000000..ce02511
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/CollisionData.as
@@ -0,0 +1,18 @@
+package alternativa.physics.collision {
+ import alternativa.types.Point3D;
+
+ public class CollisionData {
+
+ public var primitive1:CollisionPrimitive;
+ public var primitive2:CollisionPrimitive;
+
+ public var collisionNormal:Point3D = new Point3D();
+ public var collisionPoint:Point3D = new Point3D();
+ public var penetration:Number;
+
+ public var pointAxisCode1:int;
+ public var pointAxisCode2:int;
+
+ public var timeStamp:int;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/CollisionDetector.as b/0.0.0.18/src/alternativa/physics/collision/CollisionDetector.as
new file mode 100644
index 0000000..9d9de35
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/CollisionDetector.as
@@ -0,0 +1,342 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class CollisionDetector {
+
+ private var epsilon:Number;
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var vectorToBox1:Point3D = new Point3D();
+ private var axis:Point3D = new Point3D();
+ private var axis10:Point3D = new Point3D();
+ private var axis11:Point3D = new Point3D();
+ private var axis12:Point3D = new Point3D();
+ private var axis20:Point3D = new Point3D();
+ private var axis21:Point3D = new Point3D();
+ private var axis22:Point3D = new Point3D();
+ private var collisionAxis:Point3D = new Point3D();
+ private var tmpAxis:Point3D = new Point3D();
+ private var point1:Point3D = new Point3D();
+ private var point2:Point3D = new Point3D();
+ private var vector:Point3D = new Point3D();
+
+ public function CollisionDetector(epsilon:Number = 0.01) {
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Проверяет столкновение двух боксов, используя теорему о разделяющих плоскостях.
+ *
+ * @param box1
+ * @param box2
+ * @param collisionData
+ *
+ * @return
+ */
+ public function boxToBoxCollision(box1:CollisionBox, box2:CollisionBox, collisionData:CollisionData):Boolean {
+ if (box1.body.inverseMass == 0 && box2.body.inverseMass == 0) {
+ // Нет необходимости проверять столкновения между неподвижными объектами
+ return false;
+ }
+ minOverlap = Number.MAX_VALUE;
+ // Вектор из центра второго бокса в центр первого
+ vectorToBox1.difference(box1.position, box2.position);
+
+ // Проверка пересечения по основным осям
+ box1.transform.getAxis(0, axis10);
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+ box1.transform.getAxis(1, axis11);
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+ box1.transform.getAxis(2, axis12);
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+ box2.transform.getAxis(0, axis20);
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+ box2.transform.getAxis(1, axis21);
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+ box2.transform.getAxis(2, axis22);
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Сохраняем ось с наименьшим перекрытием на случай обнаружения контакта ребро-грань
+ var bestMainAxis:int = bestAxisIndex;
+
+ // Проверка производных осей
+ axis.cross2(axis10, axis20);
+ if (!testAxis(box1, box2, axis, 6, vectorToBox1)) return false;
+ axis.cross2(axis10, axis21);
+ if (!testAxis(box1, box2, axis, 7, vectorToBox1)) return false;
+ axis.cross2(axis10, axis22);
+ if (!testAxis(box1, box2, axis, 8, vectorToBox1)) return false;
+
+ axis.cross2(axis11, axis20);
+ if (!testAxis(box1, box2, axis, 9, vectorToBox1)) return false;
+ axis.cross2(axis11, axis21);
+ if (!testAxis(box1, box2, axis, 10, vectorToBox1)) return false;
+ axis.cross2(axis11, axis22);
+ if (!testAxis(box1, box2, axis, 11, vectorToBox1)) return false;
+
+ axis.cross2(axis12, axis20);
+ if (!testAxis(box1, box2, axis, 12, vectorToBox1)) return false;
+ axis.cross2(axis12, axis21);
+ if (!testAxis(box1, box2, axis, 13, vectorToBox1)) return false;
+ axis.cross2(axis12, axis22);
+ if (!testAxis(box1, box2, axis, 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 3) {
+ // Пересечение вершины второго бокса с гранью первого
+ fillPointBoxPenetrationData(box1, box2, vectorToBox1, bestAxisIndex, minOverlap, collisionData);
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ return true;
+ } else if (bestAxisIndex < 6) {
+ // Пересечение вершины первого бокса с гранью второго
+ vectorToBox1.invert();
+ fillPointBoxPenetrationData(box2, box1, vectorToBox1, bestAxisIndex - 3, minOverlap, collisionData);
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ collisionData.collisionNormal.invert();
+ var tmp:Number = collisionData.pointAxisCode1;
+ collisionData.pointAxisCode1 = collisionData.pointAxisCode2;
+ collisionData.pointAxisCode2 = tmp;
+ return true;
+ } else {
+ // Пересечение рёбер
+ bestAxisIndex -= 6;
+ var axisIndex1:int = int(bestAxisIndex/3);
+ var axisIndex2:int = bestAxisIndex%3;
+
+ box1.transform.getAxis(axisIndex1, axis10);
+ box2.transform.getAxis(axisIndex2, axis20);
+ collisionAxis.cross2(axis10, axis20);
+ collisionAxis.normalize();
+ // Разворот оси в сторону первого бокса
+ if (collisionAxis.dot(vectorToBox1) < 0) {
+ collisionAxis.invert();
+ }
+
+ var edgeHalfLength1:Number;
+ var edgeHalfLength2:Number;
+ var pointEdgeCode1:int;
+ var pointEdgeCode2:int;
+
+ point1.copy(box1.halfSize);
+ point2.copy(box2.halfSize);
+ // x
+ if (axisIndex1 == 0) {
+ point1.x = 0;
+ edgeHalfLength1 = box1.halfSize.x;
+ pointEdgeCode1 |= 1;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.x = -point1.x;
+ pointEdgeCode1 |= 1;
+ }
+ }
+ if (axisIndex2 == 0) {
+ point2.x = 0;
+ edgeHalfLength2 = box2.halfSize.x;
+ pointEdgeCode2 |= 1;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.x = -point2.x;
+ pointEdgeCode2 |= 1;
+ }
+ }
+ // y
+ if (axisIndex1 == 1) {
+ point1.y = 0;
+ edgeHalfLength1 = box1.halfSize.y;
+ pointEdgeCode1 |= 2;
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.y = -point1.y;
+ pointEdgeCode1 |= 2;
+ }
+ }
+ if (axisIndex2 == 1) {
+ point2.y = 0;
+ edgeHalfLength2 = box2.halfSize.y;
+ pointEdgeCode2 |= 2;
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.y = -point2.y;
+ pointEdgeCode2 |= 2;
+ }
+ }
+ // z
+ if (axisIndex1 == 2) {
+ point1.z = 0;
+ edgeHalfLength1 = box1.halfSize.z;
+ pointEdgeCode1 |= 4;
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) > 0) {
+ point1.z = -point1.z;
+ pointEdgeCode1 |= 4;
+ }
+ }
+ if (axisIndex2 == 2) {
+ point2.z = 0;
+ edgeHalfLength2 = box2.halfSize.z;
+ pointEdgeCode2 |= 4;
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.dot(collisionAxis) < 0) {
+ point2.z = -point2.z;
+ pointEdgeCode2 |= 4;
+ }
+ }
+
+ point1.transform(box1.transform);
+ point2.transform(box2.transform);
+
+ // Находим точку пересечения рёбер
+ var k:Number = axis10.dot(axis20);
+ var det:Number = k*k - 1;
+ if (det > -0.0001 && det < 0.0001) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ vector.difference(point2, point1);
+ var c1:Number = axis10.dot(vector);
+ var c2:Number = axis20.dot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ if (t1 < -edgeHalfLength1 || t1 > edgeHalfLength1) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ var t2:Number = (c2 - c1*k)/det;
+ if (t2 < -edgeHalfLength2 || t2 > edgeHalfLength2) {
+ vector.copy(bestMainAxis > 2 ? point1 : point2);
+ } else {
+ vector.x = 0.5*(point1.x + point2.x + axis10.x*t1 + axis20.x*t2);
+ vector.y = 0.5*(point1.y + point2.y + axis10.y*t1 + axis20.y*t2);
+ vector.z = 0.5*(point1.z + point2.z + axis10.z*t1 + axis20.z*t2);
+ }
+ }
+ }
+ collisionData.primitive1 = box1;
+ collisionData.primitive2 = box2;
+ collisionData.penetration = minOverlap;
+ collisionData.collisionPoint.copy(vector);
+ collisionData.collisionNormal.copy(collisionAxis);
+ collisionData.pointAxisCode1 = pointEdgeCode1 | (axisIndex1 << 3) | 32;
+ collisionData.pointAxisCode2 = pointEdgeCode2 | (axisIndex2 << 3) | 32;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return
+ *
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, axisIndex:int, vectorToBox1:Point3D):Boolean {
+ if (axis.lengthSqr < 0.0001) {
+ return true;
+ }
+ axis.normalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap <= 0) {
+ return false;
+ }
+ if (overlap < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param vectorToBox1
+ * @return
+ *
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, vectorToBox1:Point3D):Number {
+ var m:Matrix3D = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.halfSize.x;
+ var projection:Number = d < 0 ? -d : d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.halfSize.y;
+ projection += d < 0 ? -d : d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.halfSize.z;
+ projection += d < 0 ? -d : d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.halfSize.x;
+ projection += d < 0 ? -d : d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.halfSize.y;
+ projection += d < 0 ? -d : d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.halfSize.z;
+ projection += d < 0 ? -d : d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) {
+ d = -d;
+ }
+
+ return projection - d;
+ }
+
+ /**
+ * Заполняет данные о контакте грани первого бокса с вершиной второго.
+ *
+ * @param box1
+ * @param box2
+ * @param vectorToBox1
+ * @param axisIndex
+ * @param overlap
+ * @param collisionData
+ * @param swapBoxes поменять примитивы местами при записи данных
+ */
+ private function fillPointBoxPenetrationData(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Point3D, axisIndex:int, overlap:Number, collisionData:CollisionData):void {
+ box1.transform.getAxis(axisIndex, collisionData.collisionNormal);
+ if (collisionData.collisionNormal.dot(vectorToBox1) < 0) {
+ collisionData.collisionNormal.invert();
+ }
+
+ var pointCode:int;
+ axis10.copy(box2.halfSize);
+ box2.transform.getAxis(0, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.x = -axis10.x;
+ pointCode |= 1;
+ }
+ box2.transform.getAxis(1, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.y = -axis10.y;
+ pointCode |= 2;
+ }
+ box2.transform.getAxis(2, axis20);
+ if (axis20.dot(collisionData.collisionNormal) < 0) {
+ axis10.z = -axis10.z;
+ pointCode |= 4;
+ }
+ axis10.transform(box2.transform);
+
+ collisionData.collisionPoint.copy(axis10);
+ collisionData.penetration = overlap;
+ collisionData.pointAxisCode1 = (axisIndex << 3) | 32;
+ collisionData.pointAxisCode2 = pointCode;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/collision/CollisionPrimitive.as b/0.0.0.18/src/alternativa/physics/collision/CollisionPrimitive.as
new file mode 100644
index 0000000..d473792
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/collision/CollisionPrimitive.as
@@ -0,0 +1,31 @@
+package alternativa.physics.collision {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class CollisionPrimitive {
+
+ public var position:Point3D = new Point3D();
+ public var transform:Matrix3D = new Matrix3D();
+ public var body:RigidBody;
+
+ public function CollisionPrimitive(body:RigidBody) {
+ this.body = body;
+ }
+
+ public function updateTransform():void {
+ transform.copy(body.transformMatrix);
+ position.x = body.position.x;
+ position.y = body.position.y;
+ position.z = body.position.z;
+ transform.d = position.x;
+ transform.h = position.y;
+ transform.l = position.z;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/force/.svn/all-wcprops
new file mode 100644
index 0000000..71adea8
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/all-wcprops
@@ -0,0 +1,77 @@
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force
+END
+ParticleAnchoredBungee.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleAnchoredBungee.as
+END
+ParticleBuoyancy.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleBuoyancy.as
+END
+RigidBodySpring.as
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodySpring.as
+END
+ParticleDrag.as
+K 25
+svn:wc:ra_dav:version-url
+V 116
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleDrag.as
+END
+IParticelForceGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 127
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/IParticelForceGenerator.as
+END
+RigidBodyGravity.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodyGravity.as
+END
+ParticleSpring.as
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleSpring.as
+END
+RigidBodyAnchoredBungee.as
+K 25
+svn:wc:ra_dav:version-url
+V 127
+/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodyAnchoredBungee.as
+END
+ParticleBungee.as
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleBungee.as
+END
+ParticleGravity.as
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleGravity.as
+END
+ParticleAnchoredSpring.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleAnchoredSpring.as
+END
+IRigidBodyForceGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/IRigidBodyForceGenerator.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/entries b/0.0.0.18/src/alternativa/physics/force/.svn/entries
new file mode 100644
index 0000000..ed6e268
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/entries
@@ -0,0 +1,172 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-03T05:15:16.843707Z
+4732
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+ParticleAnchoredBungee.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+ed65cd81f1a99df1cc3c04e061cc15fd
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleBuoyancy.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+4479f6ebbce6c7326410485e5c62b9a9
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+RigidBodySpring.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+3f0de100aebf0d3250e608872e6f3863
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleDrag.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+1a8e8b326a47d4771d157259e43deb53
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+IParticelForceGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+645a997b618f0708a8311054767340a7
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+RigidBodyGravity.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+b206b142e125e9fe4a1775f8c4768b17
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleSpring.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+fb5cb524f90e27812074bc2668c548d1
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+RigidBodyAnchoredBungee.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+a64b67f4f850a73a3ffc226ab8fb0774
+2008-12-03T05:15:16.843707Z
+4732
+mike
+
+ParticleBungee.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+d79408ff66b4c9d92f9a2be73417ba3b
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleGravity.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+3b0d1310430d785b3ec7ffebf8ea7ea9
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleAnchoredSpring.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+2a0eee0933c8973ec42bb28a8b0b7160
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+IRigidBodyForceGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+824ea9b7a68dda215974882d6f32f892
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/format b/0.0.0.18/src/alternativa/physics/force/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IParticelForceGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IParticelForceGenerator.as.svn-base
new file mode 100644
index 0000000..06a4eec
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IParticelForceGenerator.as.svn-base
@@ -0,0 +1,7 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+
+ public interface IParticelForceGenerator {
+ function updateForce(particle:Particle, time:Number):void;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IRigidBodyForceGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IRigidBodyForceGenerator.as.svn-base
new file mode 100644
index 0000000..d49cc8c
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/IRigidBodyForceGenerator.as.svn-base
@@ -0,0 +1,10 @@
+package alternativa.physics.force {
+ import alternativa.physics.rigid.RigidBody;
+
+ /**
+ *
+ */
+ public interface IRigidBodyForceGenerator {
+ function updateForce(body:RigidBody, time:Number):void;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredBungee.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredBungee.as.svn-base
new file mode 100644
index 0000000..399dac4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredBungee.as.svn-base
@@ -0,0 +1,37 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleAnchoredBungee implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var anchor:Point3D;
+ private var springCoeff:Number;
+ private var restLength:Number;
+ public var relaxed:Boolean;
+
+ public function ParticleAnchoredBungee(anchor:Point3D, springCoeff:Number, restLength:Number) {
+ this.anchor = anchor;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - anchor.x;
+ force.y = particle.position.y - anchor.y;
+ force.z = particle.position.z - anchor.z;
+ var len:Number = force.length;
+ if (len < restLength) {
+ relaxed = true;
+ return;
+ }
+ relaxed = false;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredSpring.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredSpring.as.svn-base
new file mode 100644
index 0000000..ed9263d
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleAnchoredSpring.as.svn-base
@@ -0,0 +1,31 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleAnchoredSpring implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var anchor:Point3D;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public function ParticleAnchoredSpring(anchor:Point3D, springCoeff:Number, restLength:Number) {
+ this.anchor = anchor;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - anchor.x;
+ force.y = particle.position.y - anchor.y;
+ force.z = particle.position.z - anchor.z;
+ var len:Number = force.length;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBungee.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBungee.as.svn-base
new file mode 100644
index 0000000..41cf3c0
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBungee.as.svn-base
@@ -0,0 +1,38 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleBungee implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var otherParticle:Particle;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public var relaxed:Boolean;
+
+ public function ParticleBungee(otherParticle:Particle, springCoeff:Number, restLength:Number) {
+ this.otherParticle = otherParticle;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - otherParticle.position.x;
+ force.y = particle.position.y - otherParticle.position.y;
+ force.z = particle.position.z - otherParticle.position.z;
+ var len:Number = force.length;
+ if (len < restLength) {
+ relaxed = true;
+ return;
+ }
+ relaxed = false;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBuoyancy.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBuoyancy.as.svn-base
new file mode 100644
index 0000000..45c0de3
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleBuoyancy.as.svn-base
@@ -0,0 +1,37 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleBuoyancy implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var maxDepth:Number;
+ private var waterHeight:Number;
+ private var volume:Number;
+ private var liquidDensity:Number;
+
+ public function ParticleBuoyancy(maxDepth:Number, waterHeight:Number, volume:Number, liquidDensity:Number = 1000) {
+ this.maxDepth = maxDepth;
+ this.waterHeight = waterHeight;
+ this.volume = volume;
+ this.liquidDensity = liquidDensity;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ var depth:Number = particle.position.y;
+ if (depth > waterHeight + maxDepth) {
+ return;
+ }
+
+ force.x = 0;
+ force.y = 0;
+ if (depth <= waterHeight - maxDepth) {
+ force.z = liquidDensity*volume;
+ } else {
+ force.z = liquidDensity*volume*(waterHeight + maxDepth - depth)/maxDepth*0.5;
+ }
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleDrag.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleDrag.as.svn-base
new file mode 100644
index 0000000..e9de457
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleDrag.as.svn-base
@@ -0,0 +1,29 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleDrag implements IParticelForceGenerator {
+ private var force:Point3D = new Point3D();
+
+ private var k1:Number;
+ private var k2:Number;
+
+ public function ParticleDrag(k1:Number, k2:Number) {
+ this.k1 = k1;
+ this.k2 = k2;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.velocity.x;
+ force.y = particle.velocity.y;
+ force.z = particle.velocity.z;
+ var len:Number = force.length;
+ var coeff:Number = len;
+ coeff = -(k1*coeff + k2*coeff*coeff)/len;
+ force.x *= coeff;
+ force.y *= coeff;
+ force.z *= coeff;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleGravity.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleGravity.as.svn-base
new file mode 100644
index 0000000..ff400d2
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleGravity.as.svn-base
@@ -0,0 +1,24 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleGravity implements IParticelForceGenerator {
+ private var force:Point3D = new Point3D();
+
+ private var _gravity:Point3D;
+
+ public function ParticleGravity(gravity:Point3D) {
+ _gravity = gravity.clone();
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ if (!particle.hasFiniteMass()) {
+ return;
+ }
+ force.x = _gravity.x*particle.mass;
+ force.y = _gravity.y*particle.mass;
+ force.z = _gravity.z*particle.mass;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleSpring.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleSpring.as.svn-base
new file mode 100644
index 0000000..aee5732
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/ParticleSpring.as.svn-base
@@ -0,0 +1,31 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleSpring implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var otherParticle:Particle;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public function ParticleSpring(otherParticle:Particle, springCoeff:Number, restLength:Number) {
+ this.otherParticle = otherParticle;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - otherParticle.position.x;
+ force.y = particle.position.y - otherParticle.position.y;
+ force.z = particle.position.z - otherParticle.position.z;
+ var len:Number = force.length;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyAnchoredBungee.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyAnchoredBungee.as.svn-base
new file mode 100644
index 0000000..85921c4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyAnchoredBungee.as.svn-base
@@ -0,0 +1,45 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class RigidBodyAnchoredBungee implements IRigidBodyForceGenerator {
+
+ private var vector1:Point3D = new Point3D();
+ private var vector2:Point3D = new Point3D();
+
+ private var _connectionPoint:Point3D = new Point3D();
+ private var _anchorPoint:Point3D = new Point3D();
+ private var _springConstant:Number;
+ private var _restLength:Number;
+
+ public function RigidBodyAnchoredBungee(connectionPoint:Point3D, anchorPoint:Point3D, springConstant:Number, restLength:Number) {
+ _connectionPoint.copy(connectionPoint);
+ _anchorPoint.copy(anchorPoint);
+ _springConstant = springConstant;
+ _restLength = restLength;
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ // TODO: Оптимизировать!!!
+ vector1.copy(_connectionPoint);
+ vector1.transform(body.transformMatrix);
+ vector2.copy(_anchorPoint);
+ vector2.subtract(vector1);
+ var len:Number = vector2.length;
+ var magnitude:Number = len - _restLength;
+ if (magnitude > 0) {
+ magnitude *= _springConstant;
+ vector2.multiply(magnitude/len);
+ body.addForceAtPoint(vector2, vector1);
+ }
+ }
+
+ public function set anchorPoint(value:Point3D):void {
+ _anchorPoint.copy(value);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyGravity.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyGravity.as.svn-base
new file mode 100644
index 0000000..e99aed1
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodyGravity.as.svn-base
@@ -0,0 +1,27 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBodyGravity implements IRigidBodyForceGenerator {
+
+ private var _gravity:Point3D = new Point3D();
+
+ public function RigidBodyGravity(g:Point3D) {
+ _gravity.copy(g);
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ if (body.inverseMass == 0) {
+ return;
+ }
+ body.addForceComponents(_gravity.x*body.mass, _gravity.y*body.mass, _gravity.z*body.mass);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodySpring.as.svn-base b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodySpring.as.svn-base
new file mode 100644
index 0000000..1a5d44a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/.svn/text-base/RigidBodySpring.as.svn-base
@@ -0,0 +1,45 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class RigidBodySpring implements IRigidBodyForceGenerator {
+
+ private var vector1:Point3D = new Point3D();
+ private var vector2:Point3D = new Point3D();
+
+ private var _connectionPoint:Point3D = new Point3D();
+ private var _otherConnectionPoint:Point3D = new Point3D();
+ private var _springConstant:Number;
+ private var _restLength:Number;
+ private var _otherBody:RigidBody;
+
+ public function RigidBodySpring(connectionPoint:Point3D, otherBody:RigidBody, otherConnectionPoint:Point3D, springConstant:Number, restLength:Number) {
+ _connectionPoint.copy(connectionPoint);
+ _otherConnectionPoint = otherConnectionPoint;
+ _otherBody = otherBody;
+ _springConstant = springConstant;
+ _restLength = restLength;
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ // TODO: Оптимизировать!!!
+ vector1.copy(_connectionPoint);
+ vector1.transform(body.transformMatrix);
+ vector2.copy(_otherConnectionPoint);
+ vector2.transform(_otherBody.transformMatrix);
+ vector2.subtract(vector1);
+ var len:Number = vector2.length;
+ if (len == 0) {
+ trace("[RigidBodySpring::updateForce] spring length is zero");
+ }
+ var magnitude:Number = len - _restLength;
+ magnitude *= _springConstant;
+ vector2.multiply(magnitude/len);
+ body.addForceAtPoint(vector2, vector1);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/IParticelForceGenerator.as b/0.0.0.18/src/alternativa/physics/force/IParticelForceGenerator.as
new file mode 100644
index 0000000..06a4eec
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/IParticelForceGenerator.as
@@ -0,0 +1,7 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+
+ public interface IParticelForceGenerator {
+ function updateForce(particle:Particle, time:Number):void;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/IRigidBodyForceGenerator.as b/0.0.0.18/src/alternativa/physics/force/IRigidBodyForceGenerator.as
new file mode 100644
index 0000000..d49cc8c
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/IRigidBodyForceGenerator.as
@@ -0,0 +1,10 @@
+package alternativa.physics.force {
+ import alternativa.physics.rigid.RigidBody;
+
+ /**
+ *
+ */
+ public interface IRigidBodyForceGenerator {
+ function updateForce(body:RigidBody, time:Number):void;
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredBungee.as b/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredBungee.as
new file mode 100644
index 0000000..399dac4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredBungee.as
@@ -0,0 +1,37 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleAnchoredBungee implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var anchor:Point3D;
+ private var springCoeff:Number;
+ private var restLength:Number;
+ public var relaxed:Boolean;
+
+ public function ParticleAnchoredBungee(anchor:Point3D, springCoeff:Number, restLength:Number) {
+ this.anchor = anchor;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - anchor.x;
+ force.y = particle.position.y - anchor.y;
+ force.z = particle.position.z - anchor.z;
+ var len:Number = force.length;
+ if (len < restLength) {
+ relaxed = true;
+ return;
+ }
+ relaxed = false;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredSpring.as b/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredSpring.as
new file mode 100644
index 0000000..ed9263d
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleAnchoredSpring.as
@@ -0,0 +1,31 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleAnchoredSpring implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var anchor:Point3D;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public function ParticleAnchoredSpring(anchor:Point3D, springCoeff:Number, restLength:Number) {
+ this.anchor = anchor;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - anchor.x;
+ force.y = particle.position.y - anchor.y;
+ force.z = particle.position.z - anchor.z;
+ var len:Number = force.length;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleBungee.as b/0.0.0.18/src/alternativa/physics/force/ParticleBungee.as
new file mode 100644
index 0000000..41cf3c0
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleBungee.as
@@ -0,0 +1,38 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleBungee implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var otherParticle:Particle;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public var relaxed:Boolean;
+
+ public function ParticleBungee(otherParticle:Particle, springCoeff:Number, restLength:Number) {
+ this.otherParticle = otherParticle;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - otherParticle.position.x;
+ force.y = particle.position.y - otherParticle.position.y;
+ force.z = particle.position.z - otherParticle.position.z;
+ var len:Number = force.length;
+ if (len < restLength) {
+ relaxed = true;
+ return;
+ }
+ relaxed = false;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleBuoyancy.as b/0.0.0.18/src/alternativa/physics/force/ParticleBuoyancy.as
new file mode 100644
index 0000000..45c0de3
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleBuoyancy.as
@@ -0,0 +1,37 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleBuoyancy implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var maxDepth:Number;
+ private var waterHeight:Number;
+ private var volume:Number;
+ private var liquidDensity:Number;
+
+ public function ParticleBuoyancy(maxDepth:Number, waterHeight:Number, volume:Number, liquidDensity:Number = 1000) {
+ this.maxDepth = maxDepth;
+ this.waterHeight = waterHeight;
+ this.volume = volume;
+ this.liquidDensity = liquidDensity;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ var depth:Number = particle.position.y;
+ if (depth > waterHeight + maxDepth) {
+ return;
+ }
+
+ force.x = 0;
+ force.y = 0;
+ if (depth <= waterHeight - maxDepth) {
+ force.z = liquidDensity*volume;
+ } else {
+ force.z = liquidDensity*volume*(waterHeight + maxDepth - depth)/maxDepth*0.5;
+ }
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleDrag.as b/0.0.0.18/src/alternativa/physics/force/ParticleDrag.as
new file mode 100644
index 0000000..e9de457
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleDrag.as
@@ -0,0 +1,29 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleDrag implements IParticelForceGenerator {
+ private var force:Point3D = new Point3D();
+
+ private var k1:Number;
+ private var k2:Number;
+
+ public function ParticleDrag(k1:Number, k2:Number) {
+ this.k1 = k1;
+ this.k2 = k2;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.velocity.x;
+ force.y = particle.velocity.y;
+ force.z = particle.velocity.z;
+ var len:Number = force.length;
+ var coeff:Number = len;
+ coeff = -(k1*coeff + k2*coeff*coeff)/len;
+ force.x *= coeff;
+ force.y *= coeff;
+ force.z *= coeff;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleGravity.as b/0.0.0.18/src/alternativa/physics/force/ParticleGravity.as
new file mode 100644
index 0000000..ff400d2
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleGravity.as
@@ -0,0 +1,24 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleGravity implements IParticelForceGenerator {
+ private var force:Point3D = new Point3D();
+
+ private var _gravity:Point3D;
+
+ public function ParticleGravity(gravity:Point3D) {
+ _gravity = gravity.clone();
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ if (!particle.hasFiniteMass()) {
+ return;
+ }
+ force.x = _gravity.x*particle.mass;
+ force.y = _gravity.y*particle.mass;
+ force.z = _gravity.z*particle.mass;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/ParticleSpring.as b/0.0.0.18/src/alternativa/physics/force/ParticleSpring.as
new file mode 100644
index 0000000..aee5732
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/ParticleSpring.as
@@ -0,0 +1,31 @@
+package alternativa.physics.force {
+ import alternativa.physics.particle.Particle;
+ import alternativa.types.Point3D;
+
+ public class ParticleSpring implements IParticelForceGenerator {
+
+ private var force:Point3D = new Point3D();
+
+ private var otherParticle:Particle;
+ private var springCoeff:Number;
+ private var restLength:Number;
+
+ public function ParticleSpring(otherParticle:Particle, springCoeff:Number, restLength:Number) {
+ this.otherParticle = otherParticle;
+ this.springCoeff = springCoeff;
+ this.restLength = restLength;
+ }
+
+ public function updateForce(particle:Particle, time:Number):void {
+ force.x = particle.position.x - otherParticle.position.x;
+ force.y = particle.position.y - otherParticle.position.y;
+ force.z = particle.position.z - otherParticle.position.z;
+ var len:Number = force.length;
+ var k:Number = (restLength - len)*springCoeff/len;
+ force.x *= k;
+ force.y *= k;
+ force.z *= k;
+ particle.addForce(force);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/RigidBodyAnchoredBungee.as b/0.0.0.18/src/alternativa/physics/force/RigidBodyAnchoredBungee.as
new file mode 100644
index 0000000..85921c4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/RigidBodyAnchoredBungee.as
@@ -0,0 +1,45 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class RigidBodyAnchoredBungee implements IRigidBodyForceGenerator {
+
+ private var vector1:Point3D = new Point3D();
+ private var vector2:Point3D = new Point3D();
+
+ private var _connectionPoint:Point3D = new Point3D();
+ private var _anchorPoint:Point3D = new Point3D();
+ private var _springConstant:Number;
+ private var _restLength:Number;
+
+ public function RigidBodyAnchoredBungee(connectionPoint:Point3D, anchorPoint:Point3D, springConstant:Number, restLength:Number) {
+ _connectionPoint.copy(connectionPoint);
+ _anchorPoint.copy(anchorPoint);
+ _springConstant = springConstant;
+ _restLength = restLength;
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ // TODO: Оптимизировать!!!
+ vector1.copy(_connectionPoint);
+ vector1.transform(body.transformMatrix);
+ vector2.copy(_anchorPoint);
+ vector2.subtract(vector1);
+ var len:Number = vector2.length;
+ var magnitude:Number = len - _restLength;
+ if (magnitude > 0) {
+ magnitude *= _springConstant;
+ vector2.multiply(magnitude/len);
+ body.addForceAtPoint(vector2, vector1);
+ }
+ }
+
+ public function set anchorPoint(value:Point3D):void {
+ _anchorPoint.copy(value);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/RigidBodyGravity.as b/0.0.0.18/src/alternativa/physics/force/RigidBodyGravity.as
new file mode 100644
index 0000000..e99aed1
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/RigidBodyGravity.as
@@ -0,0 +1,27 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBodyGravity implements IRigidBodyForceGenerator {
+
+ private var _gravity:Point3D = new Point3D();
+
+ public function RigidBodyGravity(g:Point3D) {
+ _gravity.copy(g);
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ if (body.inverseMass == 0) {
+ return;
+ }
+ body.addForceComponents(_gravity.x*body.mass, _gravity.y*body.mass, _gravity.z*body.mass);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/force/RigidBodySpring.as b/0.0.0.18/src/alternativa/physics/force/RigidBodySpring.as
new file mode 100644
index 0000000..1a5d44a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/force/RigidBodySpring.as
@@ -0,0 +1,45 @@
+package alternativa.physics.force {
+ import alternativa.physics.*;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class RigidBodySpring implements IRigidBodyForceGenerator {
+
+ private var vector1:Point3D = new Point3D();
+ private var vector2:Point3D = new Point3D();
+
+ private var _connectionPoint:Point3D = new Point3D();
+ private var _otherConnectionPoint:Point3D = new Point3D();
+ private var _springConstant:Number;
+ private var _restLength:Number;
+ private var _otherBody:RigidBody;
+
+ public function RigidBodySpring(connectionPoint:Point3D, otherBody:RigidBody, otherConnectionPoint:Point3D, springConstant:Number, restLength:Number) {
+ _connectionPoint.copy(connectionPoint);
+ _otherConnectionPoint = otherConnectionPoint;
+ _otherBody = otherBody;
+ _springConstant = springConstant;
+ _restLength = restLength;
+ }
+
+ public function updateForce(body:RigidBody, time:Number):void {
+ // TODO: Оптимизировать!!!
+ vector1.copy(_connectionPoint);
+ vector1.transform(body.transformMatrix);
+ vector2.copy(_otherConnectionPoint);
+ vector2.transform(_otherBody.transformMatrix);
+ vector2.subtract(vector1);
+ var len:Number = vector2.length;
+ if (len == 0) {
+ trace("[RigidBodySpring::updateForce] spring length is zero");
+ }
+ var magnitude:Number = len - _restLength;
+ magnitude *= _springConstant;
+ vector2.multiply(magnitude/len);
+ body.addForceAtPoint(vector2, vector1);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/particle/.svn/all-wcprops
new file mode 100644
index 0000000..c72fffa
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/all-wcprops
@@ -0,0 +1,53 @@
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/4378/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle
+END
+ParticleContact.as
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContact.as
+END
+ParticleContactResolver.as
+K 25
+svn:wc:ra_dav:version-url
+V 130
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContactResolver.as
+END
+ParticleLink.as
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleLink.as
+END
+Particle.as
+K 25
+svn:wc:ra_dav:version-url
+V 115
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/Particle.as
+END
+ParticleContactGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContactGenerator.as
+END
+ParticleRod.as
+K 25
+svn:wc:ra_dav:version-url
+V 118
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleRod.as
+END
+ParticleCable.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleCable.as
+END
+ParticleWorld.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4378/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleWorld.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/entries b/0.0.0.18/src/alternativa/physics/particle/.svn/entries
new file mode 100644
index 0000000..cc2b166
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/entries
@@ -0,0 +1,124 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle
+http://svndev.alternativaplatform.com
+
+
+
+2008-11-26T12:12:57.063201Z
+4378
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+ParticleContact.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+af51b49cb3918bc31be32dca4a9ddff2
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleContactResolver.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+09a37756afe614b640d36b5e474ae709
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleLink.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+6313230f57ccdba2b80c3ab4306d9d02
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+Particle.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+36a3a8de3fce54e29395a591388b0fd9
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleContactGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+1d585c4b2678e36ed0d44403ed35bbac
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleRod.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+a95a229bcce5f16576e8aa3bbee4d4b9
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleCable.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+b289fc57f98c5170eb964493250a309d
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleWorld.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+29ebff641b6d9cc9def3022dd679889f
+2008-11-26T12:12:57.063201Z
+4378
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/format b/0.0.0.18/src/alternativa/physics/particle/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/Particle.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/Particle.as.svn-base
new file mode 100644
index 0000000..3ac9a25
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/Particle.as.svn-base
@@ -0,0 +1,78 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class Particle {
+
+ private static var resultingAcceleration:Point3D = new Point3D();
+
+ public var position:Point3D = new Point3D();
+ public var velocity:Point3D = new Point3D();
+ public var acceleration:Point3D = new Point3D();
+ public var accumulatedForce:Point3D = new Point3D();
+ public var mass:Number;
+ public var inverseMass:Number;
+ public var damping:Number = 0.995;
+
+ public var next:Particle;
+
+ /**
+ * @param mass масса частицы. Значение меньшее или равное нулю означает бесконечную массу.
+ */
+ public function Particle(mass:Number = 0) {
+ if (mass <= 0) {
+ this.mass = 0;
+ inverseMass = 0;
+ } else {
+ this.mass = mass;
+ inverseMass = 1/mass;
+ }
+ }
+
+ /**
+ *
+ * @param time время в секундах
+ */
+ public function integrate(time:Number):void {
+ if (time <= 0) {
+ return;
+ }
+
+ position.x += velocity.x*time;
+ position.y += velocity.y*time;
+ position.z += velocity.z*time;
+
+ resultingAcceleration.x = acceleration.x + inverseMass*accumulatedForce.x;
+ resultingAcceleration.y = acceleration.y + inverseMass*accumulatedForce.y;
+ resultingAcceleration.z = acceleration.z + inverseMass*accumulatedForce.z;
+
+ velocity.x += resultingAcceleration.x*time;
+ velocity.y += resultingAcceleration.y*time;
+ velocity.z += resultingAcceleration.z*time;
+
+ var d:Number = Math.pow(damping, time);
+ velocity.x *= d;
+ velocity.y *= d;
+ velocity.z *= d;
+
+ accumulatedForce.x = 0;
+ accumulatedForce.y = 0;
+ accumulatedForce.z = 0;
+ }
+
+ public function clearForce():void {
+ accumulatedForce.x = 0;
+ accumulatedForce.y = 0;
+ accumulatedForce.z = 0;
+ }
+
+ public function addForce(force:Point3D):void {
+ accumulatedForce.x += force.x;
+ accumulatedForce.y += force.y;
+ accumulatedForce.z += force.z;
+ }
+
+ public function hasFiniteMass():Boolean {
+ return inverseMass > 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleCable.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleCable.as.svn-base
new file mode 100644
index 0000000..61d494b
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleCable.as.svn-base
@@ -0,0 +1,46 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleCable extends ParticleLink {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var maxLength:Number;
+ public var restitution:Number;
+
+ public function ParticleCable(particle1:Particle, particle2:Particle, maxLength:Number, restitution:Number) {
+ super(particle1, particle2);
+ this.maxLength = maxLength;
+ this.restitution = restitution;
+ }
+
+ override protected function getCurrentLength():Number {
+ var x:Number = particle1.position.x - particle2.position.x;
+ var y:Number = particle1.position.y - particle2.position.y;
+ var z:Number = particle1.position.z - particle2.position.z;
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ override public function fillContact(contact:ParticleContact):Boolean {
+ // Для кабеля нормаль контакта направлена в сторону второй частицы
+ vector.x = particle2.position.x - particle1.position.x;
+ vector.y = particle2.position.y - particle1.position.y;
+ vector.z = particle2.position.z - particle1.position.z;
+ var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
+
+ if (len < maxLength) {
+ return false;
+ }
+
+ contact.particle1 = particle1;
+ contact.particle2 = particle2;
+ contact.contactNormal.x = vector.x;
+ contact.contactNormal.y = vector.y;
+ contact.contactNormal.z = vector.z;
+ contact.penetration = len - maxLength;
+ contact.restitution = restitution;
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContact.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContact.as.svn-base
new file mode 100644
index 0000000..3701fba
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContact.as.svn-base
@@ -0,0 +1,175 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleContact {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var particle1:Particle;
+ public var particle2:Particle;
+ public var restitution:Number;
+ public var penetration:Number;
+ public var contactNormal:Point3D = new Point3D();
+ public var isCollision:Boolean;
+ public var frictionCoeff:Number = 0;
+
+ public var next:ParticleContact;
+ public var index:int;
+
+ /**
+ *
+ * @param particle1
+ * @param particle2
+ * @param restitution
+ * @param contactNormal
+ */
+ public function ParticleContact(index:int, particle1:Particle = null, particle2:Particle = null, contactNormal:Point3D = null, penetration:Number = 0, restitution:Number = 0) {
+ this.index = index;
+ this.particle1 = particle1;
+ this.particle2 = particle2;
+ if (contactNormal != null) {
+ this.contactNormal.x = contactNormal.x;
+ this.contactNormal.y = contactNormal.y;
+ this.contactNormal.z = contactNormal.z;
+ }
+ this.penetration = penetration;
+ this.restitution = restitution;
+ }
+
+ /**
+ *
+ * @param time
+ */
+ public function resolve(time:Number):void {
+ resolveVelocity(time);
+ resolveInterpenetration(time);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function calculateSeparatingVelocity():Number {
+ vector.x = particle1.velocity.x;
+ vector.y = particle1.velocity.y;
+ vector.z = particle1.velocity.z;
+ if (particle2 != null) {
+ vector.x -= particle2.velocity.x;
+ vector.y -= particle2.velocity.y;
+ vector.z -= particle2.velocity.z;
+ }
+ return vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z;
+ }
+
+ /**
+ *
+ * @param time
+ */
+ private function resolveVelocity(time:Number):void {
+ var separatingVelocity:Number = calculateSeparatingVelocity();
+ if (separatingVelocity > 0) {
+ return;
+ }
+ var newSeparatingVelocity:Number = -restitution*separatingVelocity;
+
+ var totalInverseMass:Number = particle1.inverseMass;
+ if (particle2 != null) {
+ totalInverseMass += particle2.inverseMass;
+ }
+ if (totalInverseMass <= 0) {
+ // Пропускаем, т.к. обе частицы бесконечной массы
+ return;
+ }
+
+ // Обработка покоящихся контактов
+ // Проверяем, не была ли относительная скорость приобретена только в течении одного кадра
+ if (particle2 != null) {
+ vector.x = particle1.acceleration.x - particle2.acceleration.x;
+ vector.y = particle1.acceleration.y - particle2.acceleration.y;
+ vector.z = particle1.acceleration.z - particle2.acceleration.z;
+ } else {
+ vector.x = particle1.acceleration.x;
+ vector.y = particle1.acceleration.y;
+ vector.z = particle1.acceleration.z;
+ }
+ var accCausedSepVelocity:Number = (vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z)*time;
+
+ if (accCausedSepVelocity < 0) {
+ // Вычтем скорость сближения, полученную под действием сил во время последнего кадра, из скорости разделения контакта
+ newSeparatingVelocity += restitution*accCausedSepVelocity;
+ if (newSeparatingVelocity < 0) {
+ // Скорость разделения не может быть отрицательной
+ newSeparatingVelocity = 0;
+ }
+ }
+
+ var impulse:Number = (newSeparatingVelocity - separatingVelocity)/totalInverseMass;
+ vector.x = impulse*contactNormal.x;
+ vector.y = impulse*contactNormal.y;
+ vector.z = impulse*contactNormal.z;
+
+ particle1.velocity.x += vector.x*particle1.inverseMass;
+ particle1.velocity.y += vector.y*particle1.inverseMass;
+ particle1.velocity.z += vector.z*particle1.inverseMass;
+
+ if (particle2 != null) {
+ particle2.velocity.x -= vector.x*particle2.inverseMass;
+ particle2.velocity.y -= vector.y*particle2.inverseMass;
+ particle2.velocity.z -= vector.z*particle2.inverseMass;
+ }
+
+// if (isCollision) {
+// var tangentSpeed:Number = particle1.velocity.length;
+// var deltaSpeed:Number = frictionCoeff*particle1.inverseMass*impulse;
+// if (tangentSpeed < deltaSpeed) {
+// particle1.velocity.x = 0;
+// particle1.velocity.y = 0;
+// particle1.velocity.z = 0;
+// } else {
+// var k:Number = (tangentSpeed - deltaSpeed)/tangentSpeed;
+// particle1.velocity.x *= k;
+// particle1.velocity.y *= k;
+// particle1.velocity.z *= k;
+// }
+// }
+ }
+
+ /**
+ *
+ * @param time
+ */
+ private function resolveInterpenetration(time:Number):void {
+ if (penetration <= 0) {
+ return;
+ }
+
+ var totalInverseMass:Number = particle1.inverseMass;
+ if (particle2 != null) {
+ totalInverseMass += particle2.inverseMass;
+ }
+ if (totalInverseMass <= 0) {
+ // Пропускаем, т.к. обе частицы бесконечной массы
+ return;
+ }
+
+ var k:Number = penetration/totalInverseMass;
+
+ vector.x = k*contactNormal.x;
+ vector.y = k*contactNormal.y;
+ vector.z = k*contactNormal.z;
+
+// trace("\nparticle1.position", particle1.position.z, particle1.velocity.z);
+ particle1.position.x += vector.x*particle1.inverseMass;
+ particle1.position.y += vector.y*particle1.inverseMass;
+ particle1.position.z += vector.z*particle1.inverseMass;
+// trace("particle1.position", particle1.position.z, particle1.velocity.z);
+
+ if (particle2 != null) {
+ particle2.position.x -= vector.x*particle2.inverseMass;
+ particle2.position.y -= vector.y*particle2.inverseMass;
+ particle2.position.z -= vector.z*particle2.inverseMass;
+ }
+ penetration = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactGenerator.as.svn-base
new file mode 100644
index 0000000..65ec7b8
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactGenerator.as.svn-base
@@ -0,0 +1,26 @@
+package alternativa.physics.particle {
+
+ /**
+ * Базовй класс для генераторов контактов. Реализует поддержку однонаправленного списока генераторов.
+ */
+ public class ParticleContactGenerator {
+
+ /**
+ * Следующий генератор в списке.
+ */
+ public var next:ParticleContactGenerator;
+
+ /**
+ * Метод создаёт информацию о контактах.
+ *
+ * @param contact первый свободный для записи контакт в списке контактов мира
+ *
+ * @return следующий свободный для записи контакт после того, как генератор записал свою информацию. Если возвращено значение null,
+ * то значит список доступных контактов исчерпан.
+ */
+ public function addContacts(contact:ParticleContact):ParticleContact {
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactResolver.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactResolver.as.svn-base
new file mode 100644
index 0000000..b65a7f5
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleContactResolver.as.svn-base
@@ -0,0 +1,87 @@
+package alternativa.physics.particle {
+ public class ParticleContactResolver {
+
+ private var iterations:int;
+ private var iterationsUsed:int;
+
+ public function ParticleContactResolver(iterations:int) {
+ this.iterations = iterations;
+ }
+
+ public function setIterations(value:int):void {
+ iterations = value;
+ }
+
+ public function resolveContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ resolveAllContacts(contacts, contactsUsed, time);
+// resolveCollisionContacts(contacts, contactsUsed, time);
+// resolveNonCollisionContacts(contacts, contactsUsed, time);
+ }
+
+ private function resolveAllContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ iterationsUsed = 0;
+ iterations = 5;
+ while (iterationsUsed < iterations) {
+ var maxSepVelocity:Number = 0;
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ var sepVelocity:Number = contact.calculateSeparatingVelocity();
+ if (sepVelocity < maxSepVelocity) {
+ maxSepVelocity = sepVelocity;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ mostSevereContact.resolve(time);
+ }
+ iterationsUsed++;
+ }
+ }
+
+ private function resolveNonCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ iterationsUsed = 0;
+ while (iterationsUsed < iterations) {
+ var maxSepVelocity:Number = 0;
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ if (!contact.isCollision) {
+ var sepVelocity:Number = contact.calculateSeparatingVelocity();
+ if (sepVelocity < maxSepVelocity) {
+ maxSepVelocity = sepVelocity;
+ mostSevereContact = contact;
+ }
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ mostSevereContact.resolve(time);
+ }
+ iterationsUsed++;
+ }
+ }
+
+ private function resolveCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ if (contact.isCollision && contact.penetration > maxPenetration) {
+ maxPenetration = contact.penetration;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ if (mostSevereContact.calculateSeparatingVelocity() < 0) {
+ mostSevereContact.resolve(time);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleLink.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleLink.as.svn-base
new file mode 100644
index 0000000..d4b999a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleLink.as.svn-base
@@ -0,0 +1,21 @@
+package alternativa.physics.particle {
+
+ public class ParticleLink {
+
+ public var particle1:Particle;
+ public var particle2:Particle;
+
+ public function ParticleLink(particle1:Particle, particle2:Particle) {
+ this.particle1 = particle1;
+ this.particle2 = particle2;
+ }
+
+ protected function getCurrentLength():Number {
+ return 0;
+ }
+
+ public function fillContact(contact:ParticleContact):Boolean {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleRod.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleRod.as.svn-base
new file mode 100644
index 0000000..e24fa5e
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleRod.as.svn-base
@@ -0,0 +1,58 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleRod extends ParticleLink {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var length:Number;
+
+ public function ParticleRod(particle1:Particle, particle2:Particle, length:Number = 0) {
+ super(particle1, particle2);
+ if (length <= 0) {
+ this.length = Point3D.difference(particle1.position, particle2.position).length;
+ } else {
+ this.length = length;
+ }
+ }
+
+ override protected function getCurrentLength():Number {
+ var x:Number = particle1.position.x - particle2.position.x;
+ var y:Number = particle1.position.y - particle2.position.y;
+ var z:Number = particle1.position.z - particle2.position.z;
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ override public function fillContact(contact:ParticleContact):Boolean {
+ vector.x = particle1.position.x - particle2.position.x;
+ vector.y = particle1.position.y - particle2.position.y;
+ vector.z = particle1.position.z - particle2.position.z;
+ var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
+
+ if (len == length) {
+ return false;
+ }
+
+ var penetration:Number;
+ if (len > length) {
+ vector.x = -vector.x;
+ vector.y = -vector.y;
+ vector.z = -vector.z;
+ penetration = len - length;
+ } else {
+ penetration = length - len;
+ }
+
+ contact.particle1 = particle1;
+ contact.particle2 = particle2;
+ contact.contactNormal.x = vector.x/len;
+ contact.contactNormal.y = vector.y/len;
+ contact.contactNormal.z = vector.z/len;
+ contact.penetration = penetration;
+ contact.restitution = 0;
+ contact.isCollision = false;
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleWorld.as.svn-base b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleWorld.as.svn-base
new file mode 100644
index 0000000..ebbf5a4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/.svn/text-base/ParticleWorld.as.svn-base
@@ -0,0 +1,113 @@
+package alternativa.physics.particle {
+ import alternativa.physics.particle.Particle;
+ import alternativa.physics.particle.ParticleContact;
+ import alternativa.physics.particle.ParticleContactGenerator;
+ import alternativa.physics.particle.ParticleContactResolver;
+ import alternativa.physics.registry.ParticleForceRegistry;
+
+ public class ParticleWorld {
+
+ public var particles:Particle;
+ public var last:Particle;
+
+ public var contacts:ParticleContact;
+ public var contactGenerators:ParticleContactGenerator;
+ public var forceRegistry:ParticleForceRegistry;
+
+ public var resolver:ParticleContactResolver;
+
+ public var maxContacts:int;
+ public var iterations:int;
+
+ public function ParticleWorld(maxContacts:int, iterations:int = 0) {
+ this.maxContacts = maxContacts;
+ this.iterations = iterations;
+
+ // Создаём контакты
+ contacts = new ParticleContact(0);
+ var contact:ParticleContact = contacts;
+ for (var i:int = 1; i < maxContacts; i++) {
+ contact = contact.next = new ParticleContact(i);
+ }
+
+ resolver = new ParticleContactResolver(0);
+ forceRegistry = new ParticleForceRegistry();
+ }
+
+ public function addParticle(particle:Particle):void {
+ if (particles == null) {
+ last = particles = particle;
+ } else {
+ last.next = particle;
+ last = particle;
+ }
+ }
+
+ public function addContactGenerator(generator:ParticleContactGenerator):void {
+ if (contactGenerators == null) {
+ contactGenerators = generator;
+ } else {
+ var gen:ParticleContactGenerator = contactGenerators;
+ while (gen.next != null) {
+ gen = gen.next;
+ }
+ gen.next = generator;
+ }
+ }
+
+ public function startFrame():void {
+ var particle:Particle = particles;
+ while (particle != null) {
+ particle.clearForce();
+ particle = particle.next;
+ }
+ }
+
+ /**
+ * Последоавтельно запускает все зарегистрированные генераторы контактов.
+ *
+ * @return количество использованных контактов
+ */
+ public function generateContacts():int {
+ var contact:ParticleContact = contacts;
+ var generator:ParticleContactGenerator = contactGenerators;
+ while (generator != null) {
+ contact = generator.addContacts(contact);
+ if (contact == null) {
+ // Доступные контакты исчерпаны
+ return maxContacts;
+ }
+ generator = generator.next;
+ }
+ return contact.index;
+ }
+
+ public function intergrate(time:Number):void {
+ var particle:Particle = particles;
+ while (particle != null) {
+ particle.integrate(time);
+ particle = particle.next;
+ }
+ }
+
+ /**
+ * Запускает физическую симуляцию.
+ *
+ * @param time
+ */
+ public function runPhysics(time:Number):void {
+ // Применение генераторов сил
+ forceRegistry.updateForces(time);
+ // Запуск интеграторов для всех частиц
+ intergrate(time);
+ // Создание контактов
+ var contactsUsed:int = generateContacts();
+ // Обработка полученных контактов
+ if (iterations == 0) {
+ resolver.setIterations(3*contactsUsed);
+ }
+ resolver.resolveContacts(contacts, contactsUsed, time);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/Particle.as b/0.0.0.18/src/alternativa/physics/particle/Particle.as
new file mode 100644
index 0000000..3ac9a25
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/Particle.as
@@ -0,0 +1,78 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class Particle {
+
+ private static var resultingAcceleration:Point3D = new Point3D();
+
+ public var position:Point3D = new Point3D();
+ public var velocity:Point3D = new Point3D();
+ public var acceleration:Point3D = new Point3D();
+ public var accumulatedForce:Point3D = new Point3D();
+ public var mass:Number;
+ public var inverseMass:Number;
+ public var damping:Number = 0.995;
+
+ public var next:Particle;
+
+ /**
+ * @param mass масса частицы. Значение меньшее или равное нулю означает бесконечную массу.
+ */
+ public function Particle(mass:Number = 0) {
+ if (mass <= 0) {
+ this.mass = 0;
+ inverseMass = 0;
+ } else {
+ this.mass = mass;
+ inverseMass = 1/mass;
+ }
+ }
+
+ /**
+ *
+ * @param time время в секундах
+ */
+ public function integrate(time:Number):void {
+ if (time <= 0) {
+ return;
+ }
+
+ position.x += velocity.x*time;
+ position.y += velocity.y*time;
+ position.z += velocity.z*time;
+
+ resultingAcceleration.x = acceleration.x + inverseMass*accumulatedForce.x;
+ resultingAcceleration.y = acceleration.y + inverseMass*accumulatedForce.y;
+ resultingAcceleration.z = acceleration.z + inverseMass*accumulatedForce.z;
+
+ velocity.x += resultingAcceleration.x*time;
+ velocity.y += resultingAcceleration.y*time;
+ velocity.z += resultingAcceleration.z*time;
+
+ var d:Number = Math.pow(damping, time);
+ velocity.x *= d;
+ velocity.y *= d;
+ velocity.z *= d;
+
+ accumulatedForce.x = 0;
+ accumulatedForce.y = 0;
+ accumulatedForce.z = 0;
+ }
+
+ public function clearForce():void {
+ accumulatedForce.x = 0;
+ accumulatedForce.y = 0;
+ accumulatedForce.z = 0;
+ }
+
+ public function addForce(force:Point3D):void {
+ accumulatedForce.x += force.x;
+ accumulatedForce.y += force.y;
+ accumulatedForce.z += force.z;
+ }
+
+ public function hasFiniteMass():Boolean {
+ return inverseMass > 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleCable.as b/0.0.0.18/src/alternativa/physics/particle/ParticleCable.as
new file mode 100644
index 0000000..61d494b
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleCable.as
@@ -0,0 +1,46 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleCable extends ParticleLink {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var maxLength:Number;
+ public var restitution:Number;
+
+ public function ParticleCable(particle1:Particle, particle2:Particle, maxLength:Number, restitution:Number) {
+ super(particle1, particle2);
+ this.maxLength = maxLength;
+ this.restitution = restitution;
+ }
+
+ override protected function getCurrentLength():Number {
+ var x:Number = particle1.position.x - particle2.position.x;
+ var y:Number = particle1.position.y - particle2.position.y;
+ var z:Number = particle1.position.z - particle2.position.z;
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ override public function fillContact(contact:ParticleContact):Boolean {
+ // Для кабеля нормаль контакта направлена в сторону второй частицы
+ vector.x = particle2.position.x - particle1.position.x;
+ vector.y = particle2.position.y - particle1.position.y;
+ vector.z = particle2.position.z - particle1.position.z;
+ var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
+
+ if (len < maxLength) {
+ return false;
+ }
+
+ contact.particle1 = particle1;
+ contact.particle2 = particle2;
+ contact.contactNormal.x = vector.x;
+ contact.contactNormal.y = vector.y;
+ contact.contactNormal.z = vector.z;
+ contact.penetration = len - maxLength;
+ contact.restitution = restitution;
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleContact.as b/0.0.0.18/src/alternativa/physics/particle/ParticleContact.as
new file mode 100644
index 0000000..3701fba
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleContact.as
@@ -0,0 +1,175 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleContact {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var particle1:Particle;
+ public var particle2:Particle;
+ public var restitution:Number;
+ public var penetration:Number;
+ public var contactNormal:Point3D = new Point3D();
+ public var isCollision:Boolean;
+ public var frictionCoeff:Number = 0;
+
+ public var next:ParticleContact;
+ public var index:int;
+
+ /**
+ *
+ * @param particle1
+ * @param particle2
+ * @param restitution
+ * @param contactNormal
+ */
+ public function ParticleContact(index:int, particle1:Particle = null, particle2:Particle = null, contactNormal:Point3D = null, penetration:Number = 0, restitution:Number = 0) {
+ this.index = index;
+ this.particle1 = particle1;
+ this.particle2 = particle2;
+ if (contactNormal != null) {
+ this.contactNormal.x = contactNormal.x;
+ this.contactNormal.y = contactNormal.y;
+ this.contactNormal.z = contactNormal.z;
+ }
+ this.penetration = penetration;
+ this.restitution = restitution;
+ }
+
+ /**
+ *
+ * @param time
+ */
+ public function resolve(time:Number):void {
+ resolveVelocity(time);
+ resolveInterpenetration(time);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function calculateSeparatingVelocity():Number {
+ vector.x = particle1.velocity.x;
+ vector.y = particle1.velocity.y;
+ vector.z = particle1.velocity.z;
+ if (particle2 != null) {
+ vector.x -= particle2.velocity.x;
+ vector.y -= particle2.velocity.y;
+ vector.z -= particle2.velocity.z;
+ }
+ return vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z;
+ }
+
+ /**
+ *
+ * @param time
+ */
+ private function resolveVelocity(time:Number):void {
+ var separatingVelocity:Number = calculateSeparatingVelocity();
+ if (separatingVelocity > 0) {
+ return;
+ }
+ var newSeparatingVelocity:Number = -restitution*separatingVelocity;
+
+ var totalInverseMass:Number = particle1.inverseMass;
+ if (particle2 != null) {
+ totalInverseMass += particle2.inverseMass;
+ }
+ if (totalInverseMass <= 0) {
+ // Пропускаем, т.к. обе частицы бесконечной массы
+ return;
+ }
+
+ // Обработка покоящихся контактов
+ // Проверяем, не была ли относительная скорость приобретена только в течении одного кадра
+ if (particle2 != null) {
+ vector.x = particle1.acceleration.x - particle2.acceleration.x;
+ vector.y = particle1.acceleration.y - particle2.acceleration.y;
+ vector.z = particle1.acceleration.z - particle2.acceleration.z;
+ } else {
+ vector.x = particle1.acceleration.x;
+ vector.y = particle1.acceleration.y;
+ vector.z = particle1.acceleration.z;
+ }
+ var accCausedSepVelocity:Number = (vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z)*time;
+
+ if (accCausedSepVelocity < 0) {
+ // Вычтем скорость сближения, полученную под действием сил во время последнего кадра, из скорости разделения контакта
+ newSeparatingVelocity += restitution*accCausedSepVelocity;
+ if (newSeparatingVelocity < 0) {
+ // Скорость разделения не может быть отрицательной
+ newSeparatingVelocity = 0;
+ }
+ }
+
+ var impulse:Number = (newSeparatingVelocity - separatingVelocity)/totalInverseMass;
+ vector.x = impulse*contactNormal.x;
+ vector.y = impulse*contactNormal.y;
+ vector.z = impulse*contactNormal.z;
+
+ particle1.velocity.x += vector.x*particle1.inverseMass;
+ particle1.velocity.y += vector.y*particle1.inverseMass;
+ particle1.velocity.z += vector.z*particle1.inverseMass;
+
+ if (particle2 != null) {
+ particle2.velocity.x -= vector.x*particle2.inverseMass;
+ particle2.velocity.y -= vector.y*particle2.inverseMass;
+ particle2.velocity.z -= vector.z*particle2.inverseMass;
+ }
+
+// if (isCollision) {
+// var tangentSpeed:Number = particle1.velocity.length;
+// var deltaSpeed:Number = frictionCoeff*particle1.inverseMass*impulse;
+// if (tangentSpeed < deltaSpeed) {
+// particle1.velocity.x = 0;
+// particle1.velocity.y = 0;
+// particle1.velocity.z = 0;
+// } else {
+// var k:Number = (tangentSpeed - deltaSpeed)/tangentSpeed;
+// particle1.velocity.x *= k;
+// particle1.velocity.y *= k;
+// particle1.velocity.z *= k;
+// }
+// }
+ }
+
+ /**
+ *
+ * @param time
+ */
+ private function resolveInterpenetration(time:Number):void {
+ if (penetration <= 0) {
+ return;
+ }
+
+ var totalInverseMass:Number = particle1.inverseMass;
+ if (particle2 != null) {
+ totalInverseMass += particle2.inverseMass;
+ }
+ if (totalInverseMass <= 0) {
+ // Пропускаем, т.к. обе частицы бесконечной массы
+ return;
+ }
+
+ var k:Number = penetration/totalInverseMass;
+
+ vector.x = k*contactNormal.x;
+ vector.y = k*contactNormal.y;
+ vector.z = k*contactNormal.z;
+
+// trace("\nparticle1.position", particle1.position.z, particle1.velocity.z);
+ particle1.position.x += vector.x*particle1.inverseMass;
+ particle1.position.y += vector.y*particle1.inverseMass;
+ particle1.position.z += vector.z*particle1.inverseMass;
+// trace("particle1.position", particle1.position.z, particle1.velocity.z);
+
+ if (particle2 != null) {
+ particle2.position.x -= vector.x*particle2.inverseMass;
+ particle2.position.y -= vector.y*particle2.inverseMass;
+ particle2.position.z -= vector.z*particle2.inverseMass;
+ }
+ penetration = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleContactGenerator.as b/0.0.0.18/src/alternativa/physics/particle/ParticleContactGenerator.as
new file mode 100644
index 0000000..65ec7b8
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleContactGenerator.as
@@ -0,0 +1,26 @@
+package alternativa.physics.particle {
+
+ /**
+ * Базовй класс для генераторов контактов. Реализует поддержку однонаправленного списока генераторов.
+ */
+ public class ParticleContactGenerator {
+
+ /**
+ * Следующий генератор в списке.
+ */
+ public var next:ParticleContactGenerator;
+
+ /**
+ * Метод создаёт информацию о контактах.
+ *
+ * @param contact первый свободный для записи контакт в списке контактов мира
+ *
+ * @return следующий свободный для записи контакт после того, как генератор записал свою информацию. Если возвращено значение null,
+ * то значит список доступных контактов исчерпан.
+ */
+ public function addContacts(contact:ParticleContact):ParticleContact {
+ return null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleContactResolver.as b/0.0.0.18/src/alternativa/physics/particle/ParticleContactResolver.as
new file mode 100644
index 0000000..b65a7f5
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleContactResolver.as
@@ -0,0 +1,87 @@
+package alternativa.physics.particle {
+ public class ParticleContactResolver {
+
+ private var iterations:int;
+ private var iterationsUsed:int;
+
+ public function ParticleContactResolver(iterations:int) {
+ this.iterations = iterations;
+ }
+
+ public function setIterations(value:int):void {
+ iterations = value;
+ }
+
+ public function resolveContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ resolveAllContacts(contacts, contactsUsed, time);
+// resolveCollisionContacts(contacts, contactsUsed, time);
+// resolveNonCollisionContacts(contacts, contactsUsed, time);
+ }
+
+ private function resolveAllContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ iterationsUsed = 0;
+ iterations = 5;
+ while (iterationsUsed < iterations) {
+ var maxSepVelocity:Number = 0;
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ var sepVelocity:Number = contact.calculateSeparatingVelocity();
+ if (sepVelocity < maxSepVelocity) {
+ maxSepVelocity = sepVelocity;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ mostSevereContact.resolve(time);
+ }
+ iterationsUsed++;
+ }
+ }
+
+ private function resolveNonCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ iterationsUsed = 0;
+ while (iterationsUsed < iterations) {
+ var maxSepVelocity:Number = 0;
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ if (!contact.isCollision) {
+ var sepVelocity:Number = contact.calculateSeparatingVelocity();
+ if (sepVelocity < maxSepVelocity) {
+ maxSepVelocity = sepVelocity;
+ mostSevereContact = contact;
+ }
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ mostSevereContact.resolve(time);
+ }
+ iterationsUsed++;
+ }
+ }
+
+ private function resolveCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
+ var maxPenetration:Number = 0;
+ var mostSevereContact:ParticleContact = null;
+ var contact:ParticleContact = contacts;
+ while (contact.index < contactsUsed) {
+ if (contact.isCollision && contact.penetration > maxPenetration) {
+ maxPenetration = contact.penetration;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact != null) {
+ if (mostSevereContact.calculateSeparatingVelocity() < 0) {
+ mostSevereContact.resolve(time);
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleLink.as b/0.0.0.18/src/alternativa/physics/particle/ParticleLink.as
new file mode 100644
index 0000000..d4b999a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleLink.as
@@ -0,0 +1,21 @@
+package alternativa.physics.particle {
+
+ public class ParticleLink {
+
+ public var particle1:Particle;
+ public var particle2:Particle;
+
+ public function ParticleLink(particle1:Particle, particle2:Particle) {
+ this.particle1 = particle1;
+ this.particle2 = particle2;
+ }
+
+ protected function getCurrentLength():Number {
+ return 0;
+ }
+
+ public function fillContact(contact:ParticleContact):Boolean {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleRod.as b/0.0.0.18/src/alternativa/physics/particle/ParticleRod.as
new file mode 100644
index 0000000..e24fa5e
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleRod.as
@@ -0,0 +1,58 @@
+package alternativa.physics.particle {
+ import alternativa.types.Point3D;
+
+ public class ParticleRod extends ParticleLink {
+
+ private static var vector:Point3D = new Point3D();
+
+ public var length:Number;
+
+ public function ParticleRod(particle1:Particle, particle2:Particle, length:Number = 0) {
+ super(particle1, particle2);
+ if (length <= 0) {
+ this.length = Point3D.difference(particle1.position, particle2.position).length;
+ } else {
+ this.length = length;
+ }
+ }
+
+ override protected function getCurrentLength():Number {
+ var x:Number = particle1.position.x - particle2.position.x;
+ var y:Number = particle1.position.y - particle2.position.y;
+ var z:Number = particle1.position.z - particle2.position.z;
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ override public function fillContact(contact:ParticleContact):Boolean {
+ vector.x = particle1.position.x - particle2.position.x;
+ vector.y = particle1.position.y - particle2.position.y;
+ vector.z = particle1.position.z - particle2.position.z;
+ var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
+
+ if (len == length) {
+ return false;
+ }
+
+ var penetration:Number;
+ if (len > length) {
+ vector.x = -vector.x;
+ vector.y = -vector.y;
+ vector.z = -vector.z;
+ penetration = len - length;
+ } else {
+ penetration = length - len;
+ }
+
+ contact.particle1 = particle1;
+ contact.particle2 = particle2;
+ contact.contactNormal.x = vector.x/len;
+ contact.contactNormal.y = vector.y/len;
+ contact.contactNormal.z = vector.z/len;
+ contact.penetration = penetration;
+ contact.restitution = 0;
+ contact.isCollision = false;
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/particle/ParticleWorld.as b/0.0.0.18/src/alternativa/physics/particle/ParticleWorld.as
new file mode 100644
index 0000000..ebbf5a4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/particle/ParticleWorld.as
@@ -0,0 +1,113 @@
+package alternativa.physics.particle {
+ import alternativa.physics.particle.Particle;
+ import alternativa.physics.particle.ParticleContact;
+ import alternativa.physics.particle.ParticleContactGenerator;
+ import alternativa.physics.particle.ParticleContactResolver;
+ import alternativa.physics.registry.ParticleForceRegistry;
+
+ public class ParticleWorld {
+
+ public var particles:Particle;
+ public var last:Particle;
+
+ public var contacts:ParticleContact;
+ public var contactGenerators:ParticleContactGenerator;
+ public var forceRegistry:ParticleForceRegistry;
+
+ public var resolver:ParticleContactResolver;
+
+ public var maxContacts:int;
+ public var iterations:int;
+
+ public function ParticleWorld(maxContacts:int, iterations:int = 0) {
+ this.maxContacts = maxContacts;
+ this.iterations = iterations;
+
+ // Создаём контакты
+ contacts = new ParticleContact(0);
+ var contact:ParticleContact = contacts;
+ for (var i:int = 1; i < maxContacts; i++) {
+ contact = contact.next = new ParticleContact(i);
+ }
+
+ resolver = new ParticleContactResolver(0);
+ forceRegistry = new ParticleForceRegistry();
+ }
+
+ public function addParticle(particle:Particle):void {
+ if (particles == null) {
+ last = particles = particle;
+ } else {
+ last.next = particle;
+ last = particle;
+ }
+ }
+
+ public function addContactGenerator(generator:ParticleContactGenerator):void {
+ if (contactGenerators == null) {
+ contactGenerators = generator;
+ } else {
+ var gen:ParticleContactGenerator = contactGenerators;
+ while (gen.next != null) {
+ gen = gen.next;
+ }
+ gen.next = generator;
+ }
+ }
+
+ public function startFrame():void {
+ var particle:Particle = particles;
+ while (particle != null) {
+ particle.clearForce();
+ particle = particle.next;
+ }
+ }
+
+ /**
+ * Последоавтельно запускает все зарегистрированные генераторы контактов.
+ *
+ * @return количество использованных контактов
+ */
+ public function generateContacts():int {
+ var contact:ParticleContact = contacts;
+ var generator:ParticleContactGenerator = contactGenerators;
+ while (generator != null) {
+ contact = generator.addContacts(contact);
+ if (contact == null) {
+ // Доступные контакты исчерпаны
+ return maxContacts;
+ }
+ generator = generator.next;
+ }
+ return contact.index;
+ }
+
+ public function intergrate(time:Number):void {
+ var particle:Particle = particles;
+ while (particle != null) {
+ particle.integrate(time);
+ particle = particle.next;
+ }
+ }
+
+ /**
+ * Запускает физическую симуляцию.
+ *
+ * @param time
+ */
+ public function runPhysics(time:Number):void {
+ // Применение генераторов сил
+ forceRegistry.updateForces(time);
+ // Запуск интеграторов для всех частиц
+ intergrate(time);
+ // Создание контактов
+ var contactsUsed:int = generateContacts();
+ // Обработка полученных контактов
+ if (iterations == 0) {
+ resolver.setIterations(3*contactsUsed);
+ }
+ resolver.resolveContacts(contacts, contactsUsed, time);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/registry/.svn/all-wcprops
new file mode 100644
index 0000000..d24107d
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry
+END
+ParticleForceRegistration.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/ParticleForceRegistration.as
+END
+BodyForceRegistry.as
+K 25
+svn:wc:ra_dav:version-url
+V 124
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/BodyForceRegistry.as
+END
+BodyForceRegistration.as
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/BodyForceRegistration.as
+END
+ParticleForceRegistry.as
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/ParticleForceRegistry.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/entries b/0.0.0.18/src/alternativa/physics/registry/.svn/entries
new file mode 100644
index 0000000..f225d87
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/entries
@@ -0,0 +1,76 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry
+http://svndev.alternativaplatform.com
+
+
+
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+ParticleForceRegistration.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+eacf3bd4d8f93fcaf786f50cccdc2be2
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+BodyForceRegistry.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+bc7c7f17acaa538a68392942fe5b4ddf
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+BodyForceRegistration.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+8b67908405c7eebf4c91e715ff7e26fd
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
+ParticleForceRegistry.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+ed017f73d079580385616a6cf0724645
+2008-11-24T06:53:44.272574Z
+4196
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/format b/0.0.0.18/src/alternativa/physics/registry/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistration.as.svn-base b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistration.as.svn-base
new file mode 100644
index 0000000..71ca5b4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistration.as.svn-base
@@ -0,0 +1,16 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistration {
+ public var body:RigidBody;
+ public var forceGenerator:IRigidBodyForceGenerator;
+
+ public var next:BodyForceRegistration;
+
+ public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
+ this.body = body;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistry.as.svn-base b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistry.as.svn-base
new file mode 100644
index 0000000..e7a0292
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/BodyForceRegistry.as.svn-base
@@ -0,0 +1,47 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistry {
+
+ protected var registrations:BodyForceRegistration;
+
+ public function BodyForceRegistry() {
+ }
+
+ public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var current:BodyForceRegistration = registrations;
+ var prev:BodyForceRegistration = null;
+ while (current != null) {
+ if (current.body == body && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:BodyForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.body, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistration.as.svn-base b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistration.as.svn-base
new file mode 100644
index 0000000..2e4ca84
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistration.as.svn-base
@@ -0,0 +1,16 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IParticelForceGenerator;
+ import alternativa.physics.particle.Particle;
+
+ public class ParticleForceRegistration {
+ public var particle:Particle;
+ public var forceGenerator:IParticelForceGenerator;
+
+ public var next:ParticleForceRegistration;
+
+ public function ParticleForceRegistration(particle:Particle, forceGenerator:IParticelForceGenerator) {
+ this.particle = particle;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistry.as.svn-base b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistry.as.svn-base
new file mode 100644
index 0000000..79e3b81
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/.svn/text-base/ParticleForceRegistry.as.svn-base
@@ -0,0 +1,47 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IParticelForceGenerator;
+ import alternativa.physics.particle.Particle;
+
+ public class ParticleForceRegistry {
+
+ protected var registrations:ParticleForceRegistration;
+
+ public function ParticleForceRegistry() {
+ }
+
+ public function add(particle:Particle, fg:IParticelForceGenerator):void {
+ var registration:ParticleForceRegistration = new ParticleForceRegistration(particle, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(particle:Particle, fg:IParticelForceGenerator):void {
+ var current:ParticleForceRegistration = registrations;
+ var prev:ParticleForceRegistration = null;
+ while (current != null) {
+ if (current.particle == particle && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:ParticleForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.particle, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistration.as b/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistration.as
new file mode 100644
index 0000000..71ca5b4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistration.as
@@ -0,0 +1,16 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistration {
+ public var body:RigidBody;
+ public var forceGenerator:IRigidBodyForceGenerator;
+
+ public var next:BodyForceRegistration;
+
+ public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
+ this.body = body;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistry.as b/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistry.as
new file mode 100644
index 0000000..e7a0292
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/BodyForceRegistry.as
@@ -0,0 +1,47 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistry {
+
+ protected var registrations:BodyForceRegistration;
+
+ public function BodyForceRegistry() {
+ }
+
+ public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var current:BodyForceRegistration = registrations;
+ var prev:BodyForceRegistration = null;
+ while (current != null) {
+ if (current.body == body && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:BodyForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.body, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistration.as b/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistration.as
new file mode 100644
index 0000000..2e4ca84
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistration.as
@@ -0,0 +1,16 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IParticelForceGenerator;
+ import alternativa.physics.particle.Particle;
+
+ public class ParticleForceRegistration {
+ public var particle:Particle;
+ public var forceGenerator:IParticelForceGenerator;
+
+ public var next:ParticleForceRegistration;
+
+ public function ParticleForceRegistration(particle:Particle, forceGenerator:IParticelForceGenerator) {
+ this.particle = particle;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistry.as b/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistry.as
new file mode 100644
index 0000000..79e3b81
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/registry/ParticleForceRegistry.as
@@ -0,0 +1,47 @@
+package alternativa.physics.registry {
+ import alternativa.physics.force.IParticelForceGenerator;
+ import alternativa.physics.particle.Particle;
+
+ public class ParticleForceRegistry {
+
+ protected var registrations:ParticleForceRegistration;
+
+ public function ParticleForceRegistry() {
+ }
+
+ public function add(particle:Particle, fg:IParticelForceGenerator):void {
+ var registration:ParticleForceRegistration = new ParticleForceRegistration(particle, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(particle:Particle, fg:IParticelForceGenerator):void {
+ var current:ParticleForceRegistration = registrations;
+ var prev:ParticleForceRegistration = null;
+ while (current != null) {
+ if (current.particle == particle && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:ParticleForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.particle, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/rigid/.svn/all-wcprops
new file mode 100644
index 0000000..4d6643b
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid
+END
+RigidBodyContactGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 129
+/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContactGenerator.as
+END
+BodyForceRegistry.as
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/BodyForceRegistry.as
+END
+RigidWorld.as
+K 25
+svn:wc:ra_dav:version-url
+V 114
+/!svn/ver/4741/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidWorld.as
+END
+BodyForceRegistration.as
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/BodyForceRegistration.as
+END
+RigidBodyContact.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/4813/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContact.as
+END
+RigidBodyContactResolver.as
+K 25
+svn:wc:ra_dav:version-url
+V 128
+/!svn/ver/4270/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContactResolver.as
+END
+RigidBody.as
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBody.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/entries b/0.0.0.18/src/alternativa/physics/rigid/.svn/entries
new file mode 100644
index 0000000..2cc5f81
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/entries
@@ -0,0 +1,115 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-17T11:38:42.040147Z
+5193
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+RigidBodyContactGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+577dac0a54d7d990734d27170ddf1f14
+2008-12-03T05:15:16.843707Z
+4732
+mike
+
+BodyForceRegistry.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+e2125f3fa80a45e6e3a304c33e5cbd47
+2008-11-26T12:04:03.108808Z
+4375
+mike
+
+RigidWorld.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+0031086a4c4b3f39f81a0f224ad8560a
+2008-12-03T08:31:09.097064Z
+4741
+mike
+
+generators
+dir
+
+BodyForceRegistration.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+bed2d6357d27f7311216db6acd156c4e
+2008-11-26T12:04:03.108808Z
+4375
+mike
+
+RigidBodyContact.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+473e71d1b8a62f4f9bcd94c2e38d26d6
+2008-12-10T13:18:05.069708Z
+4813
+mike
+
+RigidBodyContactResolver.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+ad30ebb588f523d3255ce0a768a39b40
+2008-11-25T04:51:53.783696Z
+4270
+mike
+
+RigidBody.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+8f0e9d6652c8f023b9c0b7a78135030d
+2008-12-17T11:38:42.040147Z
+5193
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/format b/0.0.0.18/src/alternativa/physics/rigid/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistration.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistration.as.svn-base
new file mode 100644
index 0000000..e030bbb
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistration.as.svn-base
@@ -0,0 +1,16 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistration {
+ public var body:RigidBody;
+ public var forceGenerator:IRigidBodyForceGenerator;
+
+ public var next:BodyForceRegistration;
+
+ public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
+ this.body = body;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistry.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistry.as.svn-base
new file mode 100644
index 0000000..a538a20
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/BodyForceRegistry.as.svn-base
@@ -0,0 +1,47 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistry {
+
+ protected var registrations:BodyForceRegistration;
+
+ public function BodyForceRegistry() {
+ }
+
+ public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var current:BodyForceRegistration = registrations;
+ var prev:BodyForceRegistration = null;
+ while (current != null) {
+ if (current.body == body && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:BodyForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.body, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBody.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBody.as.svn-base
new file mode 100644
index 0000000..da46e3f
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBody.as.svn-base
@@ -0,0 +1,707 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.altphysics;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+ import alternativa.types.Quaternion;
+
+ use namespace altphysics;
+
+ /**
+ * Класс представляет твёрдое тело.
+ */
+ public class RigidBody {
+
+ private var angularAcceleration:Point3D = new Point3D();
+ private var motionThreshold:Number = 0.1;
+ private var motionThreshold2:Number = 2*motionThreshold;
+ private var baseBias:Number = 0.8;
+ private var name:String;
+
+ public var next:RigidBody;
+ public var prev:RigidBody;
+
+ altphysics var mass:Number;
+ altphysics var inverseMass:Number;
+ altphysics var inverseInertiaTensor:Matrix3D = new Matrix3D();
+
+ altphysics var linearDamping:Number = 0.95;
+ altphysics var angularDamping:Number = 0.95;
+ altphysics var awake:Boolean = true;
+ altphysics var canSleep:Boolean = true;
+
+ altphysics var acceleration:Point3D = new Point3D();
+ altphysics var angularVelocity:Point3D = new Point3D();
+
+ altphysics var position:Point3D = new Point3D();
+ altphysics var orientation:Quaternion = new Quaternion(1, 0, 0, 0);
+ altphysics var velocity:Point3D = new Point3D();
+ altphysics var rotation:Point3D = new Point3D();
+ altphysics var lastFrameAcceleration:Point3D = new Point3D();
+
+ altphysics var transformMatrix:Matrix3D = new Matrix3D();
+ altphysics var inverseInertiaTensorWorld:Matrix3D = new Matrix3D();
+
+ altphysics var forceAccum:Point3D = new Point3D();
+ altphysics var torqueAccum:Point3D = new Point3D();
+ altphysics var motion:Number = 10;
+
+ /**
+ * Создаёт новый экзепляр твёрдого тела.
+ *
+ * @param inverseMass величина, обратная массе тела. Значение 0 указывает, что тело имеет имеет бесконечную массу и момент инерции.
+ * @param inverseInertiaTensor обратный тензор момента инерции тела. Значение null указывает, что тело имеет бесконечную массу и момент инерции.
+ */
+ public function RigidBody(inverseMass:Number = 1, inverseInertiaTensor:Matrix3D = null) {
+ if (inverseMass == 0 || inverseInertiaTensor == null) {
+ setInfinteMass();
+ } else {
+ this.inverseMass = inverseMass;
+ this.mass = 1/inverseMass;
+ this.inverseInertiaTensor.copy(inverseInertiaTensor);
+ }
+ }
+
+ /**
+ * @return имя тела
+ */
+ public function getName():String {
+ return name;
+ }
+
+ /**
+ * Устанавливает имя тела.
+ *
+ * @param value имя тела
+ */
+ public function setName(value:String):void {
+ name = value;
+ }
+
+ /**
+ * Предельное количество движения, ниже которого тело усыпляеся.
+ */
+ public function getMotionThreshold():Number {
+ return motionThreshold;
+ }
+
+ /**
+ * @private
+ */
+ public function setMotionThreshold(value:Number):void {
+ motionThreshold = value;
+ motionThreshold2 = value*2;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setBaseBias(value:Number):void {
+ baseBias = value;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function getBaseBias():Number {
+ return baseBias;
+ }
+
+ /**
+ *
+ */
+ public function getMass():Number {
+ return mass;
+ }
+
+ /**
+ *
+ */
+ public function setMass(value:Number):void {
+ if (value <= 0) {
+ inverseMass = 0;
+ mass = 0;
+ } else {
+ mass = value;
+ inverseMass = 1/value;
+ }
+ }
+
+ /**
+ *
+ */
+ public function getInverseMass():Number {
+ return inverseMass;
+ }
+
+ /**
+ * @private
+ */
+ public function setInverseMass(value:Number):void {
+ if (value > 0) {
+ inverseMass = value;
+ mass = 1/value;
+ } else {
+ inverseMass = 0;
+ mass = 0;
+ }
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function setIntertiaTensor(intertiaTensor:Matrix3D):void {
+ inverseInertiaTensor.copy(intertiaTensor);
+ inverseInertiaTensor.invert();
+ }
+
+ /**
+ *
+ * @param intertiaTensor
+ */
+ public function getIntertiaTensor(intertiaTensor:Matrix3D):void {
+ intertiaTensor.copy(inverseInertiaTensor);
+ intertiaTensor.invert();
+ }
+
+ /**
+ *
+ * @param inverseInertiaTensor
+ */
+ public function setInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
+ this.inverseInertiaTensor.copy(inverseInertiaTensor);
+ }
+
+ /**
+ *
+ * @param inverseInertiaTensor
+ */
+ public function getInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
+ inverseInertiaTensor.copy(this.inverseInertiaTensor);
+ }
+
+ /**
+ *
+ */
+ public function setInfinteMass():void {
+ inverseMass = 0;
+ mass = 0;
+ inverseInertiaTensor.toIdentity();
+ inverseInertiaTensor.a = inverseInertiaTensor.f = inverseInertiaTensor.k = 0;
+ }
+
+ /**
+ *
+ * @param linearDamping
+ * @param angularDamping
+ */
+ public function setDamping(linearDamping:Number, angularDamping:Number):void {
+ this.linearDamping = linearDamping;
+ this.angularDamping = angularDamping;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setLinearDamping(value:Number):void {
+ linearDamping = value;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function getLinearDamping():Number {
+ return linearDamping;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setAngularDamping(value:Number):void {
+ angularDamping = value;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function getAngularDamping():Number {
+ return angularDamping;
+ }
+
+ /**
+ *
+ * @param position
+ */
+ public function setPosition(position:Point3D):void {
+ this.position.x = position.x;
+ this.position.y = position.y;
+ this.position.z = position.z;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setPositionComponents(x:Number, y:Number, z:Number):void {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ }
+
+ /**
+ *
+ * @param position
+ */
+ public function getPosition(position:Point3D):void {
+ position.x = this.position.x;
+ position.y = this.position.y;
+ position.z = this.position.z;
+ }
+
+ /**
+ *
+ * @param orientation
+ */
+ public function setOrientation(orientation:Quaternion):void {
+ this.orientation.copy(orientation);
+ this.orientation.normalize();
+ }
+
+ /**
+ *
+ * @param r
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function setOrientationComponents(w:Number, x:Number, y:Number, z:Number):void {
+ orientation.reset(w, x, y, z);
+ orientation.normalize();
+ }
+
+ /**
+ *
+ * @param orientation
+ */
+ public function getOrientation(orientation:Quaternion):void {
+ orientation.copy(this.orientation);
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function getTransformMatrix(matrix:Matrix3D):void {
+ matrix.copy(transformMatrix);
+ }
+
+ /**
+ *
+ * @param velocity
+ */
+ public function setVelocity(velocity:Point3D):void {
+ this.velocity.copy(velocity);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setVelocityComponents(x:Number, y:Number, z:Number):void {
+ velocity.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param velocity
+ */
+ public function getVelocity(velocity:Point3D):void {
+ velocity.copy(this.velocity);
+ }
+
+ /**
+ *
+ * @param acceleration
+ *
+ */
+ public function setAcceleration(acceleration:Point3D):void {
+ this.acceleration.copy(acceleration);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ *
+ */
+ public function setAccelerationComponents(x:Number, y:Number, z:Number):void {
+ acceleration.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param acceleration
+ *
+ */
+ public function getAcceleration(acceleration:Point3D):void {
+ acceleration.copy(this.acceleration);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function setRotation(rotation:Point3D):void {
+ this.rotation.copy(rotation);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ *
+ */
+ public function setRotationComponents(x:Number, y:Number, z:Number):void {
+ rotation.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function addRotation(deltaRotation:Point3D):void {
+ rotation.add(deltaRotation);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function getRotation(rotation:Point3D):void {
+ rotation.copy(this.rotation);
+ }
+
+ /**
+ *
+ * @param angularVelocity
+ */
+ public function setAngularVelocity(angularVelocity:Point3D):void {
+ this.angularVelocity.x = angularVelocity.x;
+ this.angularVelocity.y = angularVelocity.y;
+ this.angularVelocity.z = angularVelocity.z;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setAngularVelocityComponents(x:Number, y:Number, z:Number):void {
+ angularVelocity.x = x;
+ angularVelocity.y = y;
+ angularVelocity.z = z;
+ }
+
+ /**
+ *
+ * @param awake
+ */
+ public function setAwake(awake:Boolean = true):void {
+ this.awake = awake;
+ if (awake) {
+ motion = motionThreshold2;
+ } else {
+ velocity.x = 0;
+ velocity.y = 0;
+ velocity.z = 0;
+
+ rotation.x = 0;
+ rotation.y = 0;
+ rotation.z = 0;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function isAwake():Boolean {
+ return awake;
+ }
+
+ /**
+ *
+ * @param canSleep
+ */
+ public function setCanSleep(canSleep:Boolean = true):void {
+ this.canSleep = canSleep;
+ if (!canSleep && !awake) {
+ setAwake();
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function isCanSleep():Boolean {
+ return canSleep;
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к центру масс тела.
+ *
+ * @param force вектор силы в глобальной системе координат
+ */
+ public function addForce(force:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ setAwake();
+ }
+
+ /**
+ *
+ * @param force
+ */
+ public function addForceComponents(x:Number, y:Number, z:Number):void {
+ forceAccum.x += x;
+ forceAccum.y += y;
+ forceAccum.z += z;
+ setAwake();
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к заданной локальной точке тела.
+ *
+ * @param force вектор силы в глобальной системе координат
+ * @param localPoint локальные координаты точки приложения силы
+ */
+ public function addForceAtLocalPoint(force:Point3D, localPoint:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ // Трансформация локальных координат в глобальные
+ var x:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
+ var y:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
+ var z:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
+ torqueAccum.x += y*force.z - z*force.y;
+ torqueAccum.y += z*force.x - x*force.z;
+ torqueAccum.z += x*force.y - y*force.x;
+ setAwake();
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к заданной точке тела, выраженной в глобальных координатах.
+ *
+ * @param force вектор силы в глобальной системе координат
+ * @param point глобальные координаты точки приложения силы
+ */
+ public function addForceAtPoint(force:Point3D, point:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ var x:Number = point.x - position.x;
+ var y:Number = point.y - position.y;
+ var z:Number = point.z - position.z;
+ torqueAccum.x += y*force.z - z*force.y;
+ torqueAccum.y += z*force.x - x*force.z;
+ torqueAccum.z += x*force.y - y*force.x;
+ setAwake();
+ }
+
+ /**
+ * Добавляет локальную силу, приложенную в заданной локальной точке тела.
+ *
+ * @param localForce вектор локальной силы
+ * @param localPoint локальные координаты точки приложения силы
+ */
+ public function addLocalForce(localForce:Point3D, localPoint:Point3D):void {
+ // Трансформация локального вектора силы в глобальный
+ var globalForceX:Number = transformMatrix.a*localForce.x + transformMatrix.b*localForce.y + transformMatrix.c*localForce.z;
+ var globalForceY:Number = transformMatrix.e*localForce.x + transformMatrix.f*localForce.y + transformMatrix.g*localForce.z;
+ var globalForceZ:Number = transformMatrix.i*localForce.x + transformMatrix.j*localForce.y + transformMatrix.k*localForce.z;
+ forceAccum.x += globalForceX;
+ forceAccum.y += globalForceY;
+ forceAccum.z += globalForceZ;
+ // Получение глобального вектора плеча силы
+ var globalPointX:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
+ var globalPointY:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
+ var globalPointZ:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
+ // Приращение момента силы
+ torqueAccum.x += globalPointY*globalForceZ - globalPointZ*globalForceY;
+ torqueAccum.y += globalPointZ*globalForceX - globalPointX*globalForceZ;
+ torqueAccum.z += globalPointX*globalForceY - globalPointY*globalForceX;
+ setAwake();
+ }
+
+ /**
+ * Добавляет момент силы.
+ *
+ * @param torque вектор момента силы
+ */
+ public function addTorque(torque:Point3D):void {
+ torqueAccum.x += torque.x;
+ torqueAccum.y += torque.y;
+ torqueAccum.z += torque.z;
+ setAwake();
+ }
+
+ /**
+ * Добавляет момент силы, заданный покомпонентно.
+ */
+ public function addTorqueComponents(x:Number, y:Number, z:Number):void {
+ torqueAccum.x += x;
+ torqueAccum.y += y;
+ torqueAccum.z += z;
+ setAwake();
+ }
+
+ /**
+ * Вычисляет векторные параметры тела в глобальной системе координат.
+ */
+ public function calculateDerivedData():void {
+ // inlined position.normalize()
+ var d:Number = orientation.w*orientation.w + orientation.x*orientation.x + orientation.y*orientation.y + orientation.z*orientation.z;
+ if (d == 0) {
+ orientation.w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ orientation.w *= d;
+ orientation.x *= d;
+ orientation.y *= d;
+ orientation.z *= d;
+ }
+ // Вычисление глобальной матрицы трансформации
+ orientation.toMatrix3D(transformMatrix);
+ transformMatrix.d = position.x;
+ transformMatrix.h = position.y;
+ transformMatrix.l = position.z;
+ // Расчёт обратного тензора инерции в мировых координатах
+ var a:Number = transformMatrix.a*inverseInertiaTensor.a + transformMatrix.b*inverseInertiaTensor.e + transformMatrix.c*inverseInertiaTensor.i;
+ var b:Number = transformMatrix.a*inverseInertiaTensor.b + transformMatrix.b*inverseInertiaTensor.f + transformMatrix.c*inverseInertiaTensor.j;
+ var c:Number = transformMatrix.a*inverseInertiaTensor.c + transformMatrix.b*inverseInertiaTensor.g + transformMatrix.c*inverseInertiaTensor.k;
+ var e:Number = transformMatrix.e*inverseInertiaTensor.a + transformMatrix.f*inverseInertiaTensor.e + transformMatrix.g*inverseInertiaTensor.i;
+ var f:Number = transformMatrix.e*inverseInertiaTensor.b + transformMatrix.f*inverseInertiaTensor.f + transformMatrix.g*inverseInertiaTensor.j;
+ var g:Number = transformMatrix.e*inverseInertiaTensor.c + transformMatrix.f*inverseInertiaTensor.g + transformMatrix.g*inverseInertiaTensor.k;
+ var i:Number = transformMatrix.i*inverseInertiaTensor.a + transformMatrix.j*inverseInertiaTensor.e + transformMatrix.k*inverseInertiaTensor.i;
+ var j:Number = transformMatrix.i*inverseInertiaTensor.b + transformMatrix.j*inverseInertiaTensor.f + transformMatrix.k*inverseInertiaTensor.j;
+ var k:Number = transformMatrix.i*inverseInertiaTensor.c + transformMatrix.j*inverseInertiaTensor.g + transformMatrix.k*inverseInertiaTensor.k;
+ // Здесь подразумевается, что матрица трансформации тела не содержит масштабирования
+ // Произведение [transformMatrix*inverseInertiaTensor]*transpose(transformMatrix)
+ inverseInertiaTensorWorld.a = a*transformMatrix.a + b*transformMatrix.b + c*transformMatrix.c;
+ inverseInertiaTensorWorld.b = a*transformMatrix.e + b*transformMatrix.f + c*transformMatrix.g;
+ inverseInertiaTensorWorld.c = a*transformMatrix.i + b*transformMatrix.j + c*transformMatrix.k;
+ inverseInertiaTensorWorld.e = e*transformMatrix.a + f*transformMatrix.b + g*transformMatrix.c;
+ inverseInertiaTensorWorld.f = e*transformMatrix.e + f*transformMatrix.f + g*transformMatrix.g;
+ inverseInertiaTensorWorld.g = e*transformMatrix.i + f*transformMatrix.j + g*transformMatrix.k;
+ inverseInertiaTensorWorld.i = i*transformMatrix.a + j*transformMatrix.b + k*transformMatrix.c;
+ inverseInertiaTensorWorld.j = i*transformMatrix.e + j*transformMatrix.f + k*transformMatrix.g;
+ inverseInertiaTensorWorld.k = i*transformMatrix.i + j*transformMatrix.j + k*transformMatrix.k;
+ }
+
+ /**
+ * Интегрирует динамические характеристики и обновляет параметры состояния тела.
+ *
+ * @param time временной шаг интегрирования
+ */
+ public function integrate(time:Number):void {
+ if (!awake) {
+ return;
+ }
+
+ lastFrameAcceleration.x = acceleration.x + forceAccum.x*inverseMass;
+ lastFrameAcceleration.y = acceleration.y + forceAccum.y*inverseMass;
+ lastFrameAcceleration.z = acceleration.z + forceAccum.z*inverseMass;
+ // angularAcceleration = inverseInertiaTensorWorld*torqueAccum
+ angularAcceleration.x = inverseInertiaTensorWorld.a*torqueAccum.x + inverseInertiaTensorWorld.b*torqueAccum.y + inverseInertiaTensorWorld.c*torqueAccum.z;
+ angularAcceleration.y = inverseInertiaTensorWorld.e*torqueAccum.x + inverseInertiaTensorWorld.f*torqueAccum.y + inverseInertiaTensorWorld.g*torqueAccum.z;
+ angularAcceleration.z = inverseInertiaTensorWorld.i*torqueAccum.x + inverseInertiaTensorWorld.j*torqueAccum.y + inverseInertiaTensorWorld.k*torqueAccum.z;
+
+ var d:Number = Math.pow(linearDamping, time);
+ var dtime:Number = d*time;
+
+ velocity.x += lastFrameAcceleration.x*dtime;
+ velocity.y += lastFrameAcceleration.y*dtime;
+ velocity.z += lastFrameAcceleration.z*dtime;
+
+ rotation.x += angularAcceleration.x*dtime;
+ rotation.y += angularAcceleration.y*dtime;
+ rotation.z += angularAcceleration.z*dtime;
+
+ rotation.x += angularVelocity.x*time;
+ rotation.y += angularVelocity.y*time;
+ rotation.z += angularVelocity.z*time;
+
+ position.x += velocity.x*time;
+ position.y += velocity.y*time;
+ position.z += velocity.z*time;
+
+ orientation.addScaledVector(rotation, time);
+
+ // TODO: Нужно ли двойное уменьшение скоростей?
+ velocity.x *= d;
+ velocity.y *= d;
+ velocity.z *= d;
+
+ rotation.x *= d;
+ rotation.y *= d;
+ rotation.z *= d;
+
+ calculateDerivedData();
+ // inlined clearAccumulators()
+ forceAccum.x = 0;
+ forceAccum.y = 0;
+ forceAccum.z = 0;
+ torqueAccum.x = 0;
+ torqueAccum.y = 0;
+ torqueAccum.z = 0;
+
+ // Усыпление тела при необходимости
+ if (canSleep) {
+ var currentMotion:Number = velocity.x*velocity.x + velocity.y*velocity.y + velocity.z*velocity.z +
+ rotation.x*rotation.x + rotation.y*rotation.y + rotation.z*rotation.z;
+ var bias:Number = Math.pow(baseBias, time);
+ motion = bias*motion + (1 - bias)*currentMotion;
+// trace("[RigidBody::integrate]", name, "motion", motion, "motionEpsilon", motionEpsilon);
+ if (motion < motionThreshold) {
+ setAwake(false);
+ } else {
+ if (motion > motionThreshold2) {
+ motion = motionThreshold2;
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public function clearAccumulators():void {
+ forceAccum.x = 0;
+ forceAccum.y = 0;
+ forceAccum.z = 0;
+ torqueAccum.x = 0;
+ torqueAccum.y = 0;
+ torqueAccum.z = 0;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContact.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContact.as.svn-base
new file mode 100644
index 0000000..22b3c65
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContact.as.svn-base
@@ -0,0 +1,550 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.*;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ * Класс описывает контакт между телами. Контакты могут составлять двунаправленный список.
+ */
+ public class RigidBodyContact {
+
+ private var tmpVelocityVector:Point3D = new Point3D();
+ private var impulse:Point3D = new Point3D();
+
+ public static var velocityLimit:Number = 1;
+
+ /**
+ * Ссылка на следующий контакт в списке.
+ */
+ public var next:RigidBodyContact;
+ /**
+ * Ссылка на предыдущий контакт в списке.
+ */
+ public var prev:RigidBodyContact;
+ /**
+ * Индекс контакта в списке.
+ */
+ public var index:int;
+
+ /**
+ * Первое тело, к которому относится контакт.
+ */
+ public var body1:RigidBody;
+ /**
+ * Первое тело, к которому относится контакт (может быть null).
+ */
+ public var body2:RigidBody;
+ /**
+ * Точка контакта.
+ */
+ public var contactPoint:Point3D = new Point3D();
+ /**
+ * Нормаль контакта. Нормаль направлена от второго тела к первому. В локальной системе координат контакта
+ * нормаль является базисным вектором, задающим направление оси X.
+ */
+ public var contactNormal:Point3D = new Point3D();
+ /**
+ * Коэффициент взаимного трения в точке контакта.
+ */
+ public var friction:Number = 0;
+ /**
+ * Коэффициент упругости удара.
+ */
+ public var restitution:Number = 0;
+ /**
+ * Глубина взаимного проникновения тел в точке контакта.
+ */
+ public var penetration:Number;
+
+ public var timeStamp:int;
+
+ /**
+ * Матрица перехода из локальной системы координат контакта в мировую систему координат без компонентов параллельного переноса.
+ * Матрица задаёт базис локальной системы координат контакта.
+ */
+ altphysics var contactToWorld:Matrix3D = new Matrix3D();
+ /**
+ * Относительная скорость в точке контакта. Вычисляется в методе calculateInternals().
+ */
+ altphysics var contactVelocity:Point3D = new Point3D();
+ /**
+ * Приращение скорости в точке контакта по направлению нормали, необходимое для прекращения сближения. Вычисляется в методе calculateInternals().
+ */
+ altphysics var desiredDeltaVelocity:Number;
+ /**
+ * Координаты точки контакта относительныо первого тела. Вычисляется в методе calculateInternals().
+ */
+ altphysics var relativeContactPosition1:Point3D = new Point3D();
+ /**
+ * Координаты точки контакта относительныо второго тела. Вычисляется в методе calculateInternals().
+ */
+ altphysics var relativeContactPosition2:Point3D = new Point3D();
+
+ /**
+ *
+ * @param index
+ */
+ public function RigidBodyContact(index:int) {
+ this.index = index;
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param friction
+ * @param restitution
+ */
+ public function setBodyData(body1:RigidBody, body2:RigidBody, friction:Number, restitution:Number):void {
+ this.body1 = body1;
+ this.body2 = body2;
+ this.friction = friction;
+ this.restitution = restitution;
+ }
+
+ /**
+ *
+ */
+ public function matchAwakeState():void {
+ if (body2 == null) {
+ return;
+ }
+
+ if (body1.awake != body2.awake) {
+ if (body1.awake) {
+ body2.setAwake();
+ } else {
+ body1.setAwake();
+ }
+ }
+ }
+
+ /**
+ * Вычисляет и сохраняет базис локальной системы координат контакта.
+ */
+ public function calculateContactBasis():void {
+ // Нормаль является ортом X локальной системы координат
+ contactToWorld.a = contactNormal.x;
+ contactToWorld.e = contactNormal.y;
+ contactToWorld.i = contactNormal.z;
+ var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
+ var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
+ var s:Number;
+ if (nx > ny) {
+ // Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
+ s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
+ contactToWorld.b = contactNormal.z*s;
+ contactToWorld.f = 0;
+ contactToWorld.j = -contactNormal.x*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j;
+ contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
+ contactToWorld.k = -contactNormal.y*contactToWorld.b;
+ } else {
+ // Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
+ s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
+ contactToWorld.b = 0;
+ contactToWorld.f = -contactNormal.z*s;
+ contactToWorld.j = contactNormal.y*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
+ contactToWorld.g = -contactNormal.x*contactToWorld.j;
+ contactToWorld.k = contactNormal.x*contactToWorld.f;
+ }
+ }
+
+ /**
+ * Вычисляет скорость точки заданного тела в точке контакта в его локальной системе координат.
+ *
+ * @param body тело, для которого выполняются вычисления
+ * @param relativeContactPosition положение точки контакта относительно тела
+ * @param time временной шаг
+ * @param result в эту переменную записывается результат
+ */
+ public function calculateLocalVelocity(body:RigidBody, relativeContactPosition:Point3D, time:Number, result:Point3D):void {
+ // Расчёт прироста скорости за последний кадр от действующих сил
+ var x:Number = body.lastFrameAcceleration.x*time;
+ var y:Number = body.lastFrameAcceleration.y*time;
+ var z:Number = body.lastFrameAcceleration.z*time;
+ // Прирост скорости в системе координат контакта
+ // Убираем прирост скорости в направлении контакта
+ var accVelocityY:Number = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z;
+ var accVelocityZ:Number = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z;
+
+ // Скорость точки контакта в мировой системе координат
+ // Скорость за счёт вращения тела
+ var rotation:Point3D = body.rotation;
+ x = rotation.y*relativeContactPosition.z - rotation.z*relativeContactPosition.y + body.velocity.x;
+ y = rotation.z*relativeContactPosition.x - rotation.x*relativeContactPosition.z + body.velocity.y;
+ z = rotation.x*relativeContactPosition.y - rotation.y*relativeContactPosition.x + body.velocity.z;
+ // Скорость точки контакта в системе координат контакта
+ // Касательные компоненты прироста скорости добавляются к скорости точки контакта. В дальнейшем они либо уберутся силой трения,
+ // либо уменьшатся ею.
+ result.x = contactToWorld.a*x + contactToWorld.e*y + contactToWorld.i*z;
+ result.y = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z + accVelocityY;
+ result.z = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z + accVelocityZ;
+ }
+
+ /**
+ * Вычисляет и устанавливает внутреннее значение для требуемого изменения относительной скорости вдоль нормали контакта.
+ * С реакциями опоры разбираемся путём вычитания из скорости сближения прироста скорости, полученного во время последнего кадра.
+ *
+ * @param time
+ */
+ public function calculateDesiredVelocity(time:Number):void {
+ // Вычисляем проекцию прироста скорости за текущий кадр на нормаль контакта. Для относительного прироста скорости, вызывающего
+ // сближение объектов, значение будет отрицательным.
+ var velocityFromAcc:Number = 0;
+ if (body1.awake) {
+ velocityFromAcc = (body1.lastFrameAcceleration.x*contactNormal.x + body1.lastFrameAcceleration.y*contactNormal.y + body1.lastFrameAcceleration.z*contactNormal.z)*time;
+ }
+ if (body2 != null && body2.awake) {
+ velocityFromAcc -= (body2.lastFrameAcceleration.x*contactNormal.x + body2.lastFrameAcceleration.y*contactNormal.y + body2.lastFrameAcceleration.z*contactNormal.z)*time;
+ }
+ // Если относительная скорость вдоль нормали контакта слишком мала, игнорируем её
+ var thisRestitution:Number = restitution;
+ if (contactVelocity.x > -velocityLimit && contactVelocity.x < velocityLimit) {
+ thisRestitution = 0;
+ }
+ // Вычисляем дельту для скорости отскока, убрав влияние прироста скорости за последний кадр
+ desiredDeltaVelocity = -contactVelocity.x - thisRestitution*(contactVelocity.x - velocityFromAcc);
+ }
+
+ /**
+ * Расчитывает внутренние данные о контакта на основе данных о его состоянии. Вызывается до того, как будет запущен
+ * алгоритм разрешения контактов. Эта функция в нормальных условиях никогда не должна вызываться вручную.
+ *
+ * @param time
+ */
+ public function calculateInternals(time:Number):void {
+ // Расчёт базиса системы координат контакта
+ // === inlined calculateContactBasis();
+ contactToWorld.a = contactNormal.x;
+ contactToWorld.e = contactNormal.y;
+ contactToWorld.i = contactNormal.z;
+ var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
+ var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
+ var s:Number;
+ if (nx > ny) {
+ // Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
+ s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
+ contactToWorld.b = contactNormal.z*s;
+ contactToWorld.f = 0;
+ contactToWorld.j = -contactNormal.x*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j;
+ contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
+ contactToWorld.k = -contactNormal.y*contactToWorld.b;
+ } else {
+ // Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
+ s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
+ contactToWorld.b = 0;
+ contactToWorld.f = -contactNormal.z*s;
+ contactToWorld.j = contactNormal.y*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
+ contactToWorld.g = -contactNormal.x*contactToWorld.j;
+ contactToWorld.k = contactNormal.x*contactToWorld.f;
+ }
+ // === end inlined
+
+ // Расчёт локальной скорости в точке контакта для первого тела
+ relativeContactPosition1.x = contactPoint.x - body1.position.x;
+ relativeContactPosition1.y = contactPoint.y - body1.position.y;
+ relativeContactPosition1.z = contactPoint.z - body1.position.z;
+ calculateLocalVelocity(body1, relativeContactPosition1, time, contactVelocity);
+ // Расчёт локальной скорости в точке контакта для второго тела
+ if (body2 != null) {
+ relativeContactPosition2.x = contactPoint.x - body2.position.x;
+ relativeContactPosition2.y = contactPoint.y - body2.position.y;
+ relativeContactPosition2.z = contactPoint.z - body2.position.z;
+ calculateLocalVelocity(body2, relativeContactPosition2, time, tmpVelocityVector);
+ // Расчёт относительной скорости в точке контакта
+ contactVelocity.x -= tmpVelocityVector.x;
+ contactVelocity.y -= tmpVelocityVector.y;
+ contactVelocity.z -= tmpVelocityVector.z;
+ }
+ // Расчёт вектора изменения скорости, требуемого для разрешения контакта
+ calculateDesiredVelocity(time);
+ }
+
+ /**
+ * Выполняет разделение контакта посредством импульса, пропорционально инерции тел.
+ */
+ public function applyVelocityChange(velocityChange1:Point3D, velocityChange2:Point3D, rotationChange1:Point3D, rotationChange2:Point3D):void {
+ var inverseInertiaTensor1:Matrix3D = body1.inverseInertiaTensorWorld;
+ var inverseInertiaTensor2:Matrix3D;
+ if (body2 != null) {
+ inverseInertiaTensor2 = body2.inverseInertiaTensorWorld;
+ }
+
+ if (friction == 0) {
+ calculateFrictionlessImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
+ } else {
+ calculateFrictionImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
+ }
+
+ // Переведём импульс в мировые координаты
+ var x:Number = impulse.x;
+ var y:Number = impulse.y;
+ var z:Number = impulse.z;
+ impulse.x = contactToWorld.a*x + contactToWorld.b*y + contactToWorld.c*z;
+ impulse.y = contactToWorld.e*x + contactToWorld.f*y + contactToWorld.g*z;
+ impulse.z = contactToWorld.i*x + contactToWorld.j*y + contactToWorld.k*z;
+
+ // Разделим импульс на вращательную и линейную часть
+ x = relativeContactPosition1.y*impulse.z - relativeContactPosition1.z*impulse.y;
+ y = relativeContactPosition1.z*impulse.x - relativeContactPosition1.x*impulse.z;
+ z = relativeContactPosition1.x*impulse.y - relativeContactPosition1.y*impulse.x;
+ rotationChange1.x = inverseInertiaTensor1.a*x + inverseInertiaTensor1.b*y + inverseInertiaTensor1.c*z;
+ rotationChange1.y = inverseInertiaTensor1.e*x + inverseInertiaTensor1.f*y + inverseInertiaTensor1.g*z;
+ rotationChange1.z = inverseInertiaTensor1.i*x + inverseInertiaTensor1.j*y + inverseInertiaTensor1.k*z;
+
+ velocityChange1.x = impulse.x*body1.inverseMass;
+ velocityChange1.y = impulse.y*body1.inverseMass;
+ velocityChange1.z = impulse.z*body1.inverseMass;
+
+ body1.velocity.x += velocityChange1.x;
+ body1.velocity.y += velocityChange1.y;
+ body1.velocity.z += velocityChange1.z;
+
+ body1.rotation.x += rotationChange1.x;
+ body1.rotation.y += rotationChange1.y;
+ body1.rotation.z += rotationChange1.z;
+
+ if (body2 != null) {
+ x = relativeContactPosition2.z*impulse.y - relativeContactPosition2.y*impulse.z;
+ y = relativeContactPosition2.x*impulse.z - relativeContactPosition2.z*impulse.x;
+ z = relativeContactPosition2.y*impulse.x - relativeContactPosition2.x*impulse.y;
+ rotationChange2.x = inverseInertiaTensor2.a*x + inverseInertiaTensor2.b*y + inverseInertiaTensor2.c*z;
+ rotationChange2.y = inverseInertiaTensor2.e*x + inverseInertiaTensor2.f*y + inverseInertiaTensor2.g*z;
+ rotationChange2.z = inverseInertiaTensor2.i*x + inverseInertiaTensor2.j*y + inverseInertiaTensor2.k*z;
+
+ velocityChange2.x = -impulse.x*body2.inverseMass;
+ velocityChange2.y = -impulse.y*body2.inverseMass;
+ velocityChange2.z = -impulse.z*body2.inverseMass;
+
+ body2.velocity.x += velocityChange2.x;
+ body2.velocity.y += velocityChange2.y;
+ body2.velocity.z += velocityChange2.z;
+
+ body2.rotation.x += rotationChange2.x;
+ body2.rotation.y += rotationChange2.y;
+ body2.rotation.z += rotationChange2.z;
+ }
+ }
+
+ /**
+ * Вычисляет импульс, необходимый для коррекции скоростей в точке контакта в отсутствие сил трения.
+ */
+ public function calculateFrictionlessImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
+ tmpVelocityVector.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
+ tmpVelocityVector.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
+ tmpVelocityVector.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
+
+ var x:Number = inverseInertiaTensor1.a*tmpVelocityVector.x + inverseInertiaTensor1.b*tmpVelocityVector.y + inverseInertiaTensor1.c*tmpVelocityVector.z;
+ var y:Number = inverseInertiaTensor1.e*tmpVelocityVector.x + inverseInertiaTensor1.f*tmpVelocityVector.y + inverseInertiaTensor1.g*tmpVelocityVector.z;
+ var z:Number = inverseInertiaTensor1.i*tmpVelocityVector.x + inverseInertiaTensor1.j*tmpVelocityVector.y + inverseInertiaTensor1.k*tmpVelocityVector.z;
+
+ tmpVelocityVector.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
+ tmpVelocityVector.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
+ tmpVelocityVector.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
+
+ var deltaVelocity:Number = tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body1.inverseMass;
+
+ if (body2 != null) {
+ tmpVelocityVector.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
+ tmpVelocityVector.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
+ tmpVelocityVector.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
+
+ x = inverseInertiaTensor2.a*tmpVelocityVector.x + inverseInertiaTensor2.b*tmpVelocityVector.y + inverseInertiaTensor2.c*tmpVelocityVector.z;
+ y = inverseInertiaTensor2.e*tmpVelocityVector.x + inverseInertiaTensor2.f*tmpVelocityVector.y + inverseInertiaTensor2.g*tmpVelocityVector.z;
+ z = inverseInertiaTensor2.i*tmpVelocityVector.x + inverseInertiaTensor2.j*tmpVelocityVector.y + inverseInertiaTensor2.k*tmpVelocityVector.z;
+
+ tmpVelocityVector.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
+ tmpVelocityVector.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
+ tmpVelocityVector.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
+
+ deltaVelocity += tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body2.inverseMass;
+ }
+
+ result.x = desiredDeltaVelocity/deltaVelocity;
+ result.y = 0;
+ result.z = 0;
+ }
+
+ private var impulseToTorque:Matrix3D = new Matrix3D();
+ private var deltaVelWorldMatrix:Matrix3D = new Matrix3D();
+ private var deltaVelWorldMatrix2:Matrix3D = new Matrix3D();
+ /**
+ * Вычисляет импульс, необходимый для коррекции скоростей в точке контакта при наличии сил трения.
+ */
+ public function calculateFrictionImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
+ var inverseMass:Number = body1.inverseMass;
+
+ relativeContactPosition1.createSkewSymmetricMatrix(impulseToTorque);
+
+ deltaVelWorldMatrix.copy(impulseToTorque);
+ deltaVelWorldMatrix.inverseCombine(inverseInertiaTensor1);
+ deltaVelWorldMatrix.inverseCombine(impulseToTorque);
+ deltaVelWorldMatrix.multByScalar(-1);
+
+ if (body2 != null) {
+ relativeContactPosition2.createSkewSymmetricMatrix(impulseToTorque);
+ deltaVelWorldMatrix2.copy(impulseToTorque);
+ deltaVelWorldMatrix2.inverseCombine(inverseInertiaTensor2);
+ deltaVelWorldMatrix2.inverseCombine(impulseToTorque);
+ deltaVelWorldMatrix2.multByScalar(-1);
+
+ deltaVelWorldMatrix.add(deltaVelWorldMatrix2);
+ inverseMass += body2.inverseMass;
+ }
+
+ deltaVelWorldMatrix2.copy(contactToWorld);
+ deltaVelWorldMatrix2.transpose();
+ deltaVelWorldMatrix2.inverseCombine(deltaVelWorldMatrix);
+ deltaVelWorldMatrix2.inverseCombine(contactToWorld);
+ deltaVelWorldMatrix2.a += inverseMass;
+ deltaVelWorldMatrix2.f += inverseMass;
+ deltaVelWorldMatrix2.k += inverseMass;
+
+ deltaVelWorldMatrix.copy(deltaVelWorldMatrix2);
+ deltaVelWorldMatrix.invert();
+
+ result.reset(desiredDeltaVelocity, -contactVelocity.y, -contactVelocity.z);
+ result.transform(deltaVelWorldMatrix);
+
+ var planarImpulse:Number = Math.sqrt(result.y*result.y + result.z*result.z);
+ if (planarImpulse > result.x*friction) {
+ result.y /= planarImpulse;
+ result.z /= planarImpulse;
+ result.x = desiredDeltaVelocity/(deltaVelWorldMatrix2.a + deltaVelWorldMatrix2.b*friction*result.y + deltaVelWorldMatrix2.c*friction*result.z);
+ result.y *= friction*result.x;
+ result.z *= friction*result.x;
+ }
+ }
+
+ private var angularInertiaWorld:Point3D = new Point3D();
+ /**
+ * Выполняет разделение контакта с учётом инертности тел.
+ */
+ public function applyPositionChange(linearChange1:Point3D, linearChange2:Point3D, angularChange1:Point3D, angularChange2:Point3D, penetration:Number):void {
+ var totalInertia:Number = 0;
+ var angularInertia1:Number;
+ var angularInertia2:Number;
+ var linearInertia1:Number;
+ var linearInertia2:Number;
+
+ // body1
+ angularInertiaWorld.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
+ angularInertiaWorld.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
+ angularInertiaWorld.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
+ var m:Matrix3D = body1.inverseInertiaTensorWorld;
+ var x:Number = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
+ var y:Number = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
+ var z:Number = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
+ angularInertiaWorld.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
+ angularInertiaWorld.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
+ angularInertiaWorld.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
+
+ angularInertia1 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
+ linearInertia1 = body1.inverseMass;
+
+ totalInertia = linearInertia1 + angularInertia1;
+
+ if (body2 != null) {
+ angularInertiaWorld.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
+ angularInertiaWorld.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
+ angularInertiaWorld.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
+ m = body2.inverseInertiaTensorWorld;
+ x = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
+ y = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
+ z = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
+ angularInertiaWorld.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
+ angularInertiaWorld.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
+ angularInertiaWorld.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
+
+ angularInertia2 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
+ linearInertia2 = body2.inverseMass;
+
+ totalInertia += linearInertia2 + angularInertia2;
+ }
+
+ // body1
+ applyPostionChangeToBody(body1, relativeContactPosition1, angularInertia1, linearInertia1, totalInertia, 1, angularChange1, linearChange1);
+
+ if (body2 != null) {
+ applyPostionChangeToBody(body2, relativeContactPosition2, angularInertia2, linearInertia2, totalInertia, -1, angularChange2, linearChange2);
+ }
+ }
+
+ private var projection:Point3D = new Point3D();
+ /**
+ *
+ * @param body
+ * @param relativeContactPosition
+ * @param angularInertia
+ * @param linearInertia
+ * @param totalInertia
+ * @param sign
+ * @param angularChange
+ * @param linearChange
+ */
+ private function applyPostionChangeToBody(body:RigidBody, relativeContactPosition:Point3D, angularInertia:Number, linearInertia:Number, totalInertia:Number, sign:Number, angularChange:Point3D, linearChange:Point3D):void {
+ var angularLimit:Number = 0.2;
+ var angularMove:Number = sign*penetration*angularInertia/totalInertia;
+ var linearMove:Number = sign*penetration*linearInertia/totalInertia;
+
+ projection.copy(contactNormal);
+ projection.multiply(-relativeContactPosition.dot(contactNormal));
+ projection.add(relativeContactPosition);
+
+ var maxMagnitude:Number = angularLimit*projection.length;
+
+ var totalMove:Number;
+ if (angularMove < -maxMagnitude) {
+ totalMove = angularMove + linearMove;
+ angularMove = -maxMagnitude;
+ linearMove = totalMove - angularMove;
+ } else if (angularMove > maxMagnitude) {
+ totalMove = angularMove + linearMove;
+ angularMove = maxMagnitude;
+ linearMove = totalMove - angularMove;
+ }
+
+ if (angularMove == 0) {
+ angularChange.reset();
+ } else {
+ angularChange.copy(relativeContactPosition);
+ angularChange.cross(contactNormal);
+ angularChange.transform(body.inverseInertiaTensorWorld);
+ angularChange.multiply(angularMove/angularInertia);
+ }
+
+ linearChange.copy(contactNormal);
+ linearChange.multiply(linearMove);
+
+ body.position.add(linearChange);
+ body.orientation.addScaledVector(angularChange, 1);
+ if (!body.awake) {
+ body.calculateDerivedData();
+ }
+ }
+
+ public function toString():String {
+ return "RigidBodyContact" +
+ "\n timeStamp: " + timeStamp +
+ "\n body1: " + body1.getName() +
+ "\n body2: " + (body2 == null ? "" : body2.getName()) +
+ "\n contactPoint: " + contactPoint +
+ "\n contactNormal: " + contactNormal +
+ "\n restitution: " + restitution +
+ "\n penetration: " + penetration +
+ "\n friction: " + friction;
+ }
+
+ }
+}
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactGenerator.as.svn-base
new file mode 100644
index 0000000..18dfb88
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactGenerator.as.svn-base
@@ -0,0 +1,22 @@
+package alternativa.physics.rigid {
+
+ /**
+ * Генератор контактов для твёрдых тел.
+ */
+ public class RigidBodyContactGenerator {
+ /**
+ * Следующий генератор в списке.
+ */
+ public var next:RigidBodyContactGenerator;
+
+ /**
+ * Метод создаёт контакты.
+ *
+ * @param contact первый свободный для записи контакт в списке контактов мира
+ * @return следующий свободный для записи контакт после окончания работы метода. Значение null означает, что доступные контакты исчерпаны.
+ */
+ public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactResolver.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactResolver.as.svn-base
new file mode 100644
index 0000000..7f6df6c
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidBodyContactResolver.as.svn-base
@@ -0,0 +1,237 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.*;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBodyContactResolver {
+
+ private var velocityIterations:int;
+ private var positionIterations:int;
+ private var velocityEpsilon:Number;
+ private var positionEpsilon:Number;
+
+ private var velocityIterationsUsed:int;
+ private var positionIterationsUsed:int;
+
+ private var linearChange1:Point3D = new Point3D();
+ private var linearChange2:Point3D = new Point3D();
+ private var angularChange1:Point3D = new Point3D();
+ private var angularChange2:Point3D = new Point3D();
+
+ private var velocityChange1:Point3D = new Point3D();
+ private var velocityChange2:Point3D = new Point3D();
+ private var rotationChange1:Point3D = new Point3D();
+ private var rotationChange2:Point3D = new Point3D();
+
+ private var deltaVector:Point3D = new Point3D();
+
+ /**
+ *
+ * @param velocityIterations
+ * @param positionIterations
+ * @param velocityEpsilon
+ * @param positionEpsilon
+ */
+ public function RigidBodyContactResolver(velocityIterations:int, positionIterations:int, velocityEpsilon:Number = 0.001, positionEpsilon:Number = 0.001) {
+ this.velocityIterations = velocityIterations;
+ this.positionIterations = positionIterations;
+ this.velocityEpsilon = velocityEpsilon;
+ this.positionEpsilon = positionEpsilon;
+ }
+
+ /**
+ *
+ * @param velocityIterations
+ * @param positionEpsilon
+ */
+ public function setIterations(velocityIterations:int, positionIterations:int):void {
+ this.velocityIterations = velocityIterations;
+ this.positionIterations = positionIterations;
+ }
+
+ /**
+ *
+ * @return
+ */
+ private function isValid():Boolean {
+ return (positionIterations > 0) && (velocityIterations > 0) && (positionEpsilon >= 0) && (velocityEpsilon >= 0);
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function resolveContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ if (numContacts == 0 || !isValid()) {
+ return;
+ }
+ // Подготовка контактов к обработке
+ prepareContacts(firstContact, numContacts, time);
+ // Разделение взаимно проникающих контактов
+ adjustPositions(firstContact, numContacts, time);
+ // Коррекция скорости для контактов
+ adjustVelocities(firstContact, numContacts, time);
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function prepareContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ // Вычисление внутренних данных контакта, необходимых для дальнейшей работы
+ contact.calculateInternals(time);
+ contact = contact.next;
+ }
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function adjustPositions(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ positionIterationsUsed = 0;
+ // Итерационное устранение взаимного проникновения тел в точках контактов в порядке наибольшего пересечения тел
+ while (positionIterationsUsed < positionIterations) {
+ var max:Number = positionEpsilon;
+ var mostSevereContact:RigidBodyContact = null;
+ // Поиск контакта с наибольшим пересечением
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.penetration > max) {
+ max = contact.penetration;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact == null) {
+ // Алгоритм прерывается, если не осталось пересечений
+ return;
+ }
+ // Будим спящего
+ mostSevereContact.matchAwakeState();
+
+ // Разделяем контакт
+ mostSevereContact.applyPositionChange(linearChange1, linearChange2, angularChange1, angularChange2, max);
+ // Обновляем величину проникновения в каждом контакте, содержащем обработанные объекты
+ contact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.body1 == mostSevereContact.body1) {
+ deltaVector.copy(angularChange1);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(linearChange1);
+ contact.penetration -= deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body1 == mostSevereContact.body2) {
+ deltaVector.copy(angularChange2);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(linearChange2);
+ contact.penetration -= deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body2 != null) {
+ if (contact.body2 == mostSevereContact.body1) {
+ deltaVector.copy(angularChange1);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(linearChange1);
+ contact.penetration += deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body2 == mostSevereContact.body2) {
+ deltaVector.copy(angularChange2);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(linearChange2);
+ contact.penetration += deltaVector.dot(contact.contactNormal);
+ }
+ }
+ contact = contact.next;
+ }
+ positionIterationsUsed++;
+ }
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function adjustVelocities(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ velocityIterationsUsed = 0;
+ while (velocityIterationsUsed < velocityIterations) {
+ var max:Number = velocityEpsilon;
+ var mostSevereContact:RigidBodyContact = null;
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.desiredDeltaVelocity > max) {
+ max = contact.desiredDeltaVelocity;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact == null) {
+ return;
+ }
+ // Будим спящих
+ mostSevereContact.matchAwakeState();
+ // Корректируем скорости
+ mostSevereContact.applyVelocityChange(velocityChange1, velocityChange2, rotationChange1, rotationChange2);
+ // Обновляем скорости сближения для каждого контакта, содержащего обработанные объекты
+ contact = firstContact;
+ var needRecalculation:Boolean = false;
+ while (contact.index < numContacts) {
+ if (contact.body1 == mostSevereContact.body1) {
+ deltaVector.copy(rotationChange1);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(velocityChange1);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.add(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body1 == mostSevereContact.body2) {
+ deltaVector.copy(rotationChange2);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(velocityChange2);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.add(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body2 != null) {
+ if (contact.body2 == mostSevereContact.body1) {
+ deltaVector.copy(rotationChange1);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(velocityChange1);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.subtract(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body2 == mostSevereContact.body2) {
+ deltaVector.copy(rotationChange2);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(velocityChange2);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.subtract(deltaVector);
+ needRecalculation = true;
+ }
+ }
+ if (needRecalculation) {
+ contact.calculateDesiredVelocity(time);
+ }
+ contact = contact.next;
+ }
+ velocityIterationsUsed++;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base
new file mode 100644
index 0000000..def9635
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base
@@ -0,0 +1,136 @@
+package alternativa.physics.rigid {
+
+ public class RigidWorld {
+
+ private var firstBody:RigidBody;
+ private var lastBody:RigidBody;
+ private var contactGenerators:RigidBodyContactGenerator;
+ private var lastContactGenerator:RigidBodyContactGenerator;
+
+ public var contacts:RigidBodyContact;
+ private var maxContacts:int;
+ private var resolver:RigidBodyContactResolver;
+
+ public var forceRegistry:BodyForceRegistry = new BodyForceRegistry();
+ public var calculateIterations:Boolean;
+
+ public var numContacts:int;
+
+ public function RigidWorld(maxContacts:int, iterations:int) {
+ this.maxContacts = maxContacts;
+ resolver = new RigidBodyContactResolver(iterations, iterations);
+ createContactList(maxContacts);
+ }
+
+ private function createContactList(numContacts:int):void {
+ if (numContacts > 0) {
+ var contact:RigidBodyContact = contacts = new RigidBodyContact(0);
+ for (var i:int = 1; i < numContacts; i++) {
+ contact.next = new RigidBodyContact(i);
+ contact = contact.next;
+ }
+ }
+ }
+
+ public function addBody(body:RigidBody):void {
+ if (firstBody == null) {
+ firstBody = body;
+ } else {
+ lastBody.next = body;
+ }
+ lastBody = body;
+ while (lastBody.next != null) {
+ lastBody = lastBody.next;
+ }
+ }
+
+ public function removeBody(body:RigidBody):void {
+ if (body == firstBody) {
+ firstBody = firstBody.next;
+ body.next = null;
+ } else {
+ var current:RigidBody = firstBody;
+ var prev:RigidBody = null;
+ while (current != body && current != null) {
+ prev = current;
+ current = current.next;
+ }
+ if (current != null) {
+ if (lastBody == body) {
+ lastBody = prev;
+ }
+ prev.next = current.next;
+ current.next = null;
+ }
+ }
+ }
+
+ public function addContactGenerator(gen:RigidBodyContactGenerator):void {
+ if (contactGenerators == null) {
+ contactGenerators = gen;
+ } else {
+ lastContactGenerator.next = gen;
+ }
+ lastContactGenerator = gen;
+ while (lastContactGenerator.next != null) {
+ lastContactGenerator = lastContactGenerator.next;
+ }
+ }
+
+ public function removeContactGenerator(gen:RigidBodyContactGenerator):void {
+ if (gen == contactGenerators) {
+ contactGenerators = gen.next;
+ gen.next = null;
+ } else {
+ var prev:RigidBodyContactGenerator = contactGenerators;
+ var curr:RigidBodyContactGenerator = contactGenerators.next;
+ while (curr != gen && curr != null) {
+ prev = curr;
+ curr = curr.next;
+ }
+ if (curr == gen) {
+ if (lastContactGenerator == gen) {
+ lastContactGenerator = prev;
+ }
+ prev.next = curr.next;
+ curr.next = null;
+ }
+ }
+ }
+
+ public function startFrame():void {
+ var body:RigidBody = firstBody;
+ while (body != null) {
+ body.clearAccumulators();
+ body.calculateDerivedData();
+ body = body.next;
+ }
+ }
+
+ private function generateContacts():int {
+ var contact:RigidBodyContact = contacts;
+ var gen:RigidBodyContactGenerator = contactGenerators;
+ while (gen != null && contact != null) {
+ contact = gen.addContacts(contact);
+ gen = gen.next;
+ }
+ return contact == null ? maxContacts : contact.index;
+ }
+
+ public function runPhysics(time:Number):void {
+ forceRegistry.updateForces(time);
+ var body:RigidBody = firstBody;
+ while (body != null) {
+ body.integrate(time);
+ body = body.next;
+ }
+ numContacts = generateContacts();
+// trace("[RigidWorld.runPhysics] numContacts", numContacts);
+ if (calculateIterations) {
+ resolver.setIterations(4*numContacts, 4*numContacts);
+ }
+ resolver.resolveContacts(contacts, numContacts, time);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistration.as b/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistration.as
new file mode 100644
index 0000000..e030bbb
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistration.as
@@ -0,0 +1,16 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistration {
+ public var body:RigidBody;
+ public var forceGenerator:IRigidBodyForceGenerator;
+
+ public var next:BodyForceRegistration;
+
+ public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
+ this.body = body;
+ this.forceGenerator = forceGenerator;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistry.as b/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistry.as
new file mode 100644
index 0000000..a538a20
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/BodyForceRegistry.as
@@ -0,0 +1,47 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.force.IRigidBodyForceGenerator;
+ import alternativa.physics.rigid.RigidBody;
+
+ public class BodyForceRegistry {
+
+ protected var registrations:BodyForceRegistration;
+
+ public function BodyForceRegistry() {
+ }
+
+ public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
+ registration.next = registrations;
+ registrations = registration;
+ }
+
+ public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
+ var current:BodyForceRegistration = registrations;
+ var prev:BodyForceRegistration = null;
+ while (current != null) {
+ if (current.body == body && current.forceGenerator == fg) {
+ if (current == registrations) {
+ registrations = current.next;
+ } else {
+ prev.next = current.next;
+ }
+ break;
+ }
+ prev = current;
+ current = current.next;
+ }
+ }
+
+ public function clear():void {
+ registrations = null;
+ }
+
+ public function updateForces(time:Number):void {
+ var current:BodyForceRegistration = registrations;
+ while (current != null) {
+ current.forceGenerator.updateForce(current.body, time);
+ current = current.next;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/RigidBody.as b/0.0.0.18/src/alternativa/physics/rigid/RigidBody.as
new file mode 100644
index 0000000..da46e3f
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/RigidBody.as
@@ -0,0 +1,707 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.altphysics;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+ import alternativa.types.Quaternion;
+
+ use namespace altphysics;
+
+ /**
+ * Класс представляет твёрдое тело.
+ */
+ public class RigidBody {
+
+ private var angularAcceleration:Point3D = new Point3D();
+ private var motionThreshold:Number = 0.1;
+ private var motionThreshold2:Number = 2*motionThreshold;
+ private var baseBias:Number = 0.8;
+ private var name:String;
+
+ public var next:RigidBody;
+ public var prev:RigidBody;
+
+ altphysics var mass:Number;
+ altphysics var inverseMass:Number;
+ altphysics var inverseInertiaTensor:Matrix3D = new Matrix3D();
+
+ altphysics var linearDamping:Number = 0.95;
+ altphysics var angularDamping:Number = 0.95;
+ altphysics var awake:Boolean = true;
+ altphysics var canSleep:Boolean = true;
+
+ altphysics var acceleration:Point3D = new Point3D();
+ altphysics var angularVelocity:Point3D = new Point3D();
+
+ altphysics var position:Point3D = new Point3D();
+ altphysics var orientation:Quaternion = new Quaternion(1, 0, 0, 0);
+ altphysics var velocity:Point3D = new Point3D();
+ altphysics var rotation:Point3D = new Point3D();
+ altphysics var lastFrameAcceleration:Point3D = new Point3D();
+
+ altphysics var transformMatrix:Matrix3D = new Matrix3D();
+ altphysics var inverseInertiaTensorWorld:Matrix3D = new Matrix3D();
+
+ altphysics var forceAccum:Point3D = new Point3D();
+ altphysics var torqueAccum:Point3D = new Point3D();
+ altphysics var motion:Number = 10;
+
+ /**
+ * Создаёт новый экзепляр твёрдого тела.
+ *
+ * @param inverseMass величина, обратная массе тела. Значение 0 указывает, что тело имеет имеет бесконечную массу и момент инерции.
+ * @param inverseInertiaTensor обратный тензор момента инерции тела. Значение null указывает, что тело имеет бесконечную массу и момент инерции.
+ */
+ public function RigidBody(inverseMass:Number = 1, inverseInertiaTensor:Matrix3D = null) {
+ if (inverseMass == 0 || inverseInertiaTensor == null) {
+ setInfinteMass();
+ } else {
+ this.inverseMass = inverseMass;
+ this.mass = 1/inverseMass;
+ this.inverseInertiaTensor.copy(inverseInertiaTensor);
+ }
+ }
+
+ /**
+ * @return имя тела
+ */
+ public function getName():String {
+ return name;
+ }
+
+ /**
+ * Устанавливает имя тела.
+ *
+ * @param value имя тела
+ */
+ public function setName(value:String):void {
+ name = value;
+ }
+
+ /**
+ * Предельное количество движения, ниже которого тело усыпляеся.
+ */
+ public function getMotionThreshold():Number {
+ return motionThreshold;
+ }
+
+ /**
+ * @private
+ */
+ public function setMotionThreshold(value:Number):void {
+ motionThreshold = value;
+ motionThreshold2 = value*2;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setBaseBias(value:Number):void {
+ baseBias = value;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function getBaseBias():Number {
+ return baseBias;
+ }
+
+ /**
+ *
+ */
+ public function getMass():Number {
+ return mass;
+ }
+
+ /**
+ *
+ */
+ public function setMass(value:Number):void {
+ if (value <= 0) {
+ inverseMass = 0;
+ mass = 0;
+ } else {
+ mass = value;
+ inverseMass = 1/value;
+ }
+ }
+
+ /**
+ *
+ */
+ public function getInverseMass():Number {
+ return inverseMass;
+ }
+
+ /**
+ * @private
+ */
+ public function setInverseMass(value:Number):void {
+ if (value > 0) {
+ inverseMass = value;
+ mass = 1/value;
+ } else {
+ inverseMass = 0;
+ mass = 0;
+ }
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function setIntertiaTensor(intertiaTensor:Matrix3D):void {
+ inverseInertiaTensor.copy(intertiaTensor);
+ inverseInertiaTensor.invert();
+ }
+
+ /**
+ *
+ * @param intertiaTensor
+ */
+ public function getIntertiaTensor(intertiaTensor:Matrix3D):void {
+ intertiaTensor.copy(inverseInertiaTensor);
+ intertiaTensor.invert();
+ }
+
+ /**
+ *
+ * @param inverseInertiaTensor
+ */
+ public function setInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
+ this.inverseInertiaTensor.copy(inverseInertiaTensor);
+ }
+
+ /**
+ *
+ * @param inverseInertiaTensor
+ */
+ public function getInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
+ inverseInertiaTensor.copy(this.inverseInertiaTensor);
+ }
+
+ /**
+ *
+ */
+ public function setInfinteMass():void {
+ inverseMass = 0;
+ mass = 0;
+ inverseInertiaTensor.toIdentity();
+ inverseInertiaTensor.a = inverseInertiaTensor.f = inverseInertiaTensor.k = 0;
+ }
+
+ /**
+ *
+ * @param linearDamping
+ * @param angularDamping
+ */
+ public function setDamping(linearDamping:Number, angularDamping:Number):void {
+ this.linearDamping = linearDamping;
+ this.angularDamping = angularDamping;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setLinearDamping(value:Number):void {
+ linearDamping = value;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function getLinearDamping():Number {
+ return linearDamping;
+ }
+
+ /**
+ *
+ * @param value
+ */
+ public function setAngularDamping(value:Number):void {
+ angularDamping = value;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function getAngularDamping():Number {
+ return angularDamping;
+ }
+
+ /**
+ *
+ * @param position
+ */
+ public function setPosition(position:Point3D):void {
+ this.position.x = position.x;
+ this.position.y = position.y;
+ this.position.z = position.z;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setPositionComponents(x:Number, y:Number, z:Number):void {
+ position.x = x;
+ position.y = y;
+ position.z = z;
+ }
+
+ /**
+ *
+ * @param position
+ */
+ public function getPosition(position:Point3D):void {
+ position.x = this.position.x;
+ position.y = this.position.y;
+ position.z = this.position.z;
+ }
+
+ /**
+ *
+ * @param orientation
+ */
+ public function setOrientation(orientation:Quaternion):void {
+ this.orientation.copy(orientation);
+ this.orientation.normalize();
+ }
+
+ /**
+ *
+ * @param r
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function setOrientationComponents(w:Number, x:Number, y:Number, z:Number):void {
+ orientation.reset(w, x, y, z);
+ orientation.normalize();
+ }
+
+ /**
+ *
+ * @param orientation
+ */
+ public function getOrientation(orientation:Quaternion):void {
+ orientation.copy(this.orientation);
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function getTransformMatrix(matrix:Matrix3D):void {
+ matrix.copy(transformMatrix);
+ }
+
+ /**
+ *
+ * @param velocity
+ */
+ public function setVelocity(velocity:Point3D):void {
+ this.velocity.copy(velocity);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setVelocityComponents(x:Number, y:Number, z:Number):void {
+ velocity.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param velocity
+ */
+ public function getVelocity(velocity:Point3D):void {
+ velocity.copy(this.velocity);
+ }
+
+ /**
+ *
+ * @param acceleration
+ *
+ */
+ public function setAcceleration(acceleration:Point3D):void {
+ this.acceleration.copy(acceleration);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ *
+ */
+ public function setAccelerationComponents(x:Number, y:Number, z:Number):void {
+ acceleration.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param acceleration
+ *
+ */
+ public function getAcceleration(acceleration:Point3D):void {
+ acceleration.copy(this.acceleration);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function setRotation(rotation:Point3D):void {
+ this.rotation.copy(rotation);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ *
+ */
+ public function setRotationComponents(x:Number, y:Number, z:Number):void {
+ rotation.reset(x, y, z);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function addRotation(deltaRotation:Point3D):void {
+ rotation.add(deltaRotation);
+ }
+
+ /**
+ *
+ * @param rotation
+ *
+ */
+ public function getRotation(rotation:Point3D):void {
+ rotation.copy(this.rotation);
+ }
+
+ /**
+ *
+ * @param angularVelocity
+ */
+ public function setAngularVelocity(angularVelocity:Point3D):void {
+ this.angularVelocity.x = angularVelocity.x;
+ this.angularVelocity.y = angularVelocity.y;
+ this.angularVelocity.z = angularVelocity.z;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setAngularVelocityComponents(x:Number, y:Number, z:Number):void {
+ angularVelocity.x = x;
+ angularVelocity.y = y;
+ angularVelocity.z = z;
+ }
+
+ /**
+ *
+ * @param awake
+ */
+ public function setAwake(awake:Boolean = true):void {
+ this.awake = awake;
+ if (awake) {
+ motion = motionThreshold2;
+ } else {
+ velocity.x = 0;
+ velocity.y = 0;
+ velocity.z = 0;
+
+ rotation.x = 0;
+ rotation.y = 0;
+ rotation.z = 0;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function isAwake():Boolean {
+ return awake;
+ }
+
+ /**
+ *
+ * @param canSleep
+ */
+ public function setCanSleep(canSleep:Boolean = true):void {
+ this.canSleep = canSleep;
+ if (!canSleep && !awake) {
+ setAwake();
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function isCanSleep():Boolean {
+ return canSleep;
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к центру масс тела.
+ *
+ * @param force вектор силы в глобальной системе координат
+ */
+ public function addForce(force:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ setAwake();
+ }
+
+ /**
+ *
+ * @param force
+ */
+ public function addForceComponents(x:Number, y:Number, z:Number):void {
+ forceAccum.x += x;
+ forceAccum.y += y;
+ forceAccum.z += z;
+ setAwake();
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к заданной локальной точке тела.
+ *
+ * @param force вектор силы в глобальной системе координат
+ * @param localPoint локальные координаты точки приложения силы
+ */
+ public function addForceAtLocalPoint(force:Point3D, localPoint:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ // Трансформация локальных координат в глобальные
+ var x:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
+ var y:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
+ var z:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
+ torqueAccum.x += y*force.z - z*force.y;
+ torqueAccum.y += z*force.x - x*force.z;
+ torqueAccum.z += x*force.y - y*force.x;
+ setAwake();
+ }
+
+ /**
+ * Добавляет глобальную силу, приложенную к заданной точке тела, выраженной в глобальных координатах.
+ *
+ * @param force вектор силы в глобальной системе координат
+ * @param point глобальные координаты точки приложения силы
+ */
+ public function addForceAtPoint(force:Point3D, point:Point3D):void {
+ forceAccum.x += force.x;
+ forceAccum.y += force.y;
+ forceAccum.z += force.z;
+ var x:Number = point.x - position.x;
+ var y:Number = point.y - position.y;
+ var z:Number = point.z - position.z;
+ torqueAccum.x += y*force.z - z*force.y;
+ torqueAccum.y += z*force.x - x*force.z;
+ torqueAccum.z += x*force.y - y*force.x;
+ setAwake();
+ }
+
+ /**
+ * Добавляет локальную силу, приложенную в заданной локальной точке тела.
+ *
+ * @param localForce вектор локальной силы
+ * @param localPoint локальные координаты точки приложения силы
+ */
+ public function addLocalForce(localForce:Point3D, localPoint:Point3D):void {
+ // Трансформация локального вектора силы в глобальный
+ var globalForceX:Number = transformMatrix.a*localForce.x + transformMatrix.b*localForce.y + transformMatrix.c*localForce.z;
+ var globalForceY:Number = transformMatrix.e*localForce.x + transformMatrix.f*localForce.y + transformMatrix.g*localForce.z;
+ var globalForceZ:Number = transformMatrix.i*localForce.x + transformMatrix.j*localForce.y + transformMatrix.k*localForce.z;
+ forceAccum.x += globalForceX;
+ forceAccum.y += globalForceY;
+ forceAccum.z += globalForceZ;
+ // Получение глобального вектора плеча силы
+ var globalPointX:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
+ var globalPointY:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
+ var globalPointZ:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
+ // Приращение момента силы
+ torqueAccum.x += globalPointY*globalForceZ - globalPointZ*globalForceY;
+ torqueAccum.y += globalPointZ*globalForceX - globalPointX*globalForceZ;
+ torqueAccum.z += globalPointX*globalForceY - globalPointY*globalForceX;
+ setAwake();
+ }
+
+ /**
+ * Добавляет момент силы.
+ *
+ * @param torque вектор момента силы
+ */
+ public function addTorque(torque:Point3D):void {
+ torqueAccum.x += torque.x;
+ torqueAccum.y += torque.y;
+ torqueAccum.z += torque.z;
+ setAwake();
+ }
+
+ /**
+ * Добавляет момент силы, заданный покомпонентно.
+ */
+ public function addTorqueComponents(x:Number, y:Number, z:Number):void {
+ torqueAccum.x += x;
+ torqueAccum.y += y;
+ torqueAccum.z += z;
+ setAwake();
+ }
+
+ /**
+ * Вычисляет векторные параметры тела в глобальной системе координат.
+ */
+ public function calculateDerivedData():void {
+ // inlined position.normalize()
+ var d:Number = orientation.w*orientation.w + orientation.x*orientation.x + orientation.y*orientation.y + orientation.z*orientation.z;
+ if (d == 0) {
+ orientation.w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ orientation.w *= d;
+ orientation.x *= d;
+ orientation.y *= d;
+ orientation.z *= d;
+ }
+ // Вычисление глобальной матрицы трансформации
+ orientation.toMatrix3D(transformMatrix);
+ transformMatrix.d = position.x;
+ transformMatrix.h = position.y;
+ transformMatrix.l = position.z;
+ // Расчёт обратного тензора инерции в мировых координатах
+ var a:Number = transformMatrix.a*inverseInertiaTensor.a + transformMatrix.b*inverseInertiaTensor.e + transformMatrix.c*inverseInertiaTensor.i;
+ var b:Number = transformMatrix.a*inverseInertiaTensor.b + transformMatrix.b*inverseInertiaTensor.f + transformMatrix.c*inverseInertiaTensor.j;
+ var c:Number = transformMatrix.a*inverseInertiaTensor.c + transformMatrix.b*inverseInertiaTensor.g + transformMatrix.c*inverseInertiaTensor.k;
+ var e:Number = transformMatrix.e*inverseInertiaTensor.a + transformMatrix.f*inverseInertiaTensor.e + transformMatrix.g*inverseInertiaTensor.i;
+ var f:Number = transformMatrix.e*inverseInertiaTensor.b + transformMatrix.f*inverseInertiaTensor.f + transformMatrix.g*inverseInertiaTensor.j;
+ var g:Number = transformMatrix.e*inverseInertiaTensor.c + transformMatrix.f*inverseInertiaTensor.g + transformMatrix.g*inverseInertiaTensor.k;
+ var i:Number = transformMatrix.i*inverseInertiaTensor.a + transformMatrix.j*inverseInertiaTensor.e + transformMatrix.k*inverseInertiaTensor.i;
+ var j:Number = transformMatrix.i*inverseInertiaTensor.b + transformMatrix.j*inverseInertiaTensor.f + transformMatrix.k*inverseInertiaTensor.j;
+ var k:Number = transformMatrix.i*inverseInertiaTensor.c + transformMatrix.j*inverseInertiaTensor.g + transformMatrix.k*inverseInertiaTensor.k;
+ // Здесь подразумевается, что матрица трансформации тела не содержит масштабирования
+ // Произведение [transformMatrix*inverseInertiaTensor]*transpose(transformMatrix)
+ inverseInertiaTensorWorld.a = a*transformMatrix.a + b*transformMatrix.b + c*transformMatrix.c;
+ inverseInertiaTensorWorld.b = a*transformMatrix.e + b*transformMatrix.f + c*transformMatrix.g;
+ inverseInertiaTensorWorld.c = a*transformMatrix.i + b*transformMatrix.j + c*transformMatrix.k;
+ inverseInertiaTensorWorld.e = e*transformMatrix.a + f*transformMatrix.b + g*transformMatrix.c;
+ inverseInertiaTensorWorld.f = e*transformMatrix.e + f*transformMatrix.f + g*transformMatrix.g;
+ inverseInertiaTensorWorld.g = e*transformMatrix.i + f*transformMatrix.j + g*transformMatrix.k;
+ inverseInertiaTensorWorld.i = i*transformMatrix.a + j*transformMatrix.b + k*transformMatrix.c;
+ inverseInertiaTensorWorld.j = i*transformMatrix.e + j*transformMatrix.f + k*transformMatrix.g;
+ inverseInertiaTensorWorld.k = i*transformMatrix.i + j*transformMatrix.j + k*transformMatrix.k;
+ }
+
+ /**
+ * Интегрирует динамические характеристики и обновляет параметры состояния тела.
+ *
+ * @param time временной шаг интегрирования
+ */
+ public function integrate(time:Number):void {
+ if (!awake) {
+ return;
+ }
+
+ lastFrameAcceleration.x = acceleration.x + forceAccum.x*inverseMass;
+ lastFrameAcceleration.y = acceleration.y + forceAccum.y*inverseMass;
+ lastFrameAcceleration.z = acceleration.z + forceAccum.z*inverseMass;
+ // angularAcceleration = inverseInertiaTensorWorld*torqueAccum
+ angularAcceleration.x = inverseInertiaTensorWorld.a*torqueAccum.x + inverseInertiaTensorWorld.b*torqueAccum.y + inverseInertiaTensorWorld.c*torqueAccum.z;
+ angularAcceleration.y = inverseInertiaTensorWorld.e*torqueAccum.x + inverseInertiaTensorWorld.f*torqueAccum.y + inverseInertiaTensorWorld.g*torqueAccum.z;
+ angularAcceleration.z = inverseInertiaTensorWorld.i*torqueAccum.x + inverseInertiaTensorWorld.j*torqueAccum.y + inverseInertiaTensorWorld.k*torqueAccum.z;
+
+ var d:Number = Math.pow(linearDamping, time);
+ var dtime:Number = d*time;
+
+ velocity.x += lastFrameAcceleration.x*dtime;
+ velocity.y += lastFrameAcceleration.y*dtime;
+ velocity.z += lastFrameAcceleration.z*dtime;
+
+ rotation.x += angularAcceleration.x*dtime;
+ rotation.y += angularAcceleration.y*dtime;
+ rotation.z += angularAcceleration.z*dtime;
+
+ rotation.x += angularVelocity.x*time;
+ rotation.y += angularVelocity.y*time;
+ rotation.z += angularVelocity.z*time;
+
+ position.x += velocity.x*time;
+ position.y += velocity.y*time;
+ position.z += velocity.z*time;
+
+ orientation.addScaledVector(rotation, time);
+
+ // TODO: Нужно ли двойное уменьшение скоростей?
+ velocity.x *= d;
+ velocity.y *= d;
+ velocity.z *= d;
+
+ rotation.x *= d;
+ rotation.y *= d;
+ rotation.z *= d;
+
+ calculateDerivedData();
+ // inlined clearAccumulators()
+ forceAccum.x = 0;
+ forceAccum.y = 0;
+ forceAccum.z = 0;
+ torqueAccum.x = 0;
+ torqueAccum.y = 0;
+ torqueAccum.z = 0;
+
+ // Усыпление тела при необходимости
+ if (canSleep) {
+ var currentMotion:Number = velocity.x*velocity.x + velocity.y*velocity.y + velocity.z*velocity.z +
+ rotation.x*rotation.x + rotation.y*rotation.y + rotation.z*rotation.z;
+ var bias:Number = Math.pow(baseBias, time);
+ motion = bias*motion + (1 - bias)*currentMotion;
+// trace("[RigidBody::integrate]", name, "motion", motion, "motionEpsilon", motionEpsilon);
+ if (motion < motionThreshold) {
+ setAwake(false);
+ } else {
+ if (motion > motionThreshold2) {
+ motion = motionThreshold2;
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public function clearAccumulators():void {
+ forceAccum.x = 0;
+ forceAccum.y = 0;
+ forceAccum.z = 0;
+ torqueAccum.x = 0;
+ torqueAccum.y = 0;
+ torqueAccum.z = 0;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContact.as b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContact.as
new file mode 100644
index 0000000..22b3c65
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContact.as
@@ -0,0 +1,550 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.*;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ * Класс описывает контакт между телами. Контакты могут составлять двунаправленный список.
+ */
+ public class RigidBodyContact {
+
+ private var tmpVelocityVector:Point3D = new Point3D();
+ private var impulse:Point3D = new Point3D();
+
+ public static var velocityLimit:Number = 1;
+
+ /**
+ * Ссылка на следующий контакт в списке.
+ */
+ public var next:RigidBodyContact;
+ /**
+ * Ссылка на предыдущий контакт в списке.
+ */
+ public var prev:RigidBodyContact;
+ /**
+ * Индекс контакта в списке.
+ */
+ public var index:int;
+
+ /**
+ * Первое тело, к которому относится контакт.
+ */
+ public var body1:RigidBody;
+ /**
+ * Первое тело, к которому относится контакт (может быть null).
+ */
+ public var body2:RigidBody;
+ /**
+ * Точка контакта.
+ */
+ public var contactPoint:Point3D = new Point3D();
+ /**
+ * Нормаль контакта. Нормаль направлена от второго тела к первому. В локальной системе координат контакта
+ * нормаль является базисным вектором, задающим направление оси X.
+ */
+ public var contactNormal:Point3D = new Point3D();
+ /**
+ * Коэффициент взаимного трения в точке контакта.
+ */
+ public var friction:Number = 0;
+ /**
+ * Коэффициент упругости удара.
+ */
+ public var restitution:Number = 0;
+ /**
+ * Глубина взаимного проникновения тел в точке контакта.
+ */
+ public var penetration:Number;
+
+ public var timeStamp:int;
+
+ /**
+ * Матрица перехода из локальной системы координат контакта в мировую систему координат без компонентов параллельного переноса.
+ * Матрица задаёт базис локальной системы координат контакта.
+ */
+ altphysics var contactToWorld:Matrix3D = new Matrix3D();
+ /**
+ * Относительная скорость в точке контакта. Вычисляется в методе calculateInternals().
+ */
+ altphysics var contactVelocity:Point3D = new Point3D();
+ /**
+ * Приращение скорости в точке контакта по направлению нормали, необходимое для прекращения сближения. Вычисляется в методе calculateInternals().
+ */
+ altphysics var desiredDeltaVelocity:Number;
+ /**
+ * Координаты точки контакта относительныо первого тела. Вычисляется в методе calculateInternals().
+ */
+ altphysics var relativeContactPosition1:Point3D = new Point3D();
+ /**
+ * Координаты точки контакта относительныо второго тела. Вычисляется в методе calculateInternals().
+ */
+ altphysics var relativeContactPosition2:Point3D = new Point3D();
+
+ /**
+ *
+ * @param index
+ */
+ public function RigidBodyContact(index:int) {
+ this.index = index;
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param friction
+ * @param restitution
+ */
+ public function setBodyData(body1:RigidBody, body2:RigidBody, friction:Number, restitution:Number):void {
+ this.body1 = body1;
+ this.body2 = body2;
+ this.friction = friction;
+ this.restitution = restitution;
+ }
+
+ /**
+ *
+ */
+ public function matchAwakeState():void {
+ if (body2 == null) {
+ return;
+ }
+
+ if (body1.awake != body2.awake) {
+ if (body1.awake) {
+ body2.setAwake();
+ } else {
+ body1.setAwake();
+ }
+ }
+ }
+
+ /**
+ * Вычисляет и сохраняет базис локальной системы координат контакта.
+ */
+ public function calculateContactBasis():void {
+ // Нормаль является ортом X локальной системы координат
+ contactToWorld.a = contactNormal.x;
+ contactToWorld.e = contactNormal.y;
+ contactToWorld.i = contactNormal.z;
+ var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
+ var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
+ var s:Number;
+ if (nx > ny) {
+ // Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
+ s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
+ contactToWorld.b = contactNormal.z*s;
+ contactToWorld.f = 0;
+ contactToWorld.j = -contactNormal.x*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j;
+ contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
+ contactToWorld.k = -contactNormal.y*contactToWorld.b;
+ } else {
+ // Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
+ s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
+ contactToWorld.b = 0;
+ contactToWorld.f = -contactNormal.z*s;
+ contactToWorld.j = contactNormal.y*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
+ contactToWorld.g = -contactNormal.x*contactToWorld.j;
+ contactToWorld.k = contactNormal.x*contactToWorld.f;
+ }
+ }
+
+ /**
+ * Вычисляет скорость точки заданного тела в точке контакта в его локальной системе координат.
+ *
+ * @param body тело, для которого выполняются вычисления
+ * @param relativeContactPosition положение точки контакта относительно тела
+ * @param time временной шаг
+ * @param result в эту переменную записывается результат
+ */
+ public function calculateLocalVelocity(body:RigidBody, relativeContactPosition:Point3D, time:Number, result:Point3D):void {
+ // Расчёт прироста скорости за последний кадр от действующих сил
+ var x:Number = body.lastFrameAcceleration.x*time;
+ var y:Number = body.lastFrameAcceleration.y*time;
+ var z:Number = body.lastFrameAcceleration.z*time;
+ // Прирост скорости в системе координат контакта
+ // Убираем прирост скорости в направлении контакта
+ var accVelocityY:Number = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z;
+ var accVelocityZ:Number = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z;
+
+ // Скорость точки контакта в мировой системе координат
+ // Скорость за счёт вращения тела
+ var rotation:Point3D = body.rotation;
+ x = rotation.y*relativeContactPosition.z - rotation.z*relativeContactPosition.y + body.velocity.x;
+ y = rotation.z*relativeContactPosition.x - rotation.x*relativeContactPosition.z + body.velocity.y;
+ z = rotation.x*relativeContactPosition.y - rotation.y*relativeContactPosition.x + body.velocity.z;
+ // Скорость точки контакта в системе координат контакта
+ // Касательные компоненты прироста скорости добавляются к скорости точки контакта. В дальнейшем они либо уберутся силой трения,
+ // либо уменьшатся ею.
+ result.x = contactToWorld.a*x + contactToWorld.e*y + contactToWorld.i*z;
+ result.y = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z + accVelocityY;
+ result.z = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z + accVelocityZ;
+ }
+
+ /**
+ * Вычисляет и устанавливает внутреннее значение для требуемого изменения относительной скорости вдоль нормали контакта.
+ * С реакциями опоры разбираемся путём вычитания из скорости сближения прироста скорости, полученного во время последнего кадра.
+ *
+ * @param time
+ */
+ public function calculateDesiredVelocity(time:Number):void {
+ // Вычисляем проекцию прироста скорости за текущий кадр на нормаль контакта. Для относительного прироста скорости, вызывающего
+ // сближение объектов, значение будет отрицательным.
+ var velocityFromAcc:Number = 0;
+ if (body1.awake) {
+ velocityFromAcc = (body1.lastFrameAcceleration.x*contactNormal.x + body1.lastFrameAcceleration.y*contactNormal.y + body1.lastFrameAcceleration.z*contactNormal.z)*time;
+ }
+ if (body2 != null && body2.awake) {
+ velocityFromAcc -= (body2.lastFrameAcceleration.x*contactNormal.x + body2.lastFrameAcceleration.y*contactNormal.y + body2.lastFrameAcceleration.z*contactNormal.z)*time;
+ }
+ // Если относительная скорость вдоль нормали контакта слишком мала, игнорируем её
+ var thisRestitution:Number = restitution;
+ if (contactVelocity.x > -velocityLimit && contactVelocity.x < velocityLimit) {
+ thisRestitution = 0;
+ }
+ // Вычисляем дельту для скорости отскока, убрав влияние прироста скорости за последний кадр
+ desiredDeltaVelocity = -contactVelocity.x - thisRestitution*(contactVelocity.x - velocityFromAcc);
+ }
+
+ /**
+ * Расчитывает внутренние данные о контакта на основе данных о его состоянии. Вызывается до того, как будет запущен
+ * алгоритм разрешения контактов. Эта функция в нормальных условиях никогда не должна вызываться вручную.
+ *
+ * @param time
+ */
+ public function calculateInternals(time:Number):void {
+ // Расчёт базиса системы координат контакта
+ // === inlined calculateContactBasis();
+ contactToWorld.a = contactNormal.x;
+ contactToWorld.e = contactNormal.y;
+ contactToWorld.i = contactNormal.z;
+ var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
+ var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
+ var s:Number;
+ if (nx > ny) {
+ // Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
+ s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
+ contactToWorld.b = contactNormal.z*s;
+ contactToWorld.f = 0;
+ contactToWorld.j = -contactNormal.x*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j;
+ contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
+ contactToWorld.k = -contactNormal.y*contactToWorld.b;
+ } else {
+ // Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
+ s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
+ contactToWorld.b = 0;
+ contactToWorld.f = -contactNormal.z*s;
+ contactToWorld.j = contactNormal.y*s;
+ // Локальный орт Z находится как результат векторного произведения contactNormal x localY.
+ contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
+ contactToWorld.g = -contactNormal.x*contactToWorld.j;
+ contactToWorld.k = contactNormal.x*contactToWorld.f;
+ }
+ // === end inlined
+
+ // Расчёт локальной скорости в точке контакта для первого тела
+ relativeContactPosition1.x = contactPoint.x - body1.position.x;
+ relativeContactPosition1.y = contactPoint.y - body1.position.y;
+ relativeContactPosition1.z = contactPoint.z - body1.position.z;
+ calculateLocalVelocity(body1, relativeContactPosition1, time, contactVelocity);
+ // Расчёт локальной скорости в точке контакта для второго тела
+ if (body2 != null) {
+ relativeContactPosition2.x = contactPoint.x - body2.position.x;
+ relativeContactPosition2.y = contactPoint.y - body2.position.y;
+ relativeContactPosition2.z = contactPoint.z - body2.position.z;
+ calculateLocalVelocity(body2, relativeContactPosition2, time, tmpVelocityVector);
+ // Расчёт относительной скорости в точке контакта
+ contactVelocity.x -= tmpVelocityVector.x;
+ contactVelocity.y -= tmpVelocityVector.y;
+ contactVelocity.z -= tmpVelocityVector.z;
+ }
+ // Расчёт вектора изменения скорости, требуемого для разрешения контакта
+ calculateDesiredVelocity(time);
+ }
+
+ /**
+ * Выполняет разделение контакта посредством импульса, пропорционально инерции тел.
+ */
+ public function applyVelocityChange(velocityChange1:Point3D, velocityChange2:Point3D, rotationChange1:Point3D, rotationChange2:Point3D):void {
+ var inverseInertiaTensor1:Matrix3D = body1.inverseInertiaTensorWorld;
+ var inverseInertiaTensor2:Matrix3D;
+ if (body2 != null) {
+ inverseInertiaTensor2 = body2.inverseInertiaTensorWorld;
+ }
+
+ if (friction == 0) {
+ calculateFrictionlessImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
+ } else {
+ calculateFrictionImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
+ }
+
+ // Переведём импульс в мировые координаты
+ var x:Number = impulse.x;
+ var y:Number = impulse.y;
+ var z:Number = impulse.z;
+ impulse.x = contactToWorld.a*x + contactToWorld.b*y + contactToWorld.c*z;
+ impulse.y = contactToWorld.e*x + contactToWorld.f*y + contactToWorld.g*z;
+ impulse.z = contactToWorld.i*x + contactToWorld.j*y + contactToWorld.k*z;
+
+ // Разделим импульс на вращательную и линейную часть
+ x = relativeContactPosition1.y*impulse.z - relativeContactPosition1.z*impulse.y;
+ y = relativeContactPosition1.z*impulse.x - relativeContactPosition1.x*impulse.z;
+ z = relativeContactPosition1.x*impulse.y - relativeContactPosition1.y*impulse.x;
+ rotationChange1.x = inverseInertiaTensor1.a*x + inverseInertiaTensor1.b*y + inverseInertiaTensor1.c*z;
+ rotationChange1.y = inverseInertiaTensor1.e*x + inverseInertiaTensor1.f*y + inverseInertiaTensor1.g*z;
+ rotationChange1.z = inverseInertiaTensor1.i*x + inverseInertiaTensor1.j*y + inverseInertiaTensor1.k*z;
+
+ velocityChange1.x = impulse.x*body1.inverseMass;
+ velocityChange1.y = impulse.y*body1.inverseMass;
+ velocityChange1.z = impulse.z*body1.inverseMass;
+
+ body1.velocity.x += velocityChange1.x;
+ body1.velocity.y += velocityChange1.y;
+ body1.velocity.z += velocityChange1.z;
+
+ body1.rotation.x += rotationChange1.x;
+ body1.rotation.y += rotationChange1.y;
+ body1.rotation.z += rotationChange1.z;
+
+ if (body2 != null) {
+ x = relativeContactPosition2.z*impulse.y - relativeContactPosition2.y*impulse.z;
+ y = relativeContactPosition2.x*impulse.z - relativeContactPosition2.z*impulse.x;
+ z = relativeContactPosition2.y*impulse.x - relativeContactPosition2.x*impulse.y;
+ rotationChange2.x = inverseInertiaTensor2.a*x + inverseInertiaTensor2.b*y + inverseInertiaTensor2.c*z;
+ rotationChange2.y = inverseInertiaTensor2.e*x + inverseInertiaTensor2.f*y + inverseInertiaTensor2.g*z;
+ rotationChange2.z = inverseInertiaTensor2.i*x + inverseInertiaTensor2.j*y + inverseInertiaTensor2.k*z;
+
+ velocityChange2.x = -impulse.x*body2.inverseMass;
+ velocityChange2.y = -impulse.y*body2.inverseMass;
+ velocityChange2.z = -impulse.z*body2.inverseMass;
+
+ body2.velocity.x += velocityChange2.x;
+ body2.velocity.y += velocityChange2.y;
+ body2.velocity.z += velocityChange2.z;
+
+ body2.rotation.x += rotationChange2.x;
+ body2.rotation.y += rotationChange2.y;
+ body2.rotation.z += rotationChange2.z;
+ }
+ }
+
+ /**
+ * Вычисляет импульс, необходимый для коррекции скоростей в точке контакта в отсутствие сил трения.
+ */
+ public function calculateFrictionlessImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
+ tmpVelocityVector.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
+ tmpVelocityVector.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
+ tmpVelocityVector.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
+
+ var x:Number = inverseInertiaTensor1.a*tmpVelocityVector.x + inverseInertiaTensor1.b*tmpVelocityVector.y + inverseInertiaTensor1.c*tmpVelocityVector.z;
+ var y:Number = inverseInertiaTensor1.e*tmpVelocityVector.x + inverseInertiaTensor1.f*tmpVelocityVector.y + inverseInertiaTensor1.g*tmpVelocityVector.z;
+ var z:Number = inverseInertiaTensor1.i*tmpVelocityVector.x + inverseInertiaTensor1.j*tmpVelocityVector.y + inverseInertiaTensor1.k*tmpVelocityVector.z;
+
+ tmpVelocityVector.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
+ tmpVelocityVector.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
+ tmpVelocityVector.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
+
+ var deltaVelocity:Number = tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body1.inverseMass;
+
+ if (body2 != null) {
+ tmpVelocityVector.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
+ tmpVelocityVector.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
+ tmpVelocityVector.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
+
+ x = inverseInertiaTensor2.a*tmpVelocityVector.x + inverseInertiaTensor2.b*tmpVelocityVector.y + inverseInertiaTensor2.c*tmpVelocityVector.z;
+ y = inverseInertiaTensor2.e*tmpVelocityVector.x + inverseInertiaTensor2.f*tmpVelocityVector.y + inverseInertiaTensor2.g*tmpVelocityVector.z;
+ z = inverseInertiaTensor2.i*tmpVelocityVector.x + inverseInertiaTensor2.j*tmpVelocityVector.y + inverseInertiaTensor2.k*tmpVelocityVector.z;
+
+ tmpVelocityVector.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
+ tmpVelocityVector.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
+ tmpVelocityVector.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
+
+ deltaVelocity += tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body2.inverseMass;
+ }
+
+ result.x = desiredDeltaVelocity/deltaVelocity;
+ result.y = 0;
+ result.z = 0;
+ }
+
+ private var impulseToTorque:Matrix3D = new Matrix3D();
+ private var deltaVelWorldMatrix:Matrix3D = new Matrix3D();
+ private var deltaVelWorldMatrix2:Matrix3D = new Matrix3D();
+ /**
+ * Вычисляет импульс, необходимый для коррекции скоростей в точке контакта при наличии сил трения.
+ */
+ public function calculateFrictionImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
+ var inverseMass:Number = body1.inverseMass;
+
+ relativeContactPosition1.createSkewSymmetricMatrix(impulseToTorque);
+
+ deltaVelWorldMatrix.copy(impulseToTorque);
+ deltaVelWorldMatrix.inverseCombine(inverseInertiaTensor1);
+ deltaVelWorldMatrix.inverseCombine(impulseToTorque);
+ deltaVelWorldMatrix.multByScalar(-1);
+
+ if (body2 != null) {
+ relativeContactPosition2.createSkewSymmetricMatrix(impulseToTorque);
+ deltaVelWorldMatrix2.copy(impulseToTorque);
+ deltaVelWorldMatrix2.inverseCombine(inverseInertiaTensor2);
+ deltaVelWorldMatrix2.inverseCombine(impulseToTorque);
+ deltaVelWorldMatrix2.multByScalar(-1);
+
+ deltaVelWorldMatrix.add(deltaVelWorldMatrix2);
+ inverseMass += body2.inverseMass;
+ }
+
+ deltaVelWorldMatrix2.copy(contactToWorld);
+ deltaVelWorldMatrix2.transpose();
+ deltaVelWorldMatrix2.inverseCombine(deltaVelWorldMatrix);
+ deltaVelWorldMatrix2.inverseCombine(contactToWorld);
+ deltaVelWorldMatrix2.a += inverseMass;
+ deltaVelWorldMatrix2.f += inverseMass;
+ deltaVelWorldMatrix2.k += inverseMass;
+
+ deltaVelWorldMatrix.copy(deltaVelWorldMatrix2);
+ deltaVelWorldMatrix.invert();
+
+ result.reset(desiredDeltaVelocity, -contactVelocity.y, -contactVelocity.z);
+ result.transform(deltaVelWorldMatrix);
+
+ var planarImpulse:Number = Math.sqrt(result.y*result.y + result.z*result.z);
+ if (planarImpulse > result.x*friction) {
+ result.y /= planarImpulse;
+ result.z /= planarImpulse;
+ result.x = desiredDeltaVelocity/(deltaVelWorldMatrix2.a + deltaVelWorldMatrix2.b*friction*result.y + deltaVelWorldMatrix2.c*friction*result.z);
+ result.y *= friction*result.x;
+ result.z *= friction*result.x;
+ }
+ }
+
+ private var angularInertiaWorld:Point3D = new Point3D();
+ /**
+ * Выполняет разделение контакта с учётом инертности тел.
+ */
+ public function applyPositionChange(linearChange1:Point3D, linearChange2:Point3D, angularChange1:Point3D, angularChange2:Point3D, penetration:Number):void {
+ var totalInertia:Number = 0;
+ var angularInertia1:Number;
+ var angularInertia2:Number;
+ var linearInertia1:Number;
+ var linearInertia2:Number;
+
+ // body1
+ angularInertiaWorld.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
+ angularInertiaWorld.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
+ angularInertiaWorld.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
+ var m:Matrix3D = body1.inverseInertiaTensorWorld;
+ var x:Number = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
+ var y:Number = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
+ var z:Number = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
+ angularInertiaWorld.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
+ angularInertiaWorld.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
+ angularInertiaWorld.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
+
+ angularInertia1 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
+ linearInertia1 = body1.inverseMass;
+
+ totalInertia = linearInertia1 + angularInertia1;
+
+ if (body2 != null) {
+ angularInertiaWorld.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
+ angularInertiaWorld.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
+ angularInertiaWorld.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
+ m = body2.inverseInertiaTensorWorld;
+ x = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
+ y = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
+ z = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
+ angularInertiaWorld.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
+ angularInertiaWorld.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
+ angularInertiaWorld.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
+
+ angularInertia2 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
+ linearInertia2 = body2.inverseMass;
+
+ totalInertia += linearInertia2 + angularInertia2;
+ }
+
+ // body1
+ applyPostionChangeToBody(body1, relativeContactPosition1, angularInertia1, linearInertia1, totalInertia, 1, angularChange1, linearChange1);
+
+ if (body2 != null) {
+ applyPostionChangeToBody(body2, relativeContactPosition2, angularInertia2, linearInertia2, totalInertia, -1, angularChange2, linearChange2);
+ }
+ }
+
+ private var projection:Point3D = new Point3D();
+ /**
+ *
+ * @param body
+ * @param relativeContactPosition
+ * @param angularInertia
+ * @param linearInertia
+ * @param totalInertia
+ * @param sign
+ * @param angularChange
+ * @param linearChange
+ */
+ private function applyPostionChangeToBody(body:RigidBody, relativeContactPosition:Point3D, angularInertia:Number, linearInertia:Number, totalInertia:Number, sign:Number, angularChange:Point3D, linearChange:Point3D):void {
+ var angularLimit:Number = 0.2;
+ var angularMove:Number = sign*penetration*angularInertia/totalInertia;
+ var linearMove:Number = sign*penetration*linearInertia/totalInertia;
+
+ projection.copy(contactNormal);
+ projection.multiply(-relativeContactPosition.dot(contactNormal));
+ projection.add(relativeContactPosition);
+
+ var maxMagnitude:Number = angularLimit*projection.length;
+
+ var totalMove:Number;
+ if (angularMove < -maxMagnitude) {
+ totalMove = angularMove + linearMove;
+ angularMove = -maxMagnitude;
+ linearMove = totalMove - angularMove;
+ } else if (angularMove > maxMagnitude) {
+ totalMove = angularMove + linearMove;
+ angularMove = maxMagnitude;
+ linearMove = totalMove - angularMove;
+ }
+
+ if (angularMove == 0) {
+ angularChange.reset();
+ } else {
+ angularChange.copy(relativeContactPosition);
+ angularChange.cross(contactNormal);
+ angularChange.transform(body.inverseInertiaTensorWorld);
+ angularChange.multiply(angularMove/angularInertia);
+ }
+
+ linearChange.copy(contactNormal);
+ linearChange.multiply(linearMove);
+
+ body.position.add(linearChange);
+ body.orientation.addScaledVector(angularChange, 1);
+ if (!body.awake) {
+ body.calculateDerivedData();
+ }
+ }
+
+ public function toString():String {
+ return "RigidBodyContact" +
+ "\n timeStamp: " + timeStamp +
+ "\n body1: " + body1.getName() +
+ "\n body2: " + (body2 == null ? "" : body2.getName()) +
+ "\n contactPoint: " + contactPoint +
+ "\n contactNormal: " + contactNormal +
+ "\n restitution: " + restitution +
+ "\n penetration: " + penetration +
+ "\n friction: " + friction;
+ }
+
+ }
+}
diff --git a/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactGenerator.as b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactGenerator.as
new file mode 100644
index 0000000..18dfb88
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactGenerator.as
@@ -0,0 +1,22 @@
+package alternativa.physics.rigid {
+
+ /**
+ * Генератор контактов для твёрдых тел.
+ */
+ public class RigidBodyContactGenerator {
+ /**
+ * Следующий генератор в списке.
+ */
+ public var next:RigidBodyContactGenerator;
+
+ /**
+ * Метод создаёт контакты.
+ *
+ * @param contact первый свободный для записи контакт в списке контактов мира
+ * @return следующий свободный для записи контакт после окончания работы метода. Значение null означает, что доступные контакты исчерпаны.
+ */
+ public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactResolver.as b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactResolver.as
new file mode 100644
index 0000000..7f6df6c
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/RigidBodyContactResolver.as
@@ -0,0 +1,237 @@
+package alternativa.physics.rigid {
+
+ import alternativa.physics.*;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBodyContactResolver {
+
+ private var velocityIterations:int;
+ private var positionIterations:int;
+ private var velocityEpsilon:Number;
+ private var positionEpsilon:Number;
+
+ private var velocityIterationsUsed:int;
+ private var positionIterationsUsed:int;
+
+ private var linearChange1:Point3D = new Point3D();
+ private var linearChange2:Point3D = new Point3D();
+ private var angularChange1:Point3D = new Point3D();
+ private var angularChange2:Point3D = new Point3D();
+
+ private var velocityChange1:Point3D = new Point3D();
+ private var velocityChange2:Point3D = new Point3D();
+ private var rotationChange1:Point3D = new Point3D();
+ private var rotationChange2:Point3D = new Point3D();
+
+ private var deltaVector:Point3D = new Point3D();
+
+ /**
+ *
+ * @param velocityIterations
+ * @param positionIterations
+ * @param velocityEpsilon
+ * @param positionEpsilon
+ */
+ public function RigidBodyContactResolver(velocityIterations:int, positionIterations:int, velocityEpsilon:Number = 0.001, positionEpsilon:Number = 0.001) {
+ this.velocityIterations = velocityIterations;
+ this.positionIterations = positionIterations;
+ this.velocityEpsilon = velocityEpsilon;
+ this.positionEpsilon = positionEpsilon;
+ }
+
+ /**
+ *
+ * @param velocityIterations
+ * @param positionEpsilon
+ */
+ public function setIterations(velocityIterations:int, positionIterations:int):void {
+ this.velocityIterations = velocityIterations;
+ this.positionIterations = positionIterations;
+ }
+
+ /**
+ *
+ * @return
+ */
+ private function isValid():Boolean {
+ return (positionIterations > 0) && (velocityIterations > 0) && (positionEpsilon >= 0) && (velocityEpsilon >= 0);
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function resolveContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ if (numContacts == 0 || !isValid()) {
+ return;
+ }
+ // Подготовка контактов к обработке
+ prepareContacts(firstContact, numContacts, time);
+ // Разделение взаимно проникающих контактов
+ adjustPositions(firstContact, numContacts, time);
+ // Коррекция скорости для контактов
+ adjustVelocities(firstContact, numContacts, time);
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function prepareContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ // Вычисление внутренних данных контакта, необходимых для дальнейшей работы
+ contact.calculateInternals(time);
+ contact = contact.next;
+ }
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function adjustPositions(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ positionIterationsUsed = 0;
+ // Итерационное устранение взаимного проникновения тел в точках контактов в порядке наибольшего пересечения тел
+ while (positionIterationsUsed < positionIterations) {
+ var max:Number = positionEpsilon;
+ var mostSevereContact:RigidBodyContact = null;
+ // Поиск контакта с наибольшим пересечением
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.penetration > max) {
+ max = contact.penetration;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact == null) {
+ // Алгоритм прерывается, если не осталось пересечений
+ return;
+ }
+ // Будим спящего
+ mostSevereContact.matchAwakeState();
+
+ // Разделяем контакт
+ mostSevereContact.applyPositionChange(linearChange1, linearChange2, angularChange1, angularChange2, max);
+ // Обновляем величину проникновения в каждом контакте, содержащем обработанные объекты
+ contact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.body1 == mostSevereContact.body1) {
+ deltaVector.copy(angularChange1);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(linearChange1);
+ contact.penetration -= deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body1 == mostSevereContact.body2) {
+ deltaVector.copy(angularChange2);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(linearChange2);
+ contact.penetration -= deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body2 != null) {
+ if (contact.body2 == mostSevereContact.body1) {
+ deltaVector.copy(angularChange1);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(linearChange1);
+ contact.penetration += deltaVector.dot(contact.contactNormal);
+ }
+ if (contact.body2 == mostSevereContact.body2) {
+ deltaVector.copy(angularChange2);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(linearChange2);
+ contact.penetration += deltaVector.dot(contact.contactNormal);
+ }
+ }
+ contact = contact.next;
+ }
+ positionIterationsUsed++;
+ }
+ }
+
+ /**
+ *
+ * @param firstContact
+ * @param numContacts
+ * @param time
+ */
+ public function adjustVelocities(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
+ velocityIterationsUsed = 0;
+ while (velocityIterationsUsed < velocityIterations) {
+ var max:Number = velocityEpsilon;
+ var mostSevereContact:RigidBodyContact = null;
+ var contact:RigidBodyContact = firstContact;
+ while (contact.index < numContacts) {
+ if (contact.desiredDeltaVelocity > max) {
+ max = contact.desiredDeltaVelocity;
+ mostSevereContact = contact;
+ }
+ contact = contact.next;
+ }
+ if (mostSevereContact == null) {
+ return;
+ }
+ // Будим спящих
+ mostSevereContact.matchAwakeState();
+ // Корректируем скорости
+ mostSevereContact.applyVelocityChange(velocityChange1, velocityChange2, rotationChange1, rotationChange2);
+ // Обновляем скорости сближения для каждого контакта, содержащего обработанные объекты
+ contact = firstContact;
+ var needRecalculation:Boolean = false;
+ while (contact.index < numContacts) {
+ if (contact.body1 == mostSevereContact.body1) {
+ deltaVector.copy(rotationChange1);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(velocityChange1);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.add(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body1 == mostSevereContact.body2) {
+ deltaVector.copy(rotationChange2);
+ deltaVector.cross(contact.relativeContactPosition1);
+ deltaVector.add(velocityChange2);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.add(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body2 != null) {
+ if (contact.body2 == mostSevereContact.body1) {
+ deltaVector.copy(rotationChange1);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(velocityChange1);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.subtract(deltaVector);
+ needRecalculation = true;
+ }
+ if (contact.body2 == mostSevereContact.body2) {
+ deltaVector.copy(rotationChange2);
+ deltaVector.cross(contact.relativeContactPosition2);
+ deltaVector.add(velocityChange2);
+ deltaVector.transformTranspose(contact.contactToWorld);
+ contact.contactVelocity.subtract(deltaVector);
+ needRecalculation = true;
+ }
+ }
+ if (needRecalculation) {
+ contact.calculateDesiredVelocity(time);
+ }
+ contact = contact.next;
+ }
+ velocityIterationsUsed++;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/RigidWorld.as b/0.0.0.18/src/alternativa/physics/rigid/RigidWorld.as
new file mode 100644
index 0000000..def9635
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/RigidWorld.as
@@ -0,0 +1,136 @@
+package alternativa.physics.rigid {
+
+ public class RigidWorld {
+
+ private var firstBody:RigidBody;
+ private var lastBody:RigidBody;
+ private var contactGenerators:RigidBodyContactGenerator;
+ private var lastContactGenerator:RigidBodyContactGenerator;
+
+ public var contacts:RigidBodyContact;
+ private var maxContacts:int;
+ private var resolver:RigidBodyContactResolver;
+
+ public var forceRegistry:BodyForceRegistry = new BodyForceRegistry();
+ public var calculateIterations:Boolean;
+
+ public var numContacts:int;
+
+ public function RigidWorld(maxContacts:int, iterations:int) {
+ this.maxContacts = maxContacts;
+ resolver = new RigidBodyContactResolver(iterations, iterations);
+ createContactList(maxContacts);
+ }
+
+ private function createContactList(numContacts:int):void {
+ if (numContacts > 0) {
+ var contact:RigidBodyContact = contacts = new RigidBodyContact(0);
+ for (var i:int = 1; i < numContacts; i++) {
+ contact.next = new RigidBodyContact(i);
+ contact = contact.next;
+ }
+ }
+ }
+
+ public function addBody(body:RigidBody):void {
+ if (firstBody == null) {
+ firstBody = body;
+ } else {
+ lastBody.next = body;
+ }
+ lastBody = body;
+ while (lastBody.next != null) {
+ lastBody = lastBody.next;
+ }
+ }
+
+ public function removeBody(body:RigidBody):void {
+ if (body == firstBody) {
+ firstBody = firstBody.next;
+ body.next = null;
+ } else {
+ var current:RigidBody = firstBody;
+ var prev:RigidBody = null;
+ while (current != body && current != null) {
+ prev = current;
+ current = current.next;
+ }
+ if (current != null) {
+ if (lastBody == body) {
+ lastBody = prev;
+ }
+ prev.next = current.next;
+ current.next = null;
+ }
+ }
+ }
+
+ public function addContactGenerator(gen:RigidBodyContactGenerator):void {
+ if (contactGenerators == null) {
+ contactGenerators = gen;
+ } else {
+ lastContactGenerator.next = gen;
+ }
+ lastContactGenerator = gen;
+ while (lastContactGenerator.next != null) {
+ lastContactGenerator = lastContactGenerator.next;
+ }
+ }
+
+ public function removeContactGenerator(gen:RigidBodyContactGenerator):void {
+ if (gen == contactGenerators) {
+ contactGenerators = gen.next;
+ gen.next = null;
+ } else {
+ var prev:RigidBodyContactGenerator = contactGenerators;
+ var curr:RigidBodyContactGenerator = contactGenerators.next;
+ while (curr != gen && curr != null) {
+ prev = curr;
+ curr = curr.next;
+ }
+ if (curr == gen) {
+ if (lastContactGenerator == gen) {
+ lastContactGenerator = prev;
+ }
+ prev.next = curr.next;
+ curr.next = null;
+ }
+ }
+ }
+
+ public function startFrame():void {
+ var body:RigidBody = firstBody;
+ while (body != null) {
+ body.clearAccumulators();
+ body.calculateDerivedData();
+ body = body.next;
+ }
+ }
+
+ private function generateContacts():int {
+ var contact:RigidBodyContact = contacts;
+ var gen:RigidBodyContactGenerator = contactGenerators;
+ while (gen != null && contact != null) {
+ contact = gen.addContacts(contact);
+ gen = gen.next;
+ }
+ return contact == null ? maxContacts : contact.index;
+ }
+
+ public function runPhysics(time:Number):void {
+ forceRegistry.updateForces(time);
+ var body:RigidBody = firstBody;
+ while (body != null) {
+ body.integrate(time);
+ body = body.next;
+ }
+ numContacts = generateContacts();
+// trace("[RigidWorld.runPhysics] numContacts", numContacts);
+ if (calculateIterations) {
+ resolver.setIterations(4*numContacts, 4*numContacts);
+ }
+ resolver.resolveContacts(contacts, numContacts, time);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/all-wcprops b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/all-wcprops
new file mode 100644
index 0000000..3171901
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 111
+/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators
+END
+AnchoredCableContactGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 144
+/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/AnchoredCableContactGenerator.as
+END
+RigidBoxCollisionData.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/4385/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/RigidBoxCollisionData.as
+END
+BoxWithBoxContactGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 141
+/!svn/ver/4403/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/BoxWithBoxContactGenerator.as
+END
+SphereWithPlaneContactGenerator.as
+K 25
+svn:wc:ra_dav:version-url
+V 146
+/!svn/ver/4385/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/SphereWithPlaneContactGenerator.as
+END
+RigidBox.as
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/!svn/ver/4603/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/RigidBox.as
+END
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/entries b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/entries
new file mode 100644
index 0000000..5fc7886
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/entries
@@ -0,0 +1,89 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators
+http://svndev.alternativaplatform.com
+
+
+
+2008-12-03T05:15:16.843707Z
+4732
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+AnchoredCableContactGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+aa5b3540cfe5796ebad67e11f47a4aa9
+2008-12-03T05:15:16.843707Z
+4732
+mike
+
+RigidBoxCollisionData.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+607a1d12afedbe3344ae967c6f599fd4
+2008-11-26T13:17:08.913613Z
+4385
+mike
+
+BoxWithBoxContactGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+47da766c6f7ca03fb1622378c740abb4
+2008-11-26T16:00:07.127812Z
+4403
+mike
+
+SphereWithPlaneContactGenerator.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+558aa2a28f3cc4a3932c19e5e2ab230c
+2008-11-26T13:17:08.913613Z
+4385
+mike
+has-props
+
+RigidBox.as
+file
+
+
+
+
+2010-10-28T04:34:31.000000Z
+505d9996c9461cc769d2accf7295c871
+2008-11-30T13:03:39.561308Z
+4603
+mike
+
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/format b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/prop-base/SphereWithPlaneContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/prop-base/SphereWithPlaneContactGenerator.as.svn-base
new file mode 100644
index 0000000..3160658
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/prop-base/SphereWithPlaneContactGenerator.as.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mergeinfo
+V 0
+
+END
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/AnchoredCableContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/AnchoredCableContactGenerator.as.svn-base
new file mode 100644
index 0000000..32e6169
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/AnchoredCableContactGenerator.as.svn-base
@@ -0,0 +1,56 @@
+package alternativa.physics.rigid.generators {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class AnchoredCableContactGenerator extends RigidBodyContactGenerator {
+
+ private var body:RigidBody;
+ private var mountPoint:Point3D;
+ private var _anchorPoint:Point3D;
+ private var cableLength:Number;
+ private var restitution:Number;
+
+ private var vector:Point3D = new Point3D();
+ private var point:Point3D = new Point3D();
+
+ public function AnchoredCableContactGenerator(body:RigidBody, mountPoint:Point3D, anchorPoint:Point3D, cableLength:Number, restitution:Number) {
+ super();
+ this.body = body;
+ this.mountPoint = mountPoint.clone();
+ _anchorPoint = anchorPoint.clone();
+ this.cableLength = cableLength;
+ this.restitution = restitution;
+ }
+
+ public function set anchorPoint(value:Point3D):void {
+ _anchorPoint.copy(value);
+ }
+
+ override public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ point.copy(mountPoint);
+ point.transform(body.transformMatrix);
+ vector.difference(_anchorPoint, point);
+ var len:Number = vector.length;
+ var pen:Number = len - cableLength;
+ if (pen > 0) {
+ vector.multiply(1/len);
+ contact.body1 = body;
+ contact.body2 = null;
+ contact.contactNormal.copy(vector);
+ contact.contactPoint.copy(point);
+ contact.restitution = restitution;
+ contact.penetration = pen;
+ contact.friction = 0;
+ return contact.next;
+ }
+ return contact;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/BoxWithBoxContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/BoxWithBoxContactGenerator.as.svn-base
new file mode 100644
index 0000000..2a51fdb
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/BoxWithBoxContactGenerator.as.svn-base
@@ -0,0 +1,319 @@
+package alternativa.physics.rigid.generators {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.CollisionBox;
+ import alternativa.physics.collision.CollisionData;
+ import alternativa.physics.collision.CollisionDetector;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class BoxWithBoxContactGenerator extends RigidBodyContactGenerator {
+
+ private var boxes:RigidBox;
+ private var collisionDetector:CollisionDetector;
+ private var collisionData:RigidBoxCollisionData = RigidBoxCollisionData.create();
+ private var restitution:Number;
+ private var friction:Number;
+
+ private var tolerance:Number = -0.01;
+
+ public function BoxWithBoxContactGenerator(boxes:RigidBox, collisionDetector:CollisionDetector, restitution:Number, friction:Number) {
+ super();
+ this.boxes = boxes;
+ this.collisionDetector = collisionDetector;
+ this.restitution = restitution;
+ this.friction = friction;
+ }
+
+ public function getBoxes():RigidBox {
+ return boxes;
+ }
+
+ public function setBoxes(boxes:RigidBox):void {
+ this.boxes = boxes;
+ }
+
+ public function getBoxesCount():int {
+ var counter:int = 0;
+ var box:RigidBox = boxes;
+ while (box != null) {
+ counter++;
+ box = box.next;
+ }
+ return counter;
+ }
+
+ private function updateCillisionBoxes():void {
+ var box:RigidBox = boxes;
+ while (box != null) {
+ box.collisionBox.updateTransform();
+ box = box.next;
+ }
+ }
+
+ override public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ updateCillisionBoxes();
+
+ var box:RigidBox = boxes;
+ var otherBox:RigidBox;
+ var key:*;
+ var cachedCollisionData:RigidBoxCollisionData;
+ while (box != null) {
+ otherBox = box.next;
+ while (otherBox != null) {
+ var contactExists:Boolean = collisionDetector.boxToBoxCollision(box.collisionBox, otherBox.collisionBox, collisionData);
+ collisionData.otherBox = otherBox;
+ if (contactExists) {
+ var cached:Boolean = false;
+ // Поиск и обновление полученного контакта в кэше
+ for (key in box.boxCollisionCache) {
+ cachedCollisionData = key;
+ if (!cachedCollisionData.fresh && cachedCollisionData.equals(collisionData)) {
+ cached = true;
+ cachedCollisionData.collisionNormal.copy(collisionData.collisionNormal);
+ cachedCollisionData.collisionPoint.copy(collisionData.collisionPoint);
+ cachedCollisionData.penetration = collisionData.penetration;
+ break;
+ }
+ }
+ if (!cached) {
+ // В кэше не оказалось, добавляется новый контакт
+ box.boxCollisionCache[collisionData] = true;
+ collisionData = RigidBoxCollisionData.create();
+ }
+ }
+ otherBox = otherBox.next as RigidBox;
+ }
+
+ // Создание контактов из кэша
+ var counter:int;
+ for (key in box.boxCollisionCache) {
+ cachedCollisionData = key;
+ if (cachedCollisionData.fresh) {
+ cachedCollisionData.fresh = false;
+ } else {
+ // Проверка актуальности старых контактов
+ var valid:Boolean = checkCachedContact(cachedCollisionData);
+ if (!valid) {
+ delete box.boxCollisionCache[key];
+ RigidBoxCollisionData.destroy(cachedCollisionData);
+ continue;
+ }
+ cachedCollisionData.timeStamp++;
+ }
+ // Создание контакта
+ contact.timeStamp = cachedCollisionData.timeStamp;
+ contact.body1 = box.body;
+ contact.body2 = cachedCollisionData.otherBox.body;
+ contact.contactNormal.copy(cachedCollisionData.collisionNormal);
+ contact.contactPoint.copy(cachedCollisionData.collisionPoint);
+ contact.penetration = cachedCollisionData.penetration;
+ contact.friction = friction;
+ contact.restitution = restitution;
+ contact = contact.next;
+ if (contact == null) {
+ return null;
+ }
+ }
+
+ box = box.next;
+ }
+
+ return contact;
+ }
+
+ private var _axis1:Point3D = new Point3D();
+ private var _axis2:Point3D = new Point3D();
+ private var _axis3:Point3D = new Point3D();
+ private var _toFirst:Point3D = new Point3D();
+ private var _point1:Point3D = new Point3D();
+ private var _point2:Point3D = new Point3D();
+ private var _collisionPoint:Point3D = new Point3D();
+ /**
+ *
+ * @param data
+ * @return
+ */
+ private function checkCachedContact(data:CollisionData):Boolean {
+ var box1:CollisionBox = data.primitive1 as CollisionBox;
+ var box2:CollisionBox = data.primitive2 as CollisionBox;
+ var axisFlag1:int = data.pointAxisCode1 & 32;
+ var axisFlag2:int = data.pointAxisCode2 & 32;
+ var axisIndex1:int;
+ var axisIndex2:int;
+ var penetration:Number;
+ _toFirst.difference(box1.position, box2.position);
+
+ if (axisFlag1 == axisFlag2) {
+ // Столкновение вдоль вторичной оси
+ axisIndex1 = (data.pointAxisCode1 >> 3) & 3;
+ box1.transform.getAxis(axisIndex1, _axis1);
+ axisIndex2 = (data.pointAxisCode2 >> 3) & 3;
+ box2.transform.getAxis(axisIndex2, _axis2);
+ _axis3.cross2(_axis1, _axis2);
+ _axis3.normalize();
+ var overlap:Number = collisionDetector.overlapOnAxis(box1, box2, _axis3, _toFirst);
+ if (overlap < 0) {
+ return false;
+ } else {
+ penetration = getEdgesPenetrationAndCollisionPoint(box1, box2, _axis1, axisIndex1, data.pointAxisCode1 & 7, _axis2, axisIndex2, data.pointAxisCode2 & 7, _toFirst, _collisionPoint);
+ if (penetration < tolerance) {
+ return false;
+ }
+ // Обновляем контакт
+ if (_toFirst.dot(_axis3) < 0) {
+ _axis3.invert();
+ }
+ data.collisionNormal.copy(_axis3);
+ data.collisionPoint.copy(_collisionPoint);
+ data.penetration = penetration > 0 ? penetration : 0;
+ return true;
+ }
+ } else {
+ // Столкновение вдоль основной оси
+ // TODO: Доделать проверку на попадание вершины внутрь бокса
+ if (axisFlag1 != 0) {
+ // Столкновение вершины второго бокса с гранью первого
+ _toFirst.invert();
+ axisIndex1 = (data.pointAxisCode1 >> 3) & 3;
+ penetration = getPointBoxPenetrationAndCollisionPoint(box2, box1, data.pointAxisCode2 & 7, axisIndex1, _toFirst, _collisionPoint, _axis1);
+ _axis1.invert();
+ } else {
+ // Столкновение вершины первого бокса с гранью второго
+ axisIndex2 = (data.pointAxisCode2 >> 3) & 3;
+ penetration = getPointBoxPenetrationAndCollisionPoint(box1, box2, data.pointAxisCode1 & 7, axisIndex2, _toFirst, _collisionPoint, _axis1);
+ }
+ if (penetration > tolerance) {
+ data.collisionNormal.copy(_axis1);
+ data.collisionPoint.copy(_collisionPoint);
+ data.penetration = penetration > 0 ? penetration : 0;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ */
+ private function getEdgesPenetrationAndCollisionPoint(box1:CollisionBox, box2:CollisionBox, axis1:Point3D, axisIndex1:int, pointIndex1:int, axis2:Point3D, axisIndex2:int, pointIndex2:int, toFirst:Point3D, collisionPoint:Point3D):Number {
+ _point1.x = (pointIndex1 & 1) == 0 ? box1.halfSize.x : -box1.halfSize.x;
+ _point1.y = (pointIndex1 & 2) == 0 ? box1.halfSize.y : -box1.halfSize.y;
+ _point1.z = (pointIndex1 & 4) == 0 ? box1.halfSize.z : -box1.halfSize.z;
+ _point1.transform(box1.transform);
+
+ _point2.x = (pointIndex2 & 1) == 0 ? box2.halfSize.x : -box2.halfSize.x;
+ _point2.y = (pointIndex2 & 2) == 0 ? box2.halfSize.y : -box2.halfSize.y;
+ _point2.z = (pointIndex2 & 4) == 0 ? box2.halfSize.z : -box2.halfSize.z;
+ _point2.transform(box2.transform);
+
+ var k:Number = axis1.dot(axis2);
+ var det:Number = k*k - 1;
+ if (det > -0.0001 && det < 0.0001) {
+ // Число по модулю должно быть быть больше, чем tolerance
+ return -100;
+ } else {
+ collisionPoint.difference(_point2, _point1);
+ var c1:Number = axis1.dot(collisionPoint);
+ var c2:Number = axis2.dot(collisionPoint);
+ var t1:Number = (c2*k - c1)/det;
+ var size:Number = 2*(axisIndex1 == 0 ? box1.halfSize.x : (axisIndex1 == 1 ? box1.halfSize.y : box1.halfSize.z));
+ if (t1 < 0 || t1 > size) {
+ return -100;
+ } else {
+ var t2:Number = (c2 - c1*k)/det;
+ size = 2*(axisIndex2 == 0 ? box2.halfSize.x : (axisIndex2 == 1 ? box2.halfSize.y : box2.halfSize.z));
+ if (t2 < 0 || t2 > size) {
+ return -100;
+ } else {
+ _point1.x += _axis1.x*t1;
+ _point1.y += _axis1.y*t1;
+ _point1.z += _axis1.z*t1;
+ _point2.x += _axis2.x*t2;
+ _point2.y += _axis2.y*t2;
+ _point2.z += _axis2.z*t2;
+ collisionPoint.x = 0.5*(_point1.x + _point2.x);
+ collisionPoint.y = 0.5*(_point1.y + _point2.y);
+ collisionPoint.z = 0.5*(_point1.z + _point2.z);
+ _point1.subtract(_point2);
+ return _point1.dot(toFirst) > 0 ? -_point1.length : _point1.length;
+ }
+ }
+ }
+
+ return -100;
+ }
+
+ /**
+ *
+ * @param box1 бокс, вершина которого столкнулась с гранью второго бокса
+ * @param box2 бокс, грань которого столкнулась с вершиной второго бокса
+ * @param axisIndex1
+ * @param pointIndex2
+ * @param toCenter
+ * @param collisionPoint
+ * @return
+ */
+ private function getPointBoxPenetrationAndCollisionPoint(box1:CollisionBox, box2:CollisionBox, pointIndex1:int, axisIndex2:int, toFirst:Point3D, collisionPoint:Point3D, collisionNormal:Point3D):Number {
+ // Локальные кординаты вершины
+ _point1.x = (pointIndex1 & 1) == 0 ? box1.halfSize.x : -box1.halfSize.x;
+ _point1.y = (pointIndex1 & 2) == 0 ? box1.halfSize.y : -box1.halfSize.y;
+ _point1.z = (pointIndex1 & 4) == 0 ? box1.halfSize.z : -box1.halfSize.z;
+
+ // размер второго бокса вдоль оси столкновения
+ var size:Number;
+ if (axisIndex2 == 0) {
+ size = box2.halfSize.x;
+ } else if (axisIndex2 == 1) {
+ size = box2.halfSize.y;
+ } else {
+ size = box2.halfSize.z;
+ }
+ box2.transform.getAxis(axisIndex2, collisionNormal);
+ var distance:Number = collisionNormal.dot(toFirst);
+ if (distance < 0) {
+ distance = -distance;
+ collisionNormal.invert();
+ }
+ var m:Matrix3D = box1.transform;
+ collisionPoint.x = m.a*_point1.x + m.b*_point1.y + m.c*_point1.z;
+ collisionPoint.y = m.e*_point1.x + m.f*_point1.y + m.g*_point1.z;
+ collisionPoint.z = m.i*_point1.x + m.j*_point1.y + m.k*_point1.z;
+ var projection:Number = collisionNormal.dot(collisionPoint);
+ var overlap:Number = size - projection - distance;
+ if (overlap < tolerance) {
+ return -100;
+ }
+
+ // Глобальные координаты вершины
+ collisionPoint.x += m.d;
+ collisionPoint.y += m.h;
+ collisionPoint.z += m.l;
+
+ // Координтаы вершины в системе второго бокса
+ _point2.copy(collisionPoint);
+ _point2.inverseTransform(box2.transform);
+ // Проверка на принадлежность вершины боксу
+ if (_point2.x < 0) {
+ _point2.x = -_point2.x;
+ }
+ if (_point2.y < 0) {
+ _point2.y = -_point2.y;
+ }
+ if (_point2.y < 0) {
+ _point2.y = -_point2.y;
+ }
+ if ((_point2.x + tolerance > box2.halfSize.x) || (_point2.y + tolerance > box2.halfSize.y) || (_point2.z + tolerance > box2.halfSize.z)) {
+ return -100;
+ }
+
+ return overlap;
+ }
+
+ }
+}
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBox.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBox.as.svn-base
new file mode 100644
index 0000000..d83b18a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBox.as.svn-base
@@ -0,0 +1,55 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.CollisionBox;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ import flash.utils.Dictionary;
+
+ use namespace altphysics;
+
+ public class RigidBox {
+
+ public var body:RigidBody;
+ public var collisionBox:CollisionBox;
+ public var radius:Number;
+
+ public var boxCollisionCache:Dictionary = new Dictionary();
+ public var next:RigidBox;
+
+ private var point:Point3D = new Point3D();
+
+ public function RigidBox(width:Number, length:Number, height:Number, mass:Number) {
+ body = new RigidBody();
+ collisionBox = new CollisionBox(body, new Point3D(0.5*width, 0.5*length, 0.5*height));
+ setParams(width, length, height, mass);
+ }
+
+ public function setParams(width:Number, length:Number, height:Number, mass:Number):void {
+ radius = Math.sqrt(width*width + length*length + height*height)*0.5;
+ collisionBox.halfSize.reset(0.5*width, 0.5*length, 0.5*height);
+ if (mass == 0) {
+ body.setInfinteMass();
+ } else {
+ var coeff:Number = mass/12;
+ var xx:Number = width*width;
+ var yy:Number = length*length;
+ var zz:Number = height*height;
+ var inertiaTensor:Matrix3D = new Matrix3D();
+ inertiaTensor.a = coeff*(yy + zz);
+ inertiaTensor.f = coeff*(xx + zz);
+ inertiaTensor.k = coeff*(xx + yy);
+ body.setIntertiaTensor(inertiaTensor);
+ body.setMass(mass);
+ }
+ }
+
+ public function setNext(box:RigidBox):RigidBox {
+ next = box;
+ body.next = box.body;
+ return box;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBoxCollisionData.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBoxCollisionData.as.svn-base
new file mode 100644
index 0000000..c0b3910
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/RigidBoxCollisionData.as.svn-base
@@ -0,0 +1,46 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.collision.CollisionData;
+
+ public class RigidBoxCollisionData extends CollisionData {
+
+ private static var pool:Array = new Array();
+
+ public static function create():RigidBoxCollisionData {
+ var data:RigidBoxCollisionData = pool.pop();
+ if (data == null) {
+ data = new RigidBoxCollisionData();
+ }
+ data.fresh = true;
+ data.timeStamp = 0;
+ return data;
+ }
+
+ public static function destroy(data:RigidBoxCollisionData):void {
+ data.primitive1 = null;
+ data.primitive2 = null;
+ data.otherBox = null;
+ pool.push(data);
+ }
+
+ public var fresh:Boolean;
+ public var otherBox:RigidBox;
+
+ public function RigidBoxCollisionData() {
+ super();
+ }
+
+ public function equals(data:RigidBoxCollisionData):Boolean {
+ var result:Boolean = otherBox == data.otherBox && pointAxisCode1 == data.pointAxisCode1 && pointAxisCode2 == data.pointAxisCode2;
+ return result;
+ }
+
+ public function toString():String {
+ return "RigidBoxCollisionData" +
+ "\n collisionPoint " + collisionPoint +
+ "\n collisionNormal " + collisionNormal +
+ "\n pointAxisCode1 " + pointAxisCode1.toString(2) +
+ "\n pointAxisCode2 " + pointAxisCode2.toString(2);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/SphereWithPlaneContactGenerator.as.svn-base b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/SphereWithPlaneContactGenerator.as.svn-base
new file mode 100644
index 0000000..241eaf4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/.svn/text-base/SphereWithPlaneContactGenerator.as.svn-base
@@ -0,0 +1,47 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class SphereWithPlaneContactGenerator extends RigidBodyContactGenerator {
+
+ private var body:RigidBody;
+ private var planeNormal:Point3D = new Point3D();
+ private var planeOffset:Number;
+ private var radius:Number;
+ private var restitution:Number;
+ private var friction:Number;
+
+ public function SphereWithPlaneContactGenerator(body:RigidBody, radius:Number, planeNormal:Point3D, planeOffset:Number, restitution:Number, friction:Number) {
+ super();
+ this.body = body;
+ this.radius = radius;
+ this.planeNormal.copy(planeNormal);
+ this.planeOffset = planeOffset;
+ this.restitution = restitution;
+ this.friction = friction;
+ }
+
+ override public function addContacts(firstContact:RigidBodyContact):RigidBodyContact {
+ var offset:Number = planeNormal.dot(body.position) - planeOffset;
+ if (offset < radius) {
+ firstContact.body1 = body;
+ firstContact.body2 = null;
+ firstContact.penetration = radius - offset;
+ firstContact.contactNormal.copy(planeNormal);
+ firstContact.contactPoint.copy(planeNormal);
+ firstContact.contactPoint.multiply(-radius);
+ firstContact.contactPoint.add(body.position);
+ firstContact.restitution = restitution;
+ firstContact.friction = friction;
+ return firstContact.next;
+ }
+ return firstContact;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/AnchoredCableContactGenerator.as b/0.0.0.18/src/alternativa/physics/rigid/generators/AnchoredCableContactGenerator.as
new file mode 100644
index 0000000..32e6169
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/AnchoredCableContactGenerator.as
@@ -0,0 +1,56 @@
+package alternativa.physics.rigid.generators {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class AnchoredCableContactGenerator extends RigidBodyContactGenerator {
+
+ private var body:RigidBody;
+ private var mountPoint:Point3D;
+ private var _anchorPoint:Point3D;
+ private var cableLength:Number;
+ private var restitution:Number;
+
+ private var vector:Point3D = new Point3D();
+ private var point:Point3D = new Point3D();
+
+ public function AnchoredCableContactGenerator(body:RigidBody, mountPoint:Point3D, anchorPoint:Point3D, cableLength:Number, restitution:Number) {
+ super();
+ this.body = body;
+ this.mountPoint = mountPoint.clone();
+ _anchorPoint = anchorPoint.clone();
+ this.cableLength = cableLength;
+ this.restitution = restitution;
+ }
+
+ public function set anchorPoint(value:Point3D):void {
+ _anchorPoint.copy(value);
+ }
+
+ override public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ point.copy(mountPoint);
+ point.transform(body.transformMatrix);
+ vector.difference(_anchorPoint, point);
+ var len:Number = vector.length;
+ var pen:Number = len - cableLength;
+ if (pen > 0) {
+ vector.multiply(1/len);
+ contact.body1 = body;
+ contact.body2 = null;
+ contact.contactNormal.copy(vector);
+ contact.contactPoint.copy(point);
+ contact.restitution = restitution;
+ contact.penetration = pen;
+ contact.friction = 0;
+ return contact.next;
+ }
+ return contact;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/BoxWithBoxContactGenerator.as b/0.0.0.18/src/alternativa/physics/rigid/generators/BoxWithBoxContactGenerator.as
new file mode 100644
index 0000000..2a51fdb
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/BoxWithBoxContactGenerator.as
@@ -0,0 +1,319 @@
+package alternativa.physics.rigid.generators {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.CollisionBox;
+ import alternativa.physics.collision.CollisionData;
+ import alternativa.physics.collision.CollisionDetector;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class BoxWithBoxContactGenerator extends RigidBodyContactGenerator {
+
+ private var boxes:RigidBox;
+ private var collisionDetector:CollisionDetector;
+ private var collisionData:RigidBoxCollisionData = RigidBoxCollisionData.create();
+ private var restitution:Number;
+ private var friction:Number;
+
+ private var tolerance:Number = -0.01;
+
+ public function BoxWithBoxContactGenerator(boxes:RigidBox, collisionDetector:CollisionDetector, restitution:Number, friction:Number) {
+ super();
+ this.boxes = boxes;
+ this.collisionDetector = collisionDetector;
+ this.restitution = restitution;
+ this.friction = friction;
+ }
+
+ public function getBoxes():RigidBox {
+ return boxes;
+ }
+
+ public function setBoxes(boxes:RigidBox):void {
+ this.boxes = boxes;
+ }
+
+ public function getBoxesCount():int {
+ var counter:int = 0;
+ var box:RigidBox = boxes;
+ while (box != null) {
+ counter++;
+ box = box.next;
+ }
+ return counter;
+ }
+
+ private function updateCillisionBoxes():void {
+ var box:RigidBox = boxes;
+ while (box != null) {
+ box.collisionBox.updateTransform();
+ box = box.next;
+ }
+ }
+
+ override public function addContacts(contact:RigidBodyContact):RigidBodyContact {
+ updateCillisionBoxes();
+
+ var box:RigidBox = boxes;
+ var otherBox:RigidBox;
+ var key:*;
+ var cachedCollisionData:RigidBoxCollisionData;
+ while (box != null) {
+ otherBox = box.next;
+ while (otherBox != null) {
+ var contactExists:Boolean = collisionDetector.boxToBoxCollision(box.collisionBox, otherBox.collisionBox, collisionData);
+ collisionData.otherBox = otherBox;
+ if (contactExists) {
+ var cached:Boolean = false;
+ // Поиск и обновление полученного контакта в кэше
+ for (key in box.boxCollisionCache) {
+ cachedCollisionData = key;
+ if (!cachedCollisionData.fresh && cachedCollisionData.equals(collisionData)) {
+ cached = true;
+ cachedCollisionData.collisionNormal.copy(collisionData.collisionNormal);
+ cachedCollisionData.collisionPoint.copy(collisionData.collisionPoint);
+ cachedCollisionData.penetration = collisionData.penetration;
+ break;
+ }
+ }
+ if (!cached) {
+ // В кэше не оказалось, добавляется новый контакт
+ box.boxCollisionCache[collisionData] = true;
+ collisionData = RigidBoxCollisionData.create();
+ }
+ }
+ otherBox = otherBox.next as RigidBox;
+ }
+
+ // Создание контактов из кэша
+ var counter:int;
+ for (key in box.boxCollisionCache) {
+ cachedCollisionData = key;
+ if (cachedCollisionData.fresh) {
+ cachedCollisionData.fresh = false;
+ } else {
+ // Проверка актуальности старых контактов
+ var valid:Boolean = checkCachedContact(cachedCollisionData);
+ if (!valid) {
+ delete box.boxCollisionCache[key];
+ RigidBoxCollisionData.destroy(cachedCollisionData);
+ continue;
+ }
+ cachedCollisionData.timeStamp++;
+ }
+ // Создание контакта
+ contact.timeStamp = cachedCollisionData.timeStamp;
+ contact.body1 = box.body;
+ contact.body2 = cachedCollisionData.otherBox.body;
+ contact.contactNormal.copy(cachedCollisionData.collisionNormal);
+ contact.contactPoint.copy(cachedCollisionData.collisionPoint);
+ contact.penetration = cachedCollisionData.penetration;
+ contact.friction = friction;
+ contact.restitution = restitution;
+ contact = contact.next;
+ if (contact == null) {
+ return null;
+ }
+ }
+
+ box = box.next;
+ }
+
+ return contact;
+ }
+
+ private var _axis1:Point3D = new Point3D();
+ private var _axis2:Point3D = new Point3D();
+ private var _axis3:Point3D = new Point3D();
+ private var _toFirst:Point3D = new Point3D();
+ private var _point1:Point3D = new Point3D();
+ private var _point2:Point3D = new Point3D();
+ private var _collisionPoint:Point3D = new Point3D();
+ /**
+ *
+ * @param data
+ * @return
+ */
+ private function checkCachedContact(data:CollisionData):Boolean {
+ var box1:CollisionBox = data.primitive1 as CollisionBox;
+ var box2:CollisionBox = data.primitive2 as CollisionBox;
+ var axisFlag1:int = data.pointAxisCode1 & 32;
+ var axisFlag2:int = data.pointAxisCode2 & 32;
+ var axisIndex1:int;
+ var axisIndex2:int;
+ var penetration:Number;
+ _toFirst.difference(box1.position, box2.position);
+
+ if (axisFlag1 == axisFlag2) {
+ // Столкновение вдоль вторичной оси
+ axisIndex1 = (data.pointAxisCode1 >> 3) & 3;
+ box1.transform.getAxis(axisIndex1, _axis1);
+ axisIndex2 = (data.pointAxisCode2 >> 3) & 3;
+ box2.transform.getAxis(axisIndex2, _axis2);
+ _axis3.cross2(_axis1, _axis2);
+ _axis3.normalize();
+ var overlap:Number = collisionDetector.overlapOnAxis(box1, box2, _axis3, _toFirst);
+ if (overlap < 0) {
+ return false;
+ } else {
+ penetration = getEdgesPenetrationAndCollisionPoint(box1, box2, _axis1, axisIndex1, data.pointAxisCode1 & 7, _axis2, axisIndex2, data.pointAxisCode2 & 7, _toFirst, _collisionPoint);
+ if (penetration < tolerance) {
+ return false;
+ }
+ // Обновляем контакт
+ if (_toFirst.dot(_axis3) < 0) {
+ _axis3.invert();
+ }
+ data.collisionNormal.copy(_axis3);
+ data.collisionPoint.copy(_collisionPoint);
+ data.penetration = penetration > 0 ? penetration : 0;
+ return true;
+ }
+ } else {
+ // Столкновение вдоль основной оси
+ // TODO: Доделать проверку на попадание вершины внутрь бокса
+ if (axisFlag1 != 0) {
+ // Столкновение вершины второго бокса с гранью первого
+ _toFirst.invert();
+ axisIndex1 = (data.pointAxisCode1 >> 3) & 3;
+ penetration = getPointBoxPenetrationAndCollisionPoint(box2, box1, data.pointAxisCode2 & 7, axisIndex1, _toFirst, _collisionPoint, _axis1);
+ _axis1.invert();
+ } else {
+ // Столкновение вершины первого бокса с гранью второго
+ axisIndex2 = (data.pointAxisCode2 >> 3) & 3;
+ penetration = getPointBoxPenetrationAndCollisionPoint(box1, box2, data.pointAxisCode1 & 7, axisIndex2, _toFirst, _collisionPoint, _axis1);
+ }
+ if (penetration > tolerance) {
+ data.collisionNormal.copy(_axis1);
+ data.collisionPoint.copy(_collisionPoint);
+ data.penetration = penetration > 0 ? penetration : 0;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ */
+ private function getEdgesPenetrationAndCollisionPoint(box1:CollisionBox, box2:CollisionBox, axis1:Point3D, axisIndex1:int, pointIndex1:int, axis2:Point3D, axisIndex2:int, pointIndex2:int, toFirst:Point3D, collisionPoint:Point3D):Number {
+ _point1.x = (pointIndex1 & 1) == 0 ? box1.halfSize.x : -box1.halfSize.x;
+ _point1.y = (pointIndex1 & 2) == 0 ? box1.halfSize.y : -box1.halfSize.y;
+ _point1.z = (pointIndex1 & 4) == 0 ? box1.halfSize.z : -box1.halfSize.z;
+ _point1.transform(box1.transform);
+
+ _point2.x = (pointIndex2 & 1) == 0 ? box2.halfSize.x : -box2.halfSize.x;
+ _point2.y = (pointIndex2 & 2) == 0 ? box2.halfSize.y : -box2.halfSize.y;
+ _point2.z = (pointIndex2 & 4) == 0 ? box2.halfSize.z : -box2.halfSize.z;
+ _point2.transform(box2.transform);
+
+ var k:Number = axis1.dot(axis2);
+ var det:Number = k*k - 1;
+ if (det > -0.0001 && det < 0.0001) {
+ // Число по модулю должно быть быть больше, чем tolerance
+ return -100;
+ } else {
+ collisionPoint.difference(_point2, _point1);
+ var c1:Number = axis1.dot(collisionPoint);
+ var c2:Number = axis2.dot(collisionPoint);
+ var t1:Number = (c2*k - c1)/det;
+ var size:Number = 2*(axisIndex1 == 0 ? box1.halfSize.x : (axisIndex1 == 1 ? box1.halfSize.y : box1.halfSize.z));
+ if (t1 < 0 || t1 > size) {
+ return -100;
+ } else {
+ var t2:Number = (c2 - c1*k)/det;
+ size = 2*(axisIndex2 == 0 ? box2.halfSize.x : (axisIndex2 == 1 ? box2.halfSize.y : box2.halfSize.z));
+ if (t2 < 0 || t2 > size) {
+ return -100;
+ } else {
+ _point1.x += _axis1.x*t1;
+ _point1.y += _axis1.y*t1;
+ _point1.z += _axis1.z*t1;
+ _point2.x += _axis2.x*t2;
+ _point2.y += _axis2.y*t2;
+ _point2.z += _axis2.z*t2;
+ collisionPoint.x = 0.5*(_point1.x + _point2.x);
+ collisionPoint.y = 0.5*(_point1.y + _point2.y);
+ collisionPoint.z = 0.5*(_point1.z + _point2.z);
+ _point1.subtract(_point2);
+ return _point1.dot(toFirst) > 0 ? -_point1.length : _point1.length;
+ }
+ }
+ }
+
+ return -100;
+ }
+
+ /**
+ *
+ * @param box1 бокс, вершина которого столкнулась с гранью второго бокса
+ * @param box2 бокс, грань которого столкнулась с вершиной второго бокса
+ * @param axisIndex1
+ * @param pointIndex2
+ * @param toCenter
+ * @param collisionPoint
+ * @return
+ */
+ private function getPointBoxPenetrationAndCollisionPoint(box1:CollisionBox, box2:CollisionBox, pointIndex1:int, axisIndex2:int, toFirst:Point3D, collisionPoint:Point3D, collisionNormal:Point3D):Number {
+ // Локальные кординаты вершины
+ _point1.x = (pointIndex1 & 1) == 0 ? box1.halfSize.x : -box1.halfSize.x;
+ _point1.y = (pointIndex1 & 2) == 0 ? box1.halfSize.y : -box1.halfSize.y;
+ _point1.z = (pointIndex1 & 4) == 0 ? box1.halfSize.z : -box1.halfSize.z;
+
+ // размер второго бокса вдоль оси столкновения
+ var size:Number;
+ if (axisIndex2 == 0) {
+ size = box2.halfSize.x;
+ } else if (axisIndex2 == 1) {
+ size = box2.halfSize.y;
+ } else {
+ size = box2.halfSize.z;
+ }
+ box2.transform.getAxis(axisIndex2, collisionNormal);
+ var distance:Number = collisionNormal.dot(toFirst);
+ if (distance < 0) {
+ distance = -distance;
+ collisionNormal.invert();
+ }
+ var m:Matrix3D = box1.transform;
+ collisionPoint.x = m.a*_point1.x + m.b*_point1.y + m.c*_point1.z;
+ collisionPoint.y = m.e*_point1.x + m.f*_point1.y + m.g*_point1.z;
+ collisionPoint.z = m.i*_point1.x + m.j*_point1.y + m.k*_point1.z;
+ var projection:Number = collisionNormal.dot(collisionPoint);
+ var overlap:Number = size - projection - distance;
+ if (overlap < tolerance) {
+ return -100;
+ }
+
+ // Глобальные координаты вершины
+ collisionPoint.x += m.d;
+ collisionPoint.y += m.h;
+ collisionPoint.z += m.l;
+
+ // Координтаы вершины в системе второго бокса
+ _point2.copy(collisionPoint);
+ _point2.inverseTransform(box2.transform);
+ // Проверка на принадлежность вершины боксу
+ if (_point2.x < 0) {
+ _point2.x = -_point2.x;
+ }
+ if (_point2.y < 0) {
+ _point2.y = -_point2.y;
+ }
+ if (_point2.y < 0) {
+ _point2.y = -_point2.y;
+ }
+ if ((_point2.x + tolerance > box2.halfSize.x) || (_point2.y + tolerance > box2.halfSize.y) || (_point2.z + tolerance > box2.halfSize.z)) {
+ return -100;
+ }
+
+ return overlap;
+ }
+
+ }
+}
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBox.as b/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBox.as
new file mode 100644
index 0000000..d83b18a
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBox.as
@@ -0,0 +1,55 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.CollisionBox;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.types.Matrix3D;
+ import alternativa.types.Point3D;
+
+ import flash.utils.Dictionary;
+
+ use namespace altphysics;
+
+ public class RigidBox {
+
+ public var body:RigidBody;
+ public var collisionBox:CollisionBox;
+ public var radius:Number;
+
+ public var boxCollisionCache:Dictionary = new Dictionary();
+ public var next:RigidBox;
+
+ private var point:Point3D = new Point3D();
+
+ public function RigidBox(width:Number, length:Number, height:Number, mass:Number) {
+ body = new RigidBody();
+ collisionBox = new CollisionBox(body, new Point3D(0.5*width, 0.5*length, 0.5*height));
+ setParams(width, length, height, mass);
+ }
+
+ public function setParams(width:Number, length:Number, height:Number, mass:Number):void {
+ radius = Math.sqrt(width*width + length*length + height*height)*0.5;
+ collisionBox.halfSize.reset(0.5*width, 0.5*length, 0.5*height);
+ if (mass == 0) {
+ body.setInfinteMass();
+ } else {
+ var coeff:Number = mass/12;
+ var xx:Number = width*width;
+ var yy:Number = length*length;
+ var zz:Number = height*height;
+ var inertiaTensor:Matrix3D = new Matrix3D();
+ inertiaTensor.a = coeff*(yy + zz);
+ inertiaTensor.f = coeff*(xx + zz);
+ inertiaTensor.k = coeff*(xx + yy);
+ body.setIntertiaTensor(inertiaTensor);
+ body.setMass(mass);
+ }
+ }
+
+ public function setNext(box:RigidBox):RigidBox {
+ next = box;
+ body.next = box.body;
+ return box;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBoxCollisionData.as b/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBoxCollisionData.as
new file mode 100644
index 0000000..c0b3910
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/RigidBoxCollisionData.as
@@ -0,0 +1,46 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.collision.CollisionData;
+
+ public class RigidBoxCollisionData extends CollisionData {
+
+ private static var pool:Array = new Array();
+
+ public static function create():RigidBoxCollisionData {
+ var data:RigidBoxCollisionData = pool.pop();
+ if (data == null) {
+ data = new RigidBoxCollisionData();
+ }
+ data.fresh = true;
+ data.timeStamp = 0;
+ return data;
+ }
+
+ public static function destroy(data:RigidBoxCollisionData):void {
+ data.primitive1 = null;
+ data.primitive2 = null;
+ data.otherBox = null;
+ pool.push(data);
+ }
+
+ public var fresh:Boolean;
+ public var otherBox:RigidBox;
+
+ public function RigidBoxCollisionData() {
+ super();
+ }
+
+ public function equals(data:RigidBoxCollisionData):Boolean {
+ var result:Boolean = otherBox == data.otherBox && pointAxisCode1 == data.pointAxisCode1 && pointAxisCode2 == data.pointAxisCode2;
+ return result;
+ }
+
+ public function toString():String {
+ return "RigidBoxCollisionData" +
+ "\n collisionPoint " + collisionPoint +
+ "\n collisionNormal " + collisionNormal +
+ "\n pointAxisCode1 " + pointAxisCode1.toString(2) +
+ "\n pointAxisCode2 " + pointAxisCode2.toString(2);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.0.18/src/alternativa/physics/rigid/generators/SphereWithPlaneContactGenerator.as b/0.0.0.18/src/alternativa/physics/rigid/generators/SphereWithPlaneContactGenerator.as
new file mode 100644
index 0000000..241eaf4
--- /dev/null
+++ b/0.0.0.18/src/alternativa/physics/rigid/generators/SphereWithPlaneContactGenerator.as
@@ -0,0 +1,47 @@
+package alternativa.physics.rigid.generators {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidBody;
+ import alternativa.physics.rigid.RigidBodyContact;
+ import alternativa.physics.rigid.RigidBodyContactGenerator;
+ import alternativa.types.Point3D;
+
+ use namespace altphysics;
+
+ public class SphereWithPlaneContactGenerator extends RigidBodyContactGenerator {
+
+ private var body:RigidBody;
+ private var planeNormal:Point3D = new Point3D();
+ private var planeOffset:Number;
+ private var radius:Number;
+ private var restitution:Number;
+ private var friction:Number;
+
+ public function SphereWithPlaneContactGenerator(body:RigidBody, radius:Number, planeNormal:Point3D, planeOffset:Number, restitution:Number, friction:Number) {
+ super();
+ this.body = body;
+ this.radius = radius;
+ this.planeNormal.copy(planeNormal);
+ this.planeOffset = planeOffset;
+ this.restitution = restitution;
+ this.friction = friction;
+ }
+
+ override public function addContacts(firstContact:RigidBodyContact):RigidBodyContact {
+ var offset:Number = planeNormal.dot(body.position) - planeOffset;
+ if (offset < radius) {
+ firstContact.body1 = body;
+ firstContact.body2 = null;
+ firstContact.penetration = radius - offset;
+ firstContact.contactNormal.copy(planeNormal);
+ firstContact.contactPoint.copy(planeNormal);
+ firstContact.contactPoint.multiply(-radius);
+ firstContact.contactPoint.add(body.position);
+ firstContact.restitution = restitution;
+ firstContact.friction = friction;
+ return firstContact.next;
+ }
+ return firstContact;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/.actionScriptProperties b/0.0.1.0/.actionScriptProperties
new file mode 100644
index 0000000..e5b1b89
--- /dev/null
+++ b/0.0.1.0/.actionScriptProperties
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.1.0/.flexLibProperties b/0.0.1.0/.flexLibProperties
new file mode 100644
index 0000000..2083d0b
--- /dev/null
+++ b/0.0.1.0/.flexLibProperties
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.1.0/.project b/0.0.1.0/.project
new file mode 100644
index 0000000..1f1425f
--- /dev/null
+++ b/0.0.1.0/.project
@@ -0,0 +1,19 @@
+
+
+ AlternativaPhysics
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ org.maven.ide.eclipse.maven2Nature
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/0.0.1.0/.settings/.svn/all-wcprops b/0.0.1.0/.settings/.svn/all-wcprops
new file mode 100644
index 0000000..cd91b7e
--- /dev/null
+++ b/0.0.1.0/.settings/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.settings
+END
+org.eclipse.core.resources.prefs
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.settings/org.eclipse.core.resources.prefs
+END
diff --git a/0.0.1.0/.settings/.svn/entries b/0.0.1.0/.settings/.svn/entries
new file mode 100644
index 0000000..866dd9a
--- /dev/null
+++ b/0.0.1.0/.settings/.svn/entries
@@ -0,0 +1,40 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.settings
+http://svndev.alternativaplatform.com
+
+
+
+2009-04-01T12:25:29.638016Z
+10301
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+org.eclipse.core.resources.prefs
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+4f70b476f3e5075e399505021df2f89b
+2009-04-01T12:25:29.638016Z
+10301
+mike
+
diff --git a/0.0.1.0/.settings/.svn/format b/0.0.1.0/.settings/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/.settings/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base b/0.0.1.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
new file mode 100644
index 0000000..b3d0d49
--- /dev/null
+++ b/0.0.1.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
@@ -0,0 +1,3 @@
+#Fri Mar 20 00:19:33 YEKT 2009
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/0.0.1.0/.settings/org.eclipse.core.resources.prefs b/0.0.1.0/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..b3d0d49
--- /dev/null
+++ b/0.0.1.0/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Mar 20 00:19:33 YEKT 2009
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/0.0.1.0/.svn/all-wcprops b/0.0.1.0/.svn/all-wcprops
new file mode 100644
index 0000000..118966f
--- /dev/null
+++ b/0.0.1.0/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
+END
+.flexLibProperties
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.flexLibProperties
+END
+.project
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.project
+END
+pom.xml
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/pom.xml
+END
+.actionScriptProperties
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.actionScriptProperties
+END
diff --git a/0.0.1.0/.svn/dir-prop-base b/0.0.1.0/.svn/dir-prop-base
new file mode 100644
index 0000000..8eda4b3
--- /dev/null
+++ b/0.0.1.0/.svn/dir-prop-base
@@ -0,0 +1,5 @@
+K 13
+svn:mergeinfo
+V 85
+/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.1.Vector3D:13892-14098
+END
diff --git a/0.0.1.0/.svn/entries b/0.0.1.0/.svn/entries
new file mode 100644
index 0000000..f68e93b
--- /dev/null
+++ b/0.0.1.0/.svn/entries
@@ -0,0 +1,85 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-23T08:51:29.377118Z
+14887
+mike
+has-props
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+META-INF
+dir
+
+.flexLibProperties
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+fcef7ca7fe61a3459fb00858c28adec1
+2009-05-26T08:41:33.471001Z
+13389
+mike
+
+.project
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+1e90cc68b52b93173b2b5de88eb5c3a3
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
+src
+dir
+
+pom.xml
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+66fe2d1158b7a4fe76ade73e32168db1
+2009-06-23T08:50:57.759518Z
+14886
+mike
+
+.actionScriptProperties
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+918335cb945c3f8866aeb8e6cfd1b110
+2009-06-23T08:49:19.922749Z
+14884
+mike
+
+.settings
+dir
+
diff --git a/0.0.1.0/.svn/format b/0.0.1.0/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/.svn/text-base/.actionScriptProperties.svn-base b/0.0.1.0/.svn/text-base/.actionScriptProperties.svn-base
new file mode 100644
index 0000000..e5b1b89
--- /dev/null
+++ b/0.0.1.0/.svn/text-base/.actionScriptProperties.svn-base
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.1.0/.svn/text-base/.flexLibProperties.svn-base b/0.0.1.0/.svn/text-base/.flexLibProperties.svn-base
new file mode 100644
index 0000000..2083d0b
--- /dev/null
+++ b/0.0.1.0/.svn/text-base/.flexLibProperties.svn-base
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.1.0/.svn/text-base/.project.svn-base b/0.0.1.0/.svn/text-base/.project.svn-base
new file mode 100644
index 0000000..1f1425f
--- /dev/null
+++ b/0.0.1.0/.svn/text-base/.project.svn-base
@@ -0,0 +1,19 @@
+
+
+ AlternativaPhysics
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ org.maven.ide.eclipse.maven2Nature
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/0.0.1.0/.svn/text-base/pom.xml.svn-base b/0.0.1.0/.svn/text-base/pom.xml.svn-base
new file mode 100644
index 0000000..6e943fb
--- /dev/null
+++ b/0.0.1.0/.svn/text-base/pom.xml.svn-base
@@ -0,0 +1,17 @@
+
+ 4.0.0
+ platform.clients.fp10.libraries
+ AlternativaPhysics
+ swc
+ 0.0.1.0
+
+ platform.tools.maven
+ FlashBasePom
+ 1.0
+
+
+ scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
+
+
+
+
\ No newline at end of file
diff --git a/0.0.1.0/META-INF/.svn/all-wcprops b/0.0.1.0/META-INF/.svn/all-wcprops
new file mode 100644
index 0000000..3a6c795
--- /dev/null
+++ b/0.0.1.0/META-INF/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/META-INF
+END
+MANIFEST.MF
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/META-INF/MANIFEST.MF
+END
diff --git a/0.0.1.0/META-INF/.svn/entries b/0.0.1.0/META-INF/.svn/entries
new file mode 100644
index 0000000..8d18702
--- /dev/null
+++ b/0.0.1.0/META-INF/.svn/entries
@@ -0,0 +1,40 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/META-INF
+http://svndev.alternativaplatform.com
+
+
+
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+MANIFEST.MF
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+7b64dceb50a6905850ff00a0bfbe77eb
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
diff --git a/0.0.1.0/META-INF/.svn/format b/0.0.1.0/META-INF/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/META-INF/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base b/0.0.1.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
new file mode 100644
index 0000000..5066a55
--- /dev/null
+++ b/0.0.1.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
@@ -0,0 +1 @@
+Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
diff --git a/0.0.1.0/META-INF/MANIFEST.MF b/0.0.1.0/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5066a55
--- /dev/null
+++ b/0.0.1.0/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
diff --git a/0.0.1.0/pom.xml b/0.0.1.0/pom.xml
new file mode 100644
index 0000000..6e943fb
--- /dev/null
+++ b/0.0.1.0/pom.xml
@@ -0,0 +1,17 @@
+
+ 4.0.0
+ platform.clients.fp10.libraries
+ AlternativaPhysics
+ swc
+ 0.0.1.0
+
+ platform.tools.maven
+ FlashBasePom
+ 1.0
+
+
+ scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
+
+
+
+
\ No newline at end of file
diff --git a/0.0.1.0/src/.svn/all-wcprops b/0.0.1.0/src/.svn/all-wcprops
new file mode 100644
index 0000000..7ceed03
--- /dev/null
+++ b/0.0.1.0/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src
+END
diff --git a/0.0.1.0/src/.svn/entries b/0.0.1.0/src/.svn/entries
new file mode 100644
index 0000000..a9986cd
--- /dev/null
+++ b/0.0.1.0/src/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-17T08:27:51.730249Z
+14488
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+alternativa
+dir
+
diff --git a/0.0.1.0/src/.svn/format b/0.0.1.0/src/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/.svn/all-wcprops b/0.0.1.0/src/alternativa/.svn/all-wcprops
new file mode 100644
index 0000000..c537b77
--- /dev/null
+++ b/0.0.1.0/src/alternativa/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa
+END
diff --git a/0.0.1.0/src/alternativa/.svn/entries b/0.0.1.0/src/alternativa/.svn/entries
new file mode 100644
index 0000000..9f66f5e
--- /dev/null
+++ b/0.0.1.0/src/alternativa/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-17T08:27:51.730249Z
+14488
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+physics
+dir
+
diff --git a/0.0.1.0/src/alternativa/.svn/format b/0.0.1.0/src/alternativa/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/.svn/all-wcprops
new file mode 100644
index 0000000..6c1321c
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics
+END
+altphysics.as
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/altphysics.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/.svn/entries b/0.0.1.0/src/alternativa/physics/.svn/entries
new file mode 100644
index 0000000..4b34409
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/.svn/entries
@@ -0,0 +1,49 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-17T08:27:51.730249Z
+14488
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+types
+dir
+
+altphysics.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+04fbe40a27502dbbd0dba02a76b2df50
+2009-04-20T20:09:14.715403Z
+11579
+mike
+
+collision
+dir
+
+rigid
+dir
+
diff --git a/0.0.1.0/src/alternativa/physics/.svn/format b/0.0.1.0/src/alternativa/physics/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base b/0.0.1.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
new file mode 100644
index 0000000..2715307
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/altphysics.as b/0.0.1.0/src/alternativa/physics/altphysics.as
new file mode 100644
index 0000000..2715307
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/altphysics.as
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/collision/.svn/all-wcprops
new file mode 100644
index 0000000..a1a7401
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/all-wcprops
@@ -0,0 +1,95 @@
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision
+END
+BoxSphereCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 134
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
+END
+BoxBoxCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
+END
+CollisionKdNode.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
+END
+SpherePlaneCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/SpherePlaneCollider.as
+END
+KdTreeCollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 140
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as
+END
+BoxRectCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
+END
+ICollisionPredicate.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollisionPredicate.as
+END
+BoxPlaneCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 133
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxPlaneCollider.as
+END
+CollisionKdTree.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
+END
+BoxTriangleCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxTriangleCollider.as
+END
+ICollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 135
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollisionDetector.as
+END
+IRayCollisionPredicate.as
+K 25
+svn:wc:ra_dav:version-url
+V 139
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/IRayCollisionPredicate.as
+END
+ICollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollider.as
+END
+BruteForceCollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 144
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as
+END
+SphereSphereCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 137
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/SphereSphereCollider.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/entries b/0.0.1.0/src/alternativa/physics/collision/.svn/entries
new file mode 100644
index 0000000..540362c
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/entries
@@ -0,0 +1,214 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-17T08:27:51.730249Z
+14488
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+BoxSphereCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+ad1dc1849019ac159d22cd93b1ba38d3
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+BoxBoxCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+e4eb46370e3c7098fa35d0da4e346d09
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+CollisionKdNode.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+7952384e785be1fca93657d94121b246
+2009-04-17T17:39:21.367569Z
+11341
+mike
+
+BoxRectCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+8554254b88a8ecc31bf005fdee676a67
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+KdTreeCollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+7ba4808350962bc29e80317568ad589c
+2009-06-17T08:27:51.730249Z
+14488
+mike
+
+SpherePlaneCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+7fbbf70eef5caa9a09ce679819429874
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+ICollisionPredicate.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+866471ea582e95e46e94eb7ae437c827
+2009-04-26T15:39:06.673206Z
+11995
+mike
+
+BoxPlaneCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+4344df47c2b9c1873d2dc711815b4737
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+primitives
+dir
+
+CollisionKdTree.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+92511d7533028a942e299ca3a4251da8
+2009-05-18T16:37:02.465501Z
+13087
+mike
+
+BoxTriangleCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+f95da4166903174a2c92c1ab262e719c
+2009-05-26T08:41:33.471001Z
+13389
+mike
+
+ICollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+c98a3e33a4a54e2e85da149e25b583fe
+2009-06-08T11:22:42.165256Z
+14112
+mike
+
+IRayCollisionPredicate.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+f7443fb728cf4e6157dcfb4f39665889
+2009-04-26T15:39:06.673206Z
+11995
+mike
+
+types
+dir
+
+BruteForceCollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+c166c05be4909e221c2f129c9e7cac32
+2009-06-08T11:22:42.165256Z
+14112
+mike
+
+ICollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+bc3785279b9b7810a0c84cb4fe6084fc
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+SphereSphereCollider.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+fd6e8cf39dcf007125384bc91b347a00
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/format b/0.0.1.0/src/alternativa/physics/collision/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base
new file mode 100644
index 0000000..2893b49
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base
@@ -0,0 +1,791 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
+ */
+ public class BoxBoxCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var pos1:Vector3 = new Vector3();
+ private var pos2:Vector3 = new Vector3();
+ private var vectorToBox1:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var tmpPoints:Vector. = new Vector.(8, true);
+ private var pcount:int;
+
+ /**
+ *
+ */
+ public function BoxBoxCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ tmpPoints[i] = new ContactPoint();
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param contactInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box1:CollisionBox;
+ var box2:CollisionBox;
+ if (prim1.body != null) {
+ box1 = prim1 as CollisionBox;
+ box2 = prim2 as CollisionBox;
+ } else {
+ box1 = prim2 as CollisionBox;
+ box2 = prim1 as CollisionBox;
+ }
+ var transform1:Matrix4 = box1.transform;
+ var transform2:Matrix4 = box2.transform;
+
+ // Вектор из центра второго бокса в центр первого
+ pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
+ pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
+ vectorToBox1.x = pos1.x - pos2.x;
+ vectorToBox1.y = pos1.y - pos2.y;
+ vectorToBox1.z = pos1.z - pos2.z;
+
+ // Проверка пересечения по основным осям
+// box1.transform.getAxis(0, axis10);
+ axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+// box1.transform.getAxis(1, axis11);
+ axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+// box1.transform.getAxis(2, axis12);
+ axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+// box2.transform.getAxis(0, axis20);
+ axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+// box2.transform.getAxis(1, axis21);
+ axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+// box2.transform.getAxis(2, axis22);
+ axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Проверка производных осей
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 6) {
+ // Контакт грань-(грань|ребро|вершина)
+ findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 6;
+ findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
+ }
+ contact.body1 = box1.body;
+ contact.body2 = box2.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
+ * @param contactInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
+ var swapNormal:Boolean = false;
+ if (faceAxisIdx > 2) {
+ // Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
+ // но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
+ var tmpBox:CollisionBox = box1;
+ box1 = box2;
+ box2 = tmpBox;
+ vectorToBox1.vReverse();
+ faceAxisIdx -= 3;
+ swapNormal = true;
+ }
+ box1.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
+ if (!faceReversed) colAxis.vReverse();
+ // Ищем ось второго бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box2.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
+ // по грани первого бокса. Таким образом получается список потенциальных точек контакта.
+ box2.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
+ box2.transform.transformVectors(verts1, verts2);
+ box1.transform.transformVectorsInverse(verts2, verts1);
+ var pnum:int = clip(box1.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ pcount = 0;
+ for (var i:int = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ var cp:ContactPoint = tmpPoints[pcount++];
+ box1.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = pen;
+ }
+ }
+ contactInfo.normal.vCopy(colAxis);
+ if (swapNormal) contactInfo.normal.vReverse();
+
+ if (pcount > 4) reducePoints();
+ for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).copyFrom(tmpPoints[i]);
+ contactInfo.pcount = pcount;
+ }
+
+ /**
+ *
+ * @param contactInfo
+ */
+ private function reducePoints():void {
+ var i:int;
+ var minIdx:int;
+ var cp1:ContactPoint;
+ var cp2:ContactPoint;
+ while (pcount > 4) {
+ var minLen:Number = 1e10;
+ var p1:Vector3 = (tmpPoints[pcount - 1] as ContactPoint).pos;
+ var p2:Vector3;
+ for (i = 0; i < pcount; i++) {
+ p2 = (tmpPoints[i] as ContactPoint).pos;
+ var dx:Number = p2.x - p1.x;
+ var dy:Number = p2.y - p1.y;
+ var dz:Number = p2.z - p1.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ if (len < minLen) {
+ minLen = len;
+ minIdx = i;
+ }
+ p1 = p2;
+ }
+ cp1 = tmpPoints[minIdx == 0 ? (pcount - 1) : (minIdx - 1)];
+ cp2 = tmpPoints[minIdx];
+ p1 = cp1.pos;
+ p2 = cp2.pos;
+ p2.x = 0.5*(p1.x + p2.x);
+ p2.y = 0.5*(p1.y + p2.y);
+ p2.z = 0.5*(p1.z + p2.z);
+ cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
+ if (minIdx > 0) {
+ for (i = minIdx; i < pcount; i++) tmpPoints[i - 1] = tmpPoints[i];
+ tmpPoints[pcount - 1] = cp1;
+ }
+ pcount--;
+ }
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ var hs:Vector3 = box.hs;
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clip(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param contactInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
+ box1.transform.getAxis(axisIdx1, axis10);
+ box2.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону первого бокса
+ if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box1.hs);
+ point2.vCopy(box2.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box1.hs.x;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = box2.hs.x;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box1.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = box2.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box1.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ if (axisIdx2 == 2) {
+ point2.z = 0;
+ halfLen2 = box2.hs.z;
+ edgeCode2 |= 4; // 2 << 1
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.z = -point2.z;
+ edgeCode2 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box1.transform);
+ point2.vTransformBy4(box2.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ contact.normal.vCopy(colAxis);
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) {
+ return true;
+ }
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param axis ось
+ * @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
+ * @return величина перекрытия боксов вдоль оси
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1:Vector3):Number {
+ var m:Matrix4 = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
+ import alternativa.physics.types.Vector3;
+
+ class CollisionPointTmp {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+ }
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base
new file mode 100644
index 0000000..8568dc7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base
@@ -0,0 +1,78 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BoxPlaneCollider implements ICollider {
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var normal:Vector3 = new Vector3();
+
+ public function BoxPlaneCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var box:RigidBox = body1 as RigidBox;
+// var plane:RigidPlane;
+// if (box == null) {
+// box = body2 as RigidBox;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные координаты вершин бокса
+// var sx:Number = box.halfSize.x;
+// var sy:Number = box.halfSize.y;
+// var sz:Number = box.halfSize.z;
+// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
+// (verts1[1] as Vector3).reset(sx, -sy, -sz);
+// (verts1[2] as Vector3).reset(sx, sy, -sz);
+// (verts1[3] as Vector3).reset(-sx, sy, -sz);
+// (verts1[4] as Vector3).reset(-sx, -sy, sz);
+// (verts1[5] as Vector3).reset(sx, -sy, sz);
+// (verts1[6] as Vector3).reset(sx, sy, sz);
+// (verts1[7] as Vector3).reset(-sx, sy, sz);
+//
+// box.transform.transformVectors(verts1, verts2);
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+// // Проверяем наличие столкновений с каждой вершиной
+// collisionInfo.pcount = 0;
+// for (var i:int = 0; i < 8; i++) {
+// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
+// var dist:Number = (verts2[i] as Vector3).dot(normal);
+// if (dist < offset) {
+// var cp:ContactPoint;
+// if (collisionInfo.pcount == collisionInfo.points.length) {
+// cp = new ContactPoint();
+// collisionInfo.points[collisionInfo.pcount] = cp;
+// } else {
+// cp = collisionInfo.points[collisionInfo.pcount];
+// }
+// cp.pos.copy(verts2[i]);
+// cp.r1.diff(cp.pos, box.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+// cp.penetration = offset - dist;
+// collisionInfo.pcount++;
+// }
+// }
+// if (collisionInfo.pcount > 0) {
+// collisionInfo.body1 = box;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// return true;
+// }
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base
new file mode 100644
index 0000000..b5287f3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base
@@ -0,0 +1,734 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class BoxRectCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var bPos:Vector3 = new Vector3();
+ private var rPos:Vector3 = new Vector3();
+
+ private var vectorToBox:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+
+ /**
+ *
+ */
+ public function BoxRectCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box:CollisionBox = prim1 as CollisionBox;
+ var rect:CollisionRect;
+ if (box == null) {
+ box = prim2 as CollisionBox;
+ rect = prim1 as CollisionRect;
+ } else {
+ rect = prim2 as CollisionRect;
+ }
+
+ // Вектор из центра прямоугольника в центр бокса
+ box.transform.getAxis(3, bPos);
+ rect.transform.getAxis(3, rPos);
+ vectorToBox.vDiff(bPos, rPos);
+
+ // Проверка пересечения по нормали прямоугольника
+ rect.transform.getAxis(2, axis22);
+ if (!testAxis(box, rect, axis22, 0, vectorToBox)) return false;
+
+ // Проверка пересечения по основным осям бокса
+ box.transform.getAxis(0, axis10);
+ if (!testAxis(box, rect, axis10, 1, vectorToBox)) return false;
+ box.transform.getAxis(1, axis11);
+ if (!testAxis(box, rect, axis11, 2, vectorToBox)) return false;
+ box.transform.getAxis(2, axis12);
+ if (!testAxis(box, rect, axis12, 3, vectorToBox)) return false;
+
+ // Получаем направляющие рёбер прямоугольника
+ rect.transform.getAxis(0, axis20);
+ rect.transform.getAxis(1, axis21);
+
+ // Проверка производных осей
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis20), 4, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis21), 5, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis20), 6, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis21), 7, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis20), 8, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis21), 9, vectorToBox)) return false;
+
+ if (bestAxisIndex < 4) {
+ // Контакт вдоль одной из основных осей
+ if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 4;
+ findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact);
+ }
+ contact.body1 = box.body;
+ contact.body2 = rect.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта бокса с прямоугольником.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
+ * @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
+ * @param colInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, colInfo:Contact):Boolean {
+ var pnum:int, i:int, v:Vector3, cp:ContactPoint;
+ if (faceAxisIdx == 0) {
+ // Столкновение с плоскостью прямоугольника
+
+ // Проверим положение бокса относительно плоскости прямоугольника
+ rect.transform.getAxis(2, colAxis);
+ var offset:Number = colAxis.vDot(rPos);
+ if (bPos.vDot(colAxis) < offset) return false;
+
+ // Ищем ось бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
+ // по прямоугольнику. Таким образом получается список потенциальных точек контакта.
+ box.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box.hs, incFaceAxisIdx, axis.vDot(colAxis) > 0, verts1);
+ box.transform.transformVectors(verts1, verts2);
+ rect.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByRect(rect.hs);
+ // Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ v = verts1[i];
+ if (v.z < tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ rect.transform.transformVector(v, cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = -v.z;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ } else {
+ // Столкновение с гранью бокса
+ faceAxisIdx--;
+ box.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox) > 0;
+ if (!faceReversed) colAxis.vReverse();
+
+ rect.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) return false;
+
+ getFaceVertsByAxis(rect.hs, 2, false, verts1);
+ rect.transform.transformVectors(verts1, verts2);
+ box.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByBox(box.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ box.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = pen;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(hs:Vector3, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param hs
+ * @return
+ */
+ private function clipByRect(hs:Vector3):int {
+ var pnum:int = 4;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box первый бокс
+ * @param rect второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param colInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, colInfo:Contact):void {
+ box.transform.getAxis(axisIdx1, axis10);
+ rect.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону бокса
+ if (colAxis.vDot(vectorToBox) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box.hs);
+ point2.vCopy(rect.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box.hs.x;
+ } else {
+ box.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = rect.hs.x;
+ } else {
+ rect.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = rect.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ rect.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box.transform);
+ point2.vTransformBy4(rect.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ colInfo.normal.vCopy(colAxis);
+ colInfo.pcount = 1;
+ var cp:ContactPoint = colInfo.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box
+ * @param rect
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) return true;
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия вдоль заданной оси.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param axis ось
+ * @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
+ * @return величина перекрытия вдоль оси
+ */
+ public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
+ var m:Matrix4 = box.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = rect.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base
new file mode 100644
index 0000000..2b166f3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base
@@ -0,0 +1,104 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+ /**
+ *
+ */
+ public class BoxSphereCollider implements ICollider {
+
+ private var center:Vector3 = new Vector3();
+ private var closestPt:Vector3 = new Vector3();
+
+ private var bPos:Vector3 = new Vector3();
+ private var sPos:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function BoxSphereCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var sphere:CollisionSphere = prim1 as CollisionSphere;
+ var box:CollisionBox;
+ if (sphere == null) {
+ sphere = prim2 as CollisionSphere;
+ box = prim1 as CollisionBox;
+ } else {
+ box = prim2 as CollisionBox;
+ }
+ // Трансформируем центр сферы в систему координат бокса
+ sphere.transform.getAxis(3, sPos);
+ box.transform.getAxis(3, bPos);
+ box.transform.transformVectorInverse(sPos, center);
+ // Выполняем поиск разделяющей оси
+ var hs:Vector3 = box.hs;
+ var sx:Number = hs.x + sphere.r;
+ var sy:Number = hs.y + sphere.r;
+ var sz:Number = hs.z + sphere.r;
+ if (center.x > sx || center.x < -sx
+ || center.y > sy || center.y < -sy
+ || center.z > sz || center.z < -sz) {
+ return false;
+ }
+ // Находим ближайшую к сфере точку на боксе
+ if (center.x > hs.x) {
+ closestPt.x = hs.x;
+ } else if (center.x < -hs.x) {
+ closestPt.x = -hs.x;
+ } else {
+ closestPt.x = center.x;
+ }
+
+ if (center.y > hs.y) {
+ closestPt.y = hs.y;
+ } else if (center.y < -hs.y) {
+ closestPt.y = -hs.y;
+ } else {
+ closestPt.y = center.y;
+ }
+
+ if (center.z > hs.z) {
+ closestPt.z = hs.z;
+ } else if (center.z < -hs.z) {
+ closestPt.z = -hs.z;
+ } else {
+ closestPt.z = center.z;
+ }
+
+ // TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
+
+ var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
+ if (distSqr > sphere.r*sphere.r) {
+ return false;
+ }
+ // Зафиксированно столкновение
+ contact.body1 = sphere.body;
+ contact.body2 = box.body;
+ contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
+ contact.pcount = 1;
+
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sphere.r - Math.sqrt(distSqr);
+ cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
+ cp.r1.vDiff(cp.pos, sPos);
+ cp.r2.vDiff(cp.pos, bPos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base
new file mode 100644
index 0000000..63c6efd
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base
@@ -0,0 +1,14 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public class BoxTriangleCollider implements ICollider {
+ public function BoxTriangleCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base
new file mode 100644
index 0000000..d2c2e2a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base
@@ -0,0 +1,37 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BruteForceCollisionDetector implements ICollisionDetector {
+ public function BruteForceCollisionDetector() {
+ }
+
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function init():void {
+ }
+
+ public function getAllCollisions(contacts:Vector.):int {
+ return 0;
+ }
+
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ return false;
+ }
+
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base
new file mode 100644
index 0000000..e1a1657
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base
@@ -0,0 +1,16 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.types.BoundBox;
+
+ public class CollisionKdNode {
+ public var objects:Vector.;
+ public var boundBox:BoundBox;
+ public var parent:CollisionKdNode;
+
+ public var axis:int = -1; // 0 - x, 1 - y, 2 - z
+ public var coord:Number;
+ public var positiveNode:CollisionKdNode;
+ public var negativeNode:CollisionKdNode;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base
new file mode 100644
index 0000000..6c62b79
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base
@@ -0,0 +1,259 @@
+package alternativa.physics.collision {
+
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+
+ /**
+ * @author mike
+ */
+ public class CollisionKdTree {
+
+ public var threshold:Number = 0.01;
+ public var minPrimitivesPerNode:int = 1;
+
+ public var rootNode:CollisionKdNode;
+ public var staticChildren:Vector. = new Vector.();
+ public var numStaticChildren:int;
+ private var staticBoundBoxes:Vector. = new Vector.();
+
+ /**
+ * @param primitive
+ */
+ public function addStaticPrimitive(primitive:CollisionPrimitive):void {
+ staticChildren[numStaticChildren++] = primitive;
+ }
+
+ /**
+ * @param primitive
+ * @return
+ */
+ public function removeStaticPrimitive(primitive:CollisionPrimitive):Boolean {
+ var idx:int = staticChildren.indexOf(primitive);
+ if (idx < 0) return false;
+ staticChildren.splice(idx, 1);
+ numStaticChildren--;
+ return true;
+ }
+
+ /**
+ * @param boundBox
+ */
+ public function createTree(boundBox:BoundBox = null):void {
+ if (numStaticChildren == 0) return;
+ // Создаём корневую ноду
+ rootNode = new CollisionKdNode();
+ rootNode.objects = new Vector.();
+ // Расчитываем баунды объектов и рутовой ноды
+ var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
+ for (var i:int = 0; i < numStaticChildren; i++) {
+ var child:CollisionPrimitive = staticChildren[i];
+ var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
+ rootNodeBoundBox.addBoundBox(childBoundBox);
+ rootNode.objects[i] = i;
+ }
+ staticBoundBoxes.length = numStaticChildren;
+ // Разделяем рутовую ноду
+ splitNode(rootNode);
+ }
+
+ private var splitAxis:int;
+ private var splitCoord:Number;
+ private var splitCost:Number;
+ static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
+ static private const splitCoordsX:Vector. = new Vector.();
+ static private const splitCoordsY:Vector. = new Vector.();
+ static private const splitCoordsZ:Vector. = new Vector.();
+ /**
+ * @param node
+ */
+ private function splitNode(node:CollisionKdNode):void {
+ if (node.objects.length <= minPrimitivesPerNode) return;
+
+ var objects:Vector. = node.objects;
+ var i:int, j:int, k:int, length:int = objects.length, c1:Number, c2:Number, ct1:Number, ct2:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
+
+ var nodeBoundBox:BoundBox = node.boundBox;
+
+ // Подготовка баунда с погрешностями
+ nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
+ nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
+ nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
+ nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
+ nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
+ nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
+ var doubleThreshold:Number = threshold*2;
+
+ // Собираем опорные координаты
+ var numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
+ for (i = 0; i < length; i++) {
+ var boundBox:BoundBox = staticBoundBoxes[objects[i]];
+ if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
+ if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
+
+ if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
+ if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
+
+ if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
+ if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
+ }
+
+ // Убираем дубликаты координат, ищем наилучший сплит
+ splitAxis = -1; splitCost = 1e308;
+ i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsX) {
+ if (isNaN(c1 = splitCoordsX[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minX);
+ areaPositive = area*(nodeBoundBox.maxX - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxX <= ct2) {
+ if (boundBox.minX < ct1) numNegative++;
+ } else {
+ if (boundBox.minX >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 0;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsY) {
+ if (isNaN(c1 = splitCoordsY[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minY);
+ areaPositive = area*(nodeBoundBox.maxY - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxY <= ct2) {
+ if (boundBox.minY < ct1) numNegative++;
+ } else {
+ if (boundBox.minY >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 1;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
+ while (i < numSplitCoordsZ) {
+ if (isNaN(c1 = splitCoordsZ[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minZ);
+ areaPositive = area*(nodeBoundBox.maxZ - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxZ <= ct2) {
+ if (boundBox.minZ < ct1) numNegative++;
+ } else {
+ if (boundBox.minZ >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 2;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
+ }
+
+ // Если сплит не найден, выходим
+ if (splitAxis < 0) return;
+
+ // Разделяем ноду
+ var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
+ node.axis = splitAxis;
+ node.coord = splitCoord;
+
+ // Создаём дочерние ноды
+ node.negativeNode = new CollisionKdNode();
+ node.positiveNode = new CollisionKdNode();
+ node.negativeNode.parent = node;
+ node.positiveNode.parent = node;
+ node.negativeNode.boundBox = nodeBoundBox.clone();
+ node.positiveNode.boundBox = nodeBoundBox.clone();
+ if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
+ else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
+ else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
+
+ // Распределяем объекты по дочерним нодам
+ ct1 = splitCoord - threshold; ct2 = splitCoord + threshold;
+ for (i = 0; i < length; i++) {
+ boundBox = staticBoundBoxes[objects[i]];
+ var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
+ var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
+ if (max <= ct2) {
+ // Объект в негативной стороне
+ if (node.negativeNode.objects == null) node.negativeNode.objects = new Vector.();
+ node.negativeNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ if (min >= ct1) {
+ // Объект в положительной стороне
+ if (node.positiveNode.objects == null) node.positiveNode.objects = new Vector.();
+ node.positiveNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ // Распилился
+ }
+ }
+ }
+
+ // Очистка списка объектов
+ j = 0;
+ for (i = 0; i < length; i++) {
+ if (objects[i] >= 0) objects[j++] = objects[i];
+ }
+ if (j > 0) objects.length = j; else node.objects = null;
+
+ // Разделение дочерних нод
+ if (node.negativeNode.objects != null) splitNode(node.negativeNode);
+ if (node.positiveNode.objects != null) splitNode(node.positiveNode);
+ }
+
+ /**
+ *
+ */
+ public function traceTree():void {
+ traceNode("", rootNode);
+ }
+
+ /**
+ * @param str
+ * @param node
+ */
+ private function traceNode(str:String, node:CollisionKdNode):void {
+ if (node == null) return;
+ trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.objects);
+ traceNode(str + "-", node.negativeNode);
+ traceNode(str + "+", node.positiveNode);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base
new file mode 100644
index 0000000..733231b
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base
@@ -0,0 +1,12 @@
+package alternativa.physics.collision {
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+
+ /**
+ * Интерфейс определителя столкновений между двумя примитивами.
+ */
+ public interface ICollider {
+ function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base
new file mode 100644
index 0000000..83e94d7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base
@@ -0,0 +1,73 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Интерфейс детектора столкновений.
+ */
+ public interface ICollisionDetector {
+
+ /**
+ * Добавляет физический примитив в коллайдер.
+ *
+ * @param primitive добавляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успешно добавлен, иначе false
+ */
+ function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Удаляет физический примитив из коллайдера.
+ *
+ * @param primitive удаляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успшено удалён, иначе false
+ */
+ function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Выполняет инициализацию детектора после обновления списка примитивов.
+ */
+ function init():void;
+
+ /**
+ * Получает все столкновения в текущей конфигурации физической геометрии.
+ *
+ * @param contacts список контактов, в кторые будет записана информация о столкновении
+ * @return количество найденных столкновений
+ */
+ function getAllCollisions(contacts:Vector.):int;
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+
+ /**
+ * Тестирует луч на пересечение со статической физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base
new file mode 100644
index 0000000..bb1b756
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base
@@ -0,0 +1,9 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public interface ICollisionPredicate {
+
+ function considerCollision(primitive:CollisionPrimitive):Boolean;
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base
new file mode 100644
index 0000000..0f3d929
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base
@@ -0,0 +1,7 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Body;
+
+ public interface IRayCollisionPredicate {
+ function considerBody(body:Body):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base
new file mode 100644
index 0000000..46435f9
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base
@@ -0,0 +1,423 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
+ */
+ public class KdTreeCollisionDetector implements ICollisionDetector {
+
+ altphysics var tree:CollisionKdTree;
+ altphysics var dynamicPrimitives:Vector.;
+ altphysics var dynamicPrimitivesNum:int;
+ altphysics var threshold:Number = 0.0001;
+ private var colliders:Object = {};
+
+ private var _time:MinMax = new MinMax();
+ private var _n:Vector3 = new Vector3();
+ private var _o:Vector3 = new Vector3();
+ private var _dynamicIntersection:RayIntersection = new RayIntersection();
+
+ /**
+ *
+ */
+ public function KdTreeCollisionDetector() {
+ tree = new CollisionKdTree();
+ dynamicPrimitives = new Vector.();
+
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
+// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
+
+// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
+ addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
+ }
+
+ /**
+ * @param primitive
+ * @param isStatic
+ * @return
+ */
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) tree.addStaticPrimitive(primitive);
+ else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
+ return true;
+ }
+
+ /**
+ *
+ * @param primitive
+ * @param isStatic
+ * @return
+ *
+ */
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) return tree.removeStaticPrimitive(primitive);
+ var idx:int = dynamicPrimitives.indexOf(primitive);
+ if (idx < 0) return false;
+ dynamicPrimitives.splice(idx, 1);
+ dynamicPrimitivesNum--;
+ return true;
+ }
+
+ /**
+ *
+ */
+ public function init():void {
+ tree.createTree();
+// tree.traceTree();
+ }
+
+ /**
+ *
+ * @param contacts
+ * @return
+ */
+ public function getAllCollisions(contacts:Vector.):int {
+ var colNum:int = 0;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ primitive.calculateAABB();
+ colNum += getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts, colNum);
+
+ // Столкновения динамических примитивов между собой
+ // TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
+ for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
+ if (collide(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
+ }
+ }
+
+ return colNum;
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
+ if (prim1.body != null && prim1.body == prim2.body) return false;
+ if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
+ var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
+ if (collider != null && collider.collide(prim1, prim2, contact)) {
+ if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
+ if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
+ // Сохраняем ссылку на контакт для каждого тела
+ if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
+ if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
+ // Вычисляем максимальную глубину пересечения для контакта
+ contact.maxPenetration = (contact.points[0] as ContactPoint).penetration;
+ var pen:Number;
+ for (var i:int = contact.pcount - 1; i >= 1; i--) {
+ if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента
+ * @param predicate
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
+ var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
+
+ if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
+
+ if (hasDynamicIntersection && hasStaticIntersection) {
+ if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ if (hasStaticIntersection) return true;
+
+ result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ *
+ */
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // Вычисление точки пересечения с корневм узлом
+ if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
+ if (_time.max < 0 || _time.min > maxTime) return false;
+
+ if (_time.min <= 0) {
+ _time.min = 0;
+ _o.x = origin.x;
+ _o.y = origin.y;
+ _o.z = origin.z;
+ } else {
+ _o.x = origin.x + _time.min*dir.x;
+ _o.y = origin.y + _time.min*dir.y;
+ _o.z = origin.z + _time.min*dir.z;
+ }
+ if (_time.max > maxTime) _time.max = maxTime;
+
+ var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
+ return hasIntersection ? result.t <= maxTime : false;
+ }
+
+ /**
+ *
+ * @param type1
+ * @param type2
+ * @param collider
+ */
+ private function addCollider(type1:int, type2:int, collider:ICollider):void {
+ colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
+ }
+
+ /**
+ * Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
+ *
+ * @param node
+ * @param primitive
+ * @param contacts
+ * @param startIndex
+ * @return
+ */
+ private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Vector., startIndex:int):int {
+ var colNum:int = 0;
+ if (node.objects != null) {
+ // Поиск столкновений со статическими примитивами узла
+ var primitives:Vector. = tree.staticChildren;
+ var indices:Vector. = node.objects;
+ for (var i:int = indices.length - 1; i >= 0; i--)
+ if (collide(primitive, primitives[indices[i]], contacts[startIndex + colNum])) colNum++;
+ }
+ if (node.axis == -1) return colNum;
+ var min:Number;
+ var max:Number;
+ switch (node.axis) {
+ case 0:
+ min = primitive.aabb.minX;
+ max = primitive.aabb.maxX;
+ break;
+ case 1:
+ min = primitive.aabb.minY;
+ max = primitive.aabb.maxY;
+ break;
+ case 2:
+ min = primitive.aabb.minZ;
+ max = primitive.aabb.maxZ;
+ break;
+ }
+ if (min < node.coord) colNum += getPrimitiveNodeCollisions(node.negativeNode, primitive, contacts, startIndex + colNum);
+ if (max > node.coord) colNum += getPrimitiveNodeCollisions(node.positiveNode, primitive, contacts, startIndex + colNum);
+ return colNum;
+ }
+
+ /**
+ * Тест пересечения луча с динамическими примитивами.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ */
+ private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var minTime:Number = maxTime + 1;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ result.primitive = primitive;
+ result.normal.x = _n.x;
+ result.normal.y = _n.y;
+ result.normal.z = _n.z;
+ }
+ }
+ if (minTime > maxTime) return false;
+ result.pos.x = origin.x + dir.x*minTime;
+ result.pos.y = origin.y + dir.y*minTime;
+ result.pos.z = origin.z + dir.z*minTime;
+ result.t = minTime;
+ return true;
+ }
+
+ /**
+ * Вычисляет точки пересечения луча с AABB.
+ *
+ * @param origin точка начала луча
+ * @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
+ * @param bb AABB, с которым пересекается луч
+ * @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
+ * @return true в случае наличия хотя бы одного пересечения, иначе false
+ */
+ private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
+ time.min = -1;
+ time.max = 1e308;
+ var t1:Number;
+ var t2:Number;
+ // Цикл по осям бокса
+ for (var i:int = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ if (dir.x < threshold && dir.x > -threshold) {
+ if (origin.x < bb.minX || origin.x > bb.maxX) return false;
+ else continue;
+ }
+ t1 = (bb.minX - origin.x)/dir.x;
+ t2 = (bb.maxX - origin.x)/dir.x;
+ break;
+ case 1:
+ if (dir.y < threshold && dir.y > -threshold) {
+ if (origin.y < bb.minY || origin.y > bb.maxY) return false;
+ else continue;
+ }
+ t1 = (bb.minY - origin.y)/dir.y;
+ t2 = (bb.maxY - origin.y)/dir.y;
+ break;
+ case 2:
+ if (dir.z < threshold && dir.z > -threshold) {
+ if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
+ else continue;
+ }
+ t1 = (bb.minZ - origin.z)/dir.z;
+ t2 = (bb.maxZ - origin.z)/dir.z;
+ break;
+ }
+ if (t1 < t2) {
+ if (t1 > time.min) time.min = t1;
+ if (t2 < time.max) time.max = t2;
+ } else {
+ if (t2 > time.min) time.min = t2;
+ if (t1 < time.max) time.max = t1;
+ }
+ if (time.max < time.min) return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param node
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param t1 время входа луча в узел
+ * @param t2 время выхода луча из узла
+ * @param intersection
+ */
+ private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // При наличии в узле объектов, проверяем пересечение с ними
+ if (node.objects != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.objects, predicate, result)) return true;
+ // Выход из функции если это конечный узел
+ if (node.axis == -1) return false;
+
+ // Определение времени пересечения луча и плоскости разделения узла
+ var splitTime:Number;
+ var currChildNode:CollisionKdNode;
+ switch (node.axis) {
+ case 0:
+ if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.x)/dir.x;
+ currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 1:
+ if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.y)/dir.y;
+ currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 2:
+ if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.z)/dir.z;
+ currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ }
+ // Определение порядка проверки
+ if (splitTime < t1 || splitTime > t2) {
+ // Луч не переходит в соседний дочерний узел
+ return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
+ } else {
+ // Луч переходит из одного дочернего узла в другой
+ var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
+ if (intersects) return true;
+ _o.x = origin.x + splitTime*dir.x;
+ _o.y = origin.y + splitTime*dir.y;
+ _o.z = origin.z + splitTime*dir.z;
+ return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
+ }
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param primitives
+ * @param indices
+ * @param intersection
+ * @return
+ *
+ */
+ private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector., indices:Vector., predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ var pnum:int = indices.length;
+ var minTime:Number = 1e308;
+ for (var i:int = 0; i < pnum; i++) {
+ var primitive:CollisionPrimitive = primitives[indices[i]];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ intersection.primitive = primitive;
+ intersection.normal.x = _n.x;
+ intersection.normal.y = _n.y;
+ intersection.normal.z = _n.z;
+ }
+ }
+ if (minTime == 1e308) return false;
+ intersection.pos.x = origin.x + dir.x*minTime;
+ intersection.pos.y = origin.y + dir.y*minTime;
+ intersection.pos.z = origin.z + dir.z*minTime;
+ intersection.t = minTime;
+ return true;
+ }
+
+ }
+}
+
+ class MinMax {
+ public var min:Number = 0;
+ public var max:Number = 0;
+ }
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base
new file mode 100644
index 0000000..9b527d8
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base
@@ -0,0 +1,58 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ *
+ */
+ public class SpherePlaneCollider implements ICollider {
+
+ private var normal:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function SpherePlaneCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var sphere:RigidSphere = body1 as RigidSphere;
+// var plane:RigidPlane;
+// if (sphere == null) {
+// sphere = body2 as RigidSphere;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+//
+// var dist:Number = sphere.state.pos.dot(normal) - offset;
+// if (dist > sphere.r) return false;
+//
+// collisionInfo.body1 = sphere;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// collisionInfo.pcount = 1;
+//
+// var cp:ContactPoint = collisionInfo.points[0];
+// cp.penetration = sphere.r - dist;
+// cp.pos.copy(normal).reverse().scale(sphere.r).add(sphere.state.pos);
+// cp.r1.diff(cp.pos, sphere.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base
new file mode 100644
index 0000000..9111a84
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base
@@ -0,0 +1,60 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class SphereSphereCollider implements ICollider {
+
+ private var p1:Vector3 = new Vector3();
+ private var p2:Vector3 = new Vector3();
+
+ public function SphereSphereCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var s1:CollisionSphere;
+ var s2:CollisionSphere;
+ if (prim1.body != null) {
+ s1 = prim1 as CollisionSphere;
+ s2 = prim2 as CollisionSphere;
+ } else {
+ s1 = prim2 as CollisionSphere;
+ s2 = prim1 as CollisionSphere;
+ }
+
+ s1.transform.getAxis(3, p1);
+ s2.transform.getAxis(3, p2);
+ var dx:Number = p1.x - p2.x;
+ var dy:Number = p1.y - p2.y;
+ var dz:Number = p1.z - p2.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ var sum:Number = s1.r + s2.r;
+ if (len > sum*sum) return false;
+ len = Math.sqrt(len);
+ dx /= len;
+ dy /= len;
+ dz /= len;
+
+ contact.body1 = s1.body;
+ contact.body2 = s2.body;
+ contact.normal.x = dx;
+ contact.normal.y = dy;
+ contact.normal.z = dz;
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sum - len;
+ cp.pos.x = p1.x - dx*s1.r;
+ cp.pos.y = p1.y - dy*s1.r;
+ cp.pos.z = p1.z - dz*s1.r;
+ cp.r1.vDiff(cp.pos, p1);
+ cp.r2.vDiff(cp.pos, p2);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as b/0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
new file mode 100644
index 0000000..2893b49
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
@@ -0,0 +1,791 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
+ */
+ public class BoxBoxCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var pos1:Vector3 = new Vector3();
+ private var pos2:Vector3 = new Vector3();
+ private var vectorToBox1:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var tmpPoints:Vector. = new Vector.(8, true);
+ private var pcount:int;
+
+ /**
+ *
+ */
+ public function BoxBoxCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ tmpPoints[i] = new ContactPoint();
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param contactInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box1:CollisionBox;
+ var box2:CollisionBox;
+ if (prim1.body != null) {
+ box1 = prim1 as CollisionBox;
+ box2 = prim2 as CollisionBox;
+ } else {
+ box1 = prim2 as CollisionBox;
+ box2 = prim1 as CollisionBox;
+ }
+ var transform1:Matrix4 = box1.transform;
+ var transform2:Matrix4 = box2.transform;
+
+ // Вектор из центра второго бокса в центр первого
+ pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
+ pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
+ vectorToBox1.x = pos1.x - pos2.x;
+ vectorToBox1.y = pos1.y - pos2.y;
+ vectorToBox1.z = pos1.z - pos2.z;
+
+ // Проверка пересечения по основным осям
+// box1.transform.getAxis(0, axis10);
+ axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+// box1.transform.getAxis(1, axis11);
+ axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+// box1.transform.getAxis(2, axis12);
+ axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+// box2.transform.getAxis(0, axis20);
+ axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+// box2.transform.getAxis(1, axis21);
+ axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+// box2.transform.getAxis(2, axis22);
+ axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Проверка производных осей
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 6) {
+ // Контакт грань-(грань|ребро|вершина)
+ findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 6;
+ findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
+ }
+ contact.body1 = box1.body;
+ contact.body2 = box2.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
+ * @param contactInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
+ var swapNormal:Boolean = false;
+ if (faceAxisIdx > 2) {
+ // Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
+ // но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
+ var tmpBox:CollisionBox = box1;
+ box1 = box2;
+ box2 = tmpBox;
+ vectorToBox1.vReverse();
+ faceAxisIdx -= 3;
+ swapNormal = true;
+ }
+ box1.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
+ if (!faceReversed) colAxis.vReverse();
+ // Ищем ось второго бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box2.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
+ // по грани первого бокса. Таким образом получается список потенциальных точек контакта.
+ box2.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
+ box2.transform.transformVectors(verts1, verts2);
+ box1.transform.transformVectorsInverse(verts2, verts1);
+ var pnum:int = clip(box1.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ pcount = 0;
+ for (var i:int = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ var cp:ContactPoint = tmpPoints[pcount++];
+ box1.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = pen;
+ }
+ }
+ contactInfo.normal.vCopy(colAxis);
+ if (swapNormal) contactInfo.normal.vReverse();
+
+ if (pcount > 4) reducePoints();
+ for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).copyFrom(tmpPoints[i]);
+ contactInfo.pcount = pcount;
+ }
+
+ /**
+ *
+ * @param contactInfo
+ */
+ private function reducePoints():void {
+ var i:int;
+ var minIdx:int;
+ var cp1:ContactPoint;
+ var cp2:ContactPoint;
+ while (pcount > 4) {
+ var minLen:Number = 1e10;
+ var p1:Vector3 = (tmpPoints[pcount - 1] as ContactPoint).pos;
+ var p2:Vector3;
+ for (i = 0; i < pcount; i++) {
+ p2 = (tmpPoints[i] as ContactPoint).pos;
+ var dx:Number = p2.x - p1.x;
+ var dy:Number = p2.y - p1.y;
+ var dz:Number = p2.z - p1.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ if (len < minLen) {
+ minLen = len;
+ minIdx = i;
+ }
+ p1 = p2;
+ }
+ cp1 = tmpPoints[minIdx == 0 ? (pcount - 1) : (minIdx - 1)];
+ cp2 = tmpPoints[minIdx];
+ p1 = cp1.pos;
+ p2 = cp2.pos;
+ p2.x = 0.5*(p1.x + p2.x);
+ p2.y = 0.5*(p1.y + p2.y);
+ p2.z = 0.5*(p1.z + p2.z);
+ cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
+ if (minIdx > 0) {
+ for (i = minIdx; i < pcount; i++) tmpPoints[i - 1] = tmpPoints[i];
+ tmpPoints[pcount - 1] = cp1;
+ }
+ pcount--;
+ }
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ var hs:Vector3 = box.hs;
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clip(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param contactInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
+ box1.transform.getAxis(axisIdx1, axis10);
+ box2.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону первого бокса
+ if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box1.hs);
+ point2.vCopy(box2.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box1.hs.x;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = box2.hs.x;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box1.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = box2.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box1.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ if (axisIdx2 == 2) {
+ point2.z = 0;
+ halfLen2 = box2.hs.z;
+ edgeCode2 |= 4; // 2 << 1
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.z = -point2.z;
+ edgeCode2 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box1.transform);
+ point2.vTransformBy4(box2.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ contact.normal.vCopy(colAxis);
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) {
+ return true;
+ }
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param axis ось
+ * @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
+ * @return величина перекрытия боксов вдоль оси
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1:Vector3):Number {
+ var m:Matrix4 = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
+ import alternativa.physics.types.Vector3;
+
+ class CollisionPointTmp {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+ }
diff --git a/0.0.1.0/src/alternativa/physics/collision/BoxPlaneCollider.as b/0.0.1.0/src/alternativa/physics/collision/BoxPlaneCollider.as
new file mode 100644
index 0000000..8568dc7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BoxPlaneCollider.as
@@ -0,0 +1,78 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BoxPlaneCollider implements ICollider {
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var normal:Vector3 = new Vector3();
+
+ public function BoxPlaneCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var box:RigidBox = body1 as RigidBox;
+// var plane:RigidPlane;
+// if (box == null) {
+// box = body2 as RigidBox;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные координаты вершин бокса
+// var sx:Number = box.halfSize.x;
+// var sy:Number = box.halfSize.y;
+// var sz:Number = box.halfSize.z;
+// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
+// (verts1[1] as Vector3).reset(sx, -sy, -sz);
+// (verts1[2] as Vector3).reset(sx, sy, -sz);
+// (verts1[3] as Vector3).reset(-sx, sy, -sz);
+// (verts1[4] as Vector3).reset(-sx, -sy, sz);
+// (verts1[5] as Vector3).reset(sx, -sy, sz);
+// (verts1[6] as Vector3).reset(sx, sy, sz);
+// (verts1[7] as Vector3).reset(-sx, sy, sz);
+//
+// box.transform.transformVectors(verts1, verts2);
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+// // Проверяем наличие столкновений с каждой вершиной
+// collisionInfo.pcount = 0;
+// for (var i:int = 0; i < 8; i++) {
+// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
+// var dist:Number = (verts2[i] as Vector3).dot(normal);
+// if (dist < offset) {
+// var cp:ContactPoint;
+// if (collisionInfo.pcount == collisionInfo.points.length) {
+// cp = new ContactPoint();
+// collisionInfo.points[collisionInfo.pcount] = cp;
+// } else {
+// cp = collisionInfo.points[collisionInfo.pcount];
+// }
+// cp.pos.copy(verts2[i]);
+// cp.r1.diff(cp.pos, box.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+// cp.penetration = offset - dist;
+// collisionInfo.pcount++;
+// }
+// }
+// if (collisionInfo.pcount > 0) {
+// collisionInfo.body1 = box;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// return true;
+// }
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as b/0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
new file mode 100644
index 0000000..b5287f3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
@@ -0,0 +1,734 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class BoxRectCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var bPos:Vector3 = new Vector3();
+ private var rPos:Vector3 = new Vector3();
+
+ private var vectorToBox:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+
+ /**
+ *
+ */
+ public function BoxRectCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box:CollisionBox = prim1 as CollisionBox;
+ var rect:CollisionRect;
+ if (box == null) {
+ box = prim2 as CollisionBox;
+ rect = prim1 as CollisionRect;
+ } else {
+ rect = prim2 as CollisionRect;
+ }
+
+ // Вектор из центра прямоугольника в центр бокса
+ box.transform.getAxis(3, bPos);
+ rect.transform.getAxis(3, rPos);
+ vectorToBox.vDiff(bPos, rPos);
+
+ // Проверка пересечения по нормали прямоугольника
+ rect.transform.getAxis(2, axis22);
+ if (!testAxis(box, rect, axis22, 0, vectorToBox)) return false;
+
+ // Проверка пересечения по основным осям бокса
+ box.transform.getAxis(0, axis10);
+ if (!testAxis(box, rect, axis10, 1, vectorToBox)) return false;
+ box.transform.getAxis(1, axis11);
+ if (!testAxis(box, rect, axis11, 2, vectorToBox)) return false;
+ box.transform.getAxis(2, axis12);
+ if (!testAxis(box, rect, axis12, 3, vectorToBox)) return false;
+
+ // Получаем направляющие рёбер прямоугольника
+ rect.transform.getAxis(0, axis20);
+ rect.transform.getAxis(1, axis21);
+
+ // Проверка производных осей
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis20), 4, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis21), 5, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis20), 6, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis21), 7, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis20), 8, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis21), 9, vectorToBox)) return false;
+
+ if (bestAxisIndex < 4) {
+ // Контакт вдоль одной из основных осей
+ if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 4;
+ findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact);
+ }
+ contact.body1 = box.body;
+ contact.body2 = rect.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта бокса с прямоугольником.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
+ * @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
+ * @param colInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, colInfo:Contact):Boolean {
+ var pnum:int, i:int, v:Vector3, cp:ContactPoint;
+ if (faceAxisIdx == 0) {
+ // Столкновение с плоскостью прямоугольника
+
+ // Проверим положение бокса относительно плоскости прямоугольника
+ rect.transform.getAxis(2, colAxis);
+ var offset:Number = colAxis.vDot(rPos);
+ if (bPos.vDot(colAxis) < offset) return false;
+
+ // Ищем ось бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
+ // по прямоугольнику. Таким образом получается список потенциальных точек контакта.
+ box.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box.hs, incFaceAxisIdx, axis.vDot(colAxis) > 0, verts1);
+ box.transform.transformVectors(verts1, verts2);
+ rect.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByRect(rect.hs);
+ // Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ v = verts1[i];
+ if (v.z < tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ rect.transform.transformVector(v, cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = -v.z;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ } else {
+ // Столкновение с гранью бокса
+ faceAxisIdx--;
+ box.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox) > 0;
+ if (!faceReversed) colAxis.vReverse();
+
+ rect.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) return false;
+
+ getFaceVertsByAxis(rect.hs, 2, false, verts1);
+ rect.transform.transformVectors(verts1, verts2);
+ box.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByBox(box.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ box.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = pen;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(hs:Vector3, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param hs
+ * @return
+ */
+ private function clipByRect(hs:Vector3):int {
+ var pnum:int = 4;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box первый бокс
+ * @param rect второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param colInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, colInfo:Contact):void {
+ box.transform.getAxis(axisIdx1, axis10);
+ rect.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону бокса
+ if (colAxis.vDot(vectorToBox) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box.hs);
+ point2.vCopy(rect.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box.hs.x;
+ } else {
+ box.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = rect.hs.x;
+ } else {
+ rect.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = rect.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ rect.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box.transform);
+ point2.vTransformBy4(rect.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ colInfo.normal.vCopy(colAxis);
+ colInfo.pcount = 1;
+ var cp:ContactPoint = colInfo.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box
+ * @param rect
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) return true;
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия вдоль заданной оси.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param axis ось
+ * @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
+ * @return величина перекрытия вдоль оси
+ */
+ public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
+ var m:Matrix4 = box.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = rect.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as b/0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
new file mode 100644
index 0000000..2b166f3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
@@ -0,0 +1,104 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+ /**
+ *
+ */
+ public class BoxSphereCollider implements ICollider {
+
+ private var center:Vector3 = new Vector3();
+ private var closestPt:Vector3 = new Vector3();
+
+ private var bPos:Vector3 = new Vector3();
+ private var sPos:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function BoxSphereCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var sphere:CollisionSphere = prim1 as CollisionSphere;
+ var box:CollisionBox;
+ if (sphere == null) {
+ sphere = prim2 as CollisionSphere;
+ box = prim1 as CollisionBox;
+ } else {
+ box = prim2 as CollisionBox;
+ }
+ // Трансформируем центр сферы в систему координат бокса
+ sphere.transform.getAxis(3, sPos);
+ box.transform.getAxis(3, bPos);
+ box.transform.transformVectorInverse(sPos, center);
+ // Выполняем поиск разделяющей оси
+ var hs:Vector3 = box.hs;
+ var sx:Number = hs.x + sphere.r;
+ var sy:Number = hs.y + sphere.r;
+ var sz:Number = hs.z + sphere.r;
+ if (center.x > sx || center.x < -sx
+ || center.y > sy || center.y < -sy
+ || center.z > sz || center.z < -sz) {
+ return false;
+ }
+ // Находим ближайшую к сфере точку на боксе
+ if (center.x > hs.x) {
+ closestPt.x = hs.x;
+ } else if (center.x < -hs.x) {
+ closestPt.x = -hs.x;
+ } else {
+ closestPt.x = center.x;
+ }
+
+ if (center.y > hs.y) {
+ closestPt.y = hs.y;
+ } else if (center.y < -hs.y) {
+ closestPt.y = -hs.y;
+ } else {
+ closestPt.y = center.y;
+ }
+
+ if (center.z > hs.z) {
+ closestPt.z = hs.z;
+ } else if (center.z < -hs.z) {
+ closestPt.z = -hs.z;
+ } else {
+ closestPt.z = center.z;
+ }
+
+ // TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
+
+ var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
+ if (distSqr > sphere.r*sphere.r) {
+ return false;
+ }
+ // Зафиксированно столкновение
+ contact.body1 = sphere.body;
+ contact.body2 = box.body;
+ contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
+ contact.pcount = 1;
+
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sphere.r - Math.sqrt(distSqr);
+ cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
+ cp.r1.vDiff(cp.pos, sPos);
+ cp.r2.vDiff(cp.pos, bPos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/BoxTriangleCollider.as b/0.0.1.0/src/alternativa/physics/collision/BoxTriangleCollider.as
new file mode 100644
index 0000000..63c6efd
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BoxTriangleCollider.as
@@ -0,0 +1,14 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public class BoxTriangleCollider implements ICollider {
+ public function BoxTriangleCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as b/0.0.1.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as
new file mode 100644
index 0000000..d2c2e2a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as
@@ -0,0 +1,37 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BruteForceCollisionDetector implements ICollisionDetector {
+ public function BruteForceCollisionDetector() {
+ }
+
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function init():void {
+ }
+
+ public function getAllCollisions(contacts:Vector.):int {
+ return 0;
+ }
+
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ return false;
+ }
+
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as b/0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
new file mode 100644
index 0000000..e1a1657
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
@@ -0,0 +1,16 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.types.BoundBox;
+
+ public class CollisionKdNode {
+ public var objects:Vector.;
+ public var boundBox:BoundBox;
+ public var parent:CollisionKdNode;
+
+ public var axis:int = -1; // 0 - x, 1 - y, 2 - z
+ public var coord:Number;
+ public var positiveNode:CollisionKdNode;
+ public var negativeNode:CollisionKdNode;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as b/0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
new file mode 100644
index 0000000..6c62b79
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
@@ -0,0 +1,259 @@
+package alternativa.physics.collision {
+
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+
+ /**
+ * @author mike
+ */
+ public class CollisionKdTree {
+
+ public var threshold:Number = 0.01;
+ public var minPrimitivesPerNode:int = 1;
+
+ public var rootNode:CollisionKdNode;
+ public var staticChildren:Vector. = new Vector.();
+ public var numStaticChildren:int;
+ private var staticBoundBoxes:Vector. = new Vector.();
+
+ /**
+ * @param primitive
+ */
+ public function addStaticPrimitive(primitive:CollisionPrimitive):void {
+ staticChildren[numStaticChildren++] = primitive;
+ }
+
+ /**
+ * @param primitive
+ * @return
+ */
+ public function removeStaticPrimitive(primitive:CollisionPrimitive):Boolean {
+ var idx:int = staticChildren.indexOf(primitive);
+ if (idx < 0) return false;
+ staticChildren.splice(idx, 1);
+ numStaticChildren--;
+ return true;
+ }
+
+ /**
+ * @param boundBox
+ */
+ public function createTree(boundBox:BoundBox = null):void {
+ if (numStaticChildren == 0) return;
+ // Создаём корневую ноду
+ rootNode = new CollisionKdNode();
+ rootNode.objects = new Vector.();
+ // Расчитываем баунды объектов и рутовой ноды
+ var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
+ for (var i:int = 0; i < numStaticChildren; i++) {
+ var child:CollisionPrimitive = staticChildren[i];
+ var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
+ rootNodeBoundBox.addBoundBox(childBoundBox);
+ rootNode.objects[i] = i;
+ }
+ staticBoundBoxes.length = numStaticChildren;
+ // Разделяем рутовую ноду
+ splitNode(rootNode);
+ }
+
+ private var splitAxis:int;
+ private var splitCoord:Number;
+ private var splitCost:Number;
+ static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
+ static private const splitCoordsX:Vector. = new Vector.();
+ static private const splitCoordsY:Vector. = new Vector.();
+ static private const splitCoordsZ:Vector. = new Vector.();
+ /**
+ * @param node
+ */
+ private function splitNode(node:CollisionKdNode):void {
+ if (node.objects.length <= minPrimitivesPerNode) return;
+
+ var objects:Vector. = node.objects;
+ var i:int, j:int, k:int, length:int = objects.length, c1:Number, c2:Number, ct1:Number, ct2:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
+
+ var nodeBoundBox:BoundBox = node.boundBox;
+
+ // Подготовка баунда с погрешностями
+ nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
+ nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
+ nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
+ nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
+ nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
+ nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
+ var doubleThreshold:Number = threshold*2;
+
+ // Собираем опорные координаты
+ var numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
+ for (i = 0; i < length; i++) {
+ var boundBox:BoundBox = staticBoundBoxes[objects[i]];
+ if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
+ if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
+
+ if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
+ if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
+
+ if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
+ if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
+ }
+
+ // Убираем дубликаты координат, ищем наилучший сплит
+ splitAxis = -1; splitCost = 1e308;
+ i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsX) {
+ if (isNaN(c1 = splitCoordsX[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minX);
+ areaPositive = area*(nodeBoundBox.maxX - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxX <= ct2) {
+ if (boundBox.minX < ct1) numNegative++;
+ } else {
+ if (boundBox.minX >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 0;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsY) {
+ if (isNaN(c1 = splitCoordsY[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minY);
+ areaPositive = area*(nodeBoundBox.maxY - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxY <= ct2) {
+ if (boundBox.minY < ct1) numNegative++;
+ } else {
+ if (boundBox.minY >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 1;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
+ while (i < numSplitCoordsZ) {
+ if (isNaN(c1 = splitCoordsZ[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minZ);
+ areaPositive = area*(nodeBoundBox.maxZ - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxZ <= ct2) {
+ if (boundBox.minZ < ct1) numNegative++;
+ } else {
+ if (boundBox.minZ >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 2;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
+ }
+
+ // Если сплит не найден, выходим
+ if (splitAxis < 0) return;
+
+ // Разделяем ноду
+ var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
+ node.axis = splitAxis;
+ node.coord = splitCoord;
+
+ // Создаём дочерние ноды
+ node.negativeNode = new CollisionKdNode();
+ node.positiveNode = new CollisionKdNode();
+ node.negativeNode.parent = node;
+ node.positiveNode.parent = node;
+ node.negativeNode.boundBox = nodeBoundBox.clone();
+ node.positiveNode.boundBox = nodeBoundBox.clone();
+ if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
+ else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
+ else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
+
+ // Распределяем объекты по дочерним нодам
+ ct1 = splitCoord - threshold; ct2 = splitCoord + threshold;
+ for (i = 0; i < length; i++) {
+ boundBox = staticBoundBoxes[objects[i]];
+ var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
+ var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
+ if (max <= ct2) {
+ // Объект в негативной стороне
+ if (node.negativeNode.objects == null) node.negativeNode.objects = new Vector.();
+ node.negativeNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ if (min >= ct1) {
+ // Объект в положительной стороне
+ if (node.positiveNode.objects == null) node.positiveNode.objects = new Vector.();
+ node.positiveNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ // Распилился
+ }
+ }
+ }
+
+ // Очистка списка объектов
+ j = 0;
+ for (i = 0; i < length; i++) {
+ if (objects[i] >= 0) objects[j++] = objects[i];
+ }
+ if (j > 0) objects.length = j; else node.objects = null;
+
+ // Разделение дочерних нод
+ if (node.negativeNode.objects != null) splitNode(node.negativeNode);
+ if (node.positiveNode.objects != null) splitNode(node.positiveNode);
+ }
+
+ /**
+ *
+ */
+ public function traceTree():void {
+ traceNode("", rootNode);
+ }
+
+ /**
+ * @param str
+ * @param node
+ */
+ private function traceNode(str:String, node:CollisionKdNode):void {
+ if (node == null) return;
+ trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.objects);
+ traceNode(str + "-", node.negativeNode);
+ traceNode(str + "+", node.positiveNode);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/ICollider.as b/0.0.1.0/src/alternativa/physics/collision/ICollider.as
new file mode 100644
index 0000000..733231b
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/ICollider.as
@@ -0,0 +1,12 @@
+package alternativa.physics.collision {
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+
+ /**
+ * Интерфейс определителя столкновений между двумя примитивами.
+ */
+ public interface ICollider {
+ function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/ICollisionDetector.as b/0.0.1.0/src/alternativa/physics/collision/ICollisionDetector.as
new file mode 100644
index 0000000..83e94d7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/ICollisionDetector.as
@@ -0,0 +1,73 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Интерфейс детектора столкновений.
+ */
+ public interface ICollisionDetector {
+
+ /**
+ * Добавляет физический примитив в коллайдер.
+ *
+ * @param primitive добавляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успешно добавлен, иначе false
+ */
+ function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Удаляет физический примитив из коллайдера.
+ *
+ * @param primitive удаляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успшено удалён, иначе false
+ */
+ function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Выполняет инициализацию детектора после обновления списка примитивов.
+ */
+ function init():void;
+
+ /**
+ * Получает все столкновения в текущей конфигурации физической геометрии.
+ *
+ * @param contacts список контактов, в кторые будет записана информация о столкновении
+ * @return количество найденных столкновений
+ */
+ function getAllCollisions(contacts:Vector.):int;
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+
+ /**
+ * Тестирует луч на пересечение со статической физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/ICollisionPredicate.as b/0.0.1.0/src/alternativa/physics/collision/ICollisionPredicate.as
new file mode 100644
index 0000000..bb1b756
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/ICollisionPredicate.as
@@ -0,0 +1,9 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public interface ICollisionPredicate {
+
+ function considerCollision(primitive:CollisionPrimitive):Boolean;
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/IRayCollisionPredicate.as b/0.0.1.0/src/alternativa/physics/collision/IRayCollisionPredicate.as
new file mode 100644
index 0000000..0f3d929
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/IRayCollisionPredicate.as
@@ -0,0 +1,7 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Body;
+
+ public interface IRayCollisionPredicate {
+ function considerBody(body:Body):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as b/0.0.1.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as
new file mode 100644
index 0000000..46435f9
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as
@@ -0,0 +1,423 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
+ */
+ public class KdTreeCollisionDetector implements ICollisionDetector {
+
+ altphysics var tree:CollisionKdTree;
+ altphysics var dynamicPrimitives:Vector.;
+ altphysics var dynamicPrimitivesNum:int;
+ altphysics var threshold:Number = 0.0001;
+ private var colliders:Object = {};
+
+ private var _time:MinMax = new MinMax();
+ private var _n:Vector3 = new Vector3();
+ private var _o:Vector3 = new Vector3();
+ private var _dynamicIntersection:RayIntersection = new RayIntersection();
+
+ /**
+ *
+ */
+ public function KdTreeCollisionDetector() {
+ tree = new CollisionKdTree();
+ dynamicPrimitives = new Vector.();
+
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
+// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
+
+// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
+ addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
+ }
+
+ /**
+ * @param primitive
+ * @param isStatic
+ * @return
+ */
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) tree.addStaticPrimitive(primitive);
+ else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
+ return true;
+ }
+
+ /**
+ *
+ * @param primitive
+ * @param isStatic
+ * @return
+ *
+ */
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) return tree.removeStaticPrimitive(primitive);
+ var idx:int = dynamicPrimitives.indexOf(primitive);
+ if (idx < 0) return false;
+ dynamicPrimitives.splice(idx, 1);
+ dynamicPrimitivesNum--;
+ return true;
+ }
+
+ /**
+ *
+ */
+ public function init():void {
+ tree.createTree();
+// tree.traceTree();
+ }
+
+ /**
+ *
+ * @param contacts
+ * @return
+ */
+ public function getAllCollisions(contacts:Vector.):int {
+ var colNum:int = 0;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ primitive.calculateAABB();
+ colNum += getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts, colNum);
+
+ // Столкновения динамических примитивов между собой
+ // TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
+ for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
+ if (collide(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
+ }
+ }
+
+ return colNum;
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
+ if (prim1.body != null && prim1.body == prim2.body) return false;
+ if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
+ var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
+ if (collider != null && collider.collide(prim1, prim2, contact)) {
+ if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
+ if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
+ // Сохраняем ссылку на контакт для каждого тела
+ if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
+ if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
+ // Вычисляем максимальную глубину пересечения для контакта
+ contact.maxPenetration = (contact.points[0] as ContactPoint).penetration;
+ var pen:Number;
+ for (var i:int = contact.pcount - 1; i >= 1; i--) {
+ if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента
+ * @param predicate
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
+ var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
+
+ if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
+
+ if (hasDynamicIntersection && hasStaticIntersection) {
+ if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ if (hasStaticIntersection) return true;
+
+ result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ *
+ */
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // Вычисление точки пересечения с корневм узлом
+ if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
+ if (_time.max < 0 || _time.min > maxTime) return false;
+
+ if (_time.min <= 0) {
+ _time.min = 0;
+ _o.x = origin.x;
+ _o.y = origin.y;
+ _o.z = origin.z;
+ } else {
+ _o.x = origin.x + _time.min*dir.x;
+ _o.y = origin.y + _time.min*dir.y;
+ _o.z = origin.z + _time.min*dir.z;
+ }
+ if (_time.max > maxTime) _time.max = maxTime;
+
+ var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
+ return hasIntersection ? result.t <= maxTime : false;
+ }
+
+ /**
+ *
+ * @param type1
+ * @param type2
+ * @param collider
+ */
+ private function addCollider(type1:int, type2:int, collider:ICollider):void {
+ colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
+ }
+
+ /**
+ * Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
+ *
+ * @param node
+ * @param primitive
+ * @param contacts
+ * @param startIndex
+ * @return
+ */
+ private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Vector., startIndex:int):int {
+ var colNum:int = 0;
+ if (node.objects != null) {
+ // Поиск столкновений со статическими примитивами узла
+ var primitives:Vector. = tree.staticChildren;
+ var indices:Vector. = node.objects;
+ for (var i:int = indices.length - 1; i >= 0; i--)
+ if (collide(primitive, primitives[indices[i]], contacts[startIndex + colNum])) colNum++;
+ }
+ if (node.axis == -1) return colNum;
+ var min:Number;
+ var max:Number;
+ switch (node.axis) {
+ case 0:
+ min = primitive.aabb.minX;
+ max = primitive.aabb.maxX;
+ break;
+ case 1:
+ min = primitive.aabb.minY;
+ max = primitive.aabb.maxY;
+ break;
+ case 2:
+ min = primitive.aabb.minZ;
+ max = primitive.aabb.maxZ;
+ break;
+ }
+ if (min < node.coord) colNum += getPrimitiveNodeCollisions(node.negativeNode, primitive, contacts, startIndex + colNum);
+ if (max > node.coord) colNum += getPrimitiveNodeCollisions(node.positiveNode, primitive, contacts, startIndex + colNum);
+ return colNum;
+ }
+
+ /**
+ * Тест пересечения луча с динамическими примитивами.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ */
+ private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var minTime:Number = maxTime + 1;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ result.primitive = primitive;
+ result.normal.x = _n.x;
+ result.normal.y = _n.y;
+ result.normal.z = _n.z;
+ }
+ }
+ if (minTime > maxTime) return false;
+ result.pos.x = origin.x + dir.x*minTime;
+ result.pos.y = origin.y + dir.y*minTime;
+ result.pos.z = origin.z + dir.z*minTime;
+ result.t = minTime;
+ return true;
+ }
+
+ /**
+ * Вычисляет точки пересечения луча с AABB.
+ *
+ * @param origin точка начала луча
+ * @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
+ * @param bb AABB, с которым пересекается луч
+ * @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
+ * @return true в случае наличия хотя бы одного пересечения, иначе false
+ */
+ private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
+ time.min = -1;
+ time.max = 1e308;
+ var t1:Number;
+ var t2:Number;
+ // Цикл по осям бокса
+ for (var i:int = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ if (dir.x < threshold && dir.x > -threshold) {
+ if (origin.x < bb.minX || origin.x > bb.maxX) return false;
+ else continue;
+ }
+ t1 = (bb.minX - origin.x)/dir.x;
+ t2 = (bb.maxX - origin.x)/dir.x;
+ break;
+ case 1:
+ if (dir.y < threshold && dir.y > -threshold) {
+ if (origin.y < bb.minY || origin.y > bb.maxY) return false;
+ else continue;
+ }
+ t1 = (bb.minY - origin.y)/dir.y;
+ t2 = (bb.maxY - origin.y)/dir.y;
+ break;
+ case 2:
+ if (dir.z < threshold && dir.z > -threshold) {
+ if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
+ else continue;
+ }
+ t1 = (bb.minZ - origin.z)/dir.z;
+ t2 = (bb.maxZ - origin.z)/dir.z;
+ break;
+ }
+ if (t1 < t2) {
+ if (t1 > time.min) time.min = t1;
+ if (t2 < time.max) time.max = t2;
+ } else {
+ if (t2 > time.min) time.min = t2;
+ if (t1 < time.max) time.max = t1;
+ }
+ if (time.max < time.min) return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param node
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param t1 время входа луча в узел
+ * @param t2 время выхода луча из узла
+ * @param intersection
+ */
+ private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // При наличии в узле объектов, проверяем пересечение с ними
+ if (node.objects != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.objects, predicate, result)) return true;
+ // Выход из функции если это конечный узел
+ if (node.axis == -1) return false;
+
+ // Определение времени пересечения луча и плоскости разделения узла
+ var splitTime:Number;
+ var currChildNode:CollisionKdNode;
+ switch (node.axis) {
+ case 0:
+ if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.x)/dir.x;
+ currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 1:
+ if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.y)/dir.y;
+ currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 2:
+ if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.z)/dir.z;
+ currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ }
+ // Определение порядка проверки
+ if (splitTime < t1 || splitTime > t2) {
+ // Луч не переходит в соседний дочерний узел
+ return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
+ } else {
+ // Луч переходит из одного дочернего узла в другой
+ var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
+ if (intersects) return true;
+ _o.x = origin.x + splitTime*dir.x;
+ _o.y = origin.y + splitTime*dir.y;
+ _o.z = origin.z + splitTime*dir.z;
+ return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
+ }
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param primitives
+ * @param indices
+ * @param intersection
+ * @return
+ *
+ */
+ private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector., indices:Vector., predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ var pnum:int = indices.length;
+ var minTime:Number = 1e308;
+ for (var i:int = 0; i < pnum; i++) {
+ var primitive:CollisionPrimitive = primitives[indices[i]];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ intersection.primitive = primitive;
+ intersection.normal.x = _n.x;
+ intersection.normal.y = _n.y;
+ intersection.normal.z = _n.z;
+ }
+ }
+ if (minTime == 1e308) return false;
+ intersection.pos.x = origin.x + dir.x*minTime;
+ intersection.pos.y = origin.y + dir.y*minTime;
+ intersection.pos.z = origin.z + dir.z*minTime;
+ intersection.t = minTime;
+ return true;
+ }
+
+ }
+}
+
+ class MinMax {
+ public var min:Number = 0;
+ public var max:Number = 0;
+ }
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/SpherePlaneCollider.as b/0.0.1.0/src/alternativa/physics/collision/SpherePlaneCollider.as
new file mode 100644
index 0000000..9b527d8
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/SpherePlaneCollider.as
@@ -0,0 +1,58 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ *
+ */
+ public class SpherePlaneCollider implements ICollider {
+
+ private var normal:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function SpherePlaneCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var sphere:RigidSphere = body1 as RigidSphere;
+// var plane:RigidPlane;
+// if (sphere == null) {
+// sphere = body2 as RigidSphere;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+//
+// var dist:Number = sphere.state.pos.dot(normal) - offset;
+// if (dist > sphere.r) return false;
+//
+// collisionInfo.body1 = sphere;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// collisionInfo.pcount = 1;
+//
+// var cp:ContactPoint = collisionInfo.points[0];
+// cp.penetration = sphere.r - dist;
+// cp.pos.copy(normal).reverse().scale(sphere.r).add(sphere.state.pos);
+// cp.r1.diff(cp.pos, sphere.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/SphereSphereCollider.as b/0.0.1.0/src/alternativa/physics/collision/SphereSphereCollider.as
new file mode 100644
index 0000000..9111a84
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/SphereSphereCollider.as
@@ -0,0 +1,60 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class SphereSphereCollider implements ICollider {
+
+ private var p1:Vector3 = new Vector3();
+ private var p2:Vector3 = new Vector3();
+
+ public function SphereSphereCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var s1:CollisionSphere;
+ var s2:CollisionSphere;
+ if (prim1.body != null) {
+ s1 = prim1 as CollisionSphere;
+ s2 = prim2 as CollisionSphere;
+ } else {
+ s1 = prim2 as CollisionSphere;
+ s2 = prim1 as CollisionSphere;
+ }
+
+ s1.transform.getAxis(3, p1);
+ s2.transform.getAxis(3, p2);
+ var dx:Number = p1.x - p2.x;
+ var dy:Number = p1.y - p2.y;
+ var dz:Number = p1.z - p2.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ var sum:Number = s1.r + s2.r;
+ if (len > sum*sum) return false;
+ len = Math.sqrt(len);
+ dx /= len;
+ dy /= len;
+ dz /= len;
+
+ contact.body1 = s1.body;
+ contact.body2 = s2.body;
+ contact.normal.x = dx;
+ contact.normal.y = dy;
+ contact.normal.z = dz;
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sum - len;
+ cp.pos.x = p1.x - dx*s1.r;
+ cp.pos.y = p1.y - dy*s1.r;
+ cp.pos.z = p1.z - dz*s1.r;
+ cp.r1.vDiff(cp.pos, p1);
+ cp.r2.vDiff(cp.pos, p2);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/all-wcprops
new file mode 100644
index 0000000..8d67246
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 124
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives
+END
+CollisionTriangle.as
+K 25
+svn:wc:ra_dav:version-url
+V 145
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionTriangle.as
+END
+CollisionSphere.as
+K 25
+svn:wc:ra_dav:version-url
+V 143
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionSphere.as
+END
+CollisionBox.as
+K 25
+svn:wc:ra_dav:version-url
+V 140
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionBox.as
+END
+CollisionPrimitive.as
+K 25
+svn:wc:ra_dav:version-url
+V 146
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionPrimitive.as
+END
+CollisionRect.as
+K 25
+svn:wc:ra_dav:version-url
+V 141
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionRect.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/entries b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/entries
new file mode 100644
index 0000000..76d14b6
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/entries
@@ -0,0 +1,88 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T07:53:17.170410Z
+14100
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+CollisionTriangle.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+a1ccd11eb25d172d2739fce7fef21ee2
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+CollisionSphere.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+1ca67429cc8bf27435b6ec46f8d6f80a
+2009-05-26T08:41:33.471001Z
+13389
+mike
+
+CollisionBox.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+e6a441a4317416b0d13d365f5b26ace4
+2009-06-08T07:53:17.170410Z
+14100
+mike
+
+CollisionPrimitive.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+09336a8257b1c0e46da8f1d11975b5b1
+2009-06-08T07:53:17.170410Z
+14100
+mike
+
+CollisionRect.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+c76d5cb0626964578f2df221c8072142
+2009-06-08T07:53:17.170410Z
+14100
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/format b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionBox.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionBox.as.svn-base
new file mode 100644
index 0000000..e3d90cc
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionBox.as.svn-base
@@ -0,0 +1,178 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ /**
+ * Ориентированный бокс.
+ */
+ public class CollisionBox extends CollisionPrimitive {
+
+ // Половинные размеры вдоль каждой из осей
+ public var hs:Vector3 = new Vector3();
+
+ // Используются в определении пересечения с лучом
+ private static var _o:Vector3 = new Vector3();
+ private static var _v:Vector3 = new Vector3();
+
+ /**
+ * @param hs
+ * @param collisionGroup
+ */
+ public function CollisionBox(hs:Vector3, collisionGroup:int) {
+ super(BOX, collisionGroup);
+ this.hs.vCopy(hs);
+ }
+
+ /**
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ var t:Matrix4 = transform;
+ aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + hs.z*(t.c < 0 ? -t.c : t.c);
+ aabb.minX = -aabb.maxX;
+
+ aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + hs.z*(t.g < 0 ? -t.g : t.g);
+ aabb.minY = -aabb.maxY;
+
+ aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + hs.z*(t.k < 0 ? -t.k : t.k);
+ aabb.minZ = -aabb.maxZ;
+
+ aabb.minX += t.d;
+ aabb.maxX += t.d;
+
+ aabb.minY += t.h;
+ aabb.maxY += t.h;
+
+ aabb.minZ += t.l;
+ aabb.maxZ += t.l;
+
+ return aabb;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var box:CollisionBox = source as CollisionBox;
+ if (box == null) return this;
+ super.copyFrom(box);
+ hs.vCopy(box.hs);
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionBox(hs, collisionGroup);
+ }
+
+ /**
+ * Вычисляет параметры пересечения заданного сегмента с примитивом.
+ *
+ * @param origin начальная точка сегмента в мировых координатах
+ * @param vector вектор сегмента в мировых координатах
+ * @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
+ * погрешность, считается равной нулю.
+ * @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
+ * @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ var tMin:Number = -1, tMax:Number = 1e308, t1:Number, t2:Number;
+ // Перевод параметров сегмента в систему координат примитива
+ // Inlined transform.transformVectorInverse(origin, _o);
+ var xx:Number = origin.x - transform.d;
+ var yy:Number = origin.y - transform.h;
+ var zz:Number = origin.z - transform.l;
+ _o.x = transform.a*xx + transform.e*yy + transform.i*zz;
+ _o.y = transform.b*xx + transform.f*yy + transform.j*zz;
+ _o.z = transform.c*xx + transform.g*yy + transform.k*zz;
+ // Inlined transform.deltaTransformVectorInverse(vector, _v);
+ _v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
+ _v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
+ _v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
+ // X
+ if (_v.x < threshold && _v.x > -threshold) {
+ if (_o.x < -hs.x || _o.x > hs.x) return -1;
+ } else {
+ t1 = (-hs.x - _o.x)/_v.x;
+ t2 = (hs.x - _o.x)/_v.x;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.x = -1;
+ normal.y = normal.z = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.x = 1;
+ normal.y = normal.z = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Y
+ if (_v.y < threshold && _v.y > -threshold) {
+ if (_o.y < -hs.y || _o.y > hs.y) return -1;
+ } else {
+ t1 = (-hs.y - _o.y)/_v.y;
+ t2 = (hs.y - _o.y)/_v.y;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.y = -1;
+ normal.x = normal.z = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.y = 1;
+ normal.x = normal.z = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Z
+ if (_v.z < threshold && _v.z > -threshold) {
+ if (_o.z < -hs.z || _o.z > hs.z) return -1;
+ } else {
+ t1 = (-hs.z - _o.z)/_v.z;
+ t2 = (hs.z - _o.z)/_v.z;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.z = -1;
+ normal.x = normal.y = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.z = 1;
+ normal.x = normal.y = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Перевод нормали в мировую систему координат
+ // Inlined normal.vDeltaTransformBy4(transform);
+ xx = normal.x, yy = normal.y, zz = normal.z;
+ normal.x = transform.a*xx + transform.b*yy + transform.c*zz;
+ normal.y = transform.e*xx + transform.f*yy + transform.g*zz;
+ normal.z = transform.i*xx + transform.j*yy + transform.k*zz;
+
+ return tMin;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionPrimitive.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionPrimitive.as.svn-base
new file mode 100644
index 0000000..3f92dc2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionPrimitive.as.svn-base
@@ -0,0 +1,128 @@
+package alternativa.physics.collision.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.ICollisionPredicate;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Базовый класс для примитивов, использующихся детектором столкновений.
+ */
+ public class CollisionPrimitive {
+ // Константы типов примитива
+ public static const BOX:int = 1;
+ public static const PLANE:int = 2;
+ public static const SPHERE:int = 3;
+ public static const RECT:int = 4;
+ public static const TRIANGLE:int = 5;
+
+ // Тип примитива
+ public var type:int;
+ // Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
+ // имеющих хотя бы одну общую группу.
+ public var collisionGroup:int;
+ // Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
+ // предикатом результата столкновение либо регистрируется, либо игнорируется. Nullable.
+ public var postCollisionPredicate:ICollisionPredicate;
+
+ // Тело, владеющее примитивом. Может быть null.
+ altphysics var body:Body;
+ // Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
+ altphysics var localTransform:Matrix4 = new Matrix4();
+ // Полная трансформация примитива. Не допускается масштабирование матрицы.
+ altphysics var transform:Matrix4 = new Matrix4();
+ // AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
+ altphysics var aabb:BoundBox = new BoundBox();
+
+ /**
+ * Создаёт новый экземпляр примитива.
+ *
+ * @param type тип примитива
+ * @param collisionGroup группа примитива
+ */
+ public function CollisionPrimitive(type:int, collisionGroup:int) {
+ this.type = type;
+ this.collisionGroup = collisionGroup;
+ }
+
+ /**
+ * Устанавливает тело, владеющее примитивом.
+ *
+ * @param body тело, к которому относится примитив. Если указано значение null, матрица локальной трансформации удаляется.
+ * @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
+ * заданию единичной матрицы.
+ */
+ public function setBody(body:Body, localTransform:Matrix4 = null):void {
+ if (this.body == body) return;
+ this.body = body;
+ if (body == null) this.localTransform = null;
+ else {
+ if (this.localTransform == null) this.localTransform = new Matrix4();
+ if (localTransform != null) this.localTransform.copy(localTransform);
+ }
+ }
+
+ /**
+ * Наследники должны переопределять этот метод, реализуя в нём корректный расчёт ограничивающего бокса, выравненного
+ * по осям мировой системы координат.
+ *
+ * @return ссылка на свой баунд бокс
+ */
+ public function calculateAABB():BoundBox {
+ return aabb;
+ }
+
+ /**
+ * Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
+ *
+ * @return клон примитива
+ */
+ public function clone():CollisionPrimitive {
+ var p:CollisionPrimitive = createPrimitive();
+ return p.copyFrom(this);
+ }
+
+ /**
+ * Копирует параметры указанного примитива. Объекты копируются по значению.
+ *
+ * @param source примитив, чьи параметры копируются
+ * @return this
+ */
+ public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ type = source.type;
+ transform.copy(source.transform);
+ collisionGroup = source.collisionGroup;
+ setBody(source.body, source.localTransform);
+ aabb.copyFrom(source.aabb);
+ return this;
+ }
+
+ /**
+ * Создаёт новый экземпляр примитива соотвествующего типа.
+ *
+ * @return новый экземпляр примитива
+ */
+ protected function createPrimitive():CollisionPrimitive {
+ return new CollisionPrimitive(type, collisionGroup);
+ }
+
+ /**
+ * Метод должен вычислять параметры пересечения заданного сегмента с примитивом.
+ *
+ * @param origin начальная точка сегмента в мировых координатах
+ * @param vector вектор сегмента в мировых координатах
+ * @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
+ * погрешность, считается равной нулю.
+ * @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
+ * @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
+ */
+ public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ return -1;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionRect.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionRect.as.svn-base
new file mode 100644
index 0000000..7d6aa47
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionRect.as.svn-base
@@ -0,0 +1,126 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ /**
+ * Ориентированный прямоугольник. Задаётся половинами размеров вдоль осей X (ширина) и Y (длина) локальной системы
+ * координат примитива. Таким образом, прямоугольник лежит в плоскости XY, его стороны параллельны осям этой
+ * плоскости, а нормаль направлена вдоль локальной оси Z.
+ *
+ * Прямоугольник может быть одно- или двусторонним. В случае одностороннего прямоугольника,
+ * столкновения с внутренней стороны не регистрируются.
+ */
+ public class CollisionRect extends CollisionPrimitive {
+ // Половинные размеры прямоугольника вдоль осей X и Y. Нормаль направлена вдоль оси Z.
+ public var hs:Vector3 = new Vector3();
+ // Флаг указывает, является примитив одно- или двусторонним
+ public var twoSided:Boolean = true;
+
+ // Малое значение. Используется для указания фиктивной высоты примитива и в функции определения пересечения с сегментом.
+ private static const EPSILON:Number = 0.01;
+ // Переменные используются в функции поиска пересечения с лучом
+ private static var _o:Vector3 = new Vector3();
+ private static var _v:Vector3 = new Vector3();
+
+ /**
+ * Создаёт новый экземпляр примитива.
+ *
+ * @param hs половинные размерв прямоугольника вдоль осей X и Y. Значение z игнорируется.
+ * @param collisionGroup группа примитива
+ */
+ public function CollisionRect(hs:Vector3, collisionGroup:int) {
+ super(RECT, collisionGroup);
+ this.hs.vCopy(hs);
+ }
+
+ /**
+ * Расчитывает ограничивающий бокс прямоугольника. Для избежания проблем высота примитива принимается равной
+ * не нулю, а малому значению.
+ *
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ // Баунд бокс прямоугольника имеет минимальную высоту, отличную от нуля во избежание проблем с построением kd-дерева
+ var t:Matrix4 = transform;
+ aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + EPSILON*(t.c < 0 ? -t.c : t.c);
+ aabb.minX = -aabb.maxX;
+
+ aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + EPSILON*(t.g < 0 ? -t.g : t.g);
+ aabb.minY = -aabb.maxY;
+
+ aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + EPSILON*(t.k < 0 ? -t.k : t.k);
+ aabb.minZ = -aabb.maxZ;
+
+ aabb.minX += t.d;
+ aabb.maxX += t.d;
+
+ aabb.minY += t.h;
+ aabb.maxY += t.h;
+
+ aabb.minZ += t.l;
+ aabb.maxZ += t.l;
+
+ return aabb;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var rect:CollisionRect = source as CollisionRect;
+ if (rect == null) return this;
+ super.copyFrom(rect);
+ hs.vCopy(rect.hs);
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionRect(hs, collisionGroup);
+ }
+
+ /**
+ * @param origin
+ * @param vector
+ * @param collisionGroup
+ * @param threshold
+ * @param normal
+ * @return
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ // Перевод параметров сегмента в систему координат примитива
+ // Inlined transform.transformVectorInverse(origin, _o);
+ var xx:Number = origin.x - transform.d;
+ var yy:Number = origin.y - transform.h;
+ var zz:Number = origin.z - transform.l;
+ _o.x = transform.a*xx + transform.e*yy + transform.i*zz;
+ _o.y = transform.b*xx + transform.f*yy + transform.j*zz;
+ _o.z = transform.c*xx + transform.g*yy + transform.k*zz;
+ // Inlined transform.deltaTransformVectorInverse(vector, _v);
+ _v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
+ _v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
+ _v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
+
+ // Проверка параллельности сегмента и плоскости примитива
+ if (_v.z > -threshold && _v.z < threshold) return -1;
+ var t:Number = -_o.z/_v.z;
+ if (t < 0) return -1;
+ // Проверка вхождения точки пересечения в прямоугольник
+ _o.x += _v.x*t;
+ _o.y += _v.y*t;
+ _o.z = 0;
+ if (_o.x < (-hs.x - EPSILON) || _o.x > (hs.x + EPSILON) || _o.y < (-hs.y - EPSILON) || _o.y > (hs.y + EPSILON)) return -1;
+
+ normal.x = transform.c;
+ normal.y = transform.g;
+ normal.z = transform.k;
+ return t;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionSphere.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionSphere.as.svn-base
new file mode 100644
index 0000000..1a9e6d1
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionSphere.as.svn-base
@@ -0,0 +1,80 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+
+ /**
+ * Сфера.
+ */
+ public class CollisionSphere extends CollisionPrimitive {
+
+ // Радиус сферы
+ public var r:Number = 0;
+
+ /**
+ *
+ * @param r
+ * @param collisionGroup
+ */
+ public function CollisionSphere(r:Number, collisionGroup:int) {
+ super(SPHERE, collisionGroup);
+ this.r = r;
+ }
+
+ /**
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ aabb.maxX = transform.d + r;
+ aabb.minX = transform.d - r;
+
+ aabb.maxY = transform.h + r;
+ aabb.minY = transform.h - r;
+
+ aabb.maxZ = transform.l + r;
+ aabb.minZ = transform.l - r;
+
+ return aabb;
+ }
+
+ /**
+ * @param origin
+ * @param vector
+ * @param threshold
+ * @param normal
+ * @return
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ var px:Number = origin.x - transform.d;
+ var py:Number = origin.y - transform.h;
+ var pz:Number = origin.z - transform.l;
+ var k:Number = vector.x*px + vector.y*py + vector.z*pz;
+ if (k > 0) return -1;
+ var a:Number = vector.x*vector.x + vector.y*vector.y + vector.z*vector.z;
+ var D:Number = k*k - a*(px*px + py*py + pz*pz - r*r);
+ if (D < 0) return -1;
+ return -(k + Math.sqrt(D))/a;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var sphere:CollisionSphere = source as CollisionSphere;
+ if (sphere == null) return this;
+ super.copyFrom(sphere);
+ r = sphere.r;
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionSphere(r, collisionGroup);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionTriangle.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionTriangle.as.svn-base
new file mode 100644
index 0000000..a48803a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/.svn/text-base/CollisionTriangle.as.svn-base
@@ -0,0 +1,39 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class CollisionTriangle extends CollisionPrimitive {
+
+ public var v0:Vector3 = new Vector3();
+ public var v1:Vector3 = new Vector3();
+ public var v2:Vector3 = new Vector3();
+ public var e0:Vector3 = new Vector3();
+ public var e1:Vector3 = new Vector3();
+ public var e2:Vector3 = new Vector3();
+ public var len0:Number;
+ public var len1:Number;
+ public var len2:Number;
+
+ public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
+ super(TRIANGLE, collisionGroup);
+
+ this.v0.vCopy(v0);
+ this.v1.vCopy(v1);
+ this.v2.vCopy(v2);
+
+ e0.vDiff(v1, v0);
+ len0 = e0.vLength();
+ e0.vNormalize();
+
+ e1.vDiff(v2, v1);
+ len1 = e1.vLength();
+ e1.vNormalize();
+
+ e2.vDiff(v0, v2);
+ len2 = e2.vLength();
+ e2.vNormalize();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionBox.as b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionBox.as
new file mode 100644
index 0000000..e3d90cc
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionBox.as
@@ -0,0 +1,178 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ /**
+ * Ориентированный бокс.
+ */
+ public class CollisionBox extends CollisionPrimitive {
+
+ // Половинные размеры вдоль каждой из осей
+ public var hs:Vector3 = new Vector3();
+
+ // Используются в определении пересечения с лучом
+ private static var _o:Vector3 = new Vector3();
+ private static var _v:Vector3 = new Vector3();
+
+ /**
+ * @param hs
+ * @param collisionGroup
+ */
+ public function CollisionBox(hs:Vector3, collisionGroup:int) {
+ super(BOX, collisionGroup);
+ this.hs.vCopy(hs);
+ }
+
+ /**
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ var t:Matrix4 = transform;
+ aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + hs.z*(t.c < 0 ? -t.c : t.c);
+ aabb.minX = -aabb.maxX;
+
+ aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + hs.z*(t.g < 0 ? -t.g : t.g);
+ aabb.minY = -aabb.maxY;
+
+ aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + hs.z*(t.k < 0 ? -t.k : t.k);
+ aabb.minZ = -aabb.maxZ;
+
+ aabb.minX += t.d;
+ aabb.maxX += t.d;
+
+ aabb.minY += t.h;
+ aabb.maxY += t.h;
+
+ aabb.minZ += t.l;
+ aabb.maxZ += t.l;
+
+ return aabb;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var box:CollisionBox = source as CollisionBox;
+ if (box == null) return this;
+ super.copyFrom(box);
+ hs.vCopy(box.hs);
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionBox(hs, collisionGroup);
+ }
+
+ /**
+ * Вычисляет параметры пересечения заданного сегмента с примитивом.
+ *
+ * @param origin начальная точка сегмента в мировых координатах
+ * @param vector вектор сегмента в мировых координатах
+ * @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
+ * погрешность, считается равной нулю.
+ * @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
+ * @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ var tMin:Number = -1, tMax:Number = 1e308, t1:Number, t2:Number;
+ // Перевод параметров сегмента в систему координат примитива
+ // Inlined transform.transformVectorInverse(origin, _o);
+ var xx:Number = origin.x - transform.d;
+ var yy:Number = origin.y - transform.h;
+ var zz:Number = origin.z - transform.l;
+ _o.x = transform.a*xx + transform.e*yy + transform.i*zz;
+ _o.y = transform.b*xx + transform.f*yy + transform.j*zz;
+ _o.z = transform.c*xx + transform.g*yy + transform.k*zz;
+ // Inlined transform.deltaTransformVectorInverse(vector, _v);
+ _v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
+ _v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
+ _v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
+ // X
+ if (_v.x < threshold && _v.x > -threshold) {
+ if (_o.x < -hs.x || _o.x > hs.x) return -1;
+ } else {
+ t1 = (-hs.x - _o.x)/_v.x;
+ t2 = (hs.x - _o.x)/_v.x;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.x = -1;
+ normal.y = normal.z = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.x = 1;
+ normal.y = normal.z = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Y
+ if (_v.y < threshold && _v.y > -threshold) {
+ if (_o.y < -hs.y || _o.y > hs.y) return -1;
+ } else {
+ t1 = (-hs.y - _o.y)/_v.y;
+ t2 = (hs.y - _o.y)/_v.y;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.y = -1;
+ normal.x = normal.z = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.y = 1;
+ normal.x = normal.z = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Z
+ if (_v.z < threshold && _v.z > -threshold) {
+ if (_o.z < -hs.z || _o.z > hs.z) return -1;
+ } else {
+ t1 = (-hs.z - _o.z)/_v.z;
+ t2 = (hs.z - _o.z)/_v.z;
+ if (t1 < t2) {
+ if (t1 > tMin) {
+ tMin = t1;
+ normal.z = -1;
+ normal.x = normal.y = 0;
+ }
+ if (t2 < tMax) tMax = t2;
+ } else {
+ if (t2 > tMin) {
+ tMin = t2;
+ normal.z = 1;
+ normal.x = normal.y = 0;
+ }
+ if (t1 < tMax) tMax = t1;
+ }
+ if (tMax < tMin) return -1;
+ }
+ // Перевод нормали в мировую систему координат
+ // Inlined normal.vDeltaTransformBy4(transform);
+ xx = normal.x, yy = normal.y, zz = normal.z;
+ normal.x = transform.a*xx + transform.b*yy + transform.c*zz;
+ normal.y = transform.e*xx + transform.f*yy + transform.g*zz;
+ normal.z = transform.i*xx + transform.j*yy + transform.k*zz;
+
+ return tMin;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionPrimitive.as b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionPrimitive.as
new file mode 100644
index 0000000..3f92dc2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionPrimitive.as
@@ -0,0 +1,128 @@
+package alternativa.physics.collision.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.ICollisionPredicate;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Базовый класс для примитивов, использующихся детектором столкновений.
+ */
+ public class CollisionPrimitive {
+ // Константы типов примитива
+ public static const BOX:int = 1;
+ public static const PLANE:int = 2;
+ public static const SPHERE:int = 3;
+ public static const RECT:int = 4;
+ public static const TRIANGLE:int = 5;
+
+ // Тип примитива
+ public var type:int;
+ // Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
+ // имеющих хотя бы одну общую группу.
+ public var collisionGroup:int;
+ // Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
+ // предикатом результата столкновение либо регистрируется, либо игнорируется. Nullable.
+ public var postCollisionPredicate:ICollisionPredicate;
+
+ // Тело, владеющее примитивом. Может быть null.
+ altphysics var body:Body;
+ // Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
+ altphysics var localTransform:Matrix4 = new Matrix4();
+ // Полная трансформация примитива. Не допускается масштабирование матрицы.
+ altphysics var transform:Matrix4 = new Matrix4();
+ // AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
+ altphysics var aabb:BoundBox = new BoundBox();
+
+ /**
+ * Создаёт новый экземпляр примитива.
+ *
+ * @param type тип примитива
+ * @param collisionGroup группа примитива
+ */
+ public function CollisionPrimitive(type:int, collisionGroup:int) {
+ this.type = type;
+ this.collisionGroup = collisionGroup;
+ }
+
+ /**
+ * Устанавливает тело, владеющее примитивом.
+ *
+ * @param body тело, к которому относится примитив. Если указано значение null, матрица локальной трансформации удаляется.
+ * @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
+ * заданию единичной матрицы.
+ */
+ public function setBody(body:Body, localTransform:Matrix4 = null):void {
+ if (this.body == body) return;
+ this.body = body;
+ if (body == null) this.localTransform = null;
+ else {
+ if (this.localTransform == null) this.localTransform = new Matrix4();
+ if (localTransform != null) this.localTransform.copy(localTransform);
+ }
+ }
+
+ /**
+ * Наследники должны переопределять этот метод, реализуя в нём корректный расчёт ограничивающего бокса, выравненного
+ * по осям мировой системы координат.
+ *
+ * @return ссылка на свой баунд бокс
+ */
+ public function calculateAABB():BoundBox {
+ return aabb;
+ }
+
+ /**
+ * Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
+ *
+ * @return клон примитива
+ */
+ public function clone():CollisionPrimitive {
+ var p:CollisionPrimitive = createPrimitive();
+ return p.copyFrom(this);
+ }
+
+ /**
+ * Копирует параметры указанного примитива. Объекты копируются по значению.
+ *
+ * @param source примитив, чьи параметры копируются
+ * @return this
+ */
+ public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ type = source.type;
+ transform.copy(source.transform);
+ collisionGroup = source.collisionGroup;
+ setBody(source.body, source.localTransform);
+ aabb.copyFrom(source.aabb);
+ return this;
+ }
+
+ /**
+ * Создаёт новый экземпляр примитива соотвествующего типа.
+ *
+ * @return новый экземпляр примитива
+ */
+ protected function createPrimitive():CollisionPrimitive {
+ return new CollisionPrimitive(type, collisionGroup);
+ }
+
+ /**
+ * Метод должен вычислять параметры пересечения заданного сегмента с примитивом.
+ *
+ * @param origin начальная точка сегмента в мировых координатах
+ * @param vector вектор сегмента в мировых координатах
+ * @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
+ * погрешность, считается равной нулю.
+ * @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
+ * @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
+ */
+ public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ return -1;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionRect.as b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionRect.as
new file mode 100644
index 0000000..7d6aa47
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionRect.as
@@ -0,0 +1,126 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ /**
+ * Ориентированный прямоугольник. Задаётся половинами размеров вдоль осей X (ширина) и Y (длина) локальной системы
+ * координат примитива. Таким образом, прямоугольник лежит в плоскости XY, его стороны параллельны осям этой
+ * плоскости, а нормаль направлена вдоль локальной оси Z.
+ *
+ * Прямоугольник может быть одно- или двусторонним. В случае одностороннего прямоугольника,
+ * столкновения с внутренней стороны не регистрируются.
+ */
+ public class CollisionRect extends CollisionPrimitive {
+ // Половинные размеры прямоугольника вдоль осей X и Y. Нормаль направлена вдоль оси Z.
+ public var hs:Vector3 = new Vector3();
+ // Флаг указывает, является примитив одно- или двусторонним
+ public var twoSided:Boolean = true;
+
+ // Малое значение. Используется для указания фиктивной высоты примитива и в функции определения пересечения с сегментом.
+ private static const EPSILON:Number = 0.01;
+ // Переменные используются в функции поиска пересечения с лучом
+ private static var _o:Vector3 = new Vector3();
+ private static var _v:Vector3 = new Vector3();
+
+ /**
+ * Создаёт новый экземпляр примитива.
+ *
+ * @param hs половинные размерв прямоугольника вдоль осей X и Y. Значение z игнорируется.
+ * @param collisionGroup группа примитива
+ */
+ public function CollisionRect(hs:Vector3, collisionGroup:int) {
+ super(RECT, collisionGroup);
+ this.hs.vCopy(hs);
+ }
+
+ /**
+ * Расчитывает ограничивающий бокс прямоугольника. Для избежания проблем высота примитива принимается равной
+ * не нулю, а малому значению.
+ *
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ // Баунд бокс прямоугольника имеет минимальную высоту, отличную от нуля во избежание проблем с построением kd-дерева
+ var t:Matrix4 = transform;
+ aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + EPSILON*(t.c < 0 ? -t.c : t.c);
+ aabb.minX = -aabb.maxX;
+
+ aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + EPSILON*(t.g < 0 ? -t.g : t.g);
+ aabb.minY = -aabb.maxY;
+
+ aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + EPSILON*(t.k < 0 ? -t.k : t.k);
+ aabb.minZ = -aabb.maxZ;
+
+ aabb.minX += t.d;
+ aabb.maxX += t.d;
+
+ aabb.minY += t.h;
+ aabb.maxY += t.h;
+
+ aabb.minZ += t.l;
+ aabb.maxZ += t.l;
+
+ return aabb;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var rect:CollisionRect = source as CollisionRect;
+ if (rect == null) return this;
+ super.copyFrom(rect);
+ hs.vCopy(rect.hs);
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionRect(hs, collisionGroup);
+ }
+
+ /**
+ * @param origin
+ * @param vector
+ * @param collisionGroup
+ * @param threshold
+ * @param normal
+ * @return
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ // Перевод параметров сегмента в систему координат примитива
+ // Inlined transform.transformVectorInverse(origin, _o);
+ var xx:Number = origin.x - transform.d;
+ var yy:Number = origin.y - transform.h;
+ var zz:Number = origin.z - transform.l;
+ _o.x = transform.a*xx + transform.e*yy + transform.i*zz;
+ _o.y = transform.b*xx + transform.f*yy + transform.j*zz;
+ _o.z = transform.c*xx + transform.g*yy + transform.k*zz;
+ // Inlined transform.deltaTransformVectorInverse(vector, _v);
+ _v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
+ _v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
+ _v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
+
+ // Проверка параллельности сегмента и плоскости примитива
+ if (_v.z > -threshold && _v.z < threshold) return -1;
+ var t:Number = -_o.z/_v.z;
+ if (t < 0) return -1;
+ // Проверка вхождения точки пересечения в прямоугольник
+ _o.x += _v.x*t;
+ _o.y += _v.y*t;
+ _o.z = 0;
+ if (_o.x < (-hs.x - EPSILON) || _o.x > (hs.x + EPSILON) || _o.y < (-hs.y - EPSILON) || _o.y > (hs.y + EPSILON)) return -1;
+
+ normal.x = transform.c;
+ normal.y = transform.g;
+ normal.z = transform.k;
+ return t;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionSphere.as b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionSphere.as
new file mode 100644
index 0000000..1a9e6d1
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionSphere.as
@@ -0,0 +1,80 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+
+ /**
+ * Сфера.
+ */
+ public class CollisionSphere extends CollisionPrimitive {
+
+ // Радиус сферы
+ public var r:Number = 0;
+
+ /**
+ *
+ * @param r
+ * @param collisionGroup
+ */
+ public function CollisionSphere(r:Number, collisionGroup:int) {
+ super(SPHERE, collisionGroup);
+ this.r = r;
+ }
+
+ /**
+ * @return
+ */
+ override public function calculateAABB():BoundBox {
+ aabb.maxX = transform.d + r;
+ aabb.minX = transform.d - r;
+
+ aabb.maxY = transform.h + r;
+ aabb.minY = transform.h - r;
+
+ aabb.maxZ = transform.l + r;
+ aabb.minZ = transform.l - r;
+
+ return aabb;
+ }
+
+ /**
+ * @param origin
+ * @param vector
+ * @param threshold
+ * @param normal
+ * @return
+ */
+ override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
+ var px:Number = origin.x - transform.d;
+ var py:Number = origin.y - transform.h;
+ var pz:Number = origin.z - transform.l;
+ var k:Number = vector.x*px + vector.y*py + vector.z*pz;
+ if (k > 0) return -1;
+ var a:Number = vector.x*vector.x + vector.y*vector.y + vector.z*vector.z;
+ var D:Number = k*k - a*(px*px + py*py + pz*pz - r*r);
+ if (D < 0) return -1;
+ return -(k + Math.sqrt(D))/a;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
+ var sphere:CollisionSphere = source as CollisionSphere;
+ if (sphere == null) return this;
+ super.copyFrom(sphere);
+ r = sphere.r;
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ override protected function createPrimitive():CollisionPrimitive {
+ return new CollisionSphere(r, collisionGroup);
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionTriangle.as b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionTriangle.as
new file mode 100644
index 0000000..a48803a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionTriangle.as
@@ -0,0 +1,39 @@
+package alternativa.physics.collision.primitives {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class CollisionTriangle extends CollisionPrimitive {
+
+ public var v0:Vector3 = new Vector3();
+ public var v1:Vector3 = new Vector3();
+ public var v2:Vector3 = new Vector3();
+ public var e0:Vector3 = new Vector3();
+ public var e1:Vector3 = new Vector3();
+ public var e2:Vector3 = new Vector3();
+ public var len0:Number;
+ public var len1:Number;
+ public var len2:Number;
+
+ public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
+ super(TRIANGLE, collisionGroup);
+
+ this.v0.vCopy(v0);
+ this.v1.vCopy(v1);
+ this.v2.vCopy(v2);
+
+ e0.vDiff(v1, v0);
+ len0 = e0.vLength();
+ e0.vNormalize();
+
+ e1.vDiff(v2, v1);
+ len1 = e1.vLength();
+ e1.vNormalize();
+
+ e2.vDiff(v0, v2);
+ len2 = e2.vLength();
+ e2.vNormalize();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/collision/types/.svn/all-wcprops
new file mode 100644
index 0000000..4bdd933
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/all-wcprops
@@ -0,0 +1,23 @@
+K 25
+svn:wc:ra_dav:version-url
+V 119
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types
+END
+BoundBox.as
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
+END
+RayIntersection.as
+K 25
+svn:wc:ra_dav:version-url
+V 138
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/RayIntersection.as
+END
+Ray.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/Ray.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/entries b/0.0.1.0/src/alternativa/physics/collision/types/.svn/entries
new file mode 100644
index 0000000..f7993cf
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/entries
@@ -0,0 +1,64 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+BoundBox.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+13aaaf1b1923de8a2579543651734ab7
+2009-05-18T16:37:02.465501Z
+13087
+mike
+
+RayIntersection.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+a0a4cf3dd94468fdf44cd06734927c94
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+Ray.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+55eeb1cf79a416bb863bee95f089dbcd
+2009-04-17T17:39:21.367569Z
+11341
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/format b/0.0.1.0/src/alternativa/physics/collision/types/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/BoundBox.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/BoundBox.as.svn-base
new file mode 100644
index 0000000..6efc097
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/BoundBox.as.svn-base
@@ -0,0 +1,72 @@
+package alternativa.physics.collision.types {
+
+ public class BoundBox {
+
+ public var minX:Number = 1e308;
+ public var minY:Number = 1e308;
+ public var minZ:Number = 1e308;
+ public var maxX:Number = -1e308;
+ public var maxY:Number = -1e308;
+ public var maxZ:Number = -1e308;
+
+ public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
+ this.minX = minX;
+ this.minY = minY;
+ this.minZ = minZ;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ this.maxZ = maxZ;
+ }
+
+ public function addBoundBox(boundBox:BoundBox):void {
+ minX = (boundBox.minX < minX) ? boundBox.minX : minX;
+ minY = (boundBox.minY < minY) ? boundBox.minY : minY;
+ minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
+ maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
+ maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
+ maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
+ }
+
+ public function addPoint(x:Number, y:Number, z:Number):void {
+ if (x < minX) minX = x;
+ if (x > maxX) maxX = x;
+ if (y < minY) minY = y;
+ if (y > maxY) maxY = y;
+ if (z < minZ) minZ = z;
+ if (z > maxZ) maxZ = z;
+ }
+
+ public function infinity():void {
+ minX = 1e308;
+ minY = 1e308;
+ minZ = 1e308;
+ maxX = -1e308;
+ maxY = -1e308;
+ maxZ = -1e308;
+ }
+
+ public function intersects(bb:BoundBox, tolerance:Number):Boolean {
+ return !(minX > bb.maxX + tolerance || maxX < bb.minX - tolerance || minY > bb.maxY + tolerance || maxY < bb.minY - tolerance || minZ > bb.maxZ + tolerance || maxZ < bb.minZ - tolerance);
+ }
+
+ public function copyFrom(boundBox:BoundBox):void {
+ minX = boundBox.minX;
+ minY = boundBox.minY;
+ minZ = boundBox.minZ;
+ maxX = boundBox.maxX;
+ maxY = boundBox.maxY;
+ maxZ = boundBox.maxZ;
+ }
+
+ public function clone():BoundBox {
+ var clone:BoundBox = new BoundBox();
+ clone.copyFrom(this);
+ return clone;
+ }
+
+ public function toString():String {
+ return "BoundBox [" + minX + ", " + minY + ", " + minZ + " : " + maxX + ", " + maxY + ", " + maxZ + "]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/Ray.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/Ray.as.svn-base
new file mode 100644
index 0000000..f80620a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/Ray.as.svn-base
@@ -0,0 +1,13 @@
+package alternativa.physics.collision.types {
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Структура, описывающая луч и сегмент прямой в пространстве.
+ */
+ public class Ray {
+ // Начало луча. Начальная точка сегмента на прямой луча.
+ public var origin:Vector3 = new Vector3();
+ // Направляющий вектор луча. Длина вектора задаёт длину сегмента на луче. Конечная точка сегмента вычисляется как origin + dir.
+ public var dir:Vector3 = new Vector3();
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/RayIntersection.as.svn-base b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/RayIntersection.as.svn-base
new file mode 100644
index 0000000..8a89f75
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/.svn/text-base/RayIntersection.as.svn-base
@@ -0,0 +1,25 @@
+package alternativa.physics.collision.types {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Структура описывает точку пересечения луча с физической геометрией.
+ */
+ public class RayIntersection {
+ // Физический примитив, с которым пересекается луч
+ public var primitive:CollisionPrimitive;
+ // Положенеи точки пересечения
+ public var pos:Vector3 = new Vector3();
+ // Нормаль поверхности физического примитива в точке пересечения
+ public var normal:Vector3 = new Vector3();
+ // Время пересечения (|pos - ray.origin|/|ray.dir|)
+ public var t:Number = 0;
+
+ public function copy(source:RayIntersection):void {
+ primitive = source.primitive;
+ pos.vCopy(source.pos);
+ normal.vCopy(source.normal);
+ t = source.t;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as b/0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
new file mode 100644
index 0000000..6efc097
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
@@ -0,0 +1,72 @@
+package alternativa.physics.collision.types {
+
+ public class BoundBox {
+
+ public var minX:Number = 1e308;
+ public var minY:Number = 1e308;
+ public var minZ:Number = 1e308;
+ public var maxX:Number = -1e308;
+ public var maxY:Number = -1e308;
+ public var maxZ:Number = -1e308;
+
+ public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
+ this.minX = minX;
+ this.minY = minY;
+ this.minZ = minZ;
+ this.maxX = maxX;
+ this.maxY = maxY;
+ this.maxZ = maxZ;
+ }
+
+ public function addBoundBox(boundBox:BoundBox):void {
+ minX = (boundBox.minX < minX) ? boundBox.minX : minX;
+ minY = (boundBox.minY < minY) ? boundBox.minY : minY;
+ minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
+ maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
+ maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
+ maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
+ }
+
+ public function addPoint(x:Number, y:Number, z:Number):void {
+ if (x < minX) minX = x;
+ if (x > maxX) maxX = x;
+ if (y < minY) minY = y;
+ if (y > maxY) maxY = y;
+ if (z < minZ) minZ = z;
+ if (z > maxZ) maxZ = z;
+ }
+
+ public function infinity():void {
+ minX = 1e308;
+ minY = 1e308;
+ minZ = 1e308;
+ maxX = -1e308;
+ maxY = -1e308;
+ maxZ = -1e308;
+ }
+
+ public function intersects(bb:BoundBox, tolerance:Number):Boolean {
+ return !(minX > bb.maxX + tolerance || maxX < bb.minX - tolerance || minY > bb.maxY + tolerance || maxY < bb.minY - tolerance || minZ > bb.maxZ + tolerance || maxZ < bb.minZ - tolerance);
+ }
+
+ public function copyFrom(boundBox:BoundBox):void {
+ minX = boundBox.minX;
+ minY = boundBox.minY;
+ minZ = boundBox.minZ;
+ maxX = boundBox.maxX;
+ maxY = boundBox.maxY;
+ maxZ = boundBox.maxZ;
+ }
+
+ public function clone():BoundBox {
+ var clone:BoundBox = new BoundBox();
+ clone.copyFrom(this);
+ return clone;
+ }
+
+ public function toString():String {
+ return "BoundBox [" + minX + ", " + minY + ", " + minZ + " : " + maxX + ", " + maxY + ", " + maxZ + "]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/Ray.as b/0.0.1.0/src/alternativa/physics/collision/types/Ray.as
new file mode 100644
index 0000000..f80620a
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/Ray.as
@@ -0,0 +1,13 @@
+package alternativa.physics.collision.types {
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Структура, описывающая луч и сегмент прямой в пространстве.
+ */
+ public class Ray {
+ // Начало луча. Начальная точка сегмента на прямой луча.
+ public var origin:Vector3 = new Vector3();
+ // Направляющий вектор луча. Длина вектора задаёт длину сегмента на луче. Конечная точка сегмента вычисляется как origin + dir.
+ public var dir:Vector3 = new Vector3();
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/collision/types/RayIntersection.as b/0.0.1.0/src/alternativa/physics/collision/types/RayIntersection.as
new file mode 100644
index 0000000..8a89f75
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/collision/types/RayIntersection.as
@@ -0,0 +1,25 @@
+package alternativa.physics.collision.types {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Структура описывает точку пересечения луча с физической геометрией.
+ */
+ public class RayIntersection {
+ // Физический примитив, с которым пересекается луч
+ public var primitive:CollisionPrimitive;
+ // Положенеи точки пересечения
+ public var pos:Vector3 = new Vector3();
+ // Нормаль поверхности физического примитива в точке пересечения
+ public var normal:Vector3 = new Vector3();
+ // Время пересечения (|pos - ray.origin|/|ray.dir|)
+ public var t:Number = 0;
+
+ public function copy(source:RayIntersection):void {
+ primitive = source.primitive;
+ pos.vCopy(source.pos);
+ normal.vCopy(source.normal);
+ t = source.t;
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/rigid/.svn/all-wcprops
new file mode 100644
index 0000000..ef4450f
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/all-wcprops
@@ -0,0 +1,47 @@
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid
+END
+Body.as
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/Body.as
+END
+BodyState.as
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/BodyState.as
+END
+PhysicsUtils.as
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/PhysicsUtils.as
+END
+ContactPoint.as
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/ContactPoint.as
+END
+RigidWorld.as
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/RigidWorld.as
+END
+Contact.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/Contact.as
+END
+BodyMaterial.as
+K 25
+svn:wc:ra_dav:version-url
+V 125
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/entries b/0.0.1.0/src/alternativa/physics/rigid/.svn/entries
new file mode 100644
index 0000000..0504c05
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/entries
@@ -0,0 +1,118 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+Body.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+43a884d83ae7d8a1d25eb9f1d26eb755
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+BodyState.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+8a089205beccac13a05eccfb9cfc6387
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+PhysicsUtils.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+6d88004ddab2ae19e0d15f5b1b7c016a
+2009-04-26T21:40:17.813142Z
+12023
+mike
+
+constraints
+dir
+
+ContactPoint.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+a94e73e3bfc85be2a79d6cc939512d91
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+RigidWorld.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+ee25e8fe15553bfa5789bf0e99c1524b
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+primitives
+dir
+
+Contact.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+d65ad3d488d4a06addcbfa4aaf958f10
+2009-04-13T03:04:08.898183Z
+11018
+mike
+
+BodyMaterial.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+49c7a55b5b26fdb03a60f24bac460f53
+2009-04-07T08:34:06.342994Z
+10587
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/format b/0.0.1.0/src/alternativa/physics/rigid/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Body.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Body.as.svn-base
new file mode 100644
index 0000000..4e83bfe
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Body.as.svn-base
@@ -0,0 +1,329 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Quaternion;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class Body {
+
+ public static var linDamping:Number = 0.997;
+ public static var rotDamping:Number = 0.997;
+
+ public var name:String;
+ public var movable:Boolean = true;
+
+ altphysics var id:int;
+ // Мир, в котором находится тело
+ altphysics var world:RigidWorld;
+ // Текущее и предыдущее состояние тела. Промежуточное состояние вычисляется линейной интерполяцией.
+ altphysics var state:BodyState = new BodyState();
+ altphysics var prevState:BodyState = new BodyState();
+ // Линейное и угловое ускорение тела на текущем шаге симуляции
+ altphysics var accel:Vector3 = new Vector3();
+ altphysics var angleAccel:Vector3 = new Vector3();
+ // Материал тела
+ altphysics var material:BodyMaterial = new BodyMaterial();
+
+ altphysics var invMass:Number = 1;
+ altphysics var invInertia:Matrix3 = new Matrix3();
+ altphysics var invInertiaWorld:Matrix3 = new Matrix3();
+ altphysics var baseMatrix:Matrix3 = new Matrix3();
+
+ altphysics const MAX_CONTACTS:int = 20;
+ altphysics var contacts:Vector. = new Vector.(MAX_CONTACTS);
+ altphysics var contactsNum:int;
+
+ altphysics var collisionPrimitives:Vector.;
+ altphysics var collisionPrimitivesNum:int;
+
+ // Аккумулятор сил
+ altphysics var forceAccum:Vector3 = new Vector3();
+ // Аккумулятор моментов
+ altphysics var torqueAccum:Vector3 = new Vector3();
+
+ // Внутренние переменные для избежания создания экземпляров
+ private static var _r:Vector3 = new Vector3();
+ private static var _f:Vector3 = new Vector3();
+
+ /**
+ *
+ * @param invMass
+ * @param invInertia
+ */
+ public function Body(invMass:Number, invInertia:Matrix3) {
+ this.invMass = invMass;
+ this.invInertia.copy(invInertia);
+ }
+
+ /**
+ * @param prim
+ */
+ public function addCollisionPrimitive(primitive:CollisionPrimitive, localTransform:Matrix4 = null):void {
+ if (primitive == null) throw new ArgumentError("Primitive cannot be null");
+ if (collisionPrimitives == null) collisionPrimitives = new Vector.();
+ collisionPrimitives[collisionPrimitivesNum++] = primitive;
+ primitive.setBody(this, localTransform);
+ }
+
+ /**
+ * @param t
+ * @param result
+ */
+ public function interpolate(t:Number, pos:Vector3, orientation:Quaternion):void {
+ var t1:Number = 1 - t;
+ pos.x = prevState.pos.x*t1 + state.pos.x*t;
+ pos.y = prevState.pos.y*t1 + state.pos.y*t;
+ pos.z = prevState.pos.z*t1 + state.pos.z*t;
+ orientation.w = prevState.orientation.w*t1 + state.orientation.w*t;
+ orientation.x = prevState.orientation.x*t1 + state.orientation.x*t;
+ orientation.y = prevState.orientation.y*t1 + state.orientation.y*t;
+ orientation.z = prevState.orientation.z*t1 + state.orientation.z*t;
+ }
+
+ /**
+ *
+ * @param pos
+ */
+ public function setPosition(pos:Vector3):void {
+ state.pos.vCopy(pos);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setPositionXYZ(x:Number, y:Number, z:Number):void {
+ state.pos.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param vel
+ */
+ public function setVelocity(vel:Vector3):void {
+ state.velocity.vCopy(vel);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setVelocityXYZ(x:Number, y:Number, z:Number):void {
+ state.velocity.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param rot
+ */
+ public function setRotation(rot:Vector3):void {
+ state.rotation.vCopy(rot);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setRotationXYZ(x:Number, y:Number, z:Number):void {
+ state.rotation.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param q
+ */
+ public function setOrientation(q:Quaternion):void {
+ state.orientation.copy(q);
+ }
+
+ /**
+ * @param r
+ * @param dir
+ * @param magnitude
+ */
+ public function applyRelPosWorldImpulse(r:Vector3, dir:Vector3, magnitude:Number):void {
+ var d:Number = magnitude*invMass;
+ // Линейная часть
+ state.velocity.x += d*dir.x;
+ state.velocity.y += d*dir.y;
+ state.velocity.z += d*dir.z;
+
+ // Вращательная часть
+ var rx:Number = r.x;
+ var ry:Number = r.y;
+ var rz:Number = r.z;
+ _r.x = (ry*dir.z - rz*dir.y)*magnitude;
+ _r.y = (rz*dir.x - rx*dir.z)*magnitude;
+ _r.z = (rx*dir.y - ry*dir.x)*magnitude;
+ _r.vTransformBy3(invInertiaWorld);
+ state.rotation.x += _r.x;
+ state.rotation.y += _r.y;
+ state.rotation.z += _r.z;
+ }
+
+ /**
+ * @param f
+ */
+ public function addForce(f:Vector3):void {
+ forceAccum.vAdd(f);
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addWorldForce(pos:Vector3, force:Vector3):void {
+ forceAccum.vAdd(force);
+ torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(force));
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addWorldForceScaled(pos:Vector3, force:Vector3, scale:Number):void {
+ _f.x = scale*force.x, _f.y = scale*force.y, _f.z = scale*force.z;
+ forceAccum.vAdd(_f);
+ torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(_f));
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addLocalForce(pos:Vector3, force:Vector3):void {
+ // Трансформируем точку приложения в мировую систему координат
+ baseMatrix.transformVector(pos, _r);
+ // Трансформируем вектор силы в мировую систему
+ baseMatrix.transformVector(force, _f);
+ // Добавляем силу и момент
+ forceAccum.vAdd(_f);
+ torqueAccum.vAdd(_r.vCross(_f));
+ }
+
+ /**
+ *
+ * @param localPos
+ * @param worldForce
+ */
+ public function addWorldForceAtLocalPoint(localPos:Vector3, worldForce:Vector3):void {
+ // Трансформируем точку приложения в мировую систему координат
+ baseMatrix.transformVector(localPos, _r);
+ // Добавляем силу и момент
+ forceAccum.vAdd(worldForce);
+ torqueAccum.vAdd(_r.vCross(worldForce));
+ }
+
+ /**
+ * @param dt
+ */
+ public function addExternalForces(dt:Number):void {
+ }
+
+ /**
+ * @param t
+ */
+ public function addTorque(t:Vector3):void {
+ torqueAccum.vAdd(t);
+ }
+
+ /**
+ *
+ */
+ altphysics function clearAccumulators():void {
+ forceAccum.vReset();
+ torqueAccum.vReset();
+ }
+
+ /**
+ *
+ */
+ altphysics function calcAccelerations():void {
+ accel.x = forceAccum.x*invMass;
+ accel.y = forceAccum.y*invMass;
+ accel.z = forceAccum.z*invMass;
+ invInertiaWorld.transformVector(torqueAccum, angleAccel);
+ }
+
+ /**
+ * Вычисляет производные данные.
+ */
+ altphysics function calcDerivedData():void {
+ // Вычисление базисной матрицы и обратного тензора инерции в мировых координатах
+ state.orientation.toMatrix3(baseMatrix);
+ invInertiaWorld.copy(invInertia).append(baseMatrix).prepend(baseMatrix.transpose());
+ baseMatrix.transpose();
+ if (collisionPrimitives != null) {
+ for (var i:int = 0; i < collisionPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = collisionPrimitives[i];
+ primitive.transform.setFromMatrix3(baseMatrix, state.pos);
+ if (primitive.localTransform != null) primitive.transform.prepend(primitive.localTransform);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ altphysics function saveState():void {
+ prevState.copy(state);
+ }
+
+ /**
+ *
+ */
+ altphysics function restoreState():void {
+ state.copy(prevState);
+ }
+
+ /**
+ * @param dt
+ */
+ altphysics function integrateVelocity(dt:Number):void {
+ // v = v + a*t
+ state.velocity.x += accel.x*dt;
+ state.velocity.y += accel.y*dt;
+ state.velocity.z += accel.z*dt;
+ // rot = rot + eps*t
+ state.rotation.x += angleAccel.x*dt;
+ state.rotation.y += angleAccel.y*dt;
+ state.rotation.z += angleAccel.z*dt;
+
+ state.velocity.x *= linDamping;
+ state.velocity.y *= linDamping;
+ state.velocity.z *= linDamping;
+
+ state.rotation.x *= rotDamping;
+ state.rotation.y *= rotDamping;
+ state.rotation.z *= rotDamping;
+ }
+
+ /**
+ *
+ */
+ altphysics function integratePosition(dt:Number):void {
+ // pos = pos + v*t
+ state.pos.x += state.velocity.x*dt;
+ state.pos.y += state.velocity.y*dt;
+ state.pos.z += state.velocity.z*dt;
+ // q = q + 0.5*rot*q
+ state.orientation.addScaledVector(state.rotation, dt);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyMaterial.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyMaterial.as.svn-base
new file mode 100644
index 0000000..61d4382
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyMaterial.as.svn-base
@@ -0,0 +1,11 @@
+package alternativa.physics.rigid {
+
+ public class BodyMaterial {
+
+ public var restitution:Number = 0;
+ public var friction:Number = 0.3;
+// public var dynamicFriction:Number = 0.2;
+// public var dynamicFriction:Number = 0.2;
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyState.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyState.as.svn-base
new file mode 100644
index 0000000..408b0ea
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/BodyState.as.svn-base
@@ -0,0 +1,39 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Quaternion;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Класс описывает состояние твёрдого тела.
+ */
+ public class BodyState {
+ /**
+ * Положение тела.
+ */
+ public var pos:Vector3 = new Vector3();
+ /**
+ * Ориентация тела.
+ */
+ public var orientation:Quaternion = new Quaternion();
+ /**
+ * Скорость тела.
+ */
+ public var velocity:Vector3 = new Vector3();
+ /**
+ * Угловая скорость тела.
+ */
+ public var rotation:Vector3 = new Vector3();
+
+ /**
+ * Копирует значение указанного объекта.
+ *
+ * @param state
+ */
+ public function copy(state:BodyState):void {
+ pos.vCopy(state.pos);
+ orientation.copy(state.orientation);
+ velocity.vCopy(state.velocity);
+ rotation.vCopy(state.rotation);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Contact.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Contact.as.svn-base
new file mode 100644
index 0000000..b73fc55
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/Contact.as.svn-base
@@ -0,0 +1,53 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Информация о контакте между двумя телами. Содержит нормаль, множество точек контакта и прочие величины.
+ */
+ public class Contact {
+
+// private static var pool:Vector. = new Vector.();
+//
+// public static function create():ContactInfo {
+// if (pool.length > 0) {
+// return pool.pop();
+// }
+// return new ContactInfo();
+// }
+//
+// public static function destroy(collInfo:ContactInfo):void {
+// collInfo.body1 = collInfo.body2 = null;
+// pool.push(collInfo);
+// }
+
+ // Максимальное количество точек контакта
+ private const N:int = 8;
+
+ public var body1:Body;
+ public var body2:Body;
+ // Взаимный коэффициент отскока
+ public var restitution:Number;
+ // Взаимный коэффициент трения
+ public var friction:Number;
+ // Нормаль контакта. Направлена от второго тела к первому.
+ public var normal:Vector3 = new Vector3();
+ // Список точек контакта
+ public var points:Vector. = new Vector.(N, true);
+ // Количество точек контакта
+ public var pcount:int;
+ // Максимальная глубина пересечения тел
+ public var maxPenetration:Number = 0;
+
+ public var satisfied:Boolean;
+
+ /**
+ *
+ */
+ public function Contact() {
+ for (var i:int = 0; i < N; i++) points[i] = new ContactPoint();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/ContactPoint.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/ContactPoint.as.svn-base
new file mode 100644
index 0000000..c36f958
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/ContactPoint.as.svn-base
@@ -0,0 +1,52 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Vector3;
+
+ public class ContactPoint {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+
+ // Величины, расчитываемые перед началом фазы решения контактов
+
+ // Требуемая проекция конечной скорости на нормаль для упругого контакта
+ public var normalVel:Number;
+ // Минимальная скорость разделения неупругого контакта
+ public var minSepVel:Number;
+ // Изменение проекции скорости на единицу нормального импульса
+ public var velByUnitImpulseN:Number;
+
+ public var angularInertia1:Number;
+ public var angularInertia2:Number;
+
+ // Радиус-вектор точки контакта относительно центра первого тела
+ public var r1:Vector3 = new Vector3();
+ // Радиус-вектор точки контакта относительно центра второго тела
+ public var r2:Vector3 = new Vector3();
+
+ // Величины, накапливаемые во время фазы решения контактов
+
+ // Накопленный импульс, применяемый для получения требуемой относительной скорости в точке контакта
+ public var accumImpulseN:Number;
+ // Накопленный импульс, применяемый для разделения тел в точке контакта. Не создаёт момента.
+// public var accumSepImpulse:Number;
+
+ public var satisfied:Boolean;
+
+ /**
+ *
+ * @param cp
+ */
+ public function copyFrom(cp:ContactPoint):void {
+ pos.vCopy(cp.pos);
+ penetration = cp.penetration;
+ feature1 = cp.feature1;
+ feature2 = cp.feature2;
+ r1.vCopy(cp.r1);
+ r2.vCopy(cp.r2);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/PhysicsUtils.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/PhysicsUtils.as.svn-base
new file mode 100644
index 0000000..a9aaa99
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/PhysicsUtils.as.svn-base
@@ -0,0 +1,49 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ public class PhysicsUtils {
+
+ /**
+ * @param mass
+ * @param halfSize
+ * @param result
+ */
+ public static function getBoxInvInertia(mass:Number, halfSize:Vector3, result:Matrix3):void {
+ if (mass <= 0) throw new ArgumentError();
+ /* Момент инерции бокса:
+
+ m*(hy*hy + hz*hz)/3 0 0
+ 0 m*(hz*hz + hx*hx)/3 0
+ 0 0 m*(hx*hx + hy*hy)/3
+
+ hx, hy, hz -- половина размера бокса вдоль соответствующей оси
+ */
+ result.copy(Matrix3.ZERO);
+ if (mass == Infinity) return;
+ var xx:Number = halfSize.x*halfSize.x;
+ var yy:Number = halfSize.y*halfSize.y;
+ var zz:Number = halfSize.z*halfSize.z;
+ result.a = 3/(mass*(yy + zz));
+ result.f = 3/(mass*(zz + xx));
+ result.k = 3/(mass*(xx + yy));
+ }
+
+ /**
+ * @param mass
+ * @param r
+ * @param h
+ * @param result
+ */
+ public static function getCylinderInvInertia(mass:Number, r:Number, h:Number, result:Matrix3):void {
+ if (mass <= 0) throw new ArgumentError();
+
+ result.copy(Matrix3.ZERO);
+ if (mass == Infinity) return;
+
+ result.a = result.f = 1/(mass*(h*h/12 + r*r/4));
+ result.k = 2/(mass*r*r);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base
new file mode 100644
index 0000000..fb3d9c7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/.svn/text-base/RigidWorld.as.svn-base
@@ -0,0 +1,603 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.ICollisionDetector;
+ import alternativa.physics.collision.KdTreeCollisionDetector;
+ import alternativa.physics.rigid.constraints.Constraint;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Класс реализует физическую симуляцию поведения твёрдых тел.
+ */
+ public class RigidWorld {
+
+ private static var lastBodyId:int;
+
+ // Максимальное количество контактов
+ altphysics const MAX_CONTACTS:int = 1000;
+
+ //////////////////////////////////////////////////////////////////
+ // Настроечные параметры симуляции. Могут быть изменены в любой
+ // момент времени без нарушения корректной работы симуляции.
+ //////////////////////////////////////////////////////////////////
+ // Количество шагов, за которое пересекающиеся тела должны разделиться
+ public var penResolutionSteps:int = 10;
+ // Величина допустимой глубины пересечения
+ public var allowedPenetration:Number = 0.1;
+ // Максимальная скорость, добавляемая с целью разделения тел
+ public var maxPenResolutionSpeed:Number = 0.5;
+ // Количество итераций для обработки упругих контактов
+ public var collisionIterations:int = 5;
+ // Количество итераций для обработки неупругих контактов
+ public var contactIterations:int = 5;
+ // Флаг использования предсказания состояний
+ public var usePrediction:Boolean = false;
+
+ // Переменные для процедуры разделения тел путём непосредственного их перемещенеия.
+ // !!!!!!!!!!!!!!!!!!!!!!!!
+ // !!! Экспериментально !!!
+ // !!!!!!!!!!!!!!!!!!!!!!!!
+ public var staticSeparationIterations:int = 10;
+ public var staticSeparationSteps:int = 10;
+ public var maxAngleMove:Number = 10;
+ public var useStaticSeparation:Boolean = false;
+
+ // Вектор гравитации
+ altphysics var _gravity:Vector3 = new Vector3(0, 0, -9.8);
+ // Использующийся детектор столкновений
+ altphysics var collisionDetector:ICollisionDetector;
+ // Список тел, участвующих в симуляции
+ altphysics var bodies:Vector. = new Vector.();
+ // Количество тел, участвующих в симуляции
+ altphysics var bodiesNum:int;
+ // Список контактов на текущем шаге симуляции
+ altphysics var contacts:Vector. = new Vector.(MAX_CONTACTS, true);
+ // Количество контактов на текущем шаге симуляции
+ altphysics var contactsNum:int;
+ // Список ограничений
+ altphysics var constraints:Vector. = new Vector.();
+ // Количество ограничений
+ altphysics var constraintsNum:int;
+ // Временная метка. Число прошедших шагов с начала симуляции.
+ altphysics var timeStamp:uint;
+
+ // Временные переменные для избежания создания экземпляров
+ private var _r:Vector3 = new Vector3();
+ private var _t:Vector3 = new Vector3();
+ private var _v:Vector3 = new Vector3();
+ private var _v1:Vector3 = new Vector3();
+ private var _v2:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function RigidWorld() {
+ for (var i:int = 0; i < MAX_CONTACTS; i++) contacts[i] = new Contact();
+ collisionDetector = new KdTreeCollisionDetector();
+ }
+
+ /**
+ * Вектор гравитации.
+ */
+ public function get gravity():Vector3 {
+ return _gravity.vClone();
+ }
+
+ /**
+ * @private
+ */
+ public function set gravity(value:Vector3):void {
+ _gravity.vCopy(value);
+ }
+
+ /**
+ * Добавляет тело в симуляцию.
+ * @param body
+ * @return
+ */
+ public function addBody(body:Body, isStatic:Boolean):Boolean {
+ var i:int = bodies.indexOf(body);
+ if (i > -1) return false;
+ bodies[bodiesNum++] = body;
+ body.world = this;
+ body.id = lastBodyId++;
+ if (body.collisionPrimitives != null) {
+ for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.addPrimitive(body.collisionPrimitives[i], isStatic);
+ }
+ return true;
+ }
+
+ /**
+ * Удаляет тело из симуляции.
+ * @param body
+ * @return
+ */
+ public function removeBody(body:Body, isStatic:Boolean):Boolean {
+ var i:int = bodies.indexOf(body);
+ if (i == -1) return false;
+ bodies.splice(i, 1);
+ body.world = null;
+ if (body.collisionPrimitives != null) {
+ for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.removePrimitive(body.collisionPrimitives[i], isStatic);
+ }
+ bodiesNum--;
+ return true;
+ }
+
+ /**
+ * Добавляет ограничение.
+ * @param c
+ */
+ public function addConstraint(c:Constraint):void {
+ constraints[constraintsNum++] = c;
+ c.world = this;
+ }
+
+ /**
+ * Удаляет ограничение.
+ * @param c
+ */
+ public function removeConstraint(c:Constraint):Boolean {
+ var idx:int = constraints.indexOf(c);
+ if (idx < 0) return false;
+ constraints.splice(idx, 1)
+ constraintsNum--;
+ c.world = null;
+ return true;
+ }
+
+ /**
+ * Применяет к телам действующте на них силы. Аккумуляторы сил и моментов тел очищаются после завершения шага
+ * симуляции, поэтому на момент вызова метода могут уже содержать некоторые значения.
+ *
+ * @param dt промежуток времени, в течении которого действуют силы
+ */
+ private function applyForces(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i];
+ body.addExternalForces(dt);
+ body.calcAccelerations();
+ // Ускорение свободного падения применяется только к подвижным телам во избежание некорректного изменения
+ // фиктивной скорости неподвижных тел.
+ if (body.movable) body.accel.vAdd(_gravity);
+ }
+ }
+
+ /**
+ * Определяет все столкновения на текущем шаге симуляции и заполняет список получившихся контактов.
+ *
+ * @param dt длительность шага симуляции
+ */
+ private function detectCollisions(dt:Number):void {
+ var i:int;
+ var j:int;
+ var body:Body;
+
+ for (i = 0; i < bodiesNum; i++) {
+ body = bodies[i];
+ body.contactsNum = 0;
+ body.saveState();
+ // При включённом режиме предсказания состояние тел интегрируется на один шаг вперёд
+ if (usePrediction) {
+ body.integrateVelocity(dt);
+ body.integratePosition(dt);
+ }
+ body.calcDerivedData();
+ }
+
+ contactsNum = collisionDetector.getAllCollisions(contacts);
+
+ // Расчёт относительных векторов точки контакта вынесен сюда из-за необходимости учитывать
+ // положение тел в предсказанном состоянии
+ for (i = 0; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ for (j = 0; j < contact.pcount; j++) {
+ var cp:ContactPoint = contact.points[j];
+ cp.r1.x = cp.pos.x - b1.state.pos.x;
+ cp.r1.y = cp.pos.y - b1.state.pos.y;
+ cp.r1.z = cp.pos.z - b1.state.pos.z;
+ if (b2 != null) {
+ cp.r2.x = cp.pos.x - b2.state.pos.x;
+ cp.r2.y = cp.pos.y - b2.state.pos.y;
+ cp.r2.z = cp.pos.z - b2.state.pos.z;
+ }
+ }
+ }
+
+ // Восстановление состояния тел
+ if (usePrediction) {
+ for (i = 0; i < bodiesNum; i++) {
+ body = bodies[i];
+ body.restoreState();
+ body.calcDerivedData();
+ }
+ }
+ }
+
+ /**
+ * Подготваливает полученные из детектора столкновений контакты, расчитывая значения, не меняющиеся
+ * в ходе шага симуляции.
+ */
+ private function preProcessContacts(dt:Number):void {
+ var i:int;
+ for (i = 0; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ contact.restitution = b1.material.restitution;
+ if (b2 != null && b2.material.restitution < contact.restitution) contact.restitution = b2.material.restitution;
+ contact.friction = b1.material.friction;
+ if (b2 != null && b2.material.friction < contact.friction) contact.friction = b2.material.friction;
+ for (var j:int = 0; j < contact.pcount; j++) {
+ var cp:ContactPoint = contact.points[j];
+ cp.accumImpulseN = 0;
+ // Расчитываем изменение нормальной скорости на единицу нормального импульса
+ // dV = b.invMass + ((invI * (r % n)) % r) * n
+ cp.velByUnitImpulseN = 0;
+ if (b1.movable) {
+ cp.angularInertia1 = _v.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1).vDot(contact.normal);
+ cp.velByUnitImpulseN += b1.invMass + cp.angularInertia1;
+ }
+ if (b2 != null && b2.movable) {
+ cp.angularInertia2 = _v.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2).vDot(contact.normal);
+ cp.velByUnitImpulseN += b2.invMass + cp.angularInertia2;
+ }
+ // Расчёт требуемой конечной скорости для упругого контакта
+ calcSepVelocity(b1, b2, cp, _v);
+ cp.normalVel = _v.vDot(contact.normal);
+ if (cp.normalVel < 0) cp.normalVel = - contact.restitution*cp.normalVel;
+ // Скорость разделения неупругого контакта
+ cp.minSepVel = cp.penetration > allowedPenetration ? (cp.penetration - allowedPenetration)/(penResolutionSteps*dt) : 0;
+ if (cp.minSepVel > maxPenResolutionSpeed) cp.minSepVel = maxPenResolutionSpeed;
+ }
+ }
+ for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).preProcess(dt);
+ }
+
+ /**
+ *
+ * @param dt
+ * @param forceInelastic
+ */
+ private function processContacts(dt:Number, forceInelastic:Boolean):void {
+ var iterNum:int = forceInelastic ? contactIterations : collisionIterations;
+ var i:int;
+ var forwardLoop:Boolean = false;
+ for (var iter:int = 0; iter < iterNum; iter++) {
+ forwardLoop = !forwardLoop;
+ for (i = 0; i < contactsNum; i++) resolveContact(contacts[i], forceInelastic, forwardLoop);
+ // Ограничения
+ for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).apply(dt);
+ }
+
+ // Разделение контактов путём непосредственного изменения координат и ориентации. Экспериментально.
+ if (forceInelastic && useStaticSeparation) performStaticSeparation();
+ }
+
+ /**
+ *
+ */
+ private function resolveContact(contactInfo:Contact, forceInelastic:Boolean, forwardLoop:Boolean):void {
+ var b1:Body = contactInfo.body1;
+ var b2:Body = contactInfo.body2;
+ var normal:Vector3 = contactInfo.normal;
+ var i:int;
+ if (forwardLoop) {
+ for (i = 0; i < contactInfo.pcount; i++) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
+ } else {
+ for (i = contactInfo.pcount - 1; i >= 0; i--) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
+ }
+ }
+
+ /**
+ *
+ * @param idx
+ * @param b1
+ * @param b2
+ * @param colInfo
+ * @param normal
+ * @param forceInelastic
+ */
+ private function resolveContactPoint(idx:int, b1:Body, b2:Body, contact:Contact, normal:Vector3, forceInelastic:Boolean):void {
+ var cp:ContactPoint = contact.points[idx];
+ if (!forceInelastic) cp.satisfied = true;
+
+ var newVel:Number = 0;
+ calcSepVelocity(b1, b2, cp, _v);
+ var sepVel:Number = _v.vDot(contact.normal);
+ if (forceInelastic) {
+ var minSpeVel:Number = useStaticSeparation ? 0 : cp.minSepVel;
+ if (sepVel < minSpeVel) cp.satisfied = false;
+ else if (cp.satisfied) return;
+ newVel = minSpeVel;
+ } else {
+ newVel = cp.normalVel;
+ }
+ var deltaVel:Number = newVel - sepVel;
+ var impulse:Number = deltaVel/cp.velByUnitImpulseN;
+ var accumImpulse:Number = cp.accumImpulseN + impulse;
+ if (accumImpulse < 0) accumImpulse = 0;
+ var deltaImpulse:Number = accumImpulse - cp.accumImpulseN;
+ cp.accumImpulseN = accumImpulse;
+ // Применяем импульс к телам
+ if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, normal, deltaImpulse);
+ if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, normal, -deltaImpulse);
+
+ // Учёт силы трения
+ calcSepVelocity(b1, b2, cp, _v);
+ // Расчитываем изменение касательной скорости на единицу касательного импульса
+ var tanSpeedByUnitImpulse:Number = 0;
+ _v.vAddScaled(-_v.vDot(contact.normal), contact.normal);
+ var tanSpeed:Number = _v.vLength();
+ if (tanSpeed < 0.001) return;
+ _t.vCopy(_v).vNormalize().vReverse();
+ // dV = b.invMass + ((invI * (r % t)) % r) * t
+ if (b1.movable) {
+ _v.vCross2(cp.r1, _t).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1);
+ tanSpeedByUnitImpulse += b1.invMass + _v.vDot(_t);
+ }
+ if (b2 != null && b2.movable) {
+ _v.vCross2(cp.r2, _t).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2);
+ tanSpeedByUnitImpulse += b2.invMass + _v.vDot(_t);
+ }
+
+ var tanImpulse:Number = tanSpeed/tanSpeedByUnitImpulse;
+ var max:Number = contact.friction*cp.accumImpulseN;
+ if (max < 0) {
+ if (tanImpulse < max) tanImpulse = max;
+ } else {
+ if (tanImpulse > max) tanImpulse = max;
+ }
+
+ // Применяем импульс к телам
+ if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, _t, tanImpulse);
+ if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, _t, -tanImpulse);
+ }
+
+ /**
+ *
+ * @param cp
+ * @param normal
+ * @return
+ */
+ private function calcSepVelocity(body1:Body, body2:Body, cp:ContactPoint, result:Vector3):void {
+ // sepVel = (V1 - V2)*normal
+ // V1 = V1_c + w1%r1
+ result.vCopy(body1.state.velocity).vAdd(_v1.vCross2(body1.state.rotation, cp.r1));
+ // V2 = V2_c + w2%r2
+ if (body2 != null) result.vSubtract(body2.state.velocity).vSubtract(_v2.vCross2(body2.state.rotation, cp.r2));
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ private function intergateVelocities(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ (bodies[i] as Body).integrateVelocity(dt);
+ }
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ private function integratePositions(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i] as Body;
+ if (body.movable) body.integratePosition(dt);
+ }
+ }
+
+ /**
+ *
+ */
+ private function performStaticSeparation():void {
+ var iterNum:int = staticSeparationIterations;
+// iterNum = 100;
+
+ // 1. В начале каждой итерации для всех контактов сбрасывается флаг satisfied с одновременным поиском наихудшего контакта, т.е. имеющего наибольшую величину пересечения тел.
+ // 2. Если найденный контакт имеет величину пересечения меньше предельно допустимой процедура прерывается, т.к. разделение контактов не требуется.
+ // 3. Если процедура продолжается, то выполняется разделение найденного контакта и установка его флага satisfied в true, после чего запускается внутренний цикл, состоящий
+ // из contactsNum - 1 итераций. На каждой итерации ищется наихудший контакт среди оставшихся (satisfied == false). Если такой контакт найден, то выполняется его разделение
+ // и переход к следующей итерации внутреннего цикла, иначе внутренний цикл прерывается и выполняется переход к следующей итерации внешнего цикла (шаг 1.).
+ for (var iter:int = 0; iter < iterNum; iter++) {
+ // Ищем контакт с максимальной величиной пересечения тел, одновременно сбрасывая флаги satisfied в false
+ var worstContact:Contact = contacts[0];
+ var i:int;
+ for (i = 1; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ contact.satisfied = false;
+ if (contact.maxPenetration > worstContact.maxPenetration) worstContact = contact;
+ }
+ if (worstContact.maxPenetration <= allowedPenetration) return;
+
+ resolveInterpenetration(worstContact);
+ // Внутренний цикл по оставшимся контактам
+ for (i = 1; i < contactsNum; i++) {
+ worstContact = getWorstContact();
+ if (worstContact == null) break;
+ resolveInterpenetration(worstContact);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private function getWorstContact():Contact {
+ var maxPen:Number = 0;
+ var worst:Contact = null;
+ for (var i:int = 0; i < contactsNum; i++) {
+ var c:Contact = contacts[i];
+ if (!c.satisfied && c.maxPenetration > maxPen) {
+ worst = c;
+ maxPen = c.maxPenetration;
+ }
+ }
+ return maxPen > allowedPenetration ? worst : null;
+ }
+
+ /**
+ * Разделяет указанный контакт, выполняя staticSeparationSteps итераций по списку точек контакта.
+ * 1. В начале каждой итерации ищется точка с наибольшим пересечением, одновременно сбрасываются флаги satisfied у точек.
+ * 2. Для найденной точки выполняется процедура разделения.
+ * 3. После выполняется pcount - 1 итерация по списку точек, каждый раз ищется наихудшая среди имеющих satisfied == false, для которой выполняется процедура разделения.
+ * Если наихудшая точка не найдена, то выполнется переход к следующей итерации внешнего цикла (шаг 1).
+ *
+ * В конце процедуры разделения обновляются значения пересечений для остальных точек контакта, а также для всех прочих контактов, относящихся к телам текущего.
+ *
+ * @param contact контакт для разделения
+ */
+ private function resolveInterpenetration(contact:Contact):void {
+ contact.satisfied = true;
+
+ for (var step:int = 0; step < staticSeparationSteps; step++) {
+ var worstCp:ContactPoint = contact.points[0];
+ var cp:ContactPoint;
+ var i:int;
+ for (i = 1; i < contact.pcount; i++) {
+ cp = contact.points[i];
+ cp.satisfied = false;
+ if (cp.penetration > worstCp.penetration) worstCp = cp;
+ }
+ if (worstCp.penetration <= allowedPenetration) break;
+ separateContactPoint(worstCp, contact);
+ // Разделяем оставшиеся точки
+ var maxPen:Number = 0;
+ for (i = 1; i < contact.pcount; i++) {
+ // Поиск наихудшей точки
+ for (var j:int = 0; j < contact.pcount; j++) {
+ cp = contact.points[j];
+ if (cp.satisfied) continue;
+ if (cp.penetration > maxPen) {
+ maxPen = cp.penetration;
+ worstCp = cp;
+ }
+ }
+ if (maxPen <= allowedPenetration) break;
+ separateContactPoint(worstCp, contact);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param cp
+ * @param contact
+ */
+ private function separateContactPoint(cp:ContactPoint, contact:Contact):void {
+ cp.satisfied = true;
+
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ var totalMove:Number = cp.penetration - allowedPenetration;
+ var moveCoeff:Number = totalMove/cp.velByUnitImpulseN;
+ var linMove1:Number;
+ var angleMove1:Number;
+ if (b1.movable) {
+ linMove1 = b1.invMass*moveCoeff;
+ angleMove1 = cp.angularInertia1*moveCoeff;
+ if (angleMove1 > maxAngleMove) {
+ linMove1 += angleMove1 - maxAngleMove;
+ angleMove1 -= maxAngleMove;
+ }
+ b1.state.pos.vAddScaled(linMove1, contact.normal);
+ _v1.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vScale(angleMove1);
+ b1.state.orientation.addScaledVector(_v1, 1);
+ }
+ var linMove2:Number;
+ var angleMove2:Number;
+ if (b2 != null && b2.movable) {
+ linMove2 = b2.invMass*moveCoeff;
+ angleMove2 = cp.angularInertia2*moveCoeff;
+ if (angleMove2 > maxAngleMove) {
+ linMove2 += angleMove2 - maxAngleMove;
+ angleMove2 -= maxAngleMove;
+ }
+ b2.state.pos.vAddScaled(-linMove2, contact.normal);
+ _v2.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vScale(angleMove2);
+ _v2.vReverse();
+ b2.state.orientation.addScaledVector(_v2, 1);
+ }
+ cp.penetration = allowedPenetration;
+ // Обновляем пересечения в других точках
+ var i:int;
+ for (i = 0; i < contact.pcount; i++) {
+ var cp1:ContactPoint = contact.points[i];
+ if (cp1 == cp) continue;
+ var angularMove:Number;
+ if (b1.movable) {
+ angularMove = _v.vCross2(_v1, cp1.r1).vDot(contact.normal);
+ cp1.penetration -= linMove1 + angularMove;
+ }
+ if (b2 != null && b2.movable) {
+ angularMove = _v.vCross2(_v2, cp1.r2).vDot(contact.normal);
+ cp1.penetration -= linMove2 - angularMove;
+ }
+ // Обновление максимального значения для контакта
+ if (cp1.penetration > contact.maxPenetration) contact.maxPenetration = cp1.penetration;
+ }
+ // Обновляем пересечения для других контактов
+ var c:Contact;
+ var j:int;
+ if (b1.movable) {
+ for (i = 0; i < b1.contactsNum; i++) {
+ c = b1.contacts[i];
+ if (c == contact) continue;
+ for (j = 0; j < c.pcount; j++) {
+ cp1 = c.points[j];
+ if (b1 == c.body1) cp1.penetration -= linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r1).vDot(c.normal);
+ else cp1.penetration += linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r2).vDot(c.normal);
+ if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
+ }
+ }
+ }
+ if (b2 != null && b2.movable) {
+ for (i = 0; i < b2.contactsNum; i++) {
+ c = b2.contacts[i];
+ if (c == contact) continue;
+ for (j = 0; j < c.pcount; j++) {
+ cp1 = c.points[j];
+ if (b2 == c.body1) cp1.penetration -= linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r1).vDot(c.normal);
+ else cp1.penetration += linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r2).vDot(c.normal);
+ if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private function postPhysics():void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i];
+ body.clearAccumulators();
+ body.calcDerivedData();
+ }
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ public function runPhysics(dt:Number):void {
+ timeStamp++;
+ applyForces(dt);
+ detectCollisions(dt);
+ preProcessContacts(dt);
+ processContacts(dt, false);
+ intergateVelocities(dt);
+ processContacts(dt, true);
+ integratePositions(dt);
+ postPhysics();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/Body.as b/0.0.1.0/src/alternativa/physics/rigid/Body.as
new file mode 100644
index 0000000..4e83bfe
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/Body.as
@@ -0,0 +1,329 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Quaternion;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class Body {
+
+ public static var linDamping:Number = 0.997;
+ public static var rotDamping:Number = 0.997;
+
+ public var name:String;
+ public var movable:Boolean = true;
+
+ altphysics var id:int;
+ // Мир, в котором находится тело
+ altphysics var world:RigidWorld;
+ // Текущее и предыдущее состояние тела. Промежуточное состояние вычисляется линейной интерполяцией.
+ altphysics var state:BodyState = new BodyState();
+ altphysics var prevState:BodyState = new BodyState();
+ // Линейное и угловое ускорение тела на текущем шаге симуляции
+ altphysics var accel:Vector3 = new Vector3();
+ altphysics var angleAccel:Vector3 = new Vector3();
+ // Материал тела
+ altphysics var material:BodyMaterial = new BodyMaterial();
+
+ altphysics var invMass:Number = 1;
+ altphysics var invInertia:Matrix3 = new Matrix3();
+ altphysics var invInertiaWorld:Matrix3 = new Matrix3();
+ altphysics var baseMatrix:Matrix3 = new Matrix3();
+
+ altphysics const MAX_CONTACTS:int = 20;
+ altphysics var contacts:Vector. = new Vector.(MAX_CONTACTS);
+ altphysics var contactsNum:int;
+
+ altphysics var collisionPrimitives:Vector.;
+ altphysics var collisionPrimitivesNum:int;
+
+ // Аккумулятор сил
+ altphysics var forceAccum:Vector3 = new Vector3();
+ // Аккумулятор моментов
+ altphysics var torqueAccum:Vector3 = new Vector3();
+
+ // Внутренние переменные для избежания создания экземпляров
+ private static var _r:Vector3 = new Vector3();
+ private static var _f:Vector3 = new Vector3();
+
+ /**
+ *
+ * @param invMass
+ * @param invInertia
+ */
+ public function Body(invMass:Number, invInertia:Matrix3) {
+ this.invMass = invMass;
+ this.invInertia.copy(invInertia);
+ }
+
+ /**
+ * @param prim
+ */
+ public function addCollisionPrimitive(primitive:CollisionPrimitive, localTransform:Matrix4 = null):void {
+ if (primitive == null) throw new ArgumentError("Primitive cannot be null");
+ if (collisionPrimitives == null) collisionPrimitives = new Vector.();
+ collisionPrimitives[collisionPrimitivesNum++] = primitive;
+ primitive.setBody(this, localTransform);
+ }
+
+ /**
+ * @param t
+ * @param result
+ */
+ public function interpolate(t:Number, pos:Vector3, orientation:Quaternion):void {
+ var t1:Number = 1 - t;
+ pos.x = prevState.pos.x*t1 + state.pos.x*t;
+ pos.y = prevState.pos.y*t1 + state.pos.y*t;
+ pos.z = prevState.pos.z*t1 + state.pos.z*t;
+ orientation.w = prevState.orientation.w*t1 + state.orientation.w*t;
+ orientation.x = prevState.orientation.x*t1 + state.orientation.x*t;
+ orientation.y = prevState.orientation.y*t1 + state.orientation.y*t;
+ orientation.z = prevState.orientation.z*t1 + state.orientation.z*t;
+ }
+
+ /**
+ *
+ * @param pos
+ */
+ public function setPosition(pos:Vector3):void {
+ state.pos.vCopy(pos);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setPositionXYZ(x:Number, y:Number, z:Number):void {
+ state.pos.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param vel
+ */
+ public function setVelocity(vel:Vector3):void {
+ state.velocity.vCopy(vel);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setVelocityXYZ(x:Number, y:Number, z:Number):void {
+ state.velocity.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param rot
+ */
+ public function setRotation(rot:Vector3):void {
+ state.rotation.vCopy(rot);
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function setRotationXYZ(x:Number, y:Number, z:Number):void {
+ state.rotation.vReset(x, y, z);
+ }
+
+ /**
+ *
+ * @param q
+ */
+ public function setOrientation(q:Quaternion):void {
+ state.orientation.copy(q);
+ }
+
+ /**
+ * @param r
+ * @param dir
+ * @param magnitude
+ */
+ public function applyRelPosWorldImpulse(r:Vector3, dir:Vector3, magnitude:Number):void {
+ var d:Number = magnitude*invMass;
+ // Линейная часть
+ state.velocity.x += d*dir.x;
+ state.velocity.y += d*dir.y;
+ state.velocity.z += d*dir.z;
+
+ // Вращательная часть
+ var rx:Number = r.x;
+ var ry:Number = r.y;
+ var rz:Number = r.z;
+ _r.x = (ry*dir.z - rz*dir.y)*magnitude;
+ _r.y = (rz*dir.x - rx*dir.z)*magnitude;
+ _r.z = (rx*dir.y - ry*dir.x)*magnitude;
+ _r.vTransformBy3(invInertiaWorld);
+ state.rotation.x += _r.x;
+ state.rotation.y += _r.y;
+ state.rotation.z += _r.z;
+ }
+
+ /**
+ * @param f
+ */
+ public function addForce(f:Vector3):void {
+ forceAccum.vAdd(f);
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addWorldForce(pos:Vector3, force:Vector3):void {
+ forceAccum.vAdd(force);
+ torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(force));
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addWorldForceScaled(pos:Vector3, force:Vector3, scale:Number):void {
+ _f.x = scale*force.x, _f.y = scale*force.y, _f.z = scale*force.z;
+ forceAccum.vAdd(_f);
+ torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(_f));
+ }
+
+ /**
+ * @param pos
+ * @param f
+ */
+ public function addLocalForce(pos:Vector3, force:Vector3):void {
+ // Трансформируем точку приложения в мировую систему координат
+ baseMatrix.transformVector(pos, _r);
+ // Трансформируем вектор силы в мировую систему
+ baseMatrix.transformVector(force, _f);
+ // Добавляем силу и момент
+ forceAccum.vAdd(_f);
+ torqueAccum.vAdd(_r.vCross(_f));
+ }
+
+ /**
+ *
+ * @param localPos
+ * @param worldForce
+ */
+ public function addWorldForceAtLocalPoint(localPos:Vector3, worldForce:Vector3):void {
+ // Трансформируем точку приложения в мировую систему координат
+ baseMatrix.transformVector(localPos, _r);
+ // Добавляем силу и момент
+ forceAccum.vAdd(worldForce);
+ torqueAccum.vAdd(_r.vCross(worldForce));
+ }
+
+ /**
+ * @param dt
+ */
+ public function addExternalForces(dt:Number):void {
+ }
+
+ /**
+ * @param t
+ */
+ public function addTorque(t:Vector3):void {
+ torqueAccum.vAdd(t);
+ }
+
+ /**
+ *
+ */
+ altphysics function clearAccumulators():void {
+ forceAccum.vReset();
+ torqueAccum.vReset();
+ }
+
+ /**
+ *
+ */
+ altphysics function calcAccelerations():void {
+ accel.x = forceAccum.x*invMass;
+ accel.y = forceAccum.y*invMass;
+ accel.z = forceAccum.z*invMass;
+ invInertiaWorld.transformVector(torqueAccum, angleAccel);
+ }
+
+ /**
+ * Вычисляет производные данные.
+ */
+ altphysics function calcDerivedData():void {
+ // Вычисление базисной матрицы и обратного тензора инерции в мировых координатах
+ state.orientation.toMatrix3(baseMatrix);
+ invInertiaWorld.copy(invInertia).append(baseMatrix).prepend(baseMatrix.transpose());
+ baseMatrix.transpose();
+ if (collisionPrimitives != null) {
+ for (var i:int = 0; i < collisionPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = collisionPrimitives[i];
+ primitive.transform.setFromMatrix3(baseMatrix, state.pos);
+ if (primitive.localTransform != null) primitive.transform.prepend(primitive.localTransform);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ altphysics function saveState():void {
+ prevState.copy(state);
+ }
+
+ /**
+ *
+ */
+ altphysics function restoreState():void {
+ state.copy(prevState);
+ }
+
+ /**
+ * @param dt
+ */
+ altphysics function integrateVelocity(dt:Number):void {
+ // v = v + a*t
+ state.velocity.x += accel.x*dt;
+ state.velocity.y += accel.y*dt;
+ state.velocity.z += accel.z*dt;
+ // rot = rot + eps*t
+ state.rotation.x += angleAccel.x*dt;
+ state.rotation.y += angleAccel.y*dt;
+ state.rotation.z += angleAccel.z*dt;
+
+ state.velocity.x *= linDamping;
+ state.velocity.y *= linDamping;
+ state.velocity.z *= linDamping;
+
+ state.rotation.x *= rotDamping;
+ state.rotation.y *= rotDamping;
+ state.rotation.z *= rotDamping;
+ }
+
+ /**
+ *
+ */
+ altphysics function integratePosition(dt:Number):void {
+ // pos = pos + v*t
+ state.pos.x += state.velocity.x*dt;
+ state.pos.y += state.velocity.y*dt;
+ state.pos.z += state.velocity.z*dt;
+ // q = q + 0.5*rot*q
+ state.orientation.addScaledVector(state.rotation, dt);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as b/0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
new file mode 100644
index 0000000..61d4382
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
@@ -0,0 +1,11 @@
+package alternativa.physics.rigid {
+
+ public class BodyMaterial {
+
+ public var restitution:Number = 0;
+ public var friction:Number = 0.3;
+// public var dynamicFriction:Number = 0.2;
+// public var dynamicFriction:Number = 0.2;
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/BodyState.as b/0.0.1.0/src/alternativa/physics/rigid/BodyState.as
new file mode 100644
index 0000000..408b0ea
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/BodyState.as
@@ -0,0 +1,39 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Quaternion;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Класс описывает состояние твёрдого тела.
+ */
+ public class BodyState {
+ /**
+ * Положение тела.
+ */
+ public var pos:Vector3 = new Vector3();
+ /**
+ * Ориентация тела.
+ */
+ public var orientation:Quaternion = new Quaternion();
+ /**
+ * Скорость тела.
+ */
+ public var velocity:Vector3 = new Vector3();
+ /**
+ * Угловая скорость тела.
+ */
+ public var rotation:Vector3 = new Vector3();
+
+ /**
+ * Копирует значение указанного объекта.
+ *
+ * @param state
+ */
+ public function copy(state:BodyState):void {
+ pos.vCopy(state.pos);
+ orientation.copy(state.orientation);
+ velocity.vCopy(state.velocity);
+ rotation.vCopy(state.rotation);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/Contact.as b/0.0.1.0/src/alternativa/physics/rigid/Contact.as
new file mode 100644
index 0000000..b73fc55
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/Contact.as
@@ -0,0 +1,53 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Информация о контакте между двумя телами. Содержит нормаль, множество точек контакта и прочие величины.
+ */
+ public class Contact {
+
+// private static var pool:Vector. = new Vector.();
+//
+// public static function create():ContactInfo {
+// if (pool.length > 0) {
+// return pool.pop();
+// }
+// return new ContactInfo();
+// }
+//
+// public static function destroy(collInfo:ContactInfo):void {
+// collInfo.body1 = collInfo.body2 = null;
+// pool.push(collInfo);
+// }
+
+ // Максимальное количество точек контакта
+ private const N:int = 8;
+
+ public var body1:Body;
+ public var body2:Body;
+ // Взаимный коэффициент отскока
+ public var restitution:Number;
+ // Взаимный коэффициент трения
+ public var friction:Number;
+ // Нормаль контакта. Направлена от второго тела к первому.
+ public var normal:Vector3 = new Vector3();
+ // Список точек контакта
+ public var points:Vector. = new Vector.(N, true);
+ // Количество точек контакта
+ public var pcount:int;
+ // Максимальная глубина пересечения тел
+ public var maxPenetration:Number = 0;
+
+ public var satisfied:Boolean;
+
+ /**
+ *
+ */
+ public function Contact() {
+ for (var i:int = 0; i < N; i++) points[i] = new ContactPoint();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/ContactPoint.as b/0.0.1.0/src/alternativa/physics/rigid/ContactPoint.as
new file mode 100644
index 0000000..c36f958
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/ContactPoint.as
@@ -0,0 +1,52 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Vector3;
+
+ public class ContactPoint {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+
+ // Величины, расчитываемые перед началом фазы решения контактов
+
+ // Требуемая проекция конечной скорости на нормаль для упругого контакта
+ public var normalVel:Number;
+ // Минимальная скорость разделения неупругого контакта
+ public var minSepVel:Number;
+ // Изменение проекции скорости на единицу нормального импульса
+ public var velByUnitImpulseN:Number;
+
+ public var angularInertia1:Number;
+ public var angularInertia2:Number;
+
+ // Радиус-вектор точки контакта относительно центра первого тела
+ public var r1:Vector3 = new Vector3();
+ // Радиус-вектор точки контакта относительно центра второго тела
+ public var r2:Vector3 = new Vector3();
+
+ // Величины, накапливаемые во время фазы решения контактов
+
+ // Накопленный импульс, применяемый для получения требуемой относительной скорости в точке контакта
+ public var accumImpulseN:Number;
+ // Накопленный импульс, применяемый для разделения тел в точке контакта. Не создаёт момента.
+// public var accumSepImpulse:Number;
+
+ public var satisfied:Boolean;
+
+ /**
+ *
+ * @param cp
+ */
+ public function copyFrom(cp:ContactPoint):void {
+ pos.vCopy(cp.pos);
+ penetration = cp.penetration;
+ feature1 = cp.feature1;
+ feature2 = cp.feature2;
+ r1.vCopy(cp.r1);
+ r2.vCopy(cp.r2);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/PhysicsUtils.as b/0.0.1.0/src/alternativa/physics/rigid/PhysicsUtils.as
new file mode 100644
index 0000000..a9aaa99
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/PhysicsUtils.as
@@ -0,0 +1,49 @@
+package alternativa.physics.rigid {
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ public class PhysicsUtils {
+
+ /**
+ * @param mass
+ * @param halfSize
+ * @param result
+ */
+ public static function getBoxInvInertia(mass:Number, halfSize:Vector3, result:Matrix3):void {
+ if (mass <= 0) throw new ArgumentError();
+ /* Момент инерции бокса:
+
+ m*(hy*hy + hz*hz)/3 0 0
+ 0 m*(hz*hz + hx*hx)/3 0
+ 0 0 m*(hx*hx + hy*hy)/3
+
+ hx, hy, hz -- половина размера бокса вдоль соответствующей оси
+ */
+ result.copy(Matrix3.ZERO);
+ if (mass == Infinity) return;
+ var xx:Number = halfSize.x*halfSize.x;
+ var yy:Number = halfSize.y*halfSize.y;
+ var zz:Number = halfSize.z*halfSize.z;
+ result.a = 3/(mass*(yy + zz));
+ result.f = 3/(mass*(zz + xx));
+ result.k = 3/(mass*(xx + yy));
+ }
+
+ /**
+ * @param mass
+ * @param r
+ * @param h
+ * @param result
+ */
+ public static function getCylinderInvInertia(mass:Number, r:Number, h:Number, result:Matrix3):void {
+ if (mass <= 0) throw new ArgumentError();
+
+ result.copy(Matrix3.ZERO);
+ if (mass == Infinity) return;
+
+ result.a = result.f = 1/(mass*(h*h/12 + r*r/4));
+ result.k = 2/(mass*r*r);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/RigidWorld.as b/0.0.1.0/src/alternativa/physics/rigid/RigidWorld.as
new file mode 100644
index 0000000..fb3d9c7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/RigidWorld.as
@@ -0,0 +1,603 @@
+package alternativa.physics.rigid {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.ICollisionDetector;
+ import alternativa.physics.collision.KdTreeCollisionDetector;
+ import alternativa.physics.rigid.constraints.Constraint;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Класс реализует физическую симуляцию поведения твёрдых тел.
+ */
+ public class RigidWorld {
+
+ private static var lastBodyId:int;
+
+ // Максимальное количество контактов
+ altphysics const MAX_CONTACTS:int = 1000;
+
+ //////////////////////////////////////////////////////////////////
+ // Настроечные параметры симуляции. Могут быть изменены в любой
+ // момент времени без нарушения корректной работы симуляции.
+ //////////////////////////////////////////////////////////////////
+ // Количество шагов, за которое пересекающиеся тела должны разделиться
+ public var penResolutionSteps:int = 10;
+ // Величина допустимой глубины пересечения
+ public var allowedPenetration:Number = 0.1;
+ // Максимальная скорость, добавляемая с целью разделения тел
+ public var maxPenResolutionSpeed:Number = 0.5;
+ // Количество итераций для обработки упругих контактов
+ public var collisionIterations:int = 5;
+ // Количество итераций для обработки неупругих контактов
+ public var contactIterations:int = 5;
+ // Флаг использования предсказания состояний
+ public var usePrediction:Boolean = false;
+
+ // Переменные для процедуры разделения тел путём непосредственного их перемещенеия.
+ // !!!!!!!!!!!!!!!!!!!!!!!!
+ // !!! Экспериментально !!!
+ // !!!!!!!!!!!!!!!!!!!!!!!!
+ public var staticSeparationIterations:int = 10;
+ public var staticSeparationSteps:int = 10;
+ public var maxAngleMove:Number = 10;
+ public var useStaticSeparation:Boolean = false;
+
+ // Вектор гравитации
+ altphysics var _gravity:Vector3 = new Vector3(0, 0, -9.8);
+ // Использующийся детектор столкновений
+ altphysics var collisionDetector:ICollisionDetector;
+ // Список тел, участвующих в симуляции
+ altphysics var bodies:Vector. = new Vector.();
+ // Количество тел, участвующих в симуляции
+ altphysics var bodiesNum:int;
+ // Список контактов на текущем шаге симуляции
+ altphysics var contacts:Vector. = new Vector.(MAX_CONTACTS, true);
+ // Количество контактов на текущем шаге симуляции
+ altphysics var contactsNum:int;
+ // Список ограничений
+ altphysics var constraints:Vector. = new Vector.();
+ // Количество ограничений
+ altphysics var constraintsNum:int;
+ // Временная метка. Число прошедших шагов с начала симуляции.
+ altphysics var timeStamp:uint;
+
+ // Временные переменные для избежания создания экземпляров
+ private var _r:Vector3 = new Vector3();
+ private var _t:Vector3 = new Vector3();
+ private var _v:Vector3 = new Vector3();
+ private var _v1:Vector3 = new Vector3();
+ private var _v2:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function RigidWorld() {
+ for (var i:int = 0; i < MAX_CONTACTS; i++) contacts[i] = new Contact();
+ collisionDetector = new KdTreeCollisionDetector();
+ }
+
+ /**
+ * Вектор гравитации.
+ */
+ public function get gravity():Vector3 {
+ return _gravity.vClone();
+ }
+
+ /**
+ * @private
+ */
+ public function set gravity(value:Vector3):void {
+ _gravity.vCopy(value);
+ }
+
+ /**
+ * Добавляет тело в симуляцию.
+ * @param body
+ * @return
+ */
+ public function addBody(body:Body, isStatic:Boolean):Boolean {
+ var i:int = bodies.indexOf(body);
+ if (i > -1) return false;
+ bodies[bodiesNum++] = body;
+ body.world = this;
+ body.id = lastBodyId++;
+ if (body.collisionPrimitives != null) {
+ for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.addPrimitive(body.collisionPrimitives[i], isStatic);
+ }
+ return true;
+ }
+
+ /**
+ * Удаляет тело из симуляции.
+ * @param body
+ * @return
+ */
+ public function removeBody(body:Body, isStatic:Boolean):Boolean {
+ var i:int = bodies.indexOf(body);
+ if (i == -1) return false;
+ bodies.splice(i, 1);
+ body.world = null;
+ if (body.collisionPrimitives != null) {
+ for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.removePrimitive(body.collisionPrimitives[i], isStatic);
+ }
+ bodiesNum--;
+ return true;
+ }
+
+ /**
+ * Добавляет ограничение.
+ * @param c
+ */
+ public function addConstraint(c:Constraint):void {
+ constraints[constraintsNum++] = c;
+ c.world = this;
+ }
+
+ /**
+ * Удаляет ограничение.
+ * @param c
+ */
+ public function removeConstraint(c:Constraint):Boolean {
+ var idx:int = constraints.indexOf(c);
+ if (idx < 0) return false;
+ constraints.splice(idx, 1)
+ constraintsNum--;
+ c.world = null;
+ return true;
+ }
+
+ /**
+ * Применяет к телам действующте на них силы. Аккумуляторы сил и моментов тел очищаются после завершения шага
+ * симуляции, поэтому на момент вызова метода могут уже содержать некоторые значения.
+ *
+ * @param dt промежуток времени, в течении которого действуют силы
+ */
+ private function applyForces(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i];
+ body.addExternalForces(dt);
+ body.calcAccelerations();
+ // Ускорение свободного падения применяется только к подвижным телам во избежание некорректного изменения
+ // фиктивной скорости неподвижных тел.
+ if (body.movable) body.accel.vAdd(_gravity);
+ }
+ }
+
+ /**
+ * Определяет все столкновения на текущем шаге симуляции и заполняет список получившихся контактов.
+ *
+ * @param dt длительность шага симуляции
+ */
+ private function detectCollisions(dt:Number):void {
+ var i:int;
+ var j:int;
+ var body:Body;
+
+ for (i = 0; i < bodiesNum; i++) {
+ body = bodies[i];
+ body.contactsNum = 0;
+ body.saveState();
+ // При включённом режиме предсказания состояние тел интегрируется на один шаг вперёд
+ if (usePrediction) {
+ body.integrateVelocity(dt);
+ body.integratePosition(dt);
+ }
+ body.calcDerivedData();
+ }
+
+ contactsNum = collisionDetector.getAllCollisions(contacts);
+
+ // Расчёт относительных векторов точки контакта вынесен сюда из-за необходимости учитывать
+ // положение тел в предсказанном состоянии
+ for (i = 0; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ for (j = 0; j < contact.pcount; j++) {
+ var cp:ContactPoint = contact.points[j];
+ cp.r1.x = cp.pos.x - b1.state.pos.x;
+ cp.r1.y = cp.pos.y - b1.state.pos.y;
+ cp.r1.z = cp.pos.z - b1.state.pos.z;
+ if (b2 != null) {
+ cp.r2.x = cp.pos.x - b2.state.pos.x;
+ cp.r2.y = cp.pos.y - b2.state.pos.y;
+ cp.r2.z = cp.pos.z - b2.state.pos.z;
+ }
+ }
+ }
+
+ // Восстановление состояния тел
+ if (usePrediction) {
+ for (i = 0; i < bodiesNum; i++) {
+ body = bodies[i];
+ body.restoreState();
+ body.calcDerivedData();
+ }
+ }
+ }
+
+ /**
+ * Подготваливает полученные из детектора столкновений контакты, расчитывая значения, не меняющиеся
+ * в ходе шага симуляции.
+ */
+ private function preProcessContacts(dt:Number):void {
+ var i:int;
+ for (i = 0; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ contact.restitution = b1.material.restitution;
+ if (b2 != null && b2.material.restitution < contact.restitution) contact.restitution = b2.material.restitution;
+ contact.friction = b1.material.friction;
+ if (b2 != null && b2.material.friction < contact.friction) contact.friction = b2.material.friction;
+ for (var j:int = 0; j < contact.pcount; j++) {
+ var cp:ContactPoint = contact.points[j];
+ cp.accumImpulseN = 0;
+ // Расчитываем изменение нормальной скорости на единицу нормального импульса
+ // dV = b.invMass + ((invI * (r % n)) % r) * n
+ cp.velByUnitImpulseN = 0;
+ if (b1.movable) {
+ cp.angularInertia1 = _v.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1).vDot(contact.normal);
+ cp.velByUnitImpulseN += b1.invMass + cp.angularInertia1;
+ }
+ if (b2 != null && b2.movable) {
+ cp.angularInertia2 = _v.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2).vDot(contact.normal);
+ cp.velByUnitImpulseN += b2.invMass + cp.angularInertia2;
+ }
+ // Расчёт требуемой конечной скорости для упругого контакта
+ calcSepVelocity(b1, b2, cp, _v);
+ cp.normalVel = _v.vDot(contact.normal);
+ if (cp.normalVel < 0) cp.normalVel = - contact.restitution*cp.normalVel;
+ // Скорость разделения неупругого контакта
+ cp.minSepVel = cp.penetration > allowedPenetration ? (cp.penetration - allowedPenetration)/(penResolutionSteps*dt) : 0;
+ if (cp.minSepVel > maxPenResolutionSpeed) cp.minSepVel = maxPenResolutionSpeed;
+ }
+ }
+ for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).preProcess(dt);
+ }
+
+ /**
+ *
+ * @param dt
+ * @param forceInelastic
+ */
+ private function processContacts(dt:Number, forceInelastic:Boolean):void {
+ var iterNum:int = forceInelastic ? contactIterations : collisionIterations;
+ var i:int;
+ var forwardLoop:Boolean = false;
+ for (var iter:int = 0; iter < iterNum; iter++) {
+ forwardLoop = !forwardLoop;
+ for (i = 0; i < contactsNum; i++) resolveContact(contacts[i], forceInelastic, forwardLoop);
+ // Ограничения
+ for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).apply(dt);
+ }
+
+ // Разделение контактов путём непосредственного изменения координат и ориентации. Экспериментально.
+ if (forceInelastic && useStaticSeparation) performStaticSeparation();
+ }
+
+ /**
+ *
+ */
+ private function resolveContact(contactInfo:Contact, forceInelastic:Boolean, forwardLoop:Boolean):void {
+ var b1:Body = contactInfo.body1;
+ var b2:Body = contactInfo.body2;
+ var normal:Vector3 = contactInfo.normal;
+ var i:int;
+ if (forwardLoop) {
+ for (i = 0; i < contactInfo.pcount; i++) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
+ } else {
+ for (i = contactInfo.pcount - 1; i >= 0; i--) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
+ }
+ }
+
+ /**
+ *
+ * @param idx
+ * @param b1
+ * @param b2
+ * @param colInfo
+ * @param normal
+ * @param forceInelastic
+ */
+ private function resolveContactPoint(idx:int, b1:Body, b2:Body, contact:Contact, normal:Vector3, forceInelastic:Boolean):void {
+ var cp:ContactPoint = contact.points[idx];
+ if (!forceInelastic) cp.satisfied = true;
+
+ var newVel:Number = 0;
+ calcSepVelocity(b1, b2, cp, _v);
+ var sepVel:Number = _v.vDot(contact.normal);
+ if (forceInelastic) {
+ var minSpeVel:Number = useStaticSeparation ? 0 : cp.minSepVel;
+ if (sepVel < minSpeVel) cp.satisfied = false;
+ else if (cp.satisfied) return;
+ newVel = minSpeVel;
+ } else {
+ newVel = cp.normalVel;
+ }
+ var deltaVel:Number = newVel - sepVel;
+ var impulse:Number = deltaVel/cp.velByUnitImpulseN;
+ var accumImpulse:Number = cp.accumImpulseN + impulse;
+ if (accumImpulse < 0) accumImpulse = 0;
+ var deltaImpulse:Number = accumImpulse - cp.accumImpulseN;
+ cp.accumImpulseN = accumImpulse;
+ // Применяем импульс к телам
+ if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, normal, deltaImpulse);
+ if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, normal, -deltaImpulse);
+
+ // Учёт силы трения
+ calcSepVelocity(b1, b2, cp, _v);
+ // Расчитываем изменение касательной скорости на единицу касательного импульса
+ var tanSpeedByUnitImpulse:Number = 0;
+ _v.vAddScaled(-_v.vDot(contact.normal), contact.normal);
+ var tanSpeed:Number = _v.vLength();
+ if (tanSpeed < 0.001) return;
+ _t.vCopy(_v).vNormalize().vReverse();
+ // dV = b.invMass + ((invI * (r % t)) % r) * t
+ if (b1.movable) {
+ _v.vCross2(cp.r1, _t).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1);
+ tanSpeedByUnitImpulse += b1.invMass + _v.vDot(_t);
+ }
+ if (b2 != null && b2.movable) {
+ _v.vCross2(cp.r2, _t).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2);
+ tanSpeedByUnitImpulse += b2.invMass + _v.vDot(_t);
+ }
+
+ var tanImpulse:Number = tanSpeed/tanSpeedByUnitImpulse;
+ var max:Number = contact.friction*cp.accumImpulseN;
+ if (max < 0) {
+ if (tanImpulse < max) tanImpulse = max;
+ } else {
+ if (tanImpulse > max) tanImpulse = max;
+ }
+
+ // Применяем импульс к телам
+ if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, _t, tanImpulse);
+ if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, _t, -tanImpulse);
+ }
+
+ /**
+ *
+ * @param cp
+ * @param normal
+ * @return
+ */
+ private function calcSepVelocity(body1:Body, body2:Body, cp:ContactPoint, result:Vector3):void {
+ // sepVel = (V1 - V2)*normal
+ // V1 = V1_c + w1%r1
+ result.vCopy(body1.state.velocity).vAdd(_v1.vCross2(body1.state.rotation, cp.r1));
+ // V2 = V2_c + w2%r2
+ if (body2 != null) result.vSubtract(body2.state.velocity).vSubtract(_v2.vCross2(body2.state.rotation, cp.r2));
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ private function intergateVelocities(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ (bodies[i] as Body).integrateVelocity(dt);
+ }
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ private function integratePositions(dt:Number):void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i] as Body;
+ if (body.movable) body.integratePosition(dt);
+ }
+ }
+
+ /**
+ *
+ */
+ private function performStaticSeparation():void {
+ var iterNum:int = staticSeparationIterations;
+// iterNum = 100;
+
+ // 1. В начале каждой итерации для всех контактов сбрасывается флаг satisfied с одновременным поиском наихудшего контакта, т.е. имеющего наибольшую величину пересечения тел.
+ // 2. Если найденный контакт имеет величину пересечения меньше предельно допустимой процедура прерывается, т.к. разделение контактов не требуется.
+ // 3. Если процедура продолжается, то выполняется разделение найденного контакта и установка его флага satisfied в true, после чего запускается внутренний цикл, состоящий
+ // из contactsNum - 1 итераций. На каждой итерации ищется наихудший контакт среди оставшихся (satisfied == false). Если такой контакт найден, то выполняется его разделение
+ // и переход к следующей итерации внутреннего цикла, иначе внутренний цикл прерывается и выполняется переход к следующей итерации внешнего цикла (шаг 1.).
+ for (var iter:int = 0; iter < iterNum; iter++) {
+ // Ищем контакт с максимальной величиной пересечения тел, одновременно сбрасывая флаги satisfied в false
+ var worstContact:Contact = contacts[0];
+ var i:int;
+ for (i = 1; i < contactsNum; i++) {
+ var contact:Contact = contacts[i];
+ contact.satisfied = false;
+ if (contact.maxPenetration > worstContact.maxPenetration) worstContact = contact;
+ }
+ if (worstContact.maxPenetration <= allowedPenetration) return;
+
+ resolveInterpenetration(worstContact);
+ // Внутренний цикл по оставшимся контактам
+ for (i = 1; i < contactsNum; i++) {
+ worstContact = getWorstContact();
+ if (worstContact == null) break;
+ resolveInterpenetration(worstContact);
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private function getWorstContact():Contact {
+ var maxPen:Number = 0;
+ var worst:Contact = null;
+ for (var i:int = 0; i < contactsNum; i++) {
+ var c:Contact = contacts[i];
+ if (!c.satisfied && c.maxPenetration > maxPen) {
+ worst = c;
+ maxPen = c.maxPenetration;
+ }
+ }
+ return maxPen > allowedPenetration ? worst : null;
+ }
+
+ /**
+ * Разделяет указанный контакт, выполняя staticSeparationSteps итераций по списку точек контакта.
+ * 1. В начале каждой итерации ищется точка с наибольшим пересечением, одновременно сбрасываются флаги satisfied у точек.
+ * 2. Для найденной точки выполняется процедура разделения.
+ * 3. После выполняется pcount - 1 итерация по списку точек, каждый раз ищется наихудшая среди имеющих satisfied == false, для которой выполняется процедура разделения.
+ * Если наихудшая точка не найдена, то выполнется переход к следующей итерации внешнего цикла (шаг 1).
+ *
+ * В конце процедуры разделения обновляются значения пересечений для остальных точек контакта, а также для всех прочих контактов, относящихся к телам текущего.
+ *
+ * @param contact контакт для разделения
+ */
+ private function resolveInterpenetration(contact:Contact):void {
+ contact.satisfied = true;
+
+ for (var step:int = 0; step < staticSeparationSteps; step++) {
+ var worstCp:ContactPoint = contact.points[0];
+ var cp:ContactPoint;
+ var i:int;
+ for (i = 1; i < contact.pcount; i++) {
+ cp = contact.points[i];
+ cp.satisfied = false;
+ if (cp.penetration > worstCp.penetration) worstCp = cp;
+ }
+ if (worstCp.penetration <= allowedPenetration) break;
+ separateContactPoint(worstCp, contact);
+ // Разделяем оставшиеся точки
+ var maxPen:Number = 0;
+ for (i = 1; i < contact.pcount; i++) {
+ // Поиск наихудшей точки
+ for (var j:int = 0; j < contact.pcount; j++) {
+ cp = contact.points[j];
+ if (cp.satisfied) continue;
+ if (cp.penetration > maxPen) {
+ maxPen = cp.penetration;
+ worstCp = cp;
+ }
+ }
+ if (maxPen <= allowedPenetration) break;
+ separateContactPoint(worstCp, contact);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param cp
+ * @param contact
+ */
+ private function separateContactPoint(cp:ContactPoint, contact:Contact):void {
+ cp.satisfied = true;
+
+ var b1:Body = contact.body1;
+ var b2:Body = contact.body2;
+ var totalMove:Number = cp.penetration - allowedPenetration;
+ var moveCoeff:Number = totalMove/cp.velByUnitImpulseN;
+ var linMove1:Number;
+ var angleMove1:Number;
+ if (b1.movable) {
+ linMove1 = b1.invMass*moveCoeff;
+ angleMove1 = cp.angularInertia1*moveCoeff;
+ if (angleMove1 > maxAngleMove) {
+ linMove1 += angleMove1 - maxAngleMove;
+ angleMove1 -= maxAngleMove;
+ }
+ b1.state.pos.vAddScaled(linMove1, contact.normal);
+ _v1.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vScale(angleMove1);
+ b1.state.orientation.addScaledVector(_v1, 1);
+ }
+ var linMove2:Number;
+ var angleMove2:Number;
+ if (b2 != null && b2.movable) {
+ linMove2 = b2.invMass*moveCoeff;
+ angleMove2 = cp.angularInertia2*moveCoeff;
+ if (angleMove2 > maxAngleMove) {
+ linMove2 += angleMove2 - maxAngleMove;
+ angleMove2 -= maxAngleMove;
+ }
+ b2.state.pos.vAddScaled(-linMove2, contact.normal);
+ _v2.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vScale(angleMove2);
+ _v2.vReverse();
+ b2.state.orientation.addScaledVector(_v2, 1);
+ }
+ cp.penetration = allowedPenetration;
+ // Обновляем пересечения в других точках
+ var i:int;
+ for (i = 0; i < contact.pcount; i++) {
+ var cp1:ContactPoint = contact.points[i];
+ if (cp1 == cp) continue;
+ var angularMove:Number;
+ if (b1.movable) {
+ angularMove = _v.vCross2(_v1, cp1.r1).vDot(contact.normal);
+ cp1.penetration -= linMove1 + angularMove;
+ }
+ if (b2 != null && b2.movable) {
+ angularMove = _v.vCross2(_v2, cp1.r2).vDot(contact.normal);
+ cp1.penetration -= linMove2 - angularMove;
+ }
+ // Обновление максимального значения для контакта
+ if (cp1.penetration > contact.maxPenetration) contact.maxPenetration = cp1.penetration;
+ }
+ // Обновляем пересечения для других контактов
+ var c:Contact;
+ var j:int;
+ if (b1.movable) {
+ for (i = 0; i < b1.contactsNum; i++) {
+ c = b1.contacts[i];
+ if (c == contact) continue;
+ for (j = 0; j < c.pcount; j++) {
+ cp1 = c.points[j];
+ if (b1 == c.body1) cp1.penetration -= linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r1).vDot(c.normal);
+ else cp1.penetration += linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r2).vDot(c.normal);
+ if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
+ }
+ }
+ }
+ if (b2 != null && b2.movable) {
+ for (i = 0; i < b2.contactsNum; i++) {
+ c = b2.contacts[i];
+ if (c == contact) continue;
+ for (j = 0; j < c.pcount; j++) {
+ cp1 = c.points[j];
+ if (b2 == c.body1) cp1.penetration -= linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r1).vDot(c.normal);
+ else cp1.penetration += linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r2).vDot(c.normal);
+ if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private function postPhysics():void {
+ for (var i:int = 0; i < bodiesNum; i++) {
+ var body:Body = bodies[i];
+ body.clearAccumulators();
+ body.calcDerivedData();
+ }
+ }
+
+ /**
+ *
+ * @param dt
+ */
+ public function runPhysics(dt:Number):void {
+ timeStamp++;
+ applyForces(dt);
+ detectCollisions(dt);
+ preProcessContacts(dt);
+ processContacts(dt, false);
+ intergateVelocities(dt);
+ processContacts(dt, true);
+ integratePositions(dt);
+ postPhysics();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/all-wcprops
new file mode 100644
index 0000000..c1da71f
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/all-wcprops
@@ -0,0 +1,17 @@
+K 25
+svn:wc:ra_dav:version-url
+V 121
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/constraints
+END
+Constraint.as
+K 25
+svn:wc:ra_dav:version-url
+V 135
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/constraints/Constraint.as
+END
+MaxDistanceConstraint.as
+K 25
+svn:wc:ra_dav:version-url
+V 146
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/constraints/MaxDistanceConstraint.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/entries b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/entries
new file mode 100644
index 0000000..5f29f78
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/entries
@@ -0,0 +1,52 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/constraints
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+Constraint.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+757824f9eeb3c89243d104910e0d0529
+2009-04-23T13:43:38.903278Z
+11779
+mike
+
+MaxDistanceConstraint.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+3fdf03f90fcf4b9e1bd8046b789014b4
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/format b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/Constraint.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/Constraint.as.svn-base
new file mode 100644
index 0000000..c2cd912
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/Constraint.as.svn-base
@@ -0,0 +1,35 @@
+package alternativa.physics.rigid.constraints {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidWorld;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class Constraint {
+
+ altphysics var satisfied:Boolean;
+ altphysics var world:RigidWorld;
+
+ /**
+ *
+ */
+ public function Constraint() {
+ }
+
+ /**
+ * @param dt
+ */
+ public function preProcess(dt:Number):void {
+ }
+
+ /**
+ * @param dt
+ */
+ public function apply(dt:Number):void {
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/MaxDistanceConstraint.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/MaxDistanceConstraint.as.svn-base
new file mode 100644
index 0000000..0856660
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/.svn/text-base/MaxDistanceConstraint.as.svn-base
@@ -0,0 +1,118 @@
+package alternativa.physics.rigid.constraints {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.rigid.BodyState;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class MaxDistanceConstraint extends Constraint {
+ // Первое тело ограничения
+ altphysics var body1:Body;
+ // Второе тело ограничения. Может быть null.
+ altphysics var body2:Body;
+ // Первая точка крепления в системе координат первого тела
+ altphysics var r1:Vector3;
+ // Вторая точка привязки в системе координат второго тела. В случае отсутствия второго тела, координаты задаются в мировой системе.
+ altphysics var r2:Vector3;
+ // Максимально допустимое расстояние между точками крепления
+ altphysics var maxDistance:Number;
+ // Радиус-вектор первой точки крепления, трансформированный без смещения в мировую систему координат
+ altphysics var wr1:Vector3 = new Vector3();
+ // Радиус-вектор второй точки крепления, трансформированный без смещения в мировую систему координат
+ altphysics var wr2:Vector3 = new Vector3();
+ // Минимальная скорость сближения для удовлетворения ограничения
+ private var minClosingVel:Number;
+ // Изменение относительной скорости на единицу импульса
+ private var velByUnitImpulseN:Number;
+ // Направление корректирующего импульса
+ private var impulseDirection:Vector3 = new Vector3();
+
+ // Вспомогательные переменные
+ private static var _v:Vector3 = new Vector3();
+ private static var _v1:Vector3 = new Vector3();
+ private static var _v2:Vector3 = new Vector3();
+
+ /**
+ * @param body1
+ * @param body2
+ * @param r1
+ * @param r2
+ * @param maxDistance
+ */
+ public function MaxDistanceConstraint(body1:Body, body2:Body, r1:Vector3, r2:Vector3, maxDistance:Number) {
+ super();
+ this.body1 = body1;
+ this.body2 = body2;
+ this.r1 = r1.vClone();
+ this.r2 = r2.vClone();
+ this.maxDistance = maxDistance;
+ }
+
+ /**
+ * @param dt
+ */
+ override public function preProcess(dt:Number):void {
+ // Вычислим расстояние между точками
+ body1.baseMatrix.transformVector(r1, wr1);
+ if (body2 != null) body2.baseMatrix.transformVector(r2, wr2);
+ else wr2.vCopy(r2);
+ var p1:Vector3 = body1.state.pos;
+ impulseDirection.x = wr2.x - wr1.x - p1.x;
+ impulseDirection.y = wr2.y - wr1.y - p1.y;
+ impulseDirection.z = wr2.z - wr1.z - p1.z;
+ if (body2 != null) {
+ var p2:Vector3 = body2.state.pos;
+ impulseDirection.x += p2.x;
+ impulseDirection.y += p2.y;
+ impulseDirection.z += p2.z;
+ }
+
+ var delta:Number = impulseDirection.vLength() - maxDistance;
+ if (delta > 0) {
+ satisfied = false;
+ impulseDirection.vNormalize();
+ minClosingVel = delta/(world.penResolutionSteps*dt);
+ if (minClosingVel > world.maxPenResolutionSpeed) minClosingVel = world.maxPenResolutionSpeed;
+ // Расчитываем изменение нормальной скорости на единицу нормального импульса
+ // dV = b.invMass + ((invI * (r % n)) % r) * n
+ velByUnitImpulseN = 0;
+ if (body1.movable) velByUnitImpulseN += body1.invMass + _v.vCross2(wr1, impulseDirection).vTransformBy3(body1.invInertiaWorld).vCross(wr1).vDot(impulseDirection);
+ if (body2 != null && body2.movable) velByUnitImpulseN += body2.invMass + _v.vCross2(wr2, impulseDirection).vTransformBy3(body2.invInertiaWorld).vCross(wr2).vDot(impulseDirection);
+ } else satisfied = true;
+ }
+
+ /**
+ * @param dt
+ */
+ override public function apply(dt:Number):void {
+ if (satisfied) return;
+ // Расчитываем проекцию скорости относительной скорости
+ // sepVel = (V1 - V2)*normal
+ // V1 = V1_c + w1%r1
+ var state:BodyState = body1.state;
+ _v1.vCross2(state.rotation, wr1);
+ _v.x = state.velocity.x + _v1.x;
+ _v.y = state.velocity.y + _v1.y;
+ _v.z = state.velocity.z + _v1.z;
+ // V2 = V2_c + w2%r2
+ if (body2 != null) {
+ state = body2.state;
+ _v2.vCross2(state.rotation, wr2);
+ _v.x -= state.velocity.x - _v2.x;
+ _v.y -= state.velocity.y - _v2.y;
+ _v.z -= state.velocity.z - _v2.z;
+ }
+ var closingVel:Number = _v.x*impulseDirection.x + _v.y*impulseDirection.y + _v.z*impulseDirection.z;
+ if (closingVel > minClosingVel) return;
+ var impulse:Number = (minClosingVel - closingVel)/velByUnitImpulseN;
+ if (body1.movable) body1.applyRelPosWorldImpulse(wr1, impulseDirection, impulse);
+ if (body2 != null && body2.movable) body2.applyRelPosWorldImpulse(wr2, impulseDirection, -impulse);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/Constraint.as b/0.0.1.0/src/alternativa/physics/rigid/constraints/Constraint.as
new file mode 100644
index 0000000..c2cd912
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/Constraint.as
@@ -0,0 +1,35 @@
+package alternativa.physics.rigid.constraints {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.RigidWorld;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class Constraint {
+
+ altphysics var satisfied:Boolean;
+ altphysics var world:RigidWorld;
+
+ /**
+ *
+ */
+ public function Constraint() {
+ }
+
+ /**
+ * @param dt
+ */
+ public function preProcess(dt:Number):void {
+ }
+
+ /**
+ * @param dt
+ */
+ public function apply(dt:Number):void {
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/constraints/MaxDistanceConstraint.as b/0.0.1.0/src/alternativa/physics/rigid/constraints/MaxDistanceConstraint.as
new file mode 100644
index 0000000..0856660
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/constraints/MaxDistanceConstraint.as
@@ -0,0 +1,118 @@
+package alternativa.physics.rigid.constraints {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.rigid.BodyState;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class MaxDistanceConstraint extends Constraint {
+ // Первое тело ограничения
+ altphysics var body1:Body;
+ // Второе тело ограничения. Может быть null.
+ altphysics var body2:Body;
+ // Первая точка крепления в системе координат первого тела
+ altphysics var r1:Vector3;
+ // Вторая точка привязки в системе координат второго тела. В случае отсутствия второго тела, координаты задаются в мировой системе.
+ altphysics var r2:Vector3;
+ // Максимально допустимое расстояние между точками крепления
+ altphysics var maxDistance:Number;
+ // Радиус-вектор первой точки крепления, трансформированный без смещения в мировую систему координат
+ altphysics var wr1:Vector3 = new Vector3();
+ // Радиус-вектор второй точки крепления, трансформированный без смещения в мировую систему координат
+ altphysics var wr2:Vector3 = new Vector3();
+ // Минимальная скорость сближения для удовлетворения ограничения
+ private var minClosingVel:Number;
+ // Изменение относительной скорости на единицу импульса
+ private var velByUnitImpulseN:Number;
+ // Направление корректирующего импульса
+ private var impulseDirection:Vector3 = new Vector3();
+
+ // Вспомогательные переменные
+ private static var _v:Vector3 = new Vector3();
+ private static var _v1:Vector3 = new Vector3();
+ private static var _v2:Vector3 = new Vector3();
+
+ /**
+ * @param body1
+ * @param body2
+ * @param r1
+ * @param r2
+ * @param maxDistance
+ */
+ public function MaxDistanceConstraint(body1:Body, body2:Body, r1:Vector3, r2:Vector3, maxDistance:Number) {
+ super();
+ this.body1 = body1;
+ this.body2 = body2;
+ this.r1 = r1.vClone();
+ this.r2 = r2.vClone();
+ this.maxDistance = maxDistance;
+ }
+
+ /**
+ * @param dt
+ */
+ override public function preProcess(dt:Number):void {
+ // Вычислим расстояние между точками
+ body1.baseMatrix.transformVector(r1, wr1);
+ if (body2 != null) body2.baseMatrix.transformVector(r2, wr2);
+ else wr2.vCopy(r2);
+ var p1:Vector3 = body1.state.pos;
+ impulseDirection.x = wr2.x - wr1.x - p1.x;
+ impulseDirection.y = wr2.y - wr1.y - p1.y;
+ impulseDirection.z = wr2.z - wr1.z - p1.z;
+ if (body2 != null) {
+ var p2:Vector3 = body2.state.pos;
+ impulseDirection.x += p2.x;
+ impulseDirection.y += p2.y;
+ impulseDirection.z += p2.z;
+ }
+
+ var delta:Number = impulseDirection.vLength() - maxDistance;
+ if (delta > 0) {
+ satisfied = false;
+ impulseDirection.vNormalize();
+ minClosingVel = delta/(world.penResolutionSteps*dt);
+ if (minClosingVel > world.maxPenResolutionSpeed) minClosingVel = world.maxPenResolutionSpeed;
+ // Расчитываем изменение нормальной скорости на единицу нормального импульса
+ // dV = b.invMass + ((invI * (r % n)) % r) * n
+ velByUnitImpulseN = 0;
+ if (body1.movable) velByUnitImpulseN += body1.invMass + _v.vCross2(wr1, impulseDirection).vTransformBy3(body1.invInertiaWorld).vCross(wr1).vDot(impulseDirection);
+ if (body2 != null && body2.movable) velByUnitImpulseN += body2.invMass + _v.vCross2(wr2, impulseDirection).vTransformBy3(body2.invInertiaWorld).vCross(wr2).vDot(impulseDirection);
+ } else satisfied = true;
+ }
+
+ /**
+ * @param dt
+ */
+ override public function apply(dt:Number):void {
+ if (satisfied) return;
+ // Расчитываем проекцию скорости относительной скорости
+ // sepVel = (V1 - V2)*normal
+ // V1 = V1_c + w1%r1
+ var state:BodyState = body1.state;
+ _v1.vCross2(state.rotation, wr1);
+ _v.x = state.velocity.x + _v1.x;
+ _v.y = state.velocity.y + _v1.y;
+ _v.z = state.velocity.z + _v1.z;
+ // V2 = V2_c + w2%r2
+ if (body2 != null) {
+ state = body2.state;
+ _v2.vCross2(state.rotation, wr2);
+ _v.x -= state.velocity.x - _v2.x;
+ _v.y -= state.velocity.y - _v2.y;
+ _v.z -= state.velocity.z - _v2.z;
+ }
+ var closingVel:Number = _v.x*impulseDirection.x + _v.y*impulseDirection.y + _v.z*impulseDirection.z;
+ if (closingVel > minClosingVel) return;
+ var impulse:Number = (minClosingVel - closingVel)/velByUnitImpulseN;
+ if (body1.movable) body1.applyRelPosWorldImpulse(wr1, impulseDirection, impulse);
+ if (body2 != null && body2.movable) body2.applyRelPosWorldImpulse(wr2, impulseDirection, -impulse);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/all-wcprops
new file mode 100644
index 0000000..65ac5e8
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/all-wcprops
@@ -0,0 +1,35 @@
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives
+END
+RigidPlane.as
+K 25
+svn:wc:ra_dav:version-url
+V 134
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidPlane.as
+END
+RigidSphere.as
+K 25
+svn:wc:ra_dav:version-url
+V 135
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidSphere.as
+END
+RigidBox.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidBox.as
+END
+RigidCylinder.as
+K 25
+svn:wc:ra_dav:version-url
+V 137
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidCylinder.as
+END
+RigidRect.as
+K 25
+svn:wc:ra_dav:version-url
+V 133
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidRect.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/entries b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/entries
new file mode 100644
index 0000000..46d2009
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/entries
@@ -0,0 +1,88 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/primitives
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+RigidPlane.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+c4713421e357ee1967dca9ab3ffa7290
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+RigidSphere.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+79ef38eab05657ad9627bdac4ade7271
+2009-04-23T13:43:38.903278Z
+11779
+mike
+
+RigidBox.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+da834a5d208ff30f3a74321f75301cd7
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+RigidCylinder.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+61512d31f9e211dcb0864164aa6f5210
+2009-04-23T13:43:38.903278Z
+11779
+mike
+
+RigidRect.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+4cff4e313a1f41d4ccfa6480ea78173e
+2009-04-23T13:43:38.903278Z
+11779
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/format b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidBox.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidBox.as.svn-base
new file mode 100644
index 0000000..c01ff61
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidBox.as.svn-base
@@ -0,0 +1,56 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBox extends Body {
+
+ /**
+ *
+ * @param halfSize
+ * @param mass
+ */
+ public function RigidBox(halfSize:Vector3, mass:Number) {
+ super(0, Matrix3.ZERO);
+ addCollisionPrimitive(new CollisionBox(halfSize, 1));
+ setParams(halfSize, mass);
+ }
+
+ /**
+ * @param halfSize
+ * @param mass
+ */
+ public function setParams(halfSize:Vector3, mass:Number):void {
+ /* Момент инерции бокса:
+
+ m*(hy*hy + hz*hz)/3 0 0
+ 0 m*(hz*hz + hx*hx)/3 0
+ 0 0 m*(hx*hx + hy*hy)/3
+
+ hx, hy, hz -- половина размера бокса вдоль соответствующей оси
+ */
+ invInertia.copy(Matrix3.ZERO);
+ if (mass == Infinity) invMass = 0;
+ else {
+ invMass = 1/mass;
+ var xx:Number = halfSize.x*halfSize.x;
+ var yy:Number = halfSize.y*halfSize.y;
+ var zz:Number = halfSize.z*halfSize.z;
+ invInertia.a = 3*invMass/(yy + zz);
+ invInertia.f = 3*invMass/(zz + xx);
+ invInertia.k = 3*invMass/(xx + yy);
+ }
+ var prim:CollisionBox = collisionPrimitives[0] as CollisionBox;
+ prim.hs.vCopy(halfSize);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidCylinder.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidCylinder.as.svn-base
new file mode 100644
index 0000000..0f9f2b3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidCylinder.as.svn-base
@@ -0,0 +1,32 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+
+ use namespace altphysics;
+
+ public class RigidCylinder extends Body {
+
+ altphysics var r:Number;
+ altphysics var h:Number;
+
+ public function RigidCylinder(mass:Number, radius:Number, height:Number) {
+ super(0, Matrix3.ZERO);
+ setParams(mass, radius, height);
+ }
+
+ public function setParams(mass:Number, radius:Number, height:Number):void {
+ r = radius;
+ h = height;
+ invInertiaWorld.copy(Matrix3.ZERO);
+ if (mass == Infinity) invMass = 0;
+ else {
+ invMass = 1/mass;
+ invInertia.a = invInertia.f = mass*(h*h/12 + r*r/4);
+ invInertia.k = 0.5*mass*r*r;
+ invInertia.invert();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidPlane.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidPlane.as.svn-base
new file mode 100644
index 0000000..26f74b2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidPlane.as.svn-base
@@ -0,0 +1,23 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ public class RigidPlane extends Body {
+
+ public var normal:Vector3 = new Vector3();
+ public var offset:Number;
+
+ public function RigidPlane() {
+ super(0, Matrix3.ZERO);
+ this.normal.vCopy(Vector3.Z_AXIS);
+ this.offset = 0;
+ movable = false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidRect.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidRect.as.svn-base
new file mode 100644
index 0000000..3c3913d
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidRect.as.svn-base
@@ -0,0 +1,20 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ public class RigidRect extends Body {
+
+ public function RigidRect(halfWidth:Number, halfLength:Number) {
+ super(0, Matrix3.ZERO);
+ movable = false;
+ addCollisionPrimitive(new CollisionRect(new Vector3(halfWidth, halfLength, 0), 1));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidSphere.as.svn-base b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidSphere.as.svn-base
new file mode 100644
index 0000000..9668716
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/.svn/text-base/RigidSphere.as.svn-base
@@ -0,0 +1,29 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+
+ use namespace altphysics;
+
+ public class RigidSphere extends Body {
+
+ public function RigidSphere(radius:Number, mass:Number) {
+ var m:Matrix3;
+ if (mass == Infinity) {
+ mass = 0;
+ m = Matrix3.ZERO;
+ } else {
+ mass = 1/mass;
+ var r2:Number = radius*radius;
+ m = new Matrix3();
+ m.a = m.f = m.k = 2.5*mass/r2;
+ }
+ super(mass, m);
+ if (mass == 0) movable = false;
+ addCollisionPrimitive(new CollisionSphere(radius, 1));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidBox.as b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidBox.as
new file mode 100644
index 0000000..c01ff61
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidBox.as
@@ -0,0 +1,56 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ *
+ */
+ public class RigidBox extends Body {
+
+ /**
+ *
+ * @param halfSize
+ * @param mass
+ */
+ public function RigidBox(halfSize:Vector3, mass:Number) {
+ super(0, Matrix3.ZERO);
+ addCollisionPrimitive(new CollisionBox(halfSize, 1));
+ setParams(halfSize, mass);
+ }
+
+ /**
+ * @param halfSize
+ * @param mass
+ */
+ public function setParams(halfSize:Vector3, mass:Number):void {
+ /* Момент инерции бокса:
+
+ m*(hy*hy + hz*hz)/3 0 0
+ 0 m*(hz*hz + hx*hx)/3 0
+ 0 0 m*(hx*hx + hy*hy)/3
+
+ hx, hy, hz -- половина размера бокса вдоль соответствующей оси
+ */
+ invInertia.copy(Matrix3.ZERO);
+ if (mass == Infinity) invMass = 0;
+ else {
+ invMass = 1/mass;
+ var xx:Number = halfSize.x*halfSize.x;
+ var yy:Number = halfSize.y*halfSize.y;
+ var zz:Number = halfSize.z*halfSize.z;
+ invInertia.a = 3*invMass/(yy + zz);
+ invInertia.f = 3*invMass/(zz + xx);
+ invInertia.k = 3*invMass/(xx + yy);
+ }
+ var prim:CollisionBox = collisionPrimitives[0] as CollisionBox;
+ prim.hs.vCopy(halfSize);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidCylinder.as b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidCylinder.as
new file mode 100644
index 0000000..0f9f2b3
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidCylinder.as
@@ -0,0 +1,32 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+
+ use namespace altphysics;
+
+ public class RigidCylinder extends Body {
+
+ altphysics var r:Number;
+ altphysics var h:Number;
+
+ public function RigidCylinder(mass:Number, radius:Number, height:Number) {
+ super(0, Matrix3.ZERO);
+ setParams(mass, radius, height);
+ }
+
+ public function setParams(mass:Number, radius:Number, height:Number):void {
+ r = radius;
+ h = height;
+ invInertiaWorld.copy(Matrix3.ZERO);
+ if (mass == Infinity) invMass = 0;
+ else {
+ invMass = 1/mass;
+ invInertia.a = invInertia.f = mass*(h*h/12 + r*r/4);
+ invInertia.k = 0.5*mass*r*r;
+ invInertia.invert();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidPlane.as b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidPlane.as
new file mode 100644
index 0000000..26f74b2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidPlane.as
@@ -0,0 +1,23 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ public class RigidPlane extends Body {
+
+ public var normal:Vector3 = new Vector3();
+ public var offset:Number;
+
+ public function RigidPlane() {
+ super(0, Matrix3.ZERO);
+ this.normal.vCopy(Vector3.Z_AXIS);
+ this.offset = 0;
+ movable = false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidRect.as b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidRect.as
new file mode 100644
index 0000000..3c3913d
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidRect.as
@@ -0,0 +1,20 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ public class RigidRect extends Body {
+
+ public function RigidRect(halfWidth:Number, halfLength:Number) {
+ super(0, Matrix3.ZERO);
+ movable = false;
+ addCollisionPrimitive(new CollisionRect(new Vector3(halfWidth, halfLength, 0), 1));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidSphere.as b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidSphere.as
new file mode 100644
index 0000000..9668716
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/rigid/primitives/RigidSphere.as
@@ -0,0 +1,29 @@
+package alternativa.physics.rigid.primitives {
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Body;
+ import alternativa.physics.types.Matrix3;
+
+ use namespace altphysics;
+
+ public class RigidSphere extends Body {
+
+ public function RigidSphere(radius:Number, mass:Number) {
+ var m:Matrix3;
+ if (mass == Infinity) {
+ mass = 0;
+ m = Matrix3.ZERO;
+ } else {
+ mass = 1/mass;
+ var r2:Number = radius*radius;
+ m = new Matrix3();
+ m.a = m.f = m.k = 2.5*mass/r2;
+ }
+ super(mass, m);
+ if (mass == 0) movable = false;
+ addCollisionPrimitive(new CollisionSphere(radius, 1));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/all-wcprops b/0.0.1.0/src/alternativa/physics/types/.svn/all-wcprops
new file mode 100644
index 0000000..a1c96cb
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 109
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types
+END
+Quaternion.as
+K 25
+svn:wc:ra_dav:version-url
+V 123
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types/Quaternion.as
+END
+Vector3.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types/Vector3.as
+END
+Matrix3.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types/Matrix3.as
+END
+Matrix4.as
+K 25
+svn:wc:ra_dav:version-url
+V 120
+/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types/Matrix4.as
+END
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/entries b/0.0.1.0/src/alternativa/physics/types/.svn/entries
new file mode 100644
index 0000000..13dbfab
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/entries
@@ -0,0 +1,76 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/types
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+Quaternion.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+595f36fdfea8e82e752bb989e97e3a9a
+2009-05-04T04:55:56.466789Z
+12306
+mike
+
+Vector3.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+c324eee86307bbf468a629c56a46bce4
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+Matrix3.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+2e95a69258123653d92296f40390c112
+2009-05-26T08:55:28.854881Z
+13390
+mike
+
+Matrix4.as
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+b2ed6277a2052c2568a1b4f19c469223
+2009-05-04T04:55:56.466789Z
+12306
+mike
+
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/format b/0.0.1.0/src/alternativa/physics/types/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix3.as.svn-base b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix3.as.svn-base
new file mode 100644
index 0000000..86885d7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix3.as.svn-base
@@ -0,0 +1,367 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ /**
+ *
+ */
+ public class Matrix3 {
+
+ public static const ZERO:Matrix3 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ public static const IDENTITY:Matrix3 = new Matrix3();
+
+ public var a:Number;
+ public var b:Number;
+ public var c:Number;
+
+ public var e:Number;
+ public var f:Number;
+ public var g:Number;
+
+ public var i:Number;
+ public var j:Number;
+ public var k:Number;
+
+ /**
+ *
+ * @param a
+ * @param b
+ * @param c
+ * @param e
+ * @param f
+ * @param g
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function Matrix3(a:Number = 1, b:Number = 0, c:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+
+ this.e = e;
+ this.f = f;
+ this.g = g;
+
+ this.i = i;
+ this.j = j;
+ this.k = k;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toIdentity():Matrix3 {
+ a = f = k = 1;
+ b = c = e = g = i = j = 0;
+ return this;
+ }
+
+ /**
+ * Преобразование матрицы в обратную.
+ */
+ public function invert():Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ var det:Number = 1/(-cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk);
+
+ a = (ff*kk - gg*jj)*det;
+ b = (cc*jj - bb*kk)*det;
+ c = (bb*gg - cc*ff)*det;
+ e = (gg*ii - ee*kk)*det;
+ f = (aa*kk - cc*ii)*det;
+ g = (cc*ee - aa*gg)*det;
+ i = (ee*jj - ff*ii)*det;
+ j = (bb*ii - aa*jj)*det;
+ k = (aa*ff - bb*ee)*det;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу справа: M * this
+ *
+ * @param m левый операнд умножения
+ */
+ public function append(m:Matrix3):Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ a = m.a*aa + m.b*ee + m.c*ii;
+ b = m.a*bb + m.b*ff + m.c*jj;
+ c = m.a*cc + m.b*gg + m.c*kk;
+ e = m.e*aa + m.f*ee + m.g*ii;
+ f = m.e*bb + m.f*ff + m.g*jj;
+ g = m.e*cc + m.f*gg + m.g*kk;
+ i = m.i*aa + m.j*ee + m.k*ii;
+ j = m.i*bb + m.j*ff + m.k*jj;
+ k = m.i*cc + m.j*gg + m.k*kk;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу слева: this * M
+ *
+ * @param matrix правый операнд умножения
+ */
+ public function prepend(m:Matrix3):Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ a = aa*m.a + bb*m.e + cc*m.i;
+ b = aa*m.b + bb*m.f + cc*m.j;
+ c = aa*m.c + bb*m.g + cc*m.k;
+ e = ee*m.a + ff*m.e + gg*m.i;
+ f = ee*m.b + ff*m.f + gg*m.j;
+ g = ee*m.c + ff*m.g + gg*m.k;
+ i = ii*m.a + jj*m.e + kk*m.i;
+ j = ii*m.b + jj*m.f + kk*m.j;
+ k = ii*m.c + jj*m.g + kk*m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function add(m:Matrix3):Matrix3 {
+ a += m.a;
+ b += m.b;
+ c += m.c;
+ e += m.e;
+ f += m.f;
+ g += m.g;
+ i += m.i;
+ j += m.j;
+ k += m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function subtract(m:Matrix3):Matrix3 {
+ a -= m.a;
+ b -= m.b;
+ c -= m.c;
+ e -= m.e;
+ f -= m.f;
+ g -= m.g;
+ i -= m.i;
+ j -= m.j;
+ k -= m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function transpose():Matrix3 {
+ var tmp:Number = b;
+ b = e;
+ e = tmp;
+ tmp = c;
+ c = i;
+ i = tmp;
+ tmp = g;
+ g = j;
+ j = tmp;
+
+ return this;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z;
+ vout.y = e*vin.x + f*vin.y + g*vin.z;
+ vout.z = i*vin.x + j*vin.y + k*vin.z;
+ }
+
+ /**
+ * Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + e*vin.y + i*vin.z;
+ vout.y = b*vin.x + f*vin.y + j*vin.z;
+ vout.z = c*vin.x + g*vin.y + k*vin.z;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector3To3D(vin:Vector3, vout:Vector3D):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z;
+ vout.y = e*vin.x + f*vin.y + g*vin.z;
+ vout.z = i*vin.x + j*vin.y + k*vin.z;
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function createSkewSymmetric(v:Vector3):Matrix3 {
+ a = f = k = 0;
+ b = -v.z;
+ c = v.y;
+ e = v.z;
+ g = -v.x;
+ i = -v.y;
+ j = v.x;
+ return this;
+ }
+
+ /**
+ *
+ * @param i
+ * @param axis
+ */
+ public function getAxis(i:int, axis:Vector3):void {
+ switch (i) {
+ case 0:
+ axis.x = a;
+ axis.y = e;
+ axis.z = i;
+ return;
+ case 1:
+ axis.x = b;
+ axis.y = f;
+ axis.z = j;
+ return;
+ case 2:
+ axis.x = c;
+ axis.y = g;
+ axis.z = k;
+ return;
+ }
+ }
+
+ /**
+ * Копирование значений указанной матрицы.
+ *
+ * @param matrix матрица, значения которой копируются
+ */
+ public function copy(m:Matrix3):Matrix3 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ public function setRotationMatrix(rx:Number, ry:Number, rz:Number):Matrix3 {
+ var cosX:Number = Math.cos(rx);
+ var sinX:Number = Math.sin(rx);
+ var cosY:Number = Math.cos(ry);
+ var sinY:Number = Math.sin(ry);
+ var cosZ:Number = Math.cos(rz);
+ var sinZ:Number = Math.sin(rz);
+
+ var cosZsinY:Number = cosZ*sinY;
+ var sinZsinY:Number = sinZ*sinY;
+
+ a = cosZ*cosY;
+ b = cosZsinY*sinX - sinZ*cosX;
+ c = cosZsinY*cosX + sinZ*sinX;
+
+ e = sinZ*cosY;
+ f = sinZsinY*sinX + cosZ*cosX;
+ g = sinZsinY*cosX - cosZ*sinX;
+
+ i = -sinY;
+ j = cosY*sinX;
+ k = cosY*cosX;
+
+ return this;
+ }
+
+ /**
+ * Метод формирует матрицу поворота на заданный угол относительно заданной оси.
+ *
+ * @param axis нормализованный вектор, задающий ось, относительно которой выполняется поворот
+ * @param angle угол поворота в радианах
+ */
+ public function fromAxisAngle(axis:Vector3, angle:Number):void {
+ var c1:Number = Math.cos(angle);
+ var s:Number = Math.sin(angle);
+ var t:Number = 1 - c1;
+ var x:Number = axis.x;
+ var y:Number = axis.y;
+ var z:Number = axis.z;
+
+ a = t*x*x + c1;
+ b = t*x*y - z*s;
+ c = t*x*z + y*s;
+
+ e = t*x*y + z*s;
+ f = t*y*y + c1;
+ g = t*y*z - x*s;
+
+ i = t*x*z - y*s;
+ j = t*y*z + x*s;
+ k = t*z*z + c1;
+ }
+
+ /**
+ * Клонирование матрицы.
+ *
+ * @return клон матрицы
+ */
+ public function clone():Matrix3 {
+ return new Matrix3(a, b, c, e, f, g, i, j, k);
+ }
+
+ public function toString():String {
+ return "[Matrix3 (" + a + ", " + b + ", " + c + "), (" + e + ", " + f + ", " + g + "), (" + i + ", " + j + ", " + k + ")]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix4.as.svn-base b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix4.as.svn-base
new file mode 100644
index 0000000..8cc23b2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Matrix4.as.svn-base
@@ -0,0 +1,402 @@
+package alternativa.physics.types {
+ import __AS3__.vec.Vector;
+
+ /**
+ *
+ */
+ public class Matrix4 {
+
+ public var a:Number;
+ public var b:Number;
+ public var c:Number;
+ public var d:Number;
+
+ public var e:Number;
+ public var f:Number;
+ public var g:Number;
+ public var h:Number;
+
+ public var i:Number;
+ public var j:Number;
+ public var k:Number;
+ public var l:Number;
+
+ /**
+ *
+ * @param a
+ * @param b
+ * @param c
+ * @param e
+ * @param f
+ * @param g
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function Matrix4(a:Number = 1, b:Number = 0, c:Number = 0, d:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, h:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1, l:Number = 0) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = h;
+
+ this.e = e;
+ this.f = f;
+ this.g = g;
+ this.h = h;
+
+ this.i = i;
+ this.j = j;
+ this.k = k;
+ this.l = l;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toIdentity():Matrix4 {
+ a = f = k = 1;
+ b = c = e = g = i = j = d = h = l = 0;
+ return this;
+ }
+
+ /**
+ * Преобразование матрицы в обратную.
+ */
+ public function invert():Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ var det:Number = -cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk;
+
+ a = (-gg*jj + ff*kk)/det;
+ b = (cc*jj - bb*kk)/det;
+ c = (-cc*ff + bb*gg)/det;
+ d = (dd*gg*jj - cc*hh*jj - dd*ff*kk + bb*hh*kk + cc*ff*ll - bb*gg*ll)/det;
+ e = (gg*ii - ee*kk)/det;
+ f = (-cc*ii + aa*kk)/det;
+ g = (cc*ee - aa*gg)/det;
+ h = (cc*hh*ii - dd*gg*ii + dd*ee*kk - aa*hh*kk - cc*ee*ll + aa*gg*ll)/det;
+ i = (-ff*ii + ee*jj)/det;
+ j = (bb*ii - aa*jj)/det;
+ k = (-bb*ee + aa*ff)/det;
+ l = (dd*ff*ii - bb*hh*ii - dd*ee*jj + aa*hh*jj + bb*ee*ll - aa*ff*ll)/det;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу справа: M * this
+ *
+ * @param m левый операнд умножения
+ */
+ public function append(m:Matrix4):Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ a = m.a*aa + m.b*ee + m.c*ii;
+ b = m.a*bb + m.b*ff + m.c*jj;
+ c = m.a*cc + m.b*gg + m.c*kk;
+ d = m.a*dd + m.b*hh + m.c*ll + m.d;
+ e = m.e*aa + m.f*ee + m.g*ii;
+ f = m.e*bb + m.f*ff + m.g*jj;
+ g = m.e*cc + m.f*gg + m.g*kk;
+ h = m.e*dd + m.f*hh + m.g*ll + m.h;
+ i = m.i*aa + m.j*ee + m.k*ii;
+ j = m.i*bb + m.j*ff + m.k*jj;
+ k = m.i*cc + m.j*gg + m.k*kk;
+ l = m.i*dd + m.j*hh + m.k*ll + m.l;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу слева: this * M
+ *
+ * @param matrix правый операнд умножения
+ */
+ public function prepend(m:Matrix4):Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ a = aa*m.a + bb*m.e + cc*m.i;
+ b = aa*m.b + bb*m.f + cc*m.j;
+ c = aa*m.c + bb*m.g + cc*m.k;
+ d = aa*m.d + bb*m.h + cc*m.l + dd;
+ e = ee*m.a + ff*m.e + gg*m.i;
+ f = ee*m.b + ff*m.f + gg*m.j;
+ g = ee*m.c + ff*m.g + gg*m.k;
+ h = ee*m.d + ff*m.h + gg*m.l + hh;
+ i = ii*m.a + jj*m.e + kk*m.i;
+ j = ii*m.b + jj*m.f + kk*m.j;
+ k = ii*m.c + jj*m.g + kk*m.k;
+ l = ii*m.d + jj*m.h + kk*m.l + ll;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function add(m:Matrix4):Matrix4 {
+ a += m.a;
+ b += m.b;
+ c += m.c;
+ d += m.d;
+ e += m.e;
+ f += m.f;
+ g += m.g;
+ h += m.h;
+ i += m.i;
+ j += m.j;
+ k += m.k;
+ l += m.l;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function subtract(m:Matrix4):Matrix4 {
+ a -= m.a;
+ b -= m.b;
+ c -= m.c;
+ d -= m.d;
+ e -= m.e;
+ f -= m.f;
+ g -= m.g;
+ h -= m.h;
+ i -= m.i;
+ j -= m.j;
+ k -= m.k;
+ l -= m.l;
+
+ return this;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+
+ /**
+ * Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
+ var xx:Number = vin.x - d;
+ var yy:Number = vin.y - h;
+ var zz:Number = vin.z - l;
+ vout.x = a*xx + e*yy + i*zz;
+ vout.y = b*xx + f*yy + j*zz;
+ vout.z = c*xx + g*yy + k*zz;
+ }
+
+ /**
+ *
+ * @param arrin
+ * @param Vector3
+ * @param arrout
+ * @param Vector3
+ */
+ public function transformVectors(arrin:Vector., arrout:Vector.):void {
+ var len:int = arrin.length;
+ var vin:Vector3;
+ var vout:Vector3;
+ for (var idx:int = 0; idx < len; idx++) {
+ vin = arrin[idx];
+ vout = arrout[idx];
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+ }
+
+ /**
+ *
+ * @param arrin
+ * @param Vector3
+ * @param arrout
+ * @param Vector3
+ */
+ public function transformVectorsInverse(arrin:Vector., arrout:Vector.):void {
+ var len:int = arrin.length;
+ var vin:Vector3;
+ var vout:Vector3;
+ for (var idx:int = 0; idx < len; idx++) {
+ vin = arrin[idx];
+ vout = arrout[idx];
+ var xx:Number = vin.x - d;
+ var yy:Number = vin.y - h;
+ var zz:Number = vin.z - l;
+ vout.x = a*xx + e*yy + i*zz;
+ vout.y = b*xx + f*yy + j*zz;
+ vout.z = c*xx + g*yy + k*zz;
+ }
+ }
+
+ /**
+ *
+ * @param i
+ * @param axis
+ */
+ public function getAxis(idx:int, axis:Vector3):void {
+ switch (idx) {
+ case 0:
+ axis.x = a;
+ axis.y = e;
+ axis.z = i;
+ return;
+ case 1:
+ axis.x = b;
+ axis.y = f;
+ axis.z = j;
+ return;
+ case 2:
+ axis.x = c;
+ axis.y = g;
+ axis.z = k;
+ return;
+ case 3:
+ axis.x = d;
+ axis.y = h;
+ axis.z = l;
+ return;
+ }
+ }
+
+ /**
+ * Трансформирует заданный вектор без учёта смещения центра матрицы.
+ *
+ * @param pin входной вектор
+ * @param pout вектор, в который записывается результат трансформации
+ */
+ public function deltaTransformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+
+ /**
+ * Трансформирует заданный вектор без учёта смещения центра матрицы.
+ *
+ * @param pin входной вектор
+ * @param pout вектор, в который записывается результат трансформации
+ */
+ public function deltaTransformVectorInverse(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + e*vin.y + i*vin.z;
+ vout.y = b*vin.x + f*vin.y + j*vin.z;
+ vout.z = c*vin.x + g*vin.y + k*vin.z;
+ }
+
+ /**
+ * Копирование значений указанной матрицы.
+ *
+ * @param matrix матрица, значения которой копируются
+ */
+ public function copy(m:Matrix4):Matrix4 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ d = m.d;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ h = m.h;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+ l = m.l;
+
+ return this;
+ }
+
+ public function setFromMatrix3(m:Matrix3, offset:Vector3):Matrix4 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ d = offset.x;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ h = offset.y;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+ l = offset.z;
+
+ return this;
+ }
+
+ /**
+ * @param rotations
+ */
+ public function getEulerAngles(rotations:Vector3):void {
+ if (-1 < i && i < 1) {
+ rotations.x = Math.atan2(j, k);
+ rotations.y = -Math.asin(i);
+ rotations.z = Math.atan2(e, a);
+ } else {
+ rotations.x = 0;
+ rotations.y = 0.5*((i <= -1) ? Math.PI : -Math.PI);
+ rotations.z = Math.atan2(-b, f);
+ }
+ }
+
+ /**
+ * Клонирование матрицы.
+ *
+ * @return клон матрицы
+ */
+ public function clone():Matrix4 {
+ return new Matrix4(a, b, c, d, e, f, g, h, i, j, k, l);
+ }
+
+ public function toString():String {
+ return "[Matrix4 [" + a.toFixed(3) + " " + b.toFixed(3) + " " + c.toFixed(3) + " " + d.toFixed(3) + "] [" + e.toFixed(3) + " " + f.toFixed(3) + " " + g.toFixed(3) + " " + h.toFixed(3) + "] [" + i.toFixed(3) + " " + j.toFixed(3) + " " + k.toFixed(3) + " " + l.toFixed(3) + "]]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Quaternion.as.svn-base b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Quaternion.as.svn-base
new file mode 100644
index 0000000..7ad2af0
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Quaternion.as.svn-base
@@ -0,0 +1,396 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ public class Quaternion {
+
+ public var w:Number;
+ public var x:Number;
+ public var y:Number;
+ public var z:Number;
+
+ public static function multiply(q1:Quaternion, q2:Quaternion, result:Quaternion):void {
+ result.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
+ result.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y;
+ result.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z;
+ result.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x;
+ }
+
+ public static function createFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
+ var q:Quaternion = new Quaternion();
+ q.setFromAxisAngle(axis, angle);
+ return q;
+ }
+
+ public static function createFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
+ var q:Quaternion = new Quaternion();
+ q.setFromAxisAngleComponents(x, y, z, angle);
+ return q;
+ }
+
+ public function Quaternion(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0) {
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public function reset(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0):Quaternion {
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ public function normalize():Quaternion {
+ var d:Number = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ * Умножает на указанный кватернион слева: this * q
+ *
+ * @param q множитель
+ */
+ public function prepend(q:Quaternion):Quaternion {
+ var ww:Number = w*q.w - x*q.x - y*q.y - z*q.z;
+ var xx:Number = w*q.x + x*q.w + y*q.z - z*q.y;
+ var yy:Number = w*q.y + y*q.w + z*q.x - x*q.z;
+ var zz:Number = w*q.z + z*q.w + x*q.y - y*q.x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Умножает на указанный кватернион справа: q * this
+ *
+ * @param q множитель
+ */
+ public function append(q:Quaternion):Quaternion {
+ var ww:Number = q.w*w - q.x*x - q.y*y - q.z*z;
+ var xx:Number = q.w*x + q.x*w + q.y*z - q.z*y;
+ var yy:Number = q.w*y + q.y*w + q.z*x - q.x*z;
+ var zz:Number = q.w*z + q.z*w + q.x*y - q.y*x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param vector
+ */
+ public function rotateByVector(v:Vector3):Quaternion {
+ var ww:Number = -v.x*x - v.y*y - v.z*z;
+ var xx:Number = v.x*w + v.y*z - v.z*y;
+ var yy:Number = v.y*w + v.z*x - v.x*z;
+ var zz:Number = v.z*w + v.x*y - v.y*x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Добавляет вращение, приданное вектором угловой скорости за указанное время.
+ *
+ * @param v
+ * @param scale
+ */
+ public function addScaledVector(v:Vector3, scale:Number):Quaternion {
+ var vx:Number = v.x*scale;
+ var vy:Number = v.y*scale;
+ var vz:Number = v.z*scale;
+ var ww:Number = -x*vx - y*vy - z*vz;
+ var xx:Number = vx*w + vy*z - vz*y;
+ var yy:Number = vy*w + vz*x - vx*z;
+ var zz:Number = vz*w + vx*y - vy*x;
+ w += 0.5*ww;
+ x += 0.5*xx;
+ y += 0.5*yy;
+ z += 0.5*zz;
+ // inlined normalize
+ var d:Number = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ public function toMatrix3(m:Matrix3):Quaternion {
+ var xx2:Number = 2*x*x;
+ var yy2:Number = 2*y*y;
+ var zz2:Number = 2*z*z;
+ var xy2:Number = 2*x*y;
+ var yz2:Number = 2*y*z;
+ var zx2:Number = 2*z*x;
+ var wx2:Number = 2*w*x;
+ var wy2:Number = 2*w*y;
+ var wz2:Number = 2*w*z;
+
+ m.a = 1 - yy2 - zz2;
+ m.b = xy2 - wz2;
+ m.c = zx2 + wy2;
+
+ m.e = xy2 + wz2;
+ m.f = 1 - xx2 - zz2;
+ m.g = yz2 - wx2;
+
+ m.i = zx2 - wy2;
+ m.j = yz2 + wx2;
+ m.k = 1 - xx2 - yy2;
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function length():Number {
+ return Math.sqrt(w*w + x*x + y*y + z*z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function lengthSqr():Number {
+ return w*w + x*x + y*y + z*z;
+ }
+
+ /**
+ *
+ * @param axis
+ * @param angle
+ * @return
+ */
+ public function setFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
+ w = Math.cos(0.5*angle);
+ var k:Number = Math.sin(0.5*angle)/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ x = axis.x*k;
+ y = axis.y*k;
+ z = axis.z*k;
+ return this;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param angle
+ */
+ public function setFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
+ w = Math.cos(0.5*angle);
+ var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
+ this.x = x*k;
+ this.y = y*k;
+ this.z = z*k;
+ return this;
+ }
+
+ /**
+ *
+ * @param vector
+ */
+ public function toAxisVector(v:Vector3 = null):Vector3 {
+ if (w < -1 || w > 1) {
+ normalize();
+ }
+ if (v == null) {
+ v = new Vector3();
+ }
+ if (w > -1 && w < 1) {
+ if (w == 0) {
+ v.x = x;
+ v.y = y;
+ v.z = z;
+ } else {
+ var angle:Number = 2*Math.acos(w);
+ var coeff:Number = 1/Math.sqrt(1 - w*w);
+ v.x = x*coeff*angle;
+ v.y = y*coeff*angle;
+ v.z = z*coeff*angle;
+ }
+ } else {
+ v.x = 0;
+ v.y = 0;
+ v.z = 0;
+ }
+ return v;
+ }
+
+ /**
+ *
+ * @param rotations
+ */
+ public function getEulerAngles(angles:Vector3):Vector3 {
+ var qi2:Number = 2*x*x;
+ var qj2:Number = 2*y*y;
+ var qk2:Number = 2*z*z;
+ var qij:Number = 2*x*y;
+ var qjk:Number = 2*y*z;
+ var qki:Number = 2*z*x;
+ var qri:Number = 2*w*x;
+ var qrj:Number = 2*w*y;
+ var qrk:Number = 2*w*z;
+
+ var aa:Number = 1 - qj2 - qk2;
+ var bb:Number = qij - qrk;
+ var ee:Number = qij + qrk;
+ var ff:Number = 1 - qi2 - qk2;
+ var ii:Number = qki - qrj;
+ var jj:Number = qjk + qri;
+ var kk:Number = 1 - qi2 - qj2;
+
+ if (-1 < ii && ii < 1) {
+ if (angles == null) {
+ angles = new Vector3(Math.atan2(jj, kk), -Math.asin(ii), Math.atan2(ee, aa));
+ } else {
+ angles.x = Math.atan2(jj, kk);
+ angles.y = -Math.asin(ii);
+ angles.z = Math.atan2(ee, aa);
+ }
+ } else {
+ if (angles == null) {
+ angles = new Vector3(0, 0.5*((ii <= -1) ? Math.PI : -Math.PI), Math.atan2(-bb, ff));
+ } else {
+ angles.x = 0;
+ angles.y = 0.5*((ii <= -1) ? Math.PI : -Math.PI);
+ angles.z = Math.atan2(-bb, ff);
+ }
+ }
+ return angles;
+ }
+
+ /**
+ *
+ */
+ public function conjugate():void {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ /**
+ * Выполняет линейную интерполяцию.
+ *
+ * @param q1 начало отрезка
+ * @param q2 конец отрезка
+ * @param t время, обычно задаётся в интервале [0, 1]
+ */
+ public function nlerp(q1:Quaternion, q2:Quaternion, t:Number):Quaternion {
+ var d:Number = 1 - t;
+ w = q1.w*d + q2.w*t;
+ x = q1.x*d + q2.x*t;
+ y = q1.y*d + q2.y*t;
+ z = q1.z*d + q2.z*t;
+ // inlined normalize
+ d = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param q
+ * @return
+ */
+ public function subtract(q:Quaternion):Quaternion {
+ w -= q.w;
+ x -= q.x;
+ y -= q.y;
+ z -= q.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param q1
+ * @param q2
+ * @return
+ */
+ public function diff(q1:Quaternion, q2:Quaternion):Quaternion {
+ w = q2.w - q1.w;
+ x = q2.x - q1.x;
+ y = q2.y - q1.y;
+ z = q2.z - q1.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param q
+ * @return
+ */
+ public function copy(q:Quaternion):Quaternion {
+ w = q.w;
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ return this;
+ }
+
+ /**
+ * @param reslult
+ */
+ public function toVector3D(result:Vector3D):Vector3D {
+ result.x = x;
+ result.y = y;
+ result.z = z;
+ result.w = w;
+ return result;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function clone():Quaternion {
+ return new Quaternion(w, x, y, z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toString():String {
+ return "[" + w + ", " + x + ", " + y + ", " + z + "]";
+ }
+ }
+}
diff --git a/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Vector3.as.svn-base b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Vector3.as.svn-base
new file mode 100644
index 0000000..298ddad
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/.svn/text-base/Vector3.as.svn-base
@@ -0,0 +1,331 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ /**
+ *
+ */
+ public class Vector3 extends Vector3D {
+
+ public static const ZERO:Vector3 = new Vector3(0, 0, 0);
+ public static const X_AXIS:Vector3 = new Vector3(1, 0, 0);
+ public static const Y_AXIS:Vector3 = new Vector3(0, 1, 0);
+ public static const Z_AXIS:Vector3 = new Vector3(0, 0, 1);
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function Vector3(x:Number = 0, y:Number = 0, z:Number = 0) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vLength():Number {
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vLengthSqr():Number {
+ return x*x + y*y + z*z;
+ }
+
+ /**
+ *
+ * @param length
+ * @return
+ */
+ public function vSetLength(length:Number):Vector3 {
+ var d:Number = x*x + y*y + z*z;
+ if (d == 0) {
+ x = length;
+ } else {
+ var k:Number = length/Math.sqrt(x*x + y*y + z*z);
+ x *= k;
+ y *= k;
+ z *= k;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vNormalize():Vector3 {
+ var d:Number = x*x + y*y + z*z;
+ if (d == 0) {
+ x = 1;
+ } else {
+ var k:Number = Math.sqrt(d);
+ x /= k;
+ y /= k;
+ z /= k;
+ }
+ return this;
+ }
+
+ /**
+ * Сложение координат.
+ *
+ * @param v точка, координаты которой прибавляются к собственным
+ */
+ public function vAdd(v:Vector3):Vector3 {
+ x += v.x;
+ y += v.y;
+ z += v.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param k
+ * @param v
+ * @return
+ */
+ public function vAddScaled(k:Number, v:Vector3):Vector3 {
+ x += k*v.x;
+ y += k*v.y;
+ z += k*v.z;
+ return this;
+ }
+
+ /**
+ * Вычитание координат.
+ *
+ * @param v точка, координаты которой вычитаются из собственных
+ */
+ public function vSubtract(v:Vector3):Vector3 {
+ x -= v.x;
+ y -= v.y;
+ z -= v.z;
+ return this;
+ }
+
+ /**
+ * Вычисляет сумму векторов.
+ *
+ * @param a уменьшаемый вектор
+ * @param b вычитаемый вектор
+ * @return this
+ */
+ public function vSum(a:Vector3, b:Vector3):Vector3 {
+ x = a.x + b.x;
+ y = a.y + b.y;
+ z = a.z + b.z;
+ return this;
+ }
+
+ /**
+ * Вычисление разности векторов.
+ *
+ * @param a уменьшаемый вектор
+ * @param b вычитаемый вектор
+ */
+ public function vDiff(a:Vector3, b:Vector3):Vector3 {
+ x = a.x - b.x;
+ y = a.y - b.y;
+ z = a.z - b.z;
+ return this;
+ }
+
+ /**
+ * Умножение на скаляр.
+ *
+ * @param k число, на которое умножаются координаты
+ */
+ public function vScale(k:Number):Vector3 {
+ x *= k;
+ y *= k;
+ z *= k;
+ return this;
+ }
+
+ /**
+ * Инвертирование вектора.
+ */
+ public function vReverse():Vector3 {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ /**
+ *
+ * @param v
+ * @return
+ */
+ public function vDot(v:Vector3):Number {
+ return x*v.x + y*v.y + z*v.z;
+ }
+
+ /**
+ * Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
+ *
+ * @param v
+ */
+ public function vCross(v:Vector3):Vector3 {
+ var xx:Number = y*v.z - z*v.y;
+ var yy:Number = z*v.x - x*v.z;
+ var zz:Number = x*v.y - y*v.x;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
+ *
+ * @param v
+ */
+ public function vCross2(a:Vector3, b:Vector3):Vector3 {
+ x = a.y*b.z - a.z*b.y;
+ y = a.z*b.x - a.x*b.z;
+ z = a.x*b.y - a.y*b.x;
+ return this;
+ }
+
+ /**
+ * Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
+ * M × r.
+ *
+ * @param m матрица трансформации
+ */
+ public function vTransformBy3(m:Matrix3):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz;
+ y = m.e*xx + m.f*yy + m.g*zz;
+ z = m.i*xx + m.j*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function vTransformBy3Tr(m:Matrix3):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.e*yy + m.i*zz;
+ y = m.b*xx + m.f*yy + m.j*zz;
+ z = m.c*xx + m.g*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ * Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
+ * M × r.
+ *
+ * @param m матрица трансформации
+ */
+ public function vTransformBy4(m:Matrix4):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz + m.d;
+ y = m.e*xx + m.f*yy + m.g*zz + m.h;
+ z = m.i*xx + m.j*yy + m.k*zz + m.l;
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ public function vTransformInverseBy4(m:Matrix4):Vector3 {
+ var xx:Number = x - m.d;
+ var yy:Number = y - m.h;
+ var zz:Number = z - m.l;
+ x = m.a*xx + m.e*yy + m.i*zz;
+ y = m.b*xx + m.f*yy + m.j*zz;
+ z = m.c*xx + m.g*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function vDeltaTransformBy4(m:Matrix4):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz;
+ y = m.e*xx + m.f*yy + m.g*zz;
+ z = m.i*xx + m.j*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ * Установка координат.
+ */
+ public function vReset(x:Number = 0, y:Number = 0, z:Number = 0):Vector3 {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * Копирование координат точки.
+ *
+ * @param v точка, координаты которой копируются
+ */
+ public function vCopy(v:Vector3):Vector3 {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ return this;
+ }
+
+ /**
+ * Клонирование точки.
+ *
+ * @return клонированная точка
+ */
+ public function vClone():Vector3 {
+ return new Vector3(x, y, z);
+ }
+
+ /**
+ * Копирует координаты вектора в экземпляр класса Vector3D.
+ *
+ * @param result экземпляр, в который копируются координаты
+ * @return переданный параметр
+ */
+ public function toVector3D(result:Vector3D):Vector3D {
+ result.x = x;
+ result.y = y;
+ result.z = z;
+ return result;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ public function copyFromVector3D(source:Vector3D):Vector3 {
+ x = source.x;
+ y = source.y;
+ z = source.z;
+ return this;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/Matrix3.as b/0.0.1.0/src/alternativa/physics/types/Matrix3.as
new file mode 100644
index 0000000..86885d7
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/Matrix3.as
@@ -0,0 +1,367 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ /**
+ *
+ */
+ public class Matrix3 {
+
+ public static const ZERO:Matrix3 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ public static const IDENTITY:Matrix3 = new Matrix3();
+
+ public var a:Number;
+ public var b:Number;
+ public var c:Number;
+
+ public var e:Number;
+ public var f:Number;
+ public var g:Number;
+
+ public var i:Number;
+ public var j:Number;
+ public var k:Number;
+
+ /**
+ *
+ * @param a
+ * @param b
+ * @param c
+ * @param e
+ * @param f
+ * @param g
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function Matrix3(a:Number = 1, b:Number = 0, c:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+
+ this.e = e;
+ this.f = f;
+ this.g = g;
+
+ this.i = i;
+ this.j = j;
+ this.k = k;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toIdentity():Matrix3 {
+ a = f = k = 1;
+ b = c = e = g = i = j = 0;
+ return this;
+ }
+
+ /**
+ * Преобразование матрицы в обратную.
+ */
+ public function invert():Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ var det:Number = 1/(-cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk);
+
+ a = (ff*kk - gg*jj)*det;
+ b = (cc*jj - bb*kk)*det;
+ c = (bb*gg - cc*ff)*det;
+ e = (gg*ii - ee*kk)*det;
+ f = (aa*kk - cc*ii)*det;
+ g = (cc*ee - aa*gg)*det;
+ i = (ee*jj - ff*ii)*det;
+ j = (bb*ii - aa*jj)*det;
+ k = (aa*ff - bb*ee)*det;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу справа: M * this
+ *
+ * @param m левый операнд умножения
+ */
+ public function append(m:Matrix3):Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ a = m.a*aa + m.b*ee + m.c*ii;
+ b = m.a*bb + m.b*ff + m.c*jj;
+ c = m.a*cc + m.b*gg + m.c*kk;
+ e = m.e*aa + m.f*ee + m.g*ii;
+ f = m.e*bb + m.f*ff + m.g*jj;
+ g = m.e*cc + m.f*gg + m.g*kk;
+ i = m.i*aa + m.j*ee + m.k*ii;
+ j = m.i*bb + m.j*ff + m.k*jj;
+ k = m.i*cc + m.j*gg + m.k*kk;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу слева: this * M
+ *
+ * @param matrix правый операнд умножения
+ */
+ public function prepend(m:Matrix3):Matrix3 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+
+ a = aa*m.a + bb*m.e + cc*m.i;
+ b = aa*m.b + bb*m.f + cc*m.j;
+ c = aa*m.c + bb*m.g + cc*m.k;
+ e = ee*m.a + ff*m.e + gg*m.i;
+ f = ee*m.b + ff*m.f + gg*m.j;
+ g = ee*m.c + ff*m.g + gg*m.k;
+ i = ii*m.a + jj*m.e + kk*m.i;
+ j = ii*m.b + jj*m.f + kk*m.j;
+ k = ii*m.c + jj*m.g + kk*m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function add(m:Matrix3):Matrix3 {
+ a += m.a;
+ b += m.b;
+ c += m.c;
+ e += m.e;
+ f += m.f;
+ g += m.g;
+ i += m.i;
+ j += m.j;
+ k += m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function subtract(m:Matrix3):Matrix3 {
+ a -= m.a;
+ b -= m.b;
+ c -= m.c;
+ e -= m.e;
+ f -= m.f;
+ g -= m.g;
+ i -= m.i;
+ j -= m.j;
+ k -= m.k;
+
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function transpose():Matrix3 {
+ var tmp:Number = b;
+ b = e;
+ e = tmp;
+ tmp = c;
+ c = i;
+ i = tmp;
+ tmp = g;
+ g = j;
+ j = tmp;
+
+ return this;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z;
+ vout.y = e*vin.x + f*vin.y + g*vin.z;
+ vout.z = i*vin.x + j*vin.y + k*vin.z;
+ }
+
+ /**
+ * Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + e*vin.y + i*vin.z;
+ vout.y = b*vin.x + f*vin.y + j*vin.z;
+ vout.z = c*vin.x + g*vin.y + k*vin.z;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector3To3D(vin:Vector3, vout:Vector3D):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z;
+ vout.y = e*vin.x + f*vin.y + g*vin.z;
+ vout.z = i*vin.x + j*vin.y + k*vin.z;
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function createSkewSymmetric(v:Vector3):Matrix3 {
+ a = f = k = 0;
+ b = -v.z;
+ c = v.y;
+ e = v.z;
+ g = -v.x;
+ i = -v.y;
+ j = v.x;
+ return this;
+ }
+
+ /**
+ *
+ * @param i
+ * @param axis
+ */
+ public function getAxis(i:int, axis:Vector3):void {
+ switch (i) {
+ case 0:
+ axis.x = a;
+ axis.y = e;
+ axis.z = i;
+ return;
+ case 1:
+ axis.x = b;
+ axis.y = f;
+ axis.z = j;
+ return;
+ case 2:
+ axis.x = c;
+ axis.y = g;
+ axis.z = k;
+ return;
+ }
+ }
+
+ /**
+ * Копирование значений указанной матрицы.
+ *
+ * @param matrix матрица, значения которой копируются
+ */
+ public function copy(m:Matrix3):Matrix3 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+
+ return this;
+ }
+
+ /**
+ * @return
+ */
+ public function setRotationMatrix(rx:Number, ry:Number, rz:Number):Matrix3 {
+ var cosX:Number = Math.cos(rx);
+ var sinX:Number = Math.sin(rx);
+ var cosY:Number = Math.cos(ry);
+ var sinY:Number = Math.sin(ry);
+ var cosZ:Number = Math.cos(rz);
+ var sinZ:Number = Math.sin(rz);
+
+ var cosZsinY:Number = cosZ*sinY;
+ var sinZsinY:Number = sinZ*sinY;
+
+ a = cosZ*cosY;
+ b = cosZsinY*sinX - sinZ*cosX;
+ c = cosZsinY*cosX + sinZ*sinX;
+
+ e = sinZ*cosY;
+ f = sinZsinY*sinX + cosZ*cosX;
+ g = sinZsinY*cosX - cosZ*sinX;
+
+ i = -sinY;
+ j = cosY*sinX;
+ k = cosY*cosX;
+
+ return this;
+ }
+
+ /**
+ * Метод формирует матрицу поворота на заданный угол относительно заданной оси.
+ *
+ * @param axis нормализованный вектор, задающий ось, относительно которой выполняется поворот
+ * @param angle угол поворота в радианах
+ */
+ public function fromAxisAngle(axis:Vector3, angle:Number):void {
+ var c1:Number = Math.cos(angle);
+ var s:Number = Math.sin(angle);
+ var t:Number = 1 - c1;
+ var x:Number = axis.x;
+ var y:Number = axis.y;
+ var z:Number = axis.z;
+
+ a = t*x*x + c1;
+ b = t*x*y - z*s;
+ c = t*x*z + y*s;
+
+ e = t*x*y + z*s;
+ f = t*y*y + c1;
+ g = t*y*z - x*s;
+
+ i = t*x*z - y*s;
+ j = t*y*z + x*s;
+ k = t*z*z + c1;
+ }
+
+ /**
+ * Клонирование матрицы.
+ *
+ * @return клон матрицы
+ */
+ public function clone():Matrix3 {
+ return new Matrix3(a, b, c, e, f, g, i, j, k);
+ }
+
+ public function toString():String {
+ return "[Matrix3 (" + a + ", " + b + ", " + c + "), (" + e + ", " + f + ", " + g + "), (" + i + ", " + j + ", " + k + ")]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/Matrix4.as b/0.0.1.0/src/alternativa/physics/types/Matrix4.as
new file mode 100644
index 0000000..8cc23b2
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/Matrix4.as
@@ -0,0 +1,402 @@
+package alternativa.physics.types {
+ import __AS3__.vec.Vector;
+
+ /**
+ *
+ */
+ public class Matrix4 {
+
+ public var a:Number;
+ public var b:Number;
+ public var c:Number;
+ public var d:Number;
+
+ public var e:Number;
+ public var f:Number;
+ public var g:Number;
+ public var h:Number;
+
+ public var i:Number;
+ public var j:Number;
+ public var k:Number;
+ public var l:Number;
+
+ /**
+ *
+ * @param a
+ * @param b
+ * @param c
+ * @param e
+ * @param f
+ * @param g
+ * @param i
+ * @param j
+ * @param k
+ */
+ public function Matrix4(a:Number = 1, b:Number = 0, c:Number = 0, d:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, h:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1, l:Number = 0) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = h;
+
+ this.e = e;
+ this.f = f;
+ this.g = g;
+ this.h = h;
+
+ this.i = i;
+ this.j = j;
+ this.k = k;
+ this.l = l;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toIdentity():Matrix4 {
+ a = f = k = 1;
+ b = c = e = g = i = j = d = h = l = 0;
+ return this;
+ }
+
+ /**
+ * Преобразование матрицы в обратную.
+ */
+ public function invert():Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ var det:Number = -cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk;
+
+ a = (-gg*jj + ff*kk)/det;
+ b = (cc*jj - bb*kk)/det;
+ c = (-cc*ff + bb*gg)/det;
+ d = (dd*gg*jj - cc*hh*jj - dd*ff*kk + bb*hh*kk + cc*ff*ll - bb*gg*ll)/det;
+ e = (gg*ii - ee*kk)/det;
+ f = (-cc*ii + aa*kk)/det;
+ g = (cc*ee - aa*gg)/det;
+ h = (cc*hh*ii - dd*gg*ii + dd*ee*kk - aa*hh*kk - cc*ee*ll + aa*gg*ll)/det;
+ i = (-ff*ii + ee*jj)/det;
+ j = (bb*ii - aa*jj)/det;
+ k = (-bb*ee + aa*ff)/det;
+ l = (dd*ff*ii - bb*hh*ii - dd*ee*jj + aa*hh*jj + bb*ee*ll - aa*ff*ll)/det;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу справа: M * this
+ *
+ * @param m левый операнд умножения
+ */
+ public function append(m:Matrix4):Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ a = m.a*aa + m.b*ee + m.c*ii;
+ b = m.a*bb + m.b*ff + m.c*jj;
+ c = m.a*cc + m.b*gg + m.c*kk;
+ d = m.a*dd + m.b*hh + m.c*ll + m.d;
+ e = m.e*aa + m.f*ee + m.g*ii;
+ f = m.e*bb + m.f*ff + m.g*jj;
+ g = m.e*cc + m.f*gg + m.g*kk;
+ h = m.e*dd + m.f*hh + m.g*ll + m.h;
+ i = m.i*aa + m.j*ee + m.k*ii;
+ j = m.i*bb + m.j*ff + m.k*jj;
+ k = m.i*cc + m.j*gg + m.k*kk;
+ l = m.i*dd + m.j*hh + m.k*ll + m.l;
+
+ return this;
+ }
+
+ /**
+ * Умножение на матрицу слева: this * M
+ *
+ * @param matrix правый операнд умножения
+ */
+ public function prepend(m:Matrix4):Matrix4 {
+ var aa:Number = a;
+ var bb:Number = b;
+ var cc:Number = c;
+ var dd:Number = d;
+ var ee:Number = e;
+ var ff:Number = f;
+ var gg:Number = g;
+ var hh:Number = h;
+ var ii:Number = i;
+ var jj:Number = j;
+ var kk:Number = k;
+ var ll:Number = l;
+
+ a = aa*m.a + bb*m.e + cc*m.i;
+ b = aa*m.b + bb*m.f + cc*m.j;
+ c = aa*m.c + bb*m.g + cc*m.k;
+ d = aa*m.d + bb*m.h + cc*m.l + dd;
+ e = ee*m.a + ff*m.e + gg*m.i;
+ f = ee*m.b + ff*m.f + gg*m.j;
+ g = ee*m.c + ff*m.g + gg*m.k;
+ h = ee*m.d + ff*m.h + gg*m.l + hh;
+ i = ii*m.a + jj*m.e + kk*m.i;
+ j = ii*m.b + jj*m.f + kk*m.j;
+ k = ii*m.c + jj*m.g + kk*m.k;
+ l = ii*m.d + jj*m.h + kk*m.l + ll;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function add(m:Matrix4):Matrix4 {
+ a += m.a;
+ b += m.b;
+ c += m.c;
+ d += m.d;
+ e += m.e;
+ f += m.f;
+ g += m.g;
+ h += m.h;
+ i += m.i;
+ j += m.j;
+ k += m.k;
+ l += m.l;
+
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function subtract(m:Matrix4):Matrix4 {
+ a -= m.a;
+ b -= m.b;
+ c -= m.c;
+ d -= m.d;
+ e -= m.e;
+ f -= m.f;
+ g -= m.g;
+ h -= m.h;
+ i -= m.i;
+ j -= m.j;
+ k -= m.k;
+ l -= m.l;
+
+ return this;
+ }
+
+ /**
+ * Трансформирует заданный вектор.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+
+ /**
+ * Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
+ *
+ * @param vin входной вектор
+ * @param vout вектор, в который записывается результат трансформации
+ */
+ public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
+ var xx:Number = vin.x - d;
+ var yy:Number = vin.y - h;
+ var zz:Number = vin.z - l;
+ vout.x = a*xx + e*yy + i*zz;
+ vout.y = b*xx + f*yy + j*zz;
+ vout.z = c*xx + g*yy + k*zz;
+ }
+
+ /**
+ *
+ * @param arrin
+ * @param Vector3
+ * @param arrout
+ * @param Vector3
+ */
+ public function transformVectors(arrin:Vector., arrout:Vector.):void {
+ var len:int = arrin.length;
+ var vin:Vector3;
+ var vout:Vector3;
+ for (var idx:int = 0; idx < len; idx++) {
+ vin = arrin[idx];
+ vout = arrout[idx];
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+ }
+
+ /**
+ *
+ * @param arrin
+ * @param Vector3
+ * @param arrout
+ * @param Vector3
+ */
+ public function transformVectorsInverse(arrin:Vector., arrout:Vector.):void {
+ var len:int = arrin.length;
+ var vin:Vector3;
+ var vout:Vector3;
+ for (var idx:int = 0; idx < len; idx++) {
+ vin = arrin[idx];
+ vout = arrout[idx];
+ var xx:Number = vin.x - d;
+ var yy:Number = vin.y - h;
+ var zz:Number = vin.z - l;
+ vout.x = a*xx + e*yy + i*zz;
+ vout.y = b*xx + f*yy + j*zz;
+ vout.z = c*xx + g*yy + k*zz;
+ }
+ }
+
+ /**
+ *
+ * @param i
+ * @param axis
+ */
+ public function getAxis(idx:int, axis:Vector3):void {
+ switch (idx) {
+ case 0:
+ axis.x = a;
+ axis.y = e;
+ axis.z = i;
+ return;
+ case 1:
+ axis.x = b;
+ axis.y = f;
+ axis.z = j;
+ return;
+ case 2:
+ axis.x = c;
+ axis.y = g;
+ axis.z = k;
+ return;
+ case 3:
+ axis.x = d;
+ axis.y = h;
+ axis.z = l;
+ return;
+ }
+ }
+
+ /**
+ * Трансформирует заданный вектор без учёта смещения центра матрицы.
+ *
+ * @param pin входной вектор
+ * @param pout вектор, в который записывается результат трансформации
+ */
+ public function deltaTransformVector(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + b*vin.y + c*vin.z + d;
+ vout.y = e*vin.x + f*vin.y + g*vin.z + h;
+ vout.z = i*vin.x + j*vin.y + k*vin.z + l;
+ }
+
+ /**
+ * Трансформирует заданный вектор без учёта смещения центра матрицы.
+ *
+ * @param pin входной вектор
+ * @param pout вектор, в который записывается результат трансформации
+ */
+ public function deltaTransformVectorInverse(vin:Vector3, vout:Vector3):void {
+ vout.x = a*vin.x + e*vin.y + i*vin.z;
+ vout.y = b*vin.x + f*vin.y + j*vin.z;
+ vout.z = c*vin.x + g*vin.y + k*vin.z;
+ }
+
+ /**
+ * Копирование значений указанной матрицы.
+ *
+ * @param matrix матрица, значения которой копируются
+ */
+ public function copy(m:Matrix4):Matrix4 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ d = m.d;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ h = m.h;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+ l = m.l;
+
+ return this;
+ }
+
+ public function setFromMatrix3(m:Matrix3, offset:Vector3):Matrix4 {
+ a = m.a;
+ b = m.b;
+ c = m.c;
+ d = offset.x;
+ e = m.e;
+ f = m.f;
+ g = m.g;
+ h = offset.y;
+ i = m.i;
+ j = m.j;
+ k = m.k;
+ l = offset.z;
+
+ return this;
+ }
+
+ /**
+ * @param rotations
+ */
+ public function getEulerAngles(rotations:Vector3):void {
+ if (-1 < i && i < 1) {
+ rotations.x = Math.atan2(j, k);
+ rotations.y = -Math.asin(i);
+ rotations.z = Math.atan2(e, a);
+ } else {
+ rotations.x = 0;
+ rotations.y = 0.5*((i <= -1) ? Math.PI : -Math.PI);
+ rotations.z = Math.atan2(-b, f);
+ }
+ }
+
+ /**
+ * Клонирование матрицы.
+ *
+ * @return клон матрицы
+ */
+ public function clone():Matrix4 {
+ return new Matrix4(a, b, c, d, e, f, g, h, i, j, k, l);
+ }
+
+ public function toString():String {
+ return "[Matrix4 [" + a.toFixed(3) + " " + b.toFixed(3) + " " + c.toFixed(3) + " " + d.toFixed(3) + "] [" + e.toFixed(3) + " " + f.toFixed(3) + " " + g.toFixed(3) + " " + h.toFixed(3) + "] [" + i.toFixed(3) + " " + j.toFixed(3) + " " + k.toFixed(3) + " " + l.toFixed(3) + "]]";
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.1.0/src/alternativa/physics/types/Quaternion.as b/0.0.1.0/src/alternativa/physics/types/Quaternion.as
new file mode 100644
index 0000000..7ad2af0
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/Quaternion.as
@@ -0,0 +1,396 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ public class Quaternion {
+
+ public var w:Number;
+ public var x:Number;
+ public var y:Number;
+ public var z:Number;
+
+ public static function multiply(q1:Quaternion, q2:Quaternion, result:Quaternion):void {
+ result.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
+ result.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y;
+ result.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z;
+ result.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x;
+ }
+
+ public static function createFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
+ var q:Quaternion = new Quaternion();
+ q.setFromAxisAngle(axis, angle);
+ return q;
+ }
+
+ public static function createFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
+ var q:Quaternion = new Quaternion();
+ q.setFromAxisAngleComponents(x, y, z, angle);
+ return q;
+ }
+
+ public function Quaternion(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0) {
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public function reset(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0):Quaternion {
+ this.w = w;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ public function normalize():Quaternion {
+ var d:Number = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ * Умножает на указанный кватернион слева: this * q
+ *
+ * @param q множитель
+ */
+ public function prepend(q:Quaternion):Quaternion {
+ var ww:Number = w*q.w - x*q.x - y*q.y - z*q.z;
+ var xx:Number = w*q.x + x*q.w + y*q.z - z*q.y;
+ var yy:Number = w*q.y + y*q.w + z*q.x - x*q.z;
+ var zz:Number = w*q.z + z*q.w + x*q.y - y*q.x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Умножает на указанный кватернион справа: q * this
+ *
+ * @param q множитель
+ */
+ public function append(q:Quaternion):Quaternion {
+ var ww:Number = q.w*w - q.x*x - q.y*y - q.z*z;
+ var xx:Number = q.w*x + q.x*w + q.y*z - q.z*y;
+ var yy:Number = q.w*y + q.y*w + q.z*x - q.x*z;
+ var zz:Number = q.w*z + q.z*w + q.x*y - q.y*x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param vector
+ */
+ public function rotateByVector(v:Vector3):Quaternion {
+ var ww:Number = -v.x*x - v.y*y - v.z*z;
+ var xx:Number = v.x*w + v.y*z - v.z*y;
+ var yy:Number = v.y*w + v.z*x - v.x*z;
+ var zz:Number = v.z*w + v.x*y - v.y*x;
+ w = ww;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Добавляет вращение, приданное вектором угловой скорости за указанное время.
+ *
+ * @param v
+ * @param scale
+ */
+ public function addScaledVector(v:Vector3, scale:Number):Quaternion {
+ var vx:Number = v.x*scale;
+ var vy:Number = v.y*scale;
+ var vz:Number = v.z*scale;
+ var ww:Number = -x*vx - y*vy - z*vz;
+ var xx:Number = vx*w + vy*z - vz*y;
+ var yy:Number = vy*w + vz*x - vx*z;
+ var zz:Number = vz*w + vx*y - vy*x;
+ w += 0.5*ww;
+ x += 0.5*xx;
+ y += 0.5*yy;
+ z += 0.5*zz;
+ // inlined normalize
+ var d:Number = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ public function toMatrix3(m:Matrix3):Quaternion {
+ var xx2:Number = 2*x*x;
+ var yy2:Number = 2*y*y;
+ var zz2:Number = 2*z*z;
+ var xy2:Number = 2*x*y;
+ var yz2:Number = 2*y*z;
+ var zx2:Number = 2*z*x;
+ var wx2:Number = 2*w*x;
+ var wy2:Number = 2*w*y;
+ var wz2:Number = 2*w*z;
+
+ m.a = 1 - yy2 - zz2;
+ m.b = xy2 - wz2;
+ m.c = zx2 + wy2;
+
+ m.e = xy2 + wz2;
+ m.f = 1 - xx2 - zz2;
+ m.g = yz2 - wx2;
+
+ m.i = zx2 - wy2;
+ m.j = yz2 + wx2;
+ m.k = 1 - xx2 - yy2;
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function length():Number {
+ return Math.sqrt(w*w + x*x + y*y + z*z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function lengthSqr():Number {
+ return w*w + x*x + y*y + z*z;
+ }
+
+ /**
+ *
+ * @param axis
+ * @param angle
+ * @return
+ */
+ public function setFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
+ w = Math.cos(0.5*angle);
+ var k:Number = Math.sin(0.5*angle)/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
+ x = axis.x*k;
+ y = axis.y*k;
+ z = axis.z*k;
+ return this;
+ }
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @param angle
+ */
+ public function setFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
+ w = Math.cos(0.5*angle);
+ var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
+ this.x = x*k;
+ this.y = y*k;
+ this.z = z*k;
+ return this;
+ }
+
+ /**
+ *
+ * @param vector
+ */
+ public function toAxisVector(v:Vector3 = null):Vector3 {
+ if (w < -1 || w > 1) {
+ normalize();
+ }
+ if (v == null) {
+ v = new Vector3();
+ }
+ if (w > -1 && w < 1) {
+ if (w == 0) {
+ v.x = x;
+ v.y = y;
+ v.z = z;
+ } else {
+ var angle:Number = 2*Math.acos(w);
+ var coeff:Number = 1/Math.sqrt(1 - w*w);
+ v.x = x*coeff*angle;
+ v.y = y*coeff*angle;
+ v.z = z*coeff*angle;
+ }
+ } else {
+ v.x = 0;
+ v.y = 0;
+ v.z = 0;
+ }
+ return v;
+ }
+
+ /**
+ *
+ * @param rotations
+ */
+ public function getEulerAngles(angles:Vector3):Vector3 {
+ var qi2:Number = 2*x*x;
+ var qj2:Number = 2*y*y;
+ var qk2:Number = 2*z*z;
+ var qij:Number = 2*x*y;
+ var qjk:Number = 2*y*z;
+ var qki:Number = 2*z*x;
+ var qri:Number = 2*w*x;
+ var qrj:Number = 2*w*y;
+ var qrk:Number = 2*w*z;
+
+ var aa:Number = 1 - qj2 - qk2;
+ var bb:Number = qij - qrk;
+ var ee:Number = qij + qrk;
+ var ff:Number = 1 - qi2 - qk2;
+ var ii:Number = qki - qrj;
+ var jj:Number = qjk + qri;
+ var kk:Number = 1 - qi2 - qj2;
+
+ if (-1 < ii && ii < 1) {
+ if (angles == null) {
+ angles = new Vector3(Math.atan2(jj, kk), -Math.asin(ii), Math.atan2(ee, aa));
+ } else {
+ angles.x = Math.atan2(jj, kk);
+ angles.y = -Math.asin(ii);
+ angles.z = Math.atan2(ee, aa);
+ }
+ } else {
+ if (angles == null) {
+ angles = new Vector3(0, 0.5*((ii <= -1) ? Math.PI : -Math.PI), Math.atan2(-bb, ff));
+ } else {
+ angles.x = 0;
+ angles.y = 0.5*((ii <= -1) ? Math.PI : -Math.PI);
+ angles.z = Math.atan2(-bb, ff);
+ }
+ }
+ return angles;
+ }
+
+ /**
+ *
+ */
+ public function conjugate():void {
+ x = -x;
+ y = -y;
+ z = -z;
+ }
+
+ /**
+ * Выполняет линейную интерполяцию.
+ *
+ * @param q1 начало отрезка
+ * @param q2 конец отрезка
+ * @param t время, обычно задаётся в интервале [0, 1]
+ */
+ public function nlerp(q1:Quaternion, q2:Quaternion, t:Number):Quaternion {
+ var d:Number = 1 - t;
+ w = q1.w*d + q2.w*t;
+ x = q1.x*d + q2.x*t;
+ y = q1.y*d + q2.y*t;
+ z = q1.z*d + q2.z*t;
+ // inlined normalize
+ d = w*w + x*x + y*y + z*z;
+ if (d == 0) {
+ w = 1;
+ } else {
+ d = 1/Math.sqrt(d);
+ w *= d;
+ x *= d;
+ y *= d;
+ z *= d;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param q
+ * @return
+ */
+ public function subtract(q:Quaternion):Quaternion {
+ w -= q.w;
+ x -= q.x;
+ y -= q.y;
+ z -= q.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param q1
+ * @param q2
+ * @return
+ */
+ public function diff(q1:Quaternion, q2:Quaternion):Quaternion {
+ w = q2.w - q1.w;
+ x = q2.x - q1.x;
+ y = q2.y - q1.y;
+ z = q2.z - q1.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param q
+ * @return
+ */
+ public function copy(q:Quaternion):Quaternion {
+ w = q.w;
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ return this;
+ }
+
+ /**
+ * @param reslult
+ */
+ public function toVector3D(result:Vector3D):Vector3D {
+ result.x = x;
+ result.y = y;
+ result.z = z;
+ result.w = w;
+ return result;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function clone():Quaternion {
+ return new Quaternion(w, x, y, z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function toString():String {
+ return "[" + w + ", " + x + ", " + y + ", " + z + "]";
+ }
+ }
+}
diff --git a/0.0.1.0/src/alternativa/physics/types/Vector3.as b/0.0.1.0/src/alternativa/physics/types/Vector3.as
new file mode 100644
index 0000000..298ddad
--- /dev/null
+++ b/0.0.1.0/src/alternativa/physics/types/Vector3.as
@@ -0,0 +1,331 @@
+package alternativa.physics.types {
+ import flash.geom.Vector3D;
+
+
+ /**
+ *
+ */
+ public class Vector3 extends Vector3D {
+
+ public static const ZERO:Vector3 = new Vector3(0, 0, 0);
+ public static const X_AXIS:Vector3 = new Vector3(1, 0, 0);
+ public static const Y_AXIS:Vector3 = new Vector3(0, 1, 0);
+ public static const Z_AXIS:Vector3 = new Vector3(0, 0, 1);
+
+ /**
+ *
+ * @param x
+ * @param y
+ * @param z
+ */
+ public function Vector3(x:Number = 0, y:Number = 0, z:Number = 0) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vLength():Number {
+ return Math.sqrt(x*x + y*y + z*z);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vLengthSqr():Number {
+ return x*x + y*y + z*z;
+ }
+
+ /**
+ *
+ * @param length
+ * @return
+ */
+ public function vSetLength(length:Number):Vector3 {
+ var d:Number = x*x + y*y + z*z;
+ if (d == 0) {
+ x = length;
+ } else {
+ var k:Number = length/Math.sqrt(x*x + y*y + z*z);
+ x *= k;
+ y *= k;
+ z *= k;
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public function vNormalize():Vector3 {
+ var d:Number = x*x + y*y + z*z;
+ if (d == 0) {
+ x = 1;
+ } else {
+ var k:Number = Math.sqrt(d);
+ x /= k;
+ y /= k;
+ z /= k;
+ }
+ return this;
+ }
+
+ /**
+ * Сложение координат.
+ *
+ * @param v точка, координаты которой прибавляются к собственным
+ */
+ public function vAdd(v:Vector3):Vector3 {
+ x += v.x;
+ y += v.y;
+ z += v.z;
+ return this;
+ }
+
+ /**
+ *
+ * @param k
+ * @param v
+ * @return
+ */
+ public function vAddScaled(k:Number, v:Vector3):Vector3 {
+ x += k*v.x;
+ y += k*v.y;
+ z += k*v.z;
+ return this;
+ }
+
+ /**
+ * Вычитание координат.
+ *
+ * @param v точка, координаты которой вычитаются из собственных
+ */
+ public function vSubtract(v:Vector3):Vector3 {
+ x -= v.x;
+ y -= v.y;
+ z -= v.z;
+ return this;
+ }
+
+ /**
+ * Вычисляет сумму векторов.
+ *
+ * @param a уменьшаемый вектор
+ * @param b вычитаемый вектор
+ * @return this
+ */
+ public function vSum(a:Vector3, b:Vector3):Vector3 {
+ x = a.x + b.x;
+ y = a.y + b.y;
+ z = a.z + b.z;
+ return this;
+ }
+
+ /**
+ * Вычисление разности векторов.
+ *
+ * @param a уменьшаемый вектор
+ * @param b вычитаемый вектор
+ */
+ public function vDiff(a:Vector3, b:Vector3):Vector3 {
+ x = a.x - b.x;
+ y = a.y - b.y;
+ z = a.z - b.z;
+ return this;
+ }
+
+ /**
+ * Умножение на скаляр.
+ *
+ * @param k число, на которое умножаются координаты
+ */
+ public function vScale(k:Number):Vector3 {
+ x *= k;
+ y *= k;
+ z *= k;
+ return this;
+ }
+
+ /**
+ * Инвертирование вектора.
+ */
+ public function vReverse():Vector3 {
+ x = -x;
+ y = -y;
+ z = -z;
+ return this;
+ }
+
+ /**
+ *
+ * @param v
+ * @return
+ */
+ public function vDot(v:Vector3):Number {
+ return x*v.x + y*v.y + z*v.z;
+ }
+
+ /**
+ * Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
+ *
+ * @param v
+ */
+ public function vCross(v:Vector3):Vector3 {
+ var xx:Number = y*v.z - z*v.y;
+ var yy:Number = z*v.x - x*v.z;
+ var zz:Number = x*v.y - y*v.x;
+ x = xx;
+ y = yy;
+ z = zz;
+ return this;
+ }
+
+ /**
+ * Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
+ *
+ * @param v
+ */
+ public function vCross2(a:Vector3, b:Vector3):Vector3 {
+ x = a.y*b.z - a.z*b.y;
+ y = a.z*b.x - a.x*b.z;
+ z = a.x*b.y - a.y*b.x;
+ return this;
+ }
+
+ /**
+ * Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
+ * M × r.
+ *
+ * @param m матрица трансформации
+ */
+ public function vTransformBy3(m:Matrix3):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz;
+ y = m.e*xx + m.f*yy + m.g*zz;
+ z = m.i*xx + m.j*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param matrix
+ */
+ public function vTransformBy3Tr(m:Matrix3):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.e*yy + m.i*zz;
+ y = m.b*xx + m.f*yy + m.j*zz;
+ z = m.c*xx + m.g*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ * Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
+ * M × r.
+ *
+ * @param m матрица трансформации
+ */
+ public function vTransformBy4(m:Matrix4):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz + m.d;
+ y = m.e*xx + m.f*yy + m.g*zz + m.h;
+ z = m.i*xx + m.j*yy + m.k*zz + m.l;
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ * @return
+ */
+ public function vTransformInverseBy4(m:Matrix4):Vector3 {
+ var xx:Number = x - m.d;
+ var yy:Number = y - m.h;
+ var zz:Number = z - m.l;
+ x = m.a*xx + m.e*yy + m.i*zz;
+ y = m.b*xx + m.f*yy + m.j*zz;
+ z = m.c*xx + m.g*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ *
+ * @param m
+ */
+ public function vDeltaTransformBy4(m:Matrix4):Vector3 {
+ var xx:Number = x;
+ var yy:Number = y;
+ var zz:Number = z;
+ x = m.a*xx + m.b*yy + m.c*zz;
+ y = m.e*xx + m.f*yy + m.g*zz;
+ z = m.i*xx + m.j*yy + m.k*zz;
+ return this;
+ }
+
+ /**
+ * Установка координат.
+ */
+ public function vReset(x:Number = 0, y:Number = 0, z:Number = 0):Vector3 {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ return this;
+ }
+
+ /**
+ * Копирование координат точки.
+ *
+ * @param v точка, координаты которой копируются
+ */
+ public function vCopy(v:Vector3):Vector3 {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ return this;
+ }
+
+ /**
+ * Клонирование точки.
+ *
+ * @return клонированная точка
+ */
+ public function vClone():Vector3 {
+ return new Vector3(x, y, z);
+ }
+
+ /**
+ * Копирует координаты вектора в экземпляр класса Vector3D.
+ *
+ * @param result экземпляр, в который копируются координаты
+ * @return переданный параметр
+ */
+ public function toVector3D(result:Vector3D):Vector3D {
+ result.x = x;
+ result.y = y;
+ result.z = z;
+ return result;
+ }
+
+ /**
+ * @param source
+ * @return
+ */
+ public function copyFromVector3D(source:Vector3D):Vector3 {
+ x = source.x;
+ y = source.y;
+ z = source.z;
+ return this;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/.actionScriptProperties b/0.0.2.0/.actionScriptProperties
new file mode 100644
index 0000000..e5b1b89
--- /dev/null
+++ b/0.0.2.0/.actionScriptProperties
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.2.0/.flexLibProperties b/0.0.2.0/.flexLibProperties
new file mode 100644
index 0000000..2083d0b
--- /dev/null
+++ b/0.0.2.0/.flexLibProperties
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.2.0/.project b/0.0.2.0/.project
new file mode 100644
index 0000000..1f1425f
--- /dev/null
+++ b/0.0.2.0/.project
@@ -0,0 +1,19 @@
+
+
+ AlternativaPhysics
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ org.maven.ide.eclipse.maven2Nature
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/0.0.2.0/.settings/.svn/all-wcprops b/0.0.2.0/.settings/.svn/all-wcprops
new file mode 100644
index 0000000..f859e6d
--- /dev/null
+++ b/0.0.2.0/.settings/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 89
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.settings
+END
+org.eclipse.core.resources.prefs
+K 25
+svn:wc:ra_dav:version-url
+V 122
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.settings/org.eclipse.core.resources.prefs
+END
diff --git a/0.0.2.0/.settings/.svn/entries b/0.0.2.0/.settings/.svn/entries
new file mode 100644
index 0000000..62c7539
--- /dev/null
+++ b/0.0.2.0/.settings/.svn/entries
@@ -0,0 +1,40 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.settings
+http://svndev.alternativaplatform.com
+
+
+
+2009-04-01T12:25:29.638016Z
+10301
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+org.eclipse.core.resources.prefs
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+4f70b476f3e5075e399505021df2f89b
+2009-04-01T12:25:29.638016Z
+10301
+mike
+
diff --git a/0.0.2.0/.settings/.svn/format b/0.0.2.0/.settings/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/.settings/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base b/0.0.2.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
new file mode 100644
index 0000000..b3d0d49
--- /dev/null
+++ b/0.0.2.0/.settings/.svn/text-base/org.eclipse.core.resources.prefs.svn-base
@@ -0,0 +1,3 @@
+#Fri Mar 20 00:19:33 YEKT 2009
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/0.0.2.0/.settings/org.eclipse.core.resources.prefs b/0.0.2.0/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..b3d0d49
--- /dev/null
+++ b/0.0.2.0/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Fri Mar 20 00:19:33 YEKT 2009
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/0.0.2.0/.svn/all-wcprops b/0.0.2.0/.svn/all-wcprops
new file mode 100644
index 0000000..15af25a
--- /dev/null
+++ b/0.0.2.0/.svn/all-wcprops
@@ -0,0 +1,29 @@
+K 25
+svn:wc:ra_dav:version-url
+V 79
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0
+END
+.flexLibProperties
+K 25
+svn:wc:ra_dav:version-url
+V 98
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.flexLibProperties
+END
+.project
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.project
+END
+pom.xml
+K 25
+svn:wc:ra_dav:version-url
+V 87
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/pom.xml
+END
+.actionScriptProperties
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/.actionScriptProperties
+END
diff --git a/0.0.2.0/.svn/dir-prop-base b/0.0.2.0/.svn/dir-prop-base
new file mode 100644
index 0000000..8eda4b3
--- /dev/null
+++ b/0.0.2.0/.svn/dir-prop-base
@@ -0,0 +1,5 @@
+K 13
+svn:mergeinfo
+V 85
+/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.1.Vector3D:13892-14098
+END
diff --git a/0.0.2.0/.svn/entries b/0.0.2.0/.svn/entries
new file mode 100644
index 0000000..2f21348
--- /dev/null
+++ b/0.0.2.0/.svn/entries
@@ -0,0 +1,85 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-29T08:50:58.692043Z
+15305
+mike
+has-props
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+META-INF
+dir
+
+.flexLibProperties
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+fcef7ca7fe61a3459fb00858c28adec1
+2009-05-26T08:41:33.471001Z
+13389
+mike
+
+.project
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+1e90cc68b52b93173b2b5de88eb5c3a3
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
+src
+dir
+
+pom.xml
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+6c2d68b3b0a55ff2ff9b5769c7cec04f
+2009-06-29T08:50:58.692043Z
+15305
+mike
+
+.actionScriptProperties
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+918335cb945c3f8866aeb8e6cfd1b110
+2009-06-23T08:49:19.922749Z
+14884
+mike
+
+.settings
+dir
+
diff --git a/0.0.2.0/.svn/format b/0.0.2.0/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/.svn/text-base/.actionScriptProperties.svn-base b/0.0.2.0/.svn/text-base/.actionScriptProperties.svn-base
new file mode 100644
index 0000000..e5b1b89
--- /dev/null
+++ b/0.0.2.0/.svn/text-base/.actionScriptProperties.svn-base
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.2.0/.svn/text-base/.flexLibProperties.svn-base b/0.0.2.0/.svn/text-base/.flexLibProperties.svn-base
new file mode 100644
index 0000000..2083d0b
--- /dev/null
+++ b/0.0.2.0/.svn/text-base/.flexLibProperties.svn-base
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/0.0.2.0/.svn/text-base/.project.svn-base b/0.0.2.0/.svn/text-base/.project.svn-base
new file mode 100644
index 0000000..1f1425f
--- /dev/null
+++ b/0.0.2.0/.svn/text-base/.project.svn-base
@@ -0,0 +1,19 @@
+
+
+ AlternativaPhysics
+
+
+
+
+
+ com.adobe.flexbuilder.project.flexbuilder
+
+
+
+
+
+ org.maven.ide.eclipse.maven2Nature
+ com.adobe.flexbuilder.project.flexlibnature
+ com.adobe.flexbuilder.project.actionscriptnature
+
+
diff --git a/0.0.2.0/.svn/text-base/pom.xml.svn-base b/0.0.2.0/.svn/text-base/pom.xml.svn-base
new file mode 100644
index 0000000..1b303ad
--- /dev/null
+++ b/0.0.2.0/.svn/text-base/pom.xml.svn-base
@@ -0,0 +1,17 @@
+
+ 4.0.0
+ platform.clients.fp10.libraries
+ AlternativaPhysics
+ swc
+ 0.0.2.0
+
+ platform.tools.maven
+ FlashBasePom
+ 1.0
+
+
+ scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0
+
+
+
+
\ No newline at end of file
diff --git a/0.0.2.0/META-INF/.svn/all-wcprops b/0.0.2.0/META-INF/.svn/all-wcprops
new file mode 100644
index 0000000..a2cf6fb
--- /dev/null
+++ b/0.0.2.0/META-INF/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 88
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/META-INF
+END
+MANIFEST.MF
+K 25
+svn:wc:ra_dav:version-url
+V 100
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/META-INF/MANIFEST.MF
+END
diff --git a/0.0.2.0/META-INF/.svn/entries b/0.0.2.0/META-INF/.svn/entries
new file mode 100644
index 0000000..dd7245d
--- /dev/null
+++ b/0.0.2.0/META-INF/.svn/entries
@@ -0,0 +1,40 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/META-INF
+http://svndev.alternativaplatform.com
+
+
+
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+MANIFEST.MF
+file
+
+
+
+
+2010-10-28T04:32:39.000000Z
+7b64dceb50a6905850ff00a0bfbe77eb
+2009-04-20T17:15:55.260110Z
+11541
+mike
+
diff --git a/0.0.2.0/META-INF/.svn/format b/0.0.2.0/META-INF/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/META-INF/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base b/0.0.2.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
new file mode 100644
index 0000000..5066a55
--- /dev/null
+++ b/0.0.2.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
@@ -0,0 +1 @@
+Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
diff --git a/0.0.2.0/META-INF/MANIFEST.MF b/0.0.2.0/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5066a55
--- /dev/null
+++ b/0.0.2.0/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
diff --git a/0.0.2.0/pom.xml b/0.0.2.0/pom.xml
new file mode 100644
index 0000000..1b303ad
--- /dev/null
+++ b/0.0.2.0/pom.xml
@@ -0,0 +1,17 @@
+
+ 4.0.0
+ platform.clients.fp10.libraries
+ AlternativaPhysics
+ swc
+ 0.0.2.0
+
+ platform.tools.maven
+ FlashBasePom
+ 1.0
+
+
+ scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0
+
+
+
+
\ No newline at end of file
diff --git a/0.0.2.0/src/.svn/all-wcprops b/0.0.2.0/src/.svn/all-wcprops
new file mode 100644
index 0000000..8d85b0c
--- /dev/null
+++ b/0.0.2.0/src/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 83
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src
+END
diff --git a/0.0.2.0/src/.svn/entries b/0.0.2.0/src/.svn/entries
new file mode 100644
index 0000000..64c71e4
--- /dev/null
+++ b/0.0.2.0/src/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-29T03:58:47.301371Z
+15198
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+alternativa
+dir
+
diff --git a/0.0.2.0/src/.svn/format b/0.0.2.0/src/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/src/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/src/alternativa/.svn/all-wcprops b/0.0.2.0/src/alternativa/.svn/all-wcprops
new file mode 100644
index 0000000..35058b9
--- /dev/null
+++ b/0.0.2.0/src/alternativa/.svn/all-wcprops
@@ -0,0 +1,5 @@
+K 25
+svn:wc:ra_dav:version-url
+V 95
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa
+END
diff --git a/0.0.2.0/src/alternativa/.svn/entries b/0.0.2.0/src/alternativa/.svn/entries
new file mode 100644
index 0000000..dccd38e
--- /dev/null
+++ b/0.0.2.0/src/alternativa/.svn/entries
@@ -0,0 +1,31 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-29T03:58:47.301371Z
+15198
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+physics
+dir
+
diff --git a/0.0.2.0/src/alternativa/.svn/format b/0.0.2.0/src/alternativa/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/src/alternativa/physics/.svn/all-wcprops b/0.0.2.0/src/alternativa/physics/.svn/all-wcprops
new file mode 100644
index 0000000..2790d23
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/.svn/all-wcprops
@@ -0,0 +1,11 @@
+K 25
+svn:wc:ra_dav:version-url
+V 103
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics
+END
+altphysics.as
+K 25
+svn:wc:ra_dav:version-url
+V 117
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/altphysics.as
+END
diff --git a/0.0.2.0/src/alternativa/physics/.svn/entries b/0.0.2.0/src/alternativa/physics/.svn/entries
new file mode 100644
index 0000000..9a1c0e3
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/.svn/entries
@@ -0,0 +1,49 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-29T03:58:47.301371Z
+15198
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+types
+dir
+
+altphysics.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+04fbe40a27502dbbd0dba02a76b2df50
+2009-04-20T20:09:14.715403Z
+11579
+mike
+
+collision
+dir
+
+rigid
+dir
+
diff --git a/0.0.2.0/src/alternativa/physics/.svn/format b/0.0.2.0/src/alternativa/physics/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base b/0.0.2.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
new file mode 100644
index 0000000..2715307
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/.svn/text-base/altphysics.as.svn-base
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/altphysics.as b/0.0.2.0/src/alternativa/physics/altphysics.as
new file mode 100644
index 0000000..2715307
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/altphysics.as
@@ -0,0 +1,3 @@
+package alternativa.physics {
+ public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/all-wcprops b/0.0.2.0/src/alternativa/physics/collision/.svn/all-wcprops
new file mode 100644
index 0000000..bb460e8
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/all-wcprops
@@ -0,0 +1,95 @@
+K 25
+svn:wc:ra_dav:version-url
+V 113
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision
+END
+BoxSphereCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 134
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BoxSphereCollider.as
+END
+BoxBoxCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 131
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BoxBoxCollider.as
+END
+CollisionKdNode.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/CollisionKdNode.as
+END
+SpherePlaneCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/SpherePlaneCollider.as
+END
+KdTreeCollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 140
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as
+END
+BoxRectCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BoxRectCollider.as
+END
+ICollisionPredicate.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/ICollisionPredicate.as
+END
+BoxPlaneCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 133
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BoxPlaneCollider.as
+END
+CollisionKdTree.as
+K 25
+svn:wc:ra_dav:version-url
+V 132
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/CollisionKdTree.as
+END
+BoxTriangleCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 136
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BoxTriangleCollider.as
+END
+ICollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 135
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/ICollisionDetector.as
+END
+IRayCollisionPredicate.as
+K 25
+svn:wc:ra_dav:version-url
+V 139
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/IRayCollisionPredicate.as
+END
+ICollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 126
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/ICollider.as
+END
+BruteForceCollisionDetector.as
+K 25
+svn:wc:ra_dav:version-url
+V 144
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as
+END
+SphereSphereCollider.as
+K 25
+svn:wc:ra_dav:version-url
+V 137
+/!svn/ver/15305/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision/SphereSphereCollider.as
+END
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/entries b/0.0.2.0/src/alternativa/physics/collision/.svn/entries
new file mode 100644
index 0000000..aa331e0
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/entries
@@ -0,0 +1,214 @@
+8
+
+dir
+46043
+http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.2.0/src/alternativa/physics/collision
+http://svndev.alternativaplatform.com
+
+
+
+2009-06-29T03:52:30.472906Z
+15194
+mike
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
+
+BoxSphereCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+ad1dc1849019ac159d22cd93b1ba38d3
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+BoxBoxCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+e4eb46370e3c7098fa35d0da4e346d09
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+CollisionKdNode.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+7952384e785be1fca93657d94121b246
+2009-04-17T17:39:21.367569Z
+11341
+mike
+
+BoxRectCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+8554254b88a8ecc31bf005fdee676a67
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
+KdTreeCollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+afefefe1aacd027b490adf1eb6a6f148
+2009-06-29T03:52:30.472906Z
+15194
+mike
+
+SpherePlaneCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+7fbbf70eef5caa9a09ce679819429874
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+ICollisionPredicate.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+866471ea582e95e46e94eb7ae437c827
+2009-04-26T15:39:06.673206Z
+11995
+mike
+
+BoxPlaneCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+4344df47c2b9c1873d2dc711815b4737
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+primitives
+dir
+
+CollisionKdTree.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+92511d7533028a942e299ca3a4251da8
+2009-05-18T16:37:02.465501Z
+13087
+mike
+
+BoxTriangleCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+f95da4166903174a2c92c1ab262e719c
+2009-05-26T08:41:33.471001Z
+13389
+mike
+
+ICollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+c98a3e33a4a54e2e85da149e25b583fe
+2009-06-08T11:22:42.165256Z
+14112
+mike
+
+IRayCollisionPredicate.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+f7443fb728cf4e6157dcfb4f39665889
+2009-04-26T15:39:06.673206Z
+11995
+mike
+
+types
+dir
+
+BruteForceCollisionDetector.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+c166c05be4909e221c2f129c9e7cac32
+2009-06-08T11:22:42.165256Z
+14112
+mike
+
+ICollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+bc3785279b9b7810a0c84cb4fe6084fc
+2009-04-13T03:07:43.422558Z
+11019
+mike
+
+SphereSphereCollider.as
+file
+
+
+
+
+2010-10-28T04:32:40.000000Z
+fd6e8cf39dcf007125384bc91b347a00
+2009-06-08T06:46:18.310732Z
+14099
+mike
+
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/format b/0.0.2.0/src/alternativa/physics/collision/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base
new file mode 100644
index 0000000..2893b49
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxBoxCollider.as.svn-base
@@ -0,0 +1,791 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
+ */
+ public class BoxBoxCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var pos1:Vector3 = new Vector3();
+ private var pos2:Vector3 = new Vector3();
+ private var vectorToBox1:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var tmpPoints:Vector. = new Vector.(8, true);
+ private var pcount:int;
+
+ /**
+ *
+ */
+ public function BoxBoxCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ tmpPoints[i] = new ContactPoint();
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param contactInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box1:CollisionBox;
+ var box2:CollisionBox;
+ if (prim1.body != null) {
+ box1 = prim1 as CollisionBox;
+ box2 = prim2 as CollisionBox;
+ } else {
+ box1 = prim2 as CollisionBox;
+ box2 = prim1 as CollisionBox;
+ }
+ var transform1:Matrix4 = box1.transform;
+ var transform2:Matrix4 = box2.transform;
+
+ // Вектор из центра второго бокса в центр первого
+ pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
+ pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
+ vectorToBox1.x = pos1.x - pos2.x;
+ vectorToBox1.y = pos1.y - pos2.y;
+ vectorToBox1.z = pos1.z - pos2.z;
+
+ // Проверка пересечения по основным осям
+// box1.transform.getAxis(0, axis10);
+ axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+// box1.transform.getAxis(1, axis11);
+ axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+// box1.transform.getAxis(2, axis12);
+ axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+// box2.transform.getAxis(0, axis20);
+ axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+// box2.transform.getAxis(1, axis21);
+ axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+// box2.transform.getAxis(2, axis22);
+ axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Проверка производных осей
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 6) {
+ // Контакт грань-(грань|ребро|вершина)
+ findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 6;
+ findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
+ }
+ contact.body1 = box1.body;
+ contact.body2 = box2.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
+ * @param contactInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
+ var swapNormal:Boolean = false;
+ if (faceAxisIdx > 2) {
+ // Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
+ // но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
+ var tmpBox:CollisionBox = box1;
+ box1 = box2;
+ box2 = tmpBox;
+ vectorToBox1.vReverse();
+ faceAxisIdx -= 3;
+ swapNormal = true;
+ }
+ box1.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
+ if (!faceReversed) colAxis.vReverse();
+ // Ищем ось второго бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box2.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
+ // по грани первого бокса. Таким образом получается список потенциальных точек контакта.
+ box2.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
+ box2.transform.transformVectors(verts1, verts2);
+ box1.transform.transformVectorsInverse(verts2, verts1);
+ var pnum:int = clip(box1.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ pcount = 0;
+ for (var i:int = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ var cp:ContactPoint = tmpPoints[pcount++];
+ box1.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = pen;
+ }
+ }
+ contactInfo.normal.vCopy(colAxis);
+ if (swapNormal) contactInfo.normal.vReverse();
+
+ if (pcount > 4) reducePoints();
+ for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).copyFrom(tmpPoints[i]);
+ contactInfo.pcount = pcount;
+ }
+
+ /**
+ *
+ * @param contactInfo
+ */
+ private function reducePoints():void {
+ var i:int;
+ var minIdx:int;
+ var cp1:ContactPoint;
+ var cp2:ContactPoint;
+ while (pcount > 4) {
+ var minLen:Number = 1e10;
+ var p1:Vector3 = (tmpPoints[pcount - 1] as ContactPoint).pos;
+ var p2:Vector3;
+ for (i = 0; i < pcount; i++) {
+ p2 = (tmpPoints[i] as ContactPoint).pos;
+ var dx:Number = p2.x - p1.x;
+ var dy:Number = p2.y - p1.y;
+ var dz:Number = p2.z - p1.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ if (len < minLen) {
+ minLen = len;
+ minIdx = i;
+ }
+ p1 = p2;
+ }
+ cp1 = tmpPoints[minIdx == 0 ? (pcount - 1) : (minIdx - 1)];
+ cp2 = tmpPoints[minIdx];
+ p1 = cp1.pos;
+ p2 = cp2.pos;
+ p2.x = 0.5*(p1.x + p2.x);
+ p2.y = 0.5*(p1.y + p2.y);
+ p2.z = 0.5*(p1.z + p2.z);
+ cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
+ if (minIdx > 0) {
+ for (i = minIdx; i < pcount; i++) tmpPoints[i - 1] = tmpPoints[i];
+ tmpPoints[pcount - 1] = cp1;
+ }
+ pcount--;
+ }
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ var hs:Vector3 = box.hs;
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clip(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param contactInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
+ box1.transform.getAxis(axisIdx1, axis10);
+ box2.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону первого бокса
+ if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box1.hs);
+ point2.vCopy(box2.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box1.hs.x;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = box2.hs.x;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box1.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = box2.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box1.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ if (axisIdx2 == 2) {
+ point2.z = 0;
+ halfLen2 = box2.hs.z;
+ edgeCode2 |= 4; // 2 << 1
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.z = -point2.z;
+ edgeCode2 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box1.transform);
+ point2.vTransformBy4(box2.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ contact.normal.vCopy(colAxis);
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) {
+ return true;
+ }
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param axis ось
+ * @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
+ * @return величина перекрытия боксов вдоль оси
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1:Vector3):Number {
+ var m:Matrix4 = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
+ import alternativa.physics.types.Vector3;
+
+ class CollisionPointTmp {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+ }
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base
new file mode 100644
index 0000000..8568dc7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxPlaneCollider.as.svn-base
@@ -0,0 +1,78 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BoxPlaneCollider implements ICollider {
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var normal:Vector3 = new Vector3();
+
+ public function BoxPlaneCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var box:RigidBox = body1 as RigidBox;
+// var plane:RigidPlane;
+// if (box == null) {
+// box = body2 as RigidBox;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные координаты вершин бокса
+// var sx:Number = box.halfSize.x;
+// var sy:Number = box.halfSize.y;
+// var sz:Number = box.halfSize.z;
+// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
+// (verts1[1] as Vector3).reset(sx, -sy, -sz);
+// (verts1[2] as Vector3).reset(sx, sy, -sz);
+// (verts1[3] as Vector3).reset(-sx, sy, -sz);
+// (verts1[4] as Vector3).reset(-sx, -sy, sz);
+// (verts1[5] as Vector3).reset(sx, -sy, sz);
+// (verts1[6] as Vector3).reset(sx, sy, sz);
+// (verts1[7] as Vector3).reset(-sx, sy, sz);
+//
+// box.transform.transformVectors(verts1, verts2);
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+// // Проверяем наличие столкновений с каждой вершиной
+// collisionInfo.pcount = 0;
+// for (var i:int = 0; i < 8; i++) {
+// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
+// var dist:Number = (verts2[i] as Vector3).dot(normal);
+// if (dist < offset) {
+// var cp:ContactPoint;
+// if (collisionInfo.pcount == collisionInfo.points.length) {
+// cp = new ContactPoint();
+// collisionInfo.points[collisionInfo.pcount] = cp;
+// } else {
+// cp = collisionInfo.points[collisionInfo.pcount];
+// }
+// cp.pos.copy(verts2[i]);
+// cp.r1.diff(cp.pos, box.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+// cp.penetration = offset - dist;
+// collisionInfo.pcount++;
+// }
+// }
+// if (collisionInfo.pcount > 0) {
+// collisionInfo.body1 = box;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// return true;
+// }
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base
new file mode 100644
index 0000000..b5287f3
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxRectCollider.as.svn-base
@@ -0,0 +1,734 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class BoxRectCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var bPos:Vector3 = new Vector3();
+ private var rPos:Vector3 = new Vector3();
+
+ private var vectorToBox:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+
+ /**
+ *
+ */
+ public function BoxRectCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box:CollisionBox = prim1 as CollisionBox;
+ var rect:CollisionRect;
+ if (box == null) {
+ box = prim2 as CollisionBox;
+ rect = prim1 as CollisionRect;
+ } else {
+ rect = prim2 as CollisionRect;
+ }
+
+ // Вектор из центра прямоугольника в центр бокса
+ box.transform.getAxis(3, bPos);
+ rect.transform.getAxis(3, rPos);
+ vectorToBox.vDiff(bPos, rPos);
+
+ // Проверка пересечения по нормали прямоугольника
+ rect.transform.getAxis(2, axis22);
+ if (!testAxis(box, rect, axis22, 0, vectorToBox)) return false;
+
+ // Проверка пересечения по основным осям бокса
+ box.transform.getAxis(0, axis10);
+ if (!testAxis(box, rect, axis10, 1, vectorToBox)) return false;
+ box.transform.getAxis(1, axis11);
+ if (!testAxis(box, rect, axis11, 2, vectorToBox)) return false;
+ box.transform.getAxis(2, axis12);
+ if (!testAxis(box, rect, axis12, 3, vectorToBox)) return false;
+
+ // Получаем направляющие рёбер прямоугольника
+ rect.transform.getAxis(0, axis20);
+ rect.transform.getAxis(1, axis21);
+
+ // Проверка производных осей
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis20), 4, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis10, axis21), 5, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis20), 6, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis11, axis21), 7, vectorToBox)) return false;
+
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis20), 8, vectorToBox)) return false;
+ if (!testAxis(box, rect, axis.vCross2(axis12, axis21), 9, vectorToBox)) return false;
+
+ if (bestAxisIndex < 4) {
+ // Контакт вдоль одной из основных осей
+ if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 4;
+ findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact);
+ }
+ contact.body1 = box.body;
+ contact.body2 = rect.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта бокса с прямоугольником.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
+ * @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
+ * @param colInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, colInfo:Contact):Boolean {
+ var pnum:int, i:int, v:Vector3, cp:ContactPoint;
+ if (faceAxisIdx == 0) {
+ // Столкновение с плоскостью прямоугольника
+
+ // Проверим положение бокса относительно плоскости прямоугольника
+ rect.transform.getAxis(2, colAxis);
+ var offset:Number = colAxis.vDot(rPos);
+ if (bPos.vDot(colAxis) < offset) return false;
+
+ // Ищем ось бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
+ // по прямоугольнику. Таким образом получается список потенциальных точек контакта.
+ box.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box.hs, incFaceAxisIdx, axis.vDot(colAxis) > 0, verts1);
+ box.transform.transformVectors(verts1, verts2);
+ rect.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByRect(rect.hs);
+ // Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ v = verts1[i];
+ if (v.z < tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ rect.transform.transformVector(v, cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = -v.z;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ } else {
+ // Столкновение с гранью бокса
+ faceAxisIdx--;
+ box.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox) > 0;
+ if (!faceReversed) colAxis.vReverse();
+
+ rect.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) return false;
+
+ getFaceVertsByAxis(rect.hs, 2, false, verts1);
+ rect.transform.transformVectors(verts1, verts2);
+ box.transform.transformVectorsInverse(verts2, verts1);
+ pnum = clipByBox(box.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ colInfo.pcount = 0;
+ for (i = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ cp = colInfo.points[colInfo.pcount++];
+ box.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = pen;
+ }
+ }
+ colInfo.normal.vCopy(colAxis);
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(hs:Vector3, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param hs
+ * @return
+ */
+ private function clipByRect(hs:Vector3):int {
+ var pnum:int = 4;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box первый бокс
+ * @param rect второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param colInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, colInfo:Contact):void {
+ box.transform.getAxis(axisIdx1, axis10);
+ rect.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону бокса
+ if (colAxis.vDot(vectorToBox) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box.hs);
+ point2.vCopy(rect.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box.hs.x;
+ } else {
+ box.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = rect.hs.x;
+ } else {
+ rect.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = rect.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ rect.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box.transform);
+ point2.vTransformBy4(rect.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ colInfo.normal.vCopy(colAxis);
+ colInfo.pcount = 1;
+ var cp:ContactPoint = colInfo.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, bPos);
+ cp.r2.vDiff(cp.pos, rPos);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box
+ * @param rect
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) return true;
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия вдоль заданной оси.
+ *
+ * @param box бокс
+ * @param rect прямоугольник
+ * @param axis ось
+ * @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
+ * @return величина перекрытия вдоль оси
+ */
+ public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
+ var m:Matrix4 = box.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = rect.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base
new file mode 100644
index 0000000..2b166f3
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxSphereCollider.as.svn-base
@@ -0,0 +1,104 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+ /**
+ *
+ */
+ public class BoxSphereCollider implements ICollider {
+
+ private var center:Vector3 = new Vector3();
+ private var closestPt:Vector3 = new Vector3();
+
+ private var bPos:Vector3 = new Vector3();
+ private var sPos:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function BoxSphereCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var sphere:CollisionSphere = prim1 as CollisionSphere;
+ var box:CollisionBox;
+ if (sphere == null) {
+ sphere = prim2 as CollisionSphere;
+ box = prim1 as CollisionBox;
+ } else {
+ box = prim2 as CollisionBox;
+ }
+ // Трансформируем центр сферы в систему координат бокса
+ sphere.transform.getAxis(3, sPos);
+ box.transform.getAxis(3, bPos);
+ box.transform.transformVectorInverse(sPos, center);
+ // Выполняем поиск разделяющей оси
+ var hs:Vector3 = box.hs;
+ var sx:Number = hs.x + sphere.r;
+ var sy:Number = hs.y + sphere.r;
+ var sz:Number = hs.z + sphere.r;
+ if (center.x > sx || center.x < -sx
+ || center.y > sy || center.y < -sy
+ || center.z > sz || center.z < -sz) {
+ return false;
+ }
+ // Находим ближайшую к сфере точку на боксе
+ if (center.x > hs.x) {
+ closestPt.x = hs.x;
+ } else if (center.x < -hs.x) {
+ closestPt.x = -hs.x;
+ } else {
+ closestPt.x = center.x;
+ }
+
+ if (center.y > hs.y) {
+ closestPt.y = hs.y;
+ } else if (center.y < -hs.y) {
+ closestPt.y = -hs.y;
+ } else {
+ closestPt.y = center.y;
+ }
+
+ if (center.z > hs.z) {
+ closestPt.z = hs.z;
+ } else if (center.z < -hs.z) {
+ closestPt.z = -hs.z;
+ } else {
+ closestPt.z = center.z;
+ }
+
+ // TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
+
+ var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
+ if (distSqr > sphere.r*sphere.r) {
+ return false;
+ }
+ // Зафиксированно столкновение
+ contact.body1 = sphere.body;
+ contact.body2 = box.body;
+ contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
+ contact.pcount = 1;
+
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sphere.r - Math.sqrt(distSqr);
+ cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
+ cp.r1.vDiff(cp.pos, sPos);
+ cp.r2.vDiff(cp.pos, bPos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base
new file mode 100644
index 0000000..63c6efd
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BoxTriangleCollider.as.svn-base
@@ -0,0 +1,14 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public class BoxTriangleCollider implements ICollider {
+ public function BoxTriangleCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base
new file mode 100644
index 0000000..d2c2e2a
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/BruteForceCollisionDetector.as.svn-base
@@ -0,0 +1,37 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BruteForceCollisionDetector implements ICollisionDetector {
+ public function BruteForceCollisionDetector() {
+ }
+
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
+ return false;
+ }
+
+ public function init():void {
+ }
+
+ public function getAllCollisions(contacts:Vector.):int {
+ return 0;
+ }
+
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ return false;
+ }
+
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base
new file mode 100644
index 0000000..e1a1657
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdNode.as.svn-base
@@ -0,0 +1,16 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.types.BoundBox;
+
+ public class CollisionKdNode {
+ public var objects:Vector.;
+ public var boundBox:BoundBox;
+ public var parent:CollisionKdNode;
+
+ public var axis:int = -1; // 0 - x, 1 - y, 2 - z
+ public var coord:Number;
+ public var positiveNode:CollisionKdNode;
+ public var negativeNode:CollisionKdNode;
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base
new file mode 100644
index 0000000..6c62b79
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/CollisionKdTree.as.svn-base
@@ -0,0 +1,259 @@
+package alternativa.physics.collision {
+
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+
+ /**
+ * @author mike
+ */
+ public class CollisionKdTree {
+
+ public var threshold:Number = 0.01;
+ public var minPrimitivesPerNode:int = 1;
+
+ public var rootNode:CollisionKdNode;
+ public var staticChildren:Vector. = new Vector.();
+ public var numStaticChildren:int;
+ private var staticBoundBoxes:Vector. = new Vector.();
+
+ /**
+ * @param primitive
+ */
+ public function addStaticPrimitive(primitive:CollisionPrimitive):void {
+ staticChildren[numStaticChildren++] = primitive;
+ }
+
+ /**
+ * @param primitive
+ * @return
+ */
+ public function removeStaticPrimitive(primitive:CollisionPrimitive):Boolean {
+ var idx:int = staticChildren.indexOf(primitive);
+ if (idx < 0) return false;
+ staticChildren.splice(idx, 1);
+ numStaticChildren--;
+ return true;
+ }
+
+ /**
+ * @param boundBox
+ */
+ public function createTree(boundBox:BoundBox = null):void {
+ if (numStaticChildren == 0) return;
+ // Создаём корневую ноду
+ rootNode = new CollisionKdNode();
+ rootNode.objects = new Vector.();
+ // Расчитываем баунды объектов и рутовой ноды
+ var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
+ for (var i:int = 0; i < numStaticChildren; i++) {
+ var child:CollisionPrimitive = staticChildren[i];
+ var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
+ rootNodeBoundBox.addBoundBox(childBoundBox);
+ rootNode.objects[i] = i;
+ }
+ staticBoundBoxes.length = numStaticChildren;
+ // Разделяем рутовую ноду
+ splitNode(rootNode);
+ }
+
+ private var splitAxis:int;
+ private var splitCoord:Number;
+ private var splitCost:Number;
+ static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
+ static private const splitCoordsX:Vector. = new Vector.();
+ static private const splitCoordsY:Vector. = new Vector.();
+ static private const splitCoordsZ:Vector. = new Vector.();
+ /**
+ * @param node
+ */
+ private function splitNode(node:CollisionKdNode):void {
+ if (node.objects.length <= minPrimitivesPerNode) return;
+
+ var objects:Vector. = node.objects;
+ var i:int, j:int, k:int, length:int = objects.length, c1:Number, c2:Number, ct1:Number, ct2:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
+
+ var nodeBoundBox:BoundBox = node.boundBox;
+
+ // Подготовка баунда с погрешностями
+ nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
+ nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
+ nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
+ nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
+ nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
+ nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
+ var doubleThreshold:Number = threshold*2;
+
+ // Собираем опорные координаты
+ var numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
+ for (i = 0; i < length; i++) {
+ var boundBox:BoundBox = staticBoundBoxes[objects[i]];
+ if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
+ if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
+
+ if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
+ if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
+
+ if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
+ if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
+ }
+
+ // Убираем дубликаты координат, ищем наилучший сплит
+ splitAxis = -1; splitCost = 1e308;
+ i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsX) {
+ if (isNaN(c1 = splitCoordsX[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minX);
+ areaPositive = area*(nodeBoundBox.maxX - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxX <= ct2) {
+ if (boundBox.minX < ct1) numNegative++;
+ } else {
+ if (boundBox.minX >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 0;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
+ while (i < numSplitCoordsY) {
+ if (isNaN(c1 = splitCoordsY[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minY);
+ areaPositive = area*(nodeBoundBox.maxY - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxY <= ct2) {
+ if (boundBox.minY < ct1) numNegative++;
+ } else {
+ if (boundBox.minY >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 1;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
+ }
+ i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
+ while (i < numSplitCoordsZ) {
+ if (isNaN(c1 = splitCoordsZ[i++])) continue;
+ ct1 = c1 - threshold;
+ ct2 = c1 + threshold;
+ areaNegative = area*(c1 - nodeBoundBox.minZ);
+ areaPositive = area*(nodeBoundBox.maxZ - c1);
+ numNegative = numPositive = 0;
+ conflict = false;
+ // Проверяем объекты
+ for (j = 0; j < length; j++) {
+ boundBox = staticBoundBoxes[objects[j]];
+ if (boundBox.maxZ <= ct2) {
+ if (boundBox.minZ < ct1) numNegative++;
+ } else {
+ if (boundBox.minZ >= ct1) numPositive++; else {conflict = true; break;}
+ }
+ }
+ // Если хороший сплит, сохраняем
+ if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
+ splitCost = cost;
+ splitAxis = 2;
+ splitCoord = c1;
+ }
+ j = i;
+ while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
+ }
+
+ // Если сплит не найден, выходим
+ if (splitAxis < 0) return;
+
+ // Разделяем ноду
+ var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
+ node.axis = splitAxis;
+ node.coord = splitCoord;
+
+ // Создаём дочерние ноды
+ node.negativeNode = new CollisionKdNode();
+ node.positiveNode = new CollisionKdNode();
+ node.negativeNode.parent = node;
+ node.positiveNode.parent = node;
+ node.negativeNode.boundBox = nodeBoundBox.clone();
+ node.positiveNode.boundBox = nodeBoundBox.clone();
+ if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
+ else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
+ else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
+
+ // Распределяем объекты по дочерним нодам
+ ct1 = splitCoord - threshold; ct2 = splitCoord + threshold;
+ for (i = 0; i < length; i++) {
+ boundBox = staticBoundBoxes[objects[i]];
+ var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
+ var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
+ if (max <= ct2) {
+ // Объект в негативной стороне
+ if (node.negativeNode.objects == null) node.negativeNode.objects = new Vector.();
+ node.negativeNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ if (min >= ct1) {
+ // Объект в положительной стороне
+ if (node.positiveNode.objects == null) node.positiveNode.objects = new Vector.();
+ node.positiveNode.objects.push(objects[i]);
+ objects[i] = -1;
+ } else {
+ // Распилился
+ }
+ }
+ }
+
+ // Очистка списка объектов
+ j = 0;
+ for (i = 0; i < length; i++) {
+ if (objects[i] >= 0) objects[j++] = objects[i];
+ }
+ if (j > 0) objects.length = j; else node.objects = null;
+
+ // Разделение дочерних нод
+ if (node.negativeNode.objects != null) splitNode(node.negativeNode);
+ if (node.positiveNode.objects != null) splitNode(node.positiveNode);
+ }
+
+ /**
+ *
+ */
+ public function traceTree():void {
+ traceNode("", rootNode);
+ }
+
+ /**
+ * @param str
+ * @param node
+ */
+ private function traceNode(str:String, node:CollisionKdNode):void {
+ if (node == null) return;
+ trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.objects);
+ traceNode(str + "-", node.negativeNode);
+ traceNode(str + "+", node.positiveNode);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base
new file mode 100644
index 0000000..733231b
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollider.as.svn-base
@@ -0,0 +1,12 @@
+package alternativa.physics.collision {
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+
+ /**
+ * Интерфейс определителя столкновений между двумя примитивами.
+ */
+ public interface ICollider {
+ function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base
new file mode 100644
index 0000000..83e94d7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionDetector.as.svn-base
@@ -0,0 +1,73 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ * Интерфейс детектора столкновений.
+ */
+ public interface ICollisionDetector {
+
+ /**
+ * Добавляет физический примитив в коллайдер.
+ *
+ * @param primitive добавляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успешно добавлен, иначе false
+ */
+ function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Удаляет физический примитив из коллайдера.
+ *
+ * @param primitive удаляемый примитив
+ * @param isStatic указывает тип примитива: статический или динамический
+ * @return true если примитив был успшено удалён, иначе false
+ */
+ function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
+
+ /**
+ * Выполняет инициализацию детектора после обновления списка примитивов.
+ */
+ function init():void;
+
+ /**
+ * Получает все столкновения в текущей конфигурации физической геометрии.
+ *
+ * @param contacts список контактов, в кторые будет записана информация о столкновении
+ * @return количество найденных столкновений
+ */
+ function getAllCollisions(contacts:Vector.):int;
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+
+ /**
+ * Тестирует луч на пересечение со статической физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
+ *
+ * @param origin начальная точка луча в мировых координатах
+ * @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
+ * @param predicate предикат, применяемый к столкновениям
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base
new file mode 100644
index 0000000..bb1b756
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/ICollisionPredicate.as.svn-base
@@ -0,0 +1,9 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+
+ public interface ICollisionPredicate {
+
+ function considerCollision(primitive:CollisionPrimitive):Boolean;
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base
new file mode 100644
index 0000000..0f3d929
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/IRayCollisionPredicate.as.svn-base
@@ -0,0 +1,7 @@
+package alternativa.physics.collision {
+ import alternativa.physics.rigid.Body;
+
+ public interface IRayCollisionPredicate {
+ function considerBody(body:Body):Boolean;
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base
new file mode 100644
index 0000000..b12218e
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/KdTreeCollisionDetector.as.svn-base
@@ -0,0 +1,452 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.types.BoundBox;
+ import alternativa.physics.collision.types.RayIntersection;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
+ */
+ public class KdTreeCollisionDetector implements ICollisionDetector {
+
+ altphysics var tree:CollisionKdTree;
+ altphysics var dynamicPrimitives:Vector.;
+ altphysics var dynamicPrimitivesNum:int;
+ altphysics var threshold:Number = 0.0001;
+ private var colliders:Object = {};
+
+ private var _time:MinMax = new MinMax();
+ private var _n:Vector3 = new Vector3();
+ private var _o:Vector3 = new Vector3();
+ private var _dynamicIntersection:RayIntersection = new RayIntersection();
+
+ /**
+ *
+ */
+ public function KdTreeCollisionDetector() {
+ tree = new CollisionKdTree();
+ dynamicPrimitives = new Vector.();
+
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
+// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
+ addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
+
+// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
+ addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
+ }
+
+ /**
+ * @param primitive
+ * @param isStatic
+ * @return
+ */
+ public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) tree.addStaticPrimitive(primitive);
+ else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
+ return true;
+ }
+
+ /**
+ *
+ * @param primitive
+ * @param isStatic
+ * @return
+ *
+ */
+ public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
+ if (isStatic) return tree.removeStaticPrimitive(primitive);
+ var idx:int = dynamicPrimitives.indexOf(primitive);
+ if (idx < 0) return false;
+ dynamicPrimitives.splice(idx, 1);
+ dynamicPrimitivesNum--;
+ return true;
+ }
+
+ /**
+ *
+ */
+ public function init():void {
+ tree.createTree();
+// tree.traceTree();
+ }
+
+ /**
+ *
+ * @param contacts
+ * @return
+ */
+ public function getAllCollisions(contacts:Vector.):int {
+ var colNum:int = 0;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ primitive.calculateAABB();
+ if (primitive.body != null && primitive.body.frozen) continue;
+ colNum += getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts, colNum);
+
+ // Столкновения динамических примитивов между собой
+ // TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
+ for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
+ if (collide(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
+ }
+ }
+
+ return colNum;
+ }
+
+ /**
+ * @param prim1
+ * @param prim2
+ * @param contact
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
+ if (prim1.body != null && prim1.body == prim2.body) return false;
+ if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
+ var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
+ if (collider != null && collider.collide(prim1, prim2, contact)) {
+ if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
+ if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
+ // Сохраняем ссылку на контакт для каждого тела
+ if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
+ if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
+ // Вычисляем максимальную глубину пересечения для контакта
+ contact.maxPenetration = (contact.points[0] as ContactPoint).penetration;
+ var pen:Number;
+ for (var i:int = contact.pcount - 1; i >= 1; i--) {
+ if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
+ * В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup идентификатор группы
+ * @param maxTime параметр, задающий длину проверяемого сегмента
+ * @param predicate
+ * @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
+ * переданной структуре не гарантируется.
+ * @return true в случае наличия пересечения, иначе false
+ */
+ public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
+ var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
+
+ if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
+
+ if (hasDynamicIntersection && hasStaticIntersection) {
+ if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ if (hasStaticIntersection) return true;
+
+ result.copy(_dynamicIntersection);
+ return true;
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ *
+ */
+ public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // Вычисление точки пересечения с корневм узлом
+ if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
+ if (_time.max < 0 || _time.min > maxTime) return false;
+
+ if (_time.min <= 0) {
+ _time.min = 0;
+ _o.x = origin.x;
+ _o.y = origin.y;
+ _o.z = origin.z;
+ } else {
+ _o.x = origin.x + _time.min*dir.x;
+ _o.y = origin.y + _time.min*dir.y;
+ _o.z = origin.z + _time.min*dir.z;
+ }
+ if (_time.max > maxTime) _time.max = maxTime;
+
+ var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
+ return hasIntersection ? result.t <= maxTime : false;
+ }
+
+ /**
+ *
+ * @param type1
+ * @param type2
+ * @param collider
+ */
+ private function addCollider(type1:int, type2:int, collider:ICollider):void {
+ colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
+ }
+
+ /**
+ * Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
+ *
+ * @param node
+ * @param primitive
+ * @param contacts
+ * @param startIndex
+ * @return
+ */
+ private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Vector., startIndex:int):int {
+ var colNum:int = 0;
+ if (node.objects != null) {
+ // Поиск столкновений со статическими примитивами узла
+ var primitives:Vector. = tree.staticChildren;
+ var indices:Vector. = node.objects;
+ for (var i:int = indices.length - 1; i >= 0; i--)
+ if (collide(primitive, primitives[indices[i]], contacts[startIndex + colNum])) colNum++;
+ }
+ if (node.axis == -1) return colNum;
+ var min:Number;
+ var max:Number;
+ switch (node.axis) {
+ case 0:
+ min = primitive.aabb.minX;
+ max = primitive.aabb.maxX;
+ break;
+ case 1:
+ min = primitive.aabb.minY;
+ max = primitive.aabb.maxY;
+ break;
+ case 2:
+ min = primitive.aabb.minZ;
+ max = primitive.aabb.maxZ;
+ break;
+ }
+ if (min < node.coord) colNum += getPrimitiveNodeCollisions(node.negativeNode, primitive, contacts, startIndex + colNum);
+ if (max > node.coord) colNum += getPrimitiveNodeCollisions(node.positiveNode, primitive, contacts, startIndex + colNum);
+ return colNum;
+ }
+
+ private static var _rayAABB:BoundBox = new BoundBox();
+ /**
+ * Тест пересечения луча с динамическими примитивами.
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param maxTime
+ * @param predicate
+ * @param result
+ * @return
+ */
+ private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ var xx:Number = origin.x + dir.x*maxTime;
+ var yy:Number = origin.y + dir.y*maxTime;
+ var zz:Number = origin.z + dir.z*maxTime;
+ if (xx < origin.x) {
+ _rayAABB.minX = xx;
+ _rayAABB.maxX = origin.x;
+ } else {
+ _rayAABB.minX = origin.x;
+ _rayAABB.maxX = xx;
+ }
+ if (yy < origin.y) {
+ _rayAABB.minY = yy;
+ _rayAABB.maxY = origin.y;
+ } else {
+ _rayAABB.minY = origin.y;
+ _rayAABB.maxY = yy;
+ }
+ if (zz < origin.z) {
+ _rayAABB.minZ = zz;
+ _rayAABB.maxZ = origin.z;
+ } else {
+ _rayAABB.minZ = origin.z;
+ _rayAABB.maxZ = zz;
+ }
+
+ var minTime:Number = maxTime + 1;
+ for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
+ var primitive:CollisionPrimitive = dynamicPrimitives[i];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ var paabb:BoundBox = primitive.aabb;
+ if (_rayAABB.maxX < paabb.minX || _rayAABB.minX > paabb.maxX || _rayAABB.maxY < paabb.minY || _rayAABB.minY > paabb.maxY || _rayAABB.maxZ < paabb.minZ || _rayAABB.minZ > paabb.maxZ) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ result.primitive = primitive;
+ result.normal.x = _n.x;
+ result.normal.y = _n.y;
+ result.normal.z = _n.z;
+ }
+ }
+ if (minTime > maxTime) return false;
+ result.pos.x = origin.x + dir.x*minTime;
+ result.pos.y = origin.y + dir.y*minTime;
+ result.pos.z = origin.z + dir.z*minTime;
+ result.t = minTime;
+ return true;
+ }
+
+ /**
+ * Вычисляет точки пересечения луча с AABB.
+ *
+ * @param origin точка начала луча
+ * @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
+ * @param bb AABB, с которым пересекается луч
+ * @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
+ * @return true в случае наличия хотя бы одного пересечения, иначе false
+ */
+ private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
+ time.min = -1;
+ time.max = 1e308;
+ var t1:Number;
+ var t2:Number;
+ // Цикл по осям бокса
+ for (var i:int = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ if (dir.x < threshold && dir.x > -threshold) {
+ if (origin.x < bb.minX || origin.x > bb.maxX) return false;
+ else continue;
+ }
+ t1 = (bb.minX - origin.x)/dir.x;
+ t2 = (bb.maxX - origin.x)/dir.x;
+ break;
+ case 1:
+ if (dir.y < threshold && dir.y > -threshold) {
+ if (origin.y < bb.minY || origin.y > bb.maxY) return false;
+ else continue;
+ }
+ t1 = (bb.minY - origin.y)/dir.y;
+ t2 = (bb.maxY - origin.y)/dir.y;
+ break;
+ case 2:
+ if (dir.z < threshold && dir.z > -threshold) {
+ if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
+ else continue;
+ }
+ t1 = (bb.minZ - origin.z)/dir.z;
+ t2 = (bb.maxZ - origin.z)/dir.z;
+ break;
+ }
+ if (t1 < t2) {
+ if (t1 > time.min) time.min = t1;
+ if (t2 < time.max) time.max = t2;
+ } else {
+ if (t2 > time.min) time.min = t2;
+ if (t1 < time.max) time.max = t1;
+ }
+ if (time.max < time.min) return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param node
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param t1 время входа луча в узел
+ * @param t2 время выхода луча из узла
+ * @param intersection
+ */
+ private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
+ // При наличии в узле объектов, проверяем пересечение с ними
+ if (node.objects != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.objects, predicate, result)) return true;
+ // Выход из функции если это конечный узел
+ if (node.axis == -1) return false;
+
+ // Определение времени пересечения луча и плоскости разделения узла
+ var splitTime:Number;
+ var currChildNode:CollisionKdNode;
+ switch (node.axis) {
+ case 0:
+ if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.x)/dir.x;
+ currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 1:
+ if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.y)/dir.y;
+ currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ case 2:
+ if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
+ else splitTime = (node.coord - origin.z)/dir.z;
+ currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
+ break;
+ }
+ // Определение порядка проверки
+ if (splitTime < t1 || splitTime > t2) {
+ // Луч не переходит в соседний дочерний узел
+ return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
+ } else {
+ // Луч переходит из одного дочернего узла в другой
+ var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
+ if (intersects) return true;
+ _o.x = origin.x + splitTime*dir.x;
+ _o.y = origin.y + splitTime*dir.y;
+ _o.z = origin.z + splitTime*dir.z;
+ return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
+ }
+ }
+
+ /**
+ *
+ * @param origin
+ * @param dir
+ * @param collisionGroup
+ * @param primitives
+ * @param indices
+ * @param intersection
+ * @return
+ *
+ */
+ private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector., indices:Vector., predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
+ var pnum:int = indices.length;
+ var minTime:Number = 1e308;
+ for (var i:int = 0; i < pnum; i++) {
+ var primitive:CollisionPrimitive = primitives[indices[i]];
+ if ((primitive.collisionGroup & collisionGroup) == 0) continue;
+ if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
+ var t:Number = primitive.getSegmentIntersection(origin, dir, threshold, _n);
+ if (t > 0 && t < minTime) {
+ minTime = t;
+ intersection.primitive = primitive;
+ intersection.normal.x = _n.x;
+ intersection.normal.y = _n.y;
+ intersection.normal.z = _n.z;
+ }
+ }
+ if (minTime == 1e308) return false;
+ intersection.pos.x = origin.x + dir.x*minTime;
+ intersection.pos.y = origin.y + dir.y*minTime;
+ intersection.pos.z = origin.z + dir.z*minTime;
+ intersection.t = minTime;
+ return true;
+ }
+
+ }
+}
+
+ class MinMax {
+ public var min:Number = 0;
+ public var max:Number = 0;
+ }
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base
new file mode 100644
index 0000000..9b527d8
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SpherePlaneCollider.as.svn-base
@@ -0,0 +1,58 @@
+package alternativa.physics.collision {
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ /**
+ *
+ */
+ public class SpherePlaneCollider implements ICollider {
+
+ private var normal:Vector3 = new Vector3();
+
+ /**
+ *
+ */
+ public function SpherePlaneCollider() {
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param collisionInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var sphere:RigidSphere = body1 as RigidSphere;
+// var plane:RigidPlane;
+// if (sphere == null) {
+// sphere = body2 as RigidSphere;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+//
+// var dist:Number = sphere.state.pos.dot(normal) - offset;
+// if (dist > sphere.r) return false;
+//
+// collisionInfo.body1 = sphere;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// collisionInfo.pcount = 1;
+//
+// var cp:ContactPoint = collisionInfo.points[0];
+// cp.penetration = sphere.r - dist;
+// cp.pos.copy(normal).reverse().scale(sphere.r).add(sphere.state.pos);
+// cp.r1.diff(cp.pos, sphere.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base
new file mode 100644
index 0000000..9111a84
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/.svn/text-base/SphereSphereCollider.as.svn-base
@@ -0,0 +1,60 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionSphere;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class SphereSphereCollider implements ICollider {
+
+ private var p1:Vector3 = new Vector3();
+ private var p2:Vector3 = new Vector3();
+
+ public function SphereSphereCollider() {
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ var s1:CollisionSphere;
+ var s2:CollisionSphere;
+ if (prim1.body != null) {
+ s1 = prim1 as CollisionSphere;
+ s2 = prim2 as CollisionSphere;
+ } else {
+ s1 = prim2 as CollisionSphere;
+ s2 = prim1 as CollisionSphere;
+ }
+
+ s1.transform.getAxis(3, p1);
+ s2.transform.getAxis(3, p2);
+ var dx:Number = p1.x - p2.x;
+ var dy:Number = p1.y - p2.y;
+ var dz:Number = p1.z - p2.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ var sum:Number = s1.r + s2.r;
+ if (len > sum*sum) return false;
+ len = Math.sqrt(len);
+ dx /= len;
+ dy /= len;
+ dz /= len;
+
+ contact.body1 = s1.body;
+ contact.body2 = s2.body;
+ contact.normal.x = dx;
+ contact.normal.y = dy;
+ contact.normal.z = dz;
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ cp.penetration = sum - len;
+ cp.pos.x = p1.x - dx*s1.r;
+ cp.pos.y = p1.y - dy*s1.r;
+ cp.pos.z = p1.z - dz*s1.r;
+ cp.r1.vDiff(cp.pos, p1);
+ cp.r2.vDiff(cp.pos, p2);
+
+ return true;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/BoxBoxCollider.as b/0.0.2.0/src/alternativa/physics/collision/BoxBoxCollider.as
new file mode 100644
index 0000000..2893b49
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/BoxBoxCollider.as
@@ -0,0 +1,791 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+
+ use namespace altphysics;
+
+ /**
+ * Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
+ */
+ public class BoxBoxCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var pos1:Vector3 = new Vector3();
+ private var pos2:Vector3 = new Vector3();
+ private var vectorToBox1:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var tmpPoints:Vector. = new Vector.(8, true);
+ private var pcount:int;
+
+ /**
+ *
+ */
+ public function BoxBoxCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ tmpPoints[i] = new ContactPoint();
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ /**
+ *
+ * @param body1
+ * @param body2
+ * @param contactInfo
+ * @return
+ */
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+ minOverlap = 1e10;
+ var box1:CollisionBox;
+ var box2:CollisionBox;
+ if (prim1.body != null) {
+ box1 = prim1 as CollisionBox;
+ box2 = prim2 as CollisionBox;
+ } else {
+ box1 = prim2 as CollisionBox;
+ box2 = prim1 as CollisionBox;
+ }
+ var transform1:Matrix4 = box1.transform;
+ var transform2:Matrix4 = box2.transform;
+
+ // Вектор из центра второго бокса в центр первого
+ pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
+ pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
+ vectorToBox1.x = pos1.x - pos2.x;
+ vectorToBox1.y = pos1.y - pos2.y;
+ vectorToBox1.z = pos1.z - pos2.z;
+
+ // Проверка пересечения по основным осям
+// box1.transform.getAxis(0, axis10);
+ axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
+ if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
+// box1.transform.getAxis(1, axis11);
+ axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
+ if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
+// box1.transform.getAxis(2, axis12);
+ axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
+ if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
+
+// box2.transform.getAxis(0, axis20);
+ axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
+ if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
+// box2.transform.getAxis(1, axis21);
+ axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
+ if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
+// box2.transform.getAxis(2, axis22);
+ axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
+ if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
+
+ // Проверка производных осей
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
+
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
+ if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
+
+ if (bestAxisIndex < 6) {
+ // Контакт грань-(грань|ребро|вершина)
+ findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
+ } else {
+ // Контакт ребро-ребро
+ bestAxisIndex -= 6;
+ findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
+ }
+ contact.body1 = box1.body;
+ contact.body2 = box2.body;
+
+ return true;
+ }
+
+ /**
+ * Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
+ * @param contactInfo структура, в которую записывается информация о точках контакта
+ */
+ private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
+ var swapNormal:Boolean = false;
+ if (faceAxisIdx > 2) {
+ // Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
+ // но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
+ var tmpBox:CollisionBox = box1;
+ box1 = box2;
+ box2 = tmpBox;
+ vectorToBox1.vReverse();
+ faceAxisIdx -= 3;
+ swapNormal = true;
+ }
+ box1.transform.getAxis(faceAxisIdx, colAxis);
+ var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
+ if (!faceReversed) colAxis.vReverse();
+ // Ищем ось второго бокса, определяющую наиболее антипараллельную грань
+ var incFaceAxisIdx:int = 0;
+ var maxDot:Number = 0;
+ for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
+ box2.transform.getAxis(axisIdx, axis);
+ var dot:Number = axis.vDot(colAxis);
+ if (dot < 0) dot = -dot;
+ if (dot > maxDot) {
+ maxDot = dot;
+ incFaceAxisIdx = axisIdx;
+ }
+ }
+ // Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
+ // по грани первого бокса. Таким образом получается список потенциальных точек контакта.
+ box2.transform.getAxis(incFaceAxisIdx, axis);
+ getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
+ box2.transform.transformVectors(verts1, verts2);
+ box1.transform.transformVectorsInverse(verts2, verts1);
+ var pnum:int = clip(box1.hs, faceAxisIdx);
+ // Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
+ var pen:Number;
+ pcount = 0;
+ for (var i:int = 0; i < pnum; i++) {
+ if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
+ var cp:ContactPoint = tmpPoints[pcount++];
+ box1.transform.transformVector(verts1[i], cp.pos);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = pen;
+ }
+ }
+ contactInfo.normal.vCopy(colAxis);
+ if (swapNormal) contactInfo.normal.vReverse();
+
+ if (pcount > 4) reducePoints();
+ for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).copyFrom(tmpPoints[i]);
+ contactInfo.pcount = pcount;
+ }
+
+ /**
+ *
+ * @param contactInfo
+ */
+ private function reducePoints():void {
+ var i:int;
+ var minIdx:int;
+ var cp1:ContactPoint;
+ var cp2:ContactPoint;
+ while (pcount > 4) {
+ var minLen:Number = 1e10;
+ var p1:Vector3 = (tmpPoints[pcount - 1] as ContactPoint).pos;
+ var p2:Vector3;
+ for (i = 0; i < pcount; i++) {
+ p2 = (tmpPoints[i] as ContactPoint).pos;
+ var dx:Number = p2.x - p1.x;
+ var dy:Number = p2.y - p1.y;
+ var dz:Number = p2.z - p1.z;
+ var len:Number = dx*dx + dy*dy + dz*dz;
+ if (len < minLen) {
+ minLen = len;
+ minIdx = i;
+ }
+ p1 = p2;
+ }
+ cp1 = tmpPoints[minIdx == 0 ? (pcount - 1) : (minIdx - 1)];
+ cp2 = tmpPoints[minIdx];
+ p1 = cp1.pos;
+ p2 = cp2.pos;
+ p2.x = 0.5*(p1.x + p2.x);
+ p2.y = 0.5*(p1.y + p2.y);
+ p2.z = 0.5*(p1.z + p2.z);
+ cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
+ if (minIdx > 0) {
+ for (i = minIdx; i < pcount; i++) tmpPoints[i - 1] = tmpPoints[i];
+ tmpPoints[pcount - 1] = cp1;
+ }
+ pcount--;
+ }
+ }
+
+ /**
+ *
+ * @param hs
+ * @param p
+ * @param axisIndex
+ * @param reverse
+ * @return
+ */
+ private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
+ switch (faceAxisIdx) {
+ case 0:
+ if (reverse) return p.x + hs.x;
+ else return hs.x - p.x;
+ case 1:
+ if (reverse) return p.y + hs.y;
+ else return hs.y - p.y;
+ case 2:
+ if (reverse) return p.z + hs.z;
+ else return hs.z - p.z;
+ }
+ return 0;
+ }
+
+ /**
+ * Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
+ *
+ * @param box бокс, в котором ишутся вершины
+ * @param axisIdx индекс нормальной оси
+ * @param reverse если указано значение true, возвращаются вершины противоположной грани
+ * @param result список, в который помещаются вершины
+ */
+ private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.):void {
+ var hs:Vector3 = box.hs;
+ switch (axisIdx) {
+ case 0:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ }
+ break;
+ case 1:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ } else {
+ (result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
+ }
+ break;
+ case 2:
+ if (reverse) {
+ (result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
+ (result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
+ (result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
+ (result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
+ } else {
+ (result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
+ (result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
+ (result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
+ (result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
+ *
+ * @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
+ * @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
+ * @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
+ */
+ private function clip(hs:Vector3, faceAxisIdx:int):int {
+ var pnum:int = 4;
+ switch (faceAxisIdx) {
+ case 0:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ case 1:
+ if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighX(hs.x, pnum, verts2, verts1);
+ case 2:
+ if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
+ if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
+ if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
+ return clipHighY(hs.y, pnum, verts2, verts1);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x > x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighX(x:Number, pnum:int, points:Vector., result:Vector.):int {
+ var x1:Number = x + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.x < x1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.x > x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.x < x1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (x - p1.x)/dx;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y > y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighY(y:Number, pnum:int, points:Vector., result:Vector.):int {
+ var y1:Number = y + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.y < y1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.y > y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.y < y1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (y - p1.y)/dy;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipLowZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z - tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z > z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ *
+ * @param x
+ * @param pnum
+ * @param points
+ * @param result
+ * @return
+ */
+ private function clipHighZ(z:Number, pnum:int, points:Vector., result:Vector.):int {
+ var z1:Number = z + tolerance;
+ var num:int = 0;
+ var p1:Vector3 = points[int(pnum - 1)];
+ var p2:Vector3;
+
+ var dx:Number;
+ var dy:Number;
+ var dz:Number;
+ var t:Number;
+
+ for (var i:int = 0; i < pnum; i++) {
+ p2 = points[i];
+ if (p1.z < z1) {
+ (result[num++] as Vector3).vCopy(p1);
+ if (p2.z > z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ } else if (p2.z < z1) {
+ dx = p2.x - p1.x;
+ dy = p2.y - p1.y;
+ dz = p2.z - p1.z;
+ t = (z - p1.z)/dz;
+ (result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
+ }
+ p1 = p2;
+ }
+ return num;
+ }
+
+ /**
+ * Вычисляет точку столкновения рёбер двух боксов.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
+ * @param axisIdx1 индекс направляющей оси ребра первого бокса
+ * @param axisIdx2 индекс направляющей оси ребра второго бокса
+ * @param contactInfo структура, в которую записывается информация о столкновении
+ */
+ private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
+ box1.transform.getAxis(axisIdx1, axis10);
+ box2.transform.getAxis(axisIdx2, axis20);
+ colAxis.vCross2(axis10, axis20).vNormalize();
+ // Разворот оси в сторону первого бокса
+ if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
+
+ /* Кодирование рёбер
+ бит 0: 1 (тип контакта ребро-ребро)
+ биты 1-2: индекс направляющей оси ребра
+ бит 3:
+ бит 4:
+ бит 5:
+ */
+ var edgeCode1:int = 1;
+ var edgeCode2:int = 1;
+
+ // Находим среднюю точку на каждом из пересекающихся рёбер
+ var halfLen1:Number;
+ var halfLen2:Number;
+ point1.vCopy(box1.hs);
+ point2.vCopy(box2.hs);
+ // x
+ if (axisIdx1 == 0) {
+ point1.x = 0;
+ halfLen1 = box1.hs.x;
+ } else {
+ box1.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.x = -point1.x;
+ edgeCode1 |= 8; // 1 << 3
+ }
+ }
+ if (axisIdx2 == 0) {
+ point2.x = 0;
+ halfLen2 = box2.hs.x;
+ } else {
+ box2.transform.getAxis(0, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.x = -point2.x;
+ edgeCode2 |= 8; // 1 << 3
+ }
+ }
+ // y
+ if (axisIdx1 == 1) {
+ point1.y = 0;
+ halfLen1 = box1.hs.y;
+ edgeCode1 |= 2; // 1 << 1
+ } else {
+ box1.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.y = -point1.y;
+ edgeCode1 |= 16; // 1 << 4
+ }
+ }
+ if (axisIdx2 == 1) {
+ point2.y = 0;
+ halfLen2 = box2.hs.y;
+ edgeCode2 |= 2; // 1 << 1
+ } else {
+ box2.transform.getAxis(1, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.y = -point2.y;
+ edgeCode2 |= 16; // 1 << 4
+ }
+ }
+ // z
+ if (axisIdx1 == 2) {
+ point1.z = 0;
+ halfLen1 = box1.hs.z;
+ edgeCode1 |= 4; // 2 << 1
+ } else {
+ box1.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) > 0) {
+ point1.z = -point1.z;
+ edgeCode1 |= 32; // 1 << 5
+ }
+ }
+ if (axisIdx2 == 2) {
+ point2.z = 0;
+ halfLen2 = box2.hs.z;
+ edgeCode2 |= 4; // 2 << 1
+ } else {
+ box2.transform.getAxis(2, tmpAxis);
+ if (tmpAxis.vDot(colAxis) < 0) {
+ point2.z = -point2.z;
+ edgeCode2 |= 32; // 1 << 5
+ }
+ }
+ // Получаем глобальные координаты средних точек рёбер
+ point1.vTransformBy4(box1.transform);
+ point2.vTransformBy4(box2.transform);
+ // Находим точку пересечения рёбер, решая систему уравнений
+ var k:Number = axis10.vDot(axis20);
+ var det:Number = k*k - 1;
+ vector.vDiff(point2, point1);
+ var c1:Number = axis10.vDot(vector);
+ var c2:Number = axis20.vDot(vector);
+ var t1:Number = (c2*k - c1)/det;
+ var t2:Number = (c2 - c1*k)/det;
+ // Запись данных о столкновении
+ contact.normal.vCopy(colAxis);
+ contact.pcount = 1;
+ var cp:ContactPoint = contact.points[0];
+ // Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
+ cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
+ cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
+ cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
+ cp.r1.vDiff(cp.pos, pos1);
+ cp.r2.vDiff(cp.pos, pos2);
+ cp.penetration = minOverlap;
+ }
+
+ /**
+ * Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
+ *
+ * @param box1
+ * @param box2
+ * @param axis
+ * @param axisIndex
+ * @param vectorToBox1
+ * @param bestAxis
+ * @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
+ */
+ private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
+ if (axis.vLengthSqr() < 0.0001) {
+ return true;
+ }
+ axis.vNormalize();
+
+ var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
+ if (overlap < -tolerance) return false;
+ if (overlap + tolerance < minOverlap) {
+ minOverlap = overlap;
+ bestAxisIndex = axisIndex;
+ }
+ return true;
+ }
+
+ /**
+ * Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
+ *
+ * @param box1 первый бокс
+ * @param box2 второй бокс
+ * @param axis ось
+ * @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
+ * @return величина перекрытия боксов вдоль оси
+ */
+ public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1:Vector3):Number {
+ var m:Matrix4 = box1.transform;
+ var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
+ if (d < 0) d = -d;
+ var projection:Number = d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ m = box2.transform;
+ d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
+ if (d < 0) d = -d;
+ projection += d;
+ d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
+ if (d < 0) d = -d;
+ projection += d;
+
+ d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
+ if (d < 0) d = -d;
+
+ return projection - d;
+ }
+
+ }
+}
+ import alternativa.physics.types.Vector3;
+
+ class CollisionPointTmp {
+
+ public var pos:Vector3 = new Vector3();
+ public var penetration:Number;
+
+ public var feature1:int;
+ public var feature2:int;
+ }
diff --git a/0.0.2.0/src/alternativa/physics/collision/BoxPlaneCollider.as b/0.0.2.0/src/alternativa/physics/collision/BoxPlaneCollider.as
new file mode 100644
index 0000000..8568dc7
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/BoxPlaneCollider.as
@@ -0,0 +1,78 @@
+package alternativa.physics.collision {
+ import __AS3__.vec.Vector;
+
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.types.Vector3;
+
+ public class BoxPlaneCollider implements ICollider {
+
+ private var verts1:Vector. = new Vector.(8, true);
+ private var verts2:Vector. = new Vector.(8, true);
+ private var normal:Vector3 = new Vector3();
+
+ public function BoxPlaneCollider() {
+ for (var i:int = 0; i < 8; i++) {
+ verts1[i] = new Vector3();
+ verts2[i] = new Vector3();
+ }
+ }
+
+ public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
+// var box:RigidBox = body1 as RigidBox;
+// var plane:RigidPlane;
+// if (box == null) {
+// box = body2 as RigidBox;
+// plane = body1 as RigidPlane;
+// } else {
+// plane = body2 as RigidPlane;
+// }
+//
+// // Вычисляем глобальные координаты вершин бокса
+// var sx:Number = box.halfSize.x;
+// var sy:Number = box.halfSize.y;
+// var sz:Number = box.halfSize.z;
+// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
+// (verts1[1] as Vector3).reset(sx, -sy, -sz);
+// (verts1[2] as Vector3).reset(sx, sy, -sz);
+// (verts1[3] as Vector3).reset(-sx, sy, -sz);
+// (verts1[4] as Vector3).reset(-sx, -sy, sz);
+// (verts1[5] as Vector3).reset(sx, -sy, sz);
+// (verts1[6] as Vector3).reset(sx, sy, sz);
+// (verts1[7] as Vector3).reset(-sx, sy, sz);
+//
+// box.transform.transformVectors(verts1, verts2);
+// // Вычисляем глобальные нормаль и смещение плоскости
+// plane.baseMatrix.transformVector(plane.normal, normal);
+// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
+// // Проверяем наличие столкновений с каждой вершиной
+// collisionInfo.pcount = 0;
+// for (var i:int = 0; i < 8; i++) {
+// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
+// var dist:Number = (verts2[i] as Vector3).dot(normal);
+// if (dist < offset) {
+// var cp:ContactPoint;
+// if (collisionInfo.pcount == collisionInfo.points.length) {
+// cp = new ContactPoint();
+// collisionInfo.points[collisionInfo.pcount] = cp;
+// } else {
+// cp = collisionInfo.points[collisionInfo.pcount];
+// }
+// cp.pos.copy(verts2[i]);
+// cp.r1.diff(cp.pos, box.state.pos);
+// cp.r2.diff(cp.pos, plane.state.pos);
+// cp.penetration = offset - dist;
+// collisionInfo.pcount++;
+// }
+// }
+// if (collisionInfo.pcount > 0) {
+// collisionInfo.body1 = box;
+// collisionInfo.body2 = plane;
+// collisionInfo.normal.copy(normal);
+// return true;
+// }
+ return false;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/0.0.2.0/src/alternativa/physics/collision/BoxRectCollider.as b/0.0.2.0/src/alternativa/physics/collision/BoxRectCollider.as
new file mode 100644
index 0000000..b5287f3
--- /dev/null
+++ b/0.0.2.0/src/alternativa/physics/collision/BoxRectCollider.as
@@ -0,0 +1,734 @@
+package alternativa.physics.collision {
+ import alternativa.physics.altphysics;
+ import alternativa.physics.collision.primitives.CollisionBox;
+ import alternativa.physics.collision.primitives.CollisionPrimitive;
+ import alternativa.physics.collision.primitives.CollisionRect;
+ import alternativa.physics.rigid.Contact;
+ import alternativa.physics.rigid.ContactPoint;
+ import alternativa.physics.types.Matrix4;
+ import alternativa.physics.types.Vector3;
+ use namespace altphysics;
+
+ public class BoxRectCollider implements ICollider {
+
+ private var tolerance:Number = 0.001;
+
+ private var bPos:Vector3 = new Vector3();
+ private var rPos:Vector3 = new Vector3();
+
+ private var vectorToBox:Vector3 = new Vector3();
+ private var axis:Vector3 = new Vector3();
+ private var axis10:Vector3 = new Vector3();
+ private var axis11:Vector3 = new Vector3();
+ private var axis12:Vector3 = new Vector3();
+ private var axis20:Vector3 = new Vector3();
+ private var axis21:Vector3 = new Vector3();
+ private var axis22:Vector3 = new Vector3();
+ private var colAxis:Vector3 = new Vector3();
+ private var tmpAxis:Vector3 = new Vector3();
+ private var vector:Vector3 = new Vector3();
+ private var point1:Vector3 = new Vector3();
+ private var point2:Vector3 = new Vector3();
+
+ private var bestAxisIndex:int;
+ private var minOverlap:Number;
+
+ private var verts1:Vector.