pbox2d 0.6.0-java → 0.8.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mvn/extensions.xml +8 -0
- data/.mvn/wrapper/maven-wrapper.properties +1 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +8 -0
- data/README.md +7 -7
- data/Rakefile +1 -2
- data/lib/box2d.jar +0 -0
- data/lib/pbox2d/version.rb +1 -1
- data/lib/pbox2d.rb +1 -0
- data/pbox2d.gemspec +6 -11
- data/pom.rb +59 -0
- data/pom.xml +82 -73
- data/src/org/jbox2d/JBox2D.gwt.xml +12 -0
- data/src/org/jbox2d/callbacks/ContactAdaptor.java +27 -0
- data/src/org/jbox2d/callbacks/ContactFilter.java +59 -0
- data/src/org/jbox2d/callbacks/ContactImpulse.java +42 -0
- data/src/org/jbox2d/callbacks/ContactListener.java +87 -0
- data/src/org/jbox2d/callbacks/DebugDraw.java +297 -0
- data/src/org/jbox2d/callbacks/DestructionListener.java +53 -0
- data/src/org/jbox2d/callbacks/PairCallback.java +29 -0
- data/src/org/jbox2d/callbacks/ParticleDestructionListener.java +20 -0
- data/src/org/jbox2d/callbacks/ParticleQueryCallback.java +19 -0
- data/src/org/jbox2d/callbacks/ParticleRaycastCallback.java +19 -0
- data/src/org/jbox2d/callbacks/QueryCallback.java +45 -0
- data/src/org/jbox2d/callbacks/RayCastCallback.java +55 -0
- data/src/org/jbox2d/callbacks/TreeCallback.java +42 -0
- data/src/org/jbox2d/callbacks/TreeRayCastCallback.java +44 -0
- data/src/org/jbox2d/collision/AABB.java +338 -0
- data/src/org/jbox2d/collision/Collision.java +1444 -0
- data/src/org/jbox2d/collision/ContactID.java +106 -0
- data/src/org/jbox2d/collision/Distance.java +773 -0
- data/src/org/jbox2d/collision/DistanceInput.java +41 -0
- data/src/org/jbox2d/collision/DistanceOutput.java +43 -0
- data/src/org/jbox2d/collision/Manifold.java +116 -0
- data/src/org/jbox2d/collision/ManifoldPoint.java +104 -0
- data/src/org/jbox2d/collision/RayCastInput.java +47 -0
- data/src/org/jbox2d/collision/RayCastOutput.java +46 -0
- data/src/org/jbox2d/collision/TimeOfImpact.java +526 -0
- data/src/org/jbox2d/collision/WorldManifold.java +200 -0
- data/src/org/jbox2d/collision/broadphase/BroadPhase.java +92 -0
- data/src/org/jbox2d/collision/broadphase/BroadPhaseStrategy.java +88 -0
- data/src/org/jbox2d/collision/broadphase/DefaultBroadPhaseBuffer.java +268 -0
- data/src/org/jbox2d/collision/broadphase/DynamicTree.java +883 -0
- data/src/org/jbox2d/collision/broadphase/DynamicTreeFlatNodes.java +873 -0
- data/src/org/jbox2d/collision/broadphase/DynamicTreeNode.java +54 -0
- data/src/org/jbox2d/collision/broadphase/Pair.java +46 -0
- data/src/org/jbox2d/collision/shapes/ChainShape.java +264 -0
- data/src/org/jbox2d/collision/shapes/CircleShape.java +207 -0
- data/src/org/jbox2d/collision/shapes/EdgeShape.java +254 -0
- data/src/org/jbox2d/collision/shapes/MassData.java +105 -0
- data/src/org/jbox2d/collision/shapes/PolygonShape.java +718 -0
- data/src/org/jbox2d/collision/shapes/Shape.java +136 -0
- data/src/org/jbox2d/collision/shapes/ShapeType.java +32 -0
- data/src/org/jbox2d/common/BufferUtils.java +209 -0
- data/src/org/jbox2d/common/Color3f.java +88 -0
- data/src/org/jbox2d/common/IViewportTransform.java +133 -0
- data/src/org/jbox2d/common/Mat22.java +609 -0
- data/src/org/jbox2d/common/Mat33.java +290 -0
- data/src/org/jbox2d/common/MathUtils.java +335 -0
- data/src/org/jbox2d/common/OBBViewportTransform.java +174 -0
- data/src/org/jbox2d/common/PlatformMathUtils.java +46 -0
- data/src/org/jbox2d/common/RaycastResult.java +37 -0
- data/src/org/jbox2d/common/Rot.java +150 -0
- data/src/org/jbox2d/common/Settings.java +246 -0
- data/src/org/jbox2d/common/Sweep.java +116 -0
- data/src/org/jbox2d/common/Timer.java +46 -0
- data/src/org/jbox2d/common/Transform.java +203 -0
- data/src/org/jbox2d/common/Vec2.java +388 -0
- data/src/org/jbox2d/common/Vec3.java +170 -0
- data/src/org/jbox2d/dynamics/Body.java +1246 -0
- data/src/org/jbox2d/dynamics/BodyDef.java +382 -0
- data/src/org/jbox2d/dynamics/BodyType.java +41 -0
- data/src/org/jbox2d/dynamics/ContactManager.java +293 -0
- data/src/org/jbox2d/dynamics/Filter.java +62 -0
- data/src/org/jbox2d/dynamics/Fixture.java +454 -0
- data/src/org/jbox2d/dynamics/FixtureDef.java +214 -0
- data/src/org/jbox2d/dynamics/FixtureProxy.java +38 -0
- data/src/org/jbox2d/dynamics/Island.java +602 -0
- data/src/org/jbox2d/dynamics/Profile.java +97 -0
- data/src/org/jbox2d/dynamics/SolverData.java +33 -0
- data/src/org/jbox2d/dynamics/TimeStep.java +46 -0
- data/src/org/jbox2d/dynamics/World.java +2075 -0
- data/src/org/jbox2d/dynamics/contacts/ChainAndCircleContact.java +57 -0
- data/src/org/jbox2d/dynamics/contacts/ChainAndPolygonContact.java +57 -0
- data/src/org/jbox2d/dynamics/contacts/CircleContact.java +50 -0
- data/src/org/jbox2d/dynamics/contacts/Contact.java +365 -0
- data/src/org/jbox2d/dynamics/contacts/ContactCreator.java +35 -0
- data/src/org/jbox2d/dynamics/contacts/ContactEdge.java +56 -0
- data/src/org/jbox2d/dynamics/contacts/ContactPositionConstraint.java +49 -0
- data/src/org/jbox2d/dynamics/contacts/ContactRegister.java +31 -0
- data/src/org/jbox2d/dynamics/contacts/ContactSolver.java +1104 -0
- data/src/org/jbox2d/dynamics/contacts/ContactVelocityConstraint.java +60 -0
- data/src/org/jbox2d/dynamics/contacts/EdgeAndCircleContact.java +52 -0
- data/src/org/jbox2d/dynamics/contacts/EdgeAndPolygonContact.java +52 -0
- data/src/org/jbox2d/dynamics/contacts/PolygonAndCircleContact.java +51 -0
- data/src/org/jbox2d/dynamics/contacts/PolygonContact.java +50 -0
- data/src/org/jbox2d/dynamics/contacts/Position.java +31 -0
- data/src/org/jbox2d/dynamics/contacts/Velocity.java +31 -0
- data/src/org/jbox2d/dynamics/joints/ConstantVolumeJoint.java +258 -0
- data/src/org/jbox2d/dynamics/joints/ConstantVolumeJointDef.java +75 -0
- data/src/org/jbox2d/dynamics/joints/DistanceJoint.java +356 -0
- data/src/org/jbox2d/dynamics/joints/DistanceJointDef.java +106 -0
- data/src/org/jbox2d/dynamics/joints/FrictionJoint.java +294 -0
- data/src/org/jbox2d/dynamics/joints/FrictionJointDef.java +78 -0
- data/src/org/jbox2d/dynamics/joints/GearJoint.java +520 -0
- data/src/org/jbox2d/dynamics/joints/GearJointDef.java +58 -0
- data/src/org/jbox2d/dynamics/joints/Jacobian.java +32 -0
- data/src/org/jbox2d/dynamics/joints/Joint.java +235 -0
- data/src/org/jbox2d/dynamics/joints/JointDef.java +65 -0
- data/src/org/jbox2d/dynamics/joints/JointEdge.java +57 -0
- data/src/org/jbox2d/dynamics/joints/JointType.java +28 -0
- data/src/org/jbox2d/dynamics/joints/LimitState.java +28 -0
- data/src/org/jbox2d/dynamics/joints/MotorJoint.java +339 -0
- data/src/org/jbox2d/dynamics/joints/MotorJointDef.java +55 -0
- data/src/org/jbox2d/dynamics/joints/MouseJoint.java +262 -0
- data/src/org/jbox2d/dynamics/joints/MouseJointDef.java +62 -0
- data/src/org/jbox2d/dynamics/joints/PrismaticJoint.java +808 -0
- data/src/org/jbox2d/dynamics/joints/PrismaticJointDef.java +120 -0
- data/src/org/jbox2d/dynamics/joints/PulleyJoint.java +393 -0
- data/src/org/jbox2d/dynamics/joints/PulleyJointDef.java +105 -0
- data/src/org/jbox2d/dynamics/joints/RevoluteJoint.java +554 -0
- data/src/org/jbox2d/dynamics/joints/RevoluteJointDef.java +137 -0
- data/src/org/jbox2d/dynamics/joints/RopeJoint.java +276 -0
- data/src/org/jbox2d/dynamics/joints/RopeJointDef.java +34 -0
- data/src/org/jbox2d/dynamics/joints/WeldJoint.java +424 -0
- data/src/org/jbox2d/dynamics/joints/WeldJointDef.java +85 -0
- data/src/org/jbox2d/dynamics/joints/WheelJoint.java +498 -0
- data/src/org/jbox2d/dynamics/joints/WheelJointDef.java +98 -0
- data/src/org/jbox2d/particle/ParticleBodyContact.java +17 -0
- data/src/org/jbox2d/particle/ParticleColor.java +52 -0
- data/src/org/jbox2d/particle/ParticleContact.java +14 -0
- data/src/org/jbox2d/particle/ParticleDef.java +24 -0
- data/src/org/jbox2d/particle/ParticleGroup.java +154 -0
- data/src/org/jbox2d/particle/ParticleGroupDef.java +62 -0
- data/src/org/jbox2d/particle/ParticleGroupType.java +8 -0
- data/src/org/jbox2d/particle/ParticleSystem.java +2172 -0
- data/src/org/jbox2d/particle/ParticleType.java +28 -0
- data/src/org/jbox2d/particle/StackQueue.java +44 -0
- data/src/org/jbox2d/particle/VoronoiDiagram.java +209 -0
- data/src/org/jbox2d/pooling/IDynamicStack.java +47 -0
- data/src/org/jbox2d/pooling/IOrderedStack.java +57 -0
- data/src/org/jbox2d/pooling/IWorldPool.java +101 -0
- data/src/org/jbox2d/pooling/arrays/FloatArray.java +50 -0
- data/src/org/jbox2d/pooling/arrays/GeneratorArray.java +33 -0
- data/src/org/jbox2d/pooling/arrays/IntArray.java +53 -0
- data/src/org/jbox2d/pooling/arrays/Vec2Array.java +57 -0
- data/src/org/jbox2d/pooling/normal/CircleStack.java +77 -0
- data/src/org/jbox2d/pooling/normal/DefaultWorldPool.java +331 -0
- data/src/org/jbox2d/pooling/normal/MutableStack.java +72 -0
- data/src/org/jbox2d/pooling/normal/OrderedStack.java +73 -0
- data/src/org/jbox2d/pooling/stacks/DynamicIntStack.java +60 -0
- metadata +161 -14
- data/lib/jbox2d-library-2.3.1-SNAPSHOT.jar +0 -0
@@ -0,0 +1,883 @@
|
|
1
|
+
/*******************************************************************************
|
2
|
+
* Copyright (c) 2013, Daniel Murphy
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
* are permitted provided that the following conditions are met:
|
7
|
+
* * Redistributions of source code must retain the above copyright notice,
|
8
|
+
* this list of conditions and the following disclaimer.
|
9
|
+
* * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
* this list of conditions and the following disclaimer in the documentation
|
11
|
+
* and/or other materials provided with the distribution.
|
12
|
+
*
|
13
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
16
|
+
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
17
|
+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
18
|
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
19
|
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
20
|
+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
21
|
+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
22
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
23
|
+
******************************************************************************/
|
24
|
+
package org.jbox2d.collision.broadphase;
|
25
|
+
|
26
|
+
import org.jbox2d.callbacks.DebugDraw;
|
27
|
+
import org.jbox2d.callbacks.TreeCallback;
|
28
|
+
import org.jbox2d.callbacks.TreeRayCastCallback;
|
29
|
+
import org.jbox2d.collision.AABB;
|
30
|
+
import org.jbox2d.collision.RayCastInput;
|
31
|
+
import org.jbox2d.common.Color3f;
|
32
|
+
import org.jbox2d.common.MathUtils;
|
33
|
+
import org.jbox2d.common.Settings;
|
34
|
+
import org.jbox2d.common.Vec2;
|
35
|
+
|
36
|
+
/**
|
37
|
+
* A dynamic tree arranges data in a binary tree to accelerate queries such as volume queries and
|
38
|
+
* ray casts. Leafs are proxies with an AABB. In the tree we expand the proxy AABB by _fatAABBFactor
|
39
|
+
* so that the proxy AABB is bigger than the client object. This allows the client object to move by
|
40
|
+
* small amounts without triggering a tree update.
|
41
|
+
*
|
42
|
+
* @author daniel
|
43
|
+
*/
|
44
|
+
public class DynamicTree implements BroadPhaseStrategy {
|
45
|
+
public static final int MAX_STACK_SIZE = 64;
|
46
|
+
public static final int NULL_NODE = -1;
|
47
|
+
|
48
|
+
private DynamicTreeNode m_root;
|
49
|
+
private DynamicTreeNode[] m_nodes;
|
50
|
+
private int m_nodeCount;
|
51
|
+
private int m_nodeCapacity;
|
52
|
+
|
53
|
+
private int m_freeList;
|
54
|
+
|
55
|
+
private final Vec2[] drawVecs = new Vec2[4];
|
56
|
+
private DynamicTreeNode[] nodeStack = new DynamicTreeNode[20];
|
57
|
+
private int nodeStackIndex = 0;
|
58
|
+
|
59
|
+
public DynamicTree() {
|
60
|
+
m_root = null;
|
61
|
+
m_nodeCount = 0;
|
62
|
+
m_nodeCapacity = 16;
|
63
|
+
m_nodes = new DynamicTreeNode[16];
|
64
|
+
|
65
|
+
// Build a linked list for the free list.
|
66
|
+
for (int i = m_nodeCapacity - 1; i >= 0; i--) {
|
67
|
+
m_nodes[i] = new DynamicTreeNode(i);
|
68
|
+
m_nodes[i].parent = (i == m_nodeCapacity - 1) ? null : m_nodes[i + 1];
|
69
|
+
m_nodes[i].height = -1;
|
70
|
+
}
|
71
|
+
m_freeList = 0;
|
72
|
+
|
73
|
+
for (int i = 0; i < drawVecs.length; i++) {
|
74
|
+
drawVecs[i] = new Vec2();
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
@Override
|
79
|
+
public final int createProxy(final AABB aabb, Object userData) {
|
80
|
+
assert(aabb.isValid());
|
81
|
+
final DynamicTreeNode node = allocateNode();
|
82
|
+
int proxyId = node.id;
|
83
|
+
// Fatten the aabb
|
84
|
+
final AABB nodeAABB = node.aabb;
|
85
|
+
nodeAABB.lowerBound.x = aabb.lowerBound.x - Settings.aabbExtension;
|
86
|
+
nodeAABB.lowerBound.y = aabb.lowerBound.y - Settings.aabbExtension;
|
87
|
+
nodeAABB.upperBound.x = aabb.upperBound.x + Settings.aabbExtension;
|
88
|
+
nodeAABB.upperBound.y = aabb.upperBound.y + Settings.aabbExtension;
|
89
|
+
node.userData = userData;
|
90
|
+
|
91
|
+
insertLeaf(proxyId);
|
92
|
+
|
93
|
+
return proxyId;
|
94
|
+
}
|
95
|
+
|
96
|
+
@Override
|
97
|
+
public final void destroyProxy(int proxyId) {
|
98
|
+
assert (0 <= proxyId && proxyId < m_nodeCapacity);
|
99
|
+
DynamicTreeNode node = m_nodes[proxyId];
|
100
|
+
assert (node.child1 == null);
|
101
|
+
|
102
|
+
removeLeaf(node);
|
103
|
+
freeNode(node);
|
104
|
+
}
|
105
|
+
|
106
|
+
@Override
|
107
|
+
public final boolean moveProxy(int proxyId, final AABB aabb, Vec2 displacement) {
|
108
|
+
assert(aabb.isValid());
|
109
|
+
assert (0 <= proxyId && proxyId < m_nodeCapacity);
|
110
|
+
final DynamicTreeNode node = m_nodes[proxyId];
|
111
|
+
assert (node.child1 == null);
|
112
|
+
|
113
|
+
final AABB nodeAABB = node.aabb;
|
114
|
+
// if (nodeAABB.contains(aabb)) {
|
115
|
+
if (nodeAABB.lowerBound.x <= aabb.lowerBound.x && nodeAABB.lowerBound.y <= aabb.lowerBound.y
|
116
|
+
&& aabb.upperBound.x <= nodeAABB.upperBound.x && aabb.upperBound.y <= nodeAABB.upperBound.y) {
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
|
120
|
+
removeLeaf(node);
|
121
|
+
|
122
|
+
// Extend AABB
|
123
|
+
final Vec2 lowerBound = nodeAABB.lowerBound;
|
124
|
+
final Vec2 upperBound = nodeAABB.upperBound;
|
125
|
+
lowerBound.x = aabb.lowerBound.x - Settings.aabbExtension;
|
126
|
+
lowerBound.y = aabb.lowerBound.y - Settings.aabbExtension;
|
127
|
+
upperBound.x = aabb.upperBound.x + Settings.aabbExtension;
|
128
|
+
upperBound.y = aabb.upperBound.y + Settings.aabbExtension;
|
129
|
+
|
130
|
+
// Predict AABB displacement.
|
131
|
+
final float dx = displacement.x * Settings.aabbMultiplier;
|
132
|
+
final float dy = displacement.y * Settings.aabbMultiplier;
|
133
|
+
if (dx < 0.0f) {
|
134
|
+
lowerBound.x += dx;
|
135
|
+
} else {
|
136
|
+
upperBound.x += dx;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (dy < 0.0f) {
|
140
|
+
lowerBound.y += dy;
|
141
|
+
} else {
|
142
|
+
upperBound.y += dy;
|
143
|
+
}
|
144
|
+
|
145
|
+
insertLeaf(proxyId);
|
146
|
+
return true;
|
147
|
+
}
|
148
|
+
|
149
|
+
@Override
|
150
|
+
public final Object getUserData(int proxyId) {
|
151
|
+
assert (0 <= proxyId && proxyId < m_nodeCapacity);
|
152
|
+
return m_nodes[proxyId].userData;
|
153
|
+
}
|
154
|
+
|
155
|
+
@Override
|
156
|
+
public final AABB getFatAABB(int proxyId) {
|
157
|
+
assert (0 <= proxyId && proxyId < m_nodeCapacity);
|
158
|
+
return m_nodes[proxyId].aabb;
|
159
|
+
}
|
160
|
+
|
161
|
+
@Override
|
162
|
+
public final void query(TreeCallback callback, AABB aabb) {
|
163
|
+
assert(aabb.isValid());
|
164
|
+
nodeStackIndex = 0;
|
165
|
+
nodeStack[nodeStackIndex++] = m_root;
|
166
|
+
|
167
|
+
while (nodeStackIndex > 0) {
|
168
|
+
DynamicTreeNode node = nodeStack[--nodeStackIndex];
|
169
|
+
if (node == null) {
|
170
|
+
continue;
|
171
|
+
}
|
172
|
+
|
173
|
+
if (AABB.testOverlap(node.aabb, aabb)) {
|
174
|
+
if (node.child1 == null) {
|
175
|
+
boolean proceed = callback.treeCallback(node.id);
|
176
|
+
if (!proceed) {
|
177
|
+
return;
|
178
|
+
}
|
179
|
+
} else {
|
180
|
+
if (nodeStack.length - nodeStackIndex - 2 <= 0) {
|
181
|
+
DynamicTreeNode[] newBuffer = new DynamicTreeNode[nodeStack.length * 2];
|
182
|
+
System.arraycopy(nodeStack, 0, newBuffer, 0, nodeStack.length);
|
183
|
+
nodeStack = newBuffer;
|
184
|
+
}
|
185
|
+
nodeStack[nodeStackIndex++] = node.child1;
|
186
|
+
nodeStack[nodeStackIndex++] = node.child2;
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
private final Vec2 r = new Vec2();
|
193
|
+
private final AABB aabb = new AABB();
|
194
|
+
private final RayCastInput subInput = new RayCastInput();
|
195
|
+
|
196
|
+
@Override
|
197
|
+
public void raycast(TreeRayCastCallback callback, RayCastInput input) {
|
198
|
+
final Vec2 p1 = input.p1;
|
199
|
+
final Vec2 p2 = input.p2;
|
200
|
+
float p1x = p1.x, p2x = p2.x, p1y = p1.y, p2y = p2.y;
|
201
|
+
float vx, vy;
|
202
|
+
float rx, ry;
|
203
|
+
float absVx, absVy;
|
204
|
+
float cx, cy;
|
205
|
+
float hx, hy;
|
206
|
+
float tempx, tempy;
|
207
|
+
r.x = p2x - p1x;
|
208
|
+
r.y = p2y - p1y;
|
209
|
+
assert ((r.x * r.x + r.y * r.y) > 0f);
|
210
|
+
r.normalize();
|
211
|
+
rx = r.x;
|
212
|
+
ry = r.y;
|
213
|
+
|
214
|
+
// v is perpendicular to the segment.
|
215
|
+
vx = -1f * ry;
|
216
|
+
vy = 1f * rx;
|
217
|
+
absVx = MathUtils.abs(vx);
|
218
|
+
absVy = MathUtils.abs(vy);
|
219
|
+
|
220
|
+
// Separating axis for segment (Gino, p80).
|
221
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
222
|
+
|
223
|
+
float maxFraction = input.maxFraction;
|
224
|
+
|
225
|
+
// Build a bounding box for the segment.
|
226
|
+
final AABB segAABB = aabb;
|
227
|
+
// Vec2 t = p1 + maxFraction * (p2 - p1);
|
228
|
+
// before inline
|
229
|
+
// temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
|
230
|
+
// Vec2.minToOut(p1, temp, segAABB.lowerBound);
|
231
|
+
// Vec2.maxToOut(p1, temp, segAABB.upperBound);
|
232
|
+
tempx = (p2x - p1x) * maxFraction + p1x;
|
233
|
+
tempy = (p2y - p1y) * maxFraction + p1y;
|
234
|
+
segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
|
235
|
+
segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
|
236
|
+
segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
|
237
|
+
segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
|
238
|
+
// end inline
|
239
|
+
|
240
|
+
nodeStackIndex = 0;
|
241
|
+
nodeStack[nodeStackIndex++] = m_root;
|
242
|
+
while (nodeStackIndex > 0) {
|
243
|
+
final DynamicTreeNode node = nodeStack[--nodeStackIndex];
|
244
|
+
if (node == null) {
|
245
|
+
continue;
|
246
|
+
}
|
247
|
+
|
248
|
+
final AABB nodeAABB = node.aabb;
|
249
|
+
if (!AABB.testOverlap(nodeAABB, segAABB)) {
|
250
|
+
continue;
|
251
|
+
}
|
252
|
+
|
253
|
+
// Separating axis for segment (Gino, p80).
|
254
|
+
// |dot(v, p1 - c)| > dot(|v|, h)
|
255
|
+
// node.aabb.getCenterToOut(c);
|
256
|
+
// node.aabb.getExtentsToOut(h);
|
257
|
+
cx = (nodeAABB.lowerBound.x + nodeAABB.upperBound.x) * .5f;
|
258
|
+
cy = (nodeAABB.lowerBound.y + nodeAABB.upperBound.y) * .5f;
|
259
|
+
hx = (nodeAABB.upperBound.x - nodeAABB.lowerBound.x) * .5f;
|
260
|
+
hy = (nodeAABB.upperBound.y - nodeAABB.lowerBound.y) * .5f;
|
261
|
+
tempx = p1x - cx;
|
262
|
+
tempy = p1y - cy;
|
263
|
+
float separation = MathUtils.abs(vx * tempx + vy * tempy) - (absVx * hx + absVy * hy);
|
264
|
+
if (separation > 0.0f) {
|
265
|
+
continue;
|
266
|
+
}
|
267
|
+
|
268
|
+
if (node.child1 == null) {
|
269
|
+
subInput.p1.x = p1x;
|
270
|
+
subInput.p1.y = p1y;
|
271
|
+
subInput.p2.x = p2x;
|
272
|
+
subInput.p2.y = p2y;
|
273
|
+
subInput.maxFraction = maxFraction;
|
274
|
+
|
275
|
+
float value = callback.raycastCallback(subInput, node.id);
|
276
|
+
|
277
|
+
if (value == 0.0f) {
|
278
|
+
// The client has terminated the ray cast.
|
279
|
+
return;
|
280
|
+
}
|
281
|
+
|
282
|
+
if (value > 0.0f) {
|
283
|
+
// Update segment bounding box.
|
284
|
+
maxFraction = value;
|
285
|
+
// temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
|
286
|
+
// Vec2.minToOut(p1, temp, segAABB.lowerBound);
|
287
|
+
// Vec2.maxToOut(p1, temp, segAABB.upperBound);
|
288
|
+
tempx = (p2x - p1x) * maxFraction + p1x;
|
289
|
+
tempy = (p2y - p1y) * maxFraction + p1y;
|
290
|
+
segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
|
291
|
+
segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
|
292
|
+
segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
|
293
|
+
segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
|
294
|
+
}
|
295
|
+
} else {
|
296
|
+
if (nodeStack.length - nodeStackIndex - 2 <= 0) {
|
297
|
+
DynamicTreeNode[] newBuffer = new DynamicTreeNode[nodeStack.length * 2];
|
298
|
+
System.arraycopy(nodeStack, 0, newBuffer, 0, nodeStack.length);
|
299
|
+
nodeStack = newBuffer;
|
300
|
+
}
|
301
|
+
nodeStack[nodeStackIndex++] = node.child1;
|
302
|
+
nodeStack[nodeStackIndex++] = node.child2;
|
303
|
+
}
|
304
|
+
}
|
305
|
+
}
|
306
|
+
|
307
|
+
@Override
|
308
|
+
public final int computeHeight() {
|
309
|
+
return computeHeight(m_root);
|
310
|
+
}
|
311
|
+
|
312
|
+
private final int computeHeight(DynamicTreeNode node) {
|
313
|
+
assert (0 <= node.id && node.id < m_nodeCapacity);
|
314
|
+
|
315
|
+
if (node.child1 == null) {
|
316
|
+
return 0;
|
317
|
+
}
|
318
|
+
int height1 = computeHeight(node.child1);
|
319
|
+
int height2 = computeHeight(node.child2);
|
320
|
+
return 1 + MathUtils.max(height1, height2);
|
321
|
+
}
|
322
|
+
|
323
|
+
/**
|
324
|
+
* Validate this tree. For testing.
|
325
|
+
*/
|
326
|
+
public void validate() {
|
327
|
+
validateStructure(m_root);
|
328
|
+
validateMetrics(m_root);
|
329
|
+
|
330
|
+
int freeCount = 0;
|
331
|
+
DynamicTreeNode freeNode = m_freeList != NULL_NODE ? m_nodes[m_freeList] : null;
|
332
|
+
while (freeNode != null) {
|
333
|
+
assert (0 <= freeNode.id && freeNode.id < m_nodeCapacity);
|
334
|
+
assert (freeNode == m_nodes[freeNode.id]);
|
335
|
+
freeNode = freeNode.parent;
|
336
|
+
++freeCount;
|
337
|
+
}
|
338
|
+
|
339
|
+
assert (getHeight() == computeHeight());
|
340
|
+
|
341
|
+
assert (m_nodeCount + freeCount == m_nodeCapacity);
|
342
|
+
}
|
343
|
+
|
344
|
+
@Override
|
345
|
+
public int getHeight() {
|
346
|
+
if (m_root == null) {
|
347
|
+
return 0;
|
348
|
+
}
|
349
|
+
return m_root.height;
|
350
|
+
}
|
351
|
+
|
352
|
+
@Override
|
353
|
+
public int getMaxBalance() {
|
354
|
+
int maxBalance = 0;
|
355
|
+
for (int i = 0; i < m_nodeCapacity; ++i) {
|
356
|
+
final DynamicTreeNode node = m_nodes[i];
|
357
|
+
if (node.height <= 1) {
|
358
|
+
continue;
|
359
|
+
}
|
360
|
+
|
361
|
+
assert (node.child1 == null == false);
|
362
|
+
|
363
|
+
DynamicTreeNode child1 = node.child1;
|
364
|
+
DynamicTreeNode child2 = node.child2;
|
365
|
+
int balance = MathUtils.abs(child2.height - child1.height);
|
366
|
+
maxBalance = MathUtils.max(maxBalance, balance);
|
367
|
+
}
|
368
|
+
|
369
|
+
return maxBalance;
|
370
|
+
}
|
371
|
+
|
372
|
+
@Override
|
373
|
+
public float getAreaRatio() {
|
374
|
+
if (m_root == null) {
|
375
|
+
return 0.0f;
|
376
|
+
}
|
377
|
+
|
378
|
+
final DynamicTreeNode root = m_root;
|
379
|
+
float rootArea = root.aabb.getPerimeter();
|
380
|
+
|
381
|
+
float totalArea = 0.0f;
|
382
|
+
for (int i = 0; i < m_nodeCapacity; ++i) {
|
383
|
+
final DynamicTreeNode node = m_nodes[i];
|
384
|
+
if (node.height < 0) {
|
385
|
+
// Free node in pool
|
386
|
+
continue;
|
387
|
+
}
|
388
|
+
|
389
|
+
totalArea += node.aabb.getPerimeter();
|
390
|
+
}
|
391
|
+
|
392
|
+
return totalArea / rootArea;
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* Build an optimal tree. Very expensive. For testing.
|
397
|
+
*/
|
398
|
+
public void rebuildBottomUp() {
|
399
|
+
int[] nodes = new int[m_nodeCount];
|
400
|
+
int count = 0;
|
401
|
+
|
402
|
+
// Build array of leaves. Free the rest.
|
403
|
+
for (int i = 0; i < m_nodeCapacity; ++i) {
|
404
|
+
if (m_nodes[i].height < 0) {
|
405
|
+
// free node in pool
|
406
|
+
continue;
|
407
|
+
}
|
408
|
+
|
409
|
+
DynamicTreeNode node = m_nodes[i];
|
410
|
+
if (node.child1 == null) {
|
411
|
+
node.parent = null;
|
412
|
+
nodes[count] = i;
|
413
|
+
++count;
|
414
|
+
} else {
|
415
|
+
freeNode(node);
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
AABB b = new AABB();
|
420
|
+
while (count > 1) {
|
421
|
+
float minCost = Float.MAX_VALUE;
|
422
|
+
int iMin = -1, jMin = -1;
|
423
|
+
for (int i = 0; i < count; ++i) {
|
424
|
+
AABB aabbi = m_nodes[nodes[i]].aabb;
|
425
|
+
|
426
|
+
for (int j = i + 1; j < count; ++j) {
|
427
|
+
AABB aabbj = m_nodes[nodes[j]].aabb;
|
428
|
+
b.combine(aabbi, aabbj);
|
429
|
+
float cost = b.getPerimeter();
|
430
|
+
if (cost < minCost) {
|
431
|
+
iMin = i;
|
432
|
+
jMin = j;
|
433
|
+
minCost = cost;
|
434
|
+
}
|
435
|
+
}
|
436
|
+
}
|
437
|
+
|
438
|
+
int index1 = nodes[iMin];
|
439
|
+
int index2 = nodes[jMin];
|
440
|
+
DynamicTreeNode child1 = m_nodes[index1];
|
441
|
+
DynamicTreeNode child2 = m_nodes[index2];
|
442
|
+
|
443
|
+
DynamicTreeNode parent = allocateNode();
|
444
|
+
parent.child1 = child1;
|
445
|
+
parent.child2 = child2;
|
446
|
+
parent.height = 1 + MathUtils.max(child1.height, child2.height);
|
447
|
+
parent.aabb.combine(child1.aabb, child2.aabb);
|
448
|
+
parent.parent = null;
|
449
|
+
|
450
|
+
child1.parent = parent;
|
451
|
+
child2.parent = parent;
|
452
|
+
|
453
|
+
nodes[jMin] = nodes[count - 1];
|
454
|
+
nodes[iMin] = parent.id;
|
455
|
+
--count;
|
456
|
+
}
|
457
|
+
|
458
|
+
m_root = m_nodes[nodes[0]];
|
459
|
+
|
460
|
+
validate();
|
461
|
+
}
|
462
|
+
|
463
|
+
private final DynamicTreeNode allocateNode() {
|
464
|
+
if (m_freeList == NULL_NODE) {
|
465
|
+
assert (m_nodeCount == m_nodeCapacity);
|
466
|
+
|
467
|
+
DynamicTreeNode[] old = m_nodes;
|
468
|
+
m_nodeCapacity *= 2;
|
469
|
+
m_nodes = new DynamicTreeNode[m_nodeCapacity];
|
470
|
+
System.arraycopy(old, 0, m_nodes, 0, old.length);
|
471
|
+
|
472
|
+
// Build a linked list for the free list.
|
473
|
+
for (int i = m_nodeCapacity - 1; i >= m_nodeCount; i--) {
|
474
|
+
m_nodes[i] = new DynamicTreeNode(i);
|
475
|
+
m_nodes[i].parent = (i == m_nodeCapacity - 1) ? null : m_nodes[i + 1];
|
476
|
+
m_nodes[i].height = -1;
|
477
|
+
}
|
478
|
+
m_freeList = m_nodeCount;
|
479
|
+
}
|
480
|
+
int nodeId = m_freeList;
|
481
|
+
final DynamicTreeNode treeNode = m_nodes[nodeId];
|
482
|
+
m_freeList = treeNode.parent != null ? treeNode.parent.id : NULL_NODE;
|
483
|
+
|
484
|
+
treeNode.parent = null;
|
485
|
+
treeNode.child1 = null;
|
486
|
+
treeNode.child2 = null;
|
487
|
+
treeNode.height = 0;
|
488
|
+
treeNode.userData = null;
|
489
|
+
++m_nodeCount;
|
490
|
+
return treeNode;
|
491
|
+
}
|
492
|
+
|
493
|
+
/**
|
494
|
+
* returns a node to the pool
|
495
|
+
*/
|
496
|
+
private final void freeNode(DynamicTreeNode node) {
|
497
|
+
assert (node != null);
|
498
|
+
assert (0 < m_nodeCount);
|
499
|
+
node.parent = m_freeList != NULL_NODE ? m_nodes[m_freeList] : null;
|
500
|
+
node.height = -1;
|
501
|
+
m_freeList = node.id;
|
502
|
+
m_nodeCount--;
|
503
|
+
}
|
504
|
+
|
505
|
+
private final AABB combinedAABB = new AABB();
|
506
|
+
|
507
|
+
private final void insertLeaf(int leaf_index) {
|
508
|
+
DynamicTreeNode leaf = m_nodes[leaf_index];
|
509
|
+
if (m_root == null) {
|
510
|
+
m_root = leaf;
|
511
|
+
m_root.parent = null;
|
512
|
+
return;
|
513
|
+
}
|
514
|
+
|
515
|
+
// find the best sibling
|
516
|
+
AABB leafAABB = leaf.aabb;
|
517
|
+
DynamicTreeNode index = m_root;
|
518
|
+
while (index.child1 != null) {
|
519
|
+
final DynamicTreeNode node = index;
|
520
|
+
DynamicTreeNode child1 = node.child1;
|
521
|
+
DynamicTreeNode child2 = node.child2;
|
522
|
+
|
523
|
+
float area = node.aabb.getPerimeter();
|
524
|
+
|
525
|
+
combinedAABB.combine(node.aabb, leafAABB);
|
526
|
+
float combinedArea = combinedAABB.getPerimeter();
|
527
|
+
|
528
|
+
// Cost of creating a new parent for this node and the new leaf
|
529
|
+
float cost = 2.0f * combinedArea;
|
530
|
+
|
531
|
+
// Minimum cost of pushing the leaf further down the tree
|
532
|
+
float inheritanceCost = 2.0f * (combinedArea - area);
|
533
|
+
|
534
|
+
// Cost of descending into child1
|
535
|
+
float cost1;
|
536
|
+
if (child1.child1 == null) {
|
537
|
+
combinedAABB.combine(leafAABB, child1.aabb);
|
538
|
+
cost1 = combinedAABB.getPerimeter() + inheritanceCost;
|
539
|
+
} else {
|
540
|
+
combinedAABB.combine(leafAABB, child1.aabb);
|
541
|
+
float oldArea = child1.aabb.getPerimeter();
|
542
|
+
float newArea = combinedAABB.getPerimeter();
|
543
|
+
cost1 = (newArea - oldArea) + inheritanceCost;
|
544
|
+
}
|
545
|
+
|
546
|
+
// Cost of descending into child2
|
547
|
+
float cost2;
|
548
|
+
if (child2.child1 == null) {
|
549
|
+
combinedAABB.combine(leafAABB, child2.aabb);
|
550
|
+
cost2 = combinedAABB.getPerimeter() + inheritanceCost;
|
551
|
+
} else {
|
552
|
+
combinedAABB.combine(leafAABB, child2.aabb);
|
553
|
+
float oldArea = child2.aabb.getPerimeter();
|
554
|
+
float newArea = combinedAABB.getPerimeter();
|
555
|
+
cost2 = newArea - oldArea + inheritanceCost;
|
556
|
+
}
|
557
|
+
|
558
|
+
// Descend according to the minimum cost.
|
559
|
+
if (cost < cost1 && cost < cost2) {
|
560
|
+
break;
|
561
|
+
}
|
562
|
+
|
563
|
+
// Descend
|
564
|
+
if (cost1 < cost2) {
|
565
|
+
index = child1;
|
566
|
+
} else {
|
567
|
+
index = child2;
|
568
|
+
}
|
569
|
+
}
|
570
|
+
|
571
|
+
DynamicTreeNode sibling = index;
|
572
|
+
DynamicTreeNode oldParent = m_nodes[sibling.id].parent;
|
573
|
+
final DynamicTreeNode newParent = allocateNode();
|
574
|
+
newParent.parent = oldParent;
|
575
|
+
newParent.userData = null;
|
576
|
+
newParent.aabb.combine(leafAABB, sibling.aabb);
|
577
|
+
newParent.height = sibling.height + 1;
|
578
|
+
|
579
|
+
if (oldParent != null) {
|
580
|
+
// The sibling was not the root.
|
581
|
+
if (oldParent.child1 == sibling) {
|
582
|
+
oldParent.child1 = newParent;
|
583
|
+
} else {
|
584
|
+
oldParent.child2 = newParent;
|
585
|
+
}
|
586
|
+
|
587
|
+
newParent.child1 = sibling;
|
588
|
+
newParent.child2 = leaf;
|
589
|
+
sibling.parent = newParent;
|
590
|
+
leaf.parent = newParent;
|
591
|
+
} else {
|
592
|
+
// The sibling was the root.
|
593
|
+
newParent.child1 = sibling;
|
594
|
+
newParent.child2 = leaf;
|
595
|
+
sibling.parent = newParent;
|
596
|
+
leaf.parent = newParent;
|
597
|
+
m_root = newParent;
|
598
|
+
}
|
599
|
+
|
600
|
+
// Walk back up the tree fixing heights and AABBs
|
601
|
+
index = leaf.parent;
|
602
|
+
while (index != null) {
|
603
|
+
index = balance(index);
|
604
|
+
|
605
|
+
DynamicTreeNode child1 = index.child1;
|
606
|
+
DynamicTreeNode child2 = index.child2;
|
607
|
+
|
608
|
+
assert (child1 != null);
|
609
|
+
assert (child2 != null);
|
610
|
+
|
611
|
+
index.height = 1 + MathUtils.max(child1.height, child2.height);
|
612
|
+
index.aabb.combine(child1.aabb, child2.aabb);
|
613
|
+
|
614
|
+
index = index.parent;
|
615
|
+
}
|
616
|
+
// validate();
|
617
|
+
}
|
618
|
+
|
619
|
+
private final void removeLeaf(DynamicTreeNode leaf) {
|
620
|
+
if (leaf == m_root) {
|
621
|
+
m_root = null;
|
622
|
+
return;
|
623
|
+
}
|
624
|
+
|
625
|
+
DynamicTreeNode parent = leaf.parent;
|
626
|
+
DynamicTreeNode grandParent = parent.parent;
|
627
|
+
DynamicTreeNode sibling;
|
628
|
+
if (parent.child1 == leaf) {
|
629
|
+
sibling = parent.child2;
|
630
|
+
} else {
|
631
|
+
sibling = parent.child1;
|
632
|
+
}
|
633
|
+
|
634
|
+
if (grandParent != null) {
|
635
|
+
// Destroy parent and connect sibling to grandParent.
|
636
|
+
if (grandParent.child1 == parent) {
|
637
|
+
grandParent.child1 = sibling;
|
638
|
+
} else {
|
639
|
+
grandParent.child2 = sibling;
|
640
|
+
}
|
641
|
+
sibling.parent = grandParent;
|
642
|
+
freeNode(parent);
|
643
|
+
|
644
|
+
// Adjust ancestor bounds.
|
645
|
+
DynamicTreeNode index = grandParent;
|
646
|
+
while (index != null) {
|
647
|
+
index = balance(index);
|
648
|
+
|
649
|
+
DynamicTreeNode child1 = index.child1;
|
650
|
+
DynamicTreeNode child2 = index.child2;
|
651
|
+
|
652
|
+
index.aabb.combine(child1.aabb, child2.aabb);
|
653
|
+
index.height = 1 + MathUtils.max(child1.height, child2.height);
|
654
|
+
|
655
|
+
index = index.parent;
|
656
|
+
}
|
657
|
+
} else {
|
658
|
+
m_root = sibling;
|
659
|
+
sibling.parent = null;
|
660
|
+
freeNode(parent);
|
661
|
+
}
|
662
|
+
|
663
|
+
// validate();
|
664
|
+
}
|
665
|
+
|
666
|
+
// Perform a left or right rotation if node A is imbalanced.
|
667
|
+
// Returns the new root index.
|
668
|
+
private DynamicTreeNode balance(DynamicTreeNode iA) {
|
669
|
+
assert (iA != null);
|
670
|
+
|
671
|
+
DynamicTreeNode A = iA;
|
672
|
+
if (A.child1 == null || A.height < 2) {
|
673
|
+
return iA;
|
674
|
+
}
|
675
|
+
|
676
|
+
DynamicTreeNode iB = A.child1;
|
677
|
+
DynamicTreeNode iC = A.child2;
|
678
|
+
assert (0 <= iB.id && iB.id < m_nodeCapacity);
|
679
|
+
assert (0 <= iC.id && iC.id < m_nodeCapacity);
|
680
|
+
|
681
|
+
DynamicTreeNode B = iB;
|
682
|
+
DynamicTreeNode C = iC;
|
683
|
+
|
684
|
+
int balance = C.height - B.height;
|
685
|
+
|
686
|
+
// Rotate C up
|
687
|
+
if (balance > 1) {
|
688
|
+
DynamicTreeNode iF = C.child1;
|
689
|
+
DynamicTreeNode iG = C.child2;
|
690
|
+
DynamicTreeNode F = iF;
|
691
|
+
DynamicTreeNode G = iG;
|
692
|
+
assert (F != null);
|
693
|
+
assert (G != null);
|
694
|
+
assert (0 <= iF.id && iF.id < m_nodeCapacity);
|
695
|
+
assert (0 <= iG.id && iG.id < m_nodeCapacity);
|
696
|
+
|
697
|
+
// Swap A and C
|
698
|
+
C.child1 = iA;
|
699
|
+
C.parent = A.parent;
|
700
|
+
A.parent = iC;
|
701
|
+
|
702
|
+
// A's old parent should point to C
|
703
|
+
if (C.parent != null) {
|
704
|
+
if (C.parent.child1 == iA) {
|
705
|
+
C.parent.child1 = iC;
|
706
|
+
} else {
|
707
|
+
assert (C.parent.child2 == iA);
|
708
|
+
C.parent.child2 = iC;
|
709
|
+
}
|
710
|
+
} else {
|
711
|
+
m_root = iC;
|
712
|
+
}
|
713
|
+
|
714
|
+
// Rotate
|
715
|
+
if (F.height > G.height) {
|
716
|
+
C.child2 = iF;
|
717
|
+
A.child2 = iG;
|
718
|
+
G.parent = iA;
|
719
|
+
A.aabb.combine(B.aabb, G.aabb);
|
720
|
+
C.aabb.combine(A.aabb, F.aabb);
|
721
|
+
|
722
|
+
A.height = 1 + MathUtils.max(B.height, G.height);
|
723
|
+
C.height = 1 + MathUtils.max(A.height, F.height);
|
724
|
+
} else {
|
725
|
+
C.child2 = iG;
|
726
|
+
A.child2 = iF;
|
727
|
+
F.parent = iA;
|
728
|
+
A.aabb.combine(B.aabb, F.aabb);
|
729
|
+
C.aabb.combine(A.aabb, G.aabb);
|
730
|
+
|
731
|
+
A.height = 1 + MathUtils.max(B.height, F.height);
|
732
|
+
C.height = 1 + MathUtils.max(A.height, G.height);
|
733
|
+
}
|
734
|
+
|
735
|
+
return iC;
|
736
|
+
}
|
737
|
+
|
738
|
+
// Rotate B up
|
739
|
+
if (balance < -1) {
|
740
|
+
DynamicTreeNode iD = B.child1;
|
741
|
+
DynamicTreeNode iE = B.child2;
|
742
|
+
DynamicTreeNode D = iD;
|
743
|
+
DynamicTreeNode E = iE;
|
744
|
+
assert (0 <= iD.id && iD.id < m_nodeCapacity);
|
745
|
+
assert (0 <= iE.id && iE.id < m_nodeCapacity);
|
746
|
+
|
747
|
+
// Swap A and B
|
748
|
+
B.child1 = iA;
|
749
|
+
B.parent = A.parent;
|
750
|
+
A.parent = iB;
|
751
|
+
|
752
|
+
// A's old parent should point to B
|
753
|
+
if (B.parent != null) {
|
754
|
+
if (B.parent.child1 == iA) {
|
755
|
+
B.parent.child1 = iB;
|
756
|
+
} else {
|
757
|
+
assert (B.parent.child2 == iA);
|
758
|
+
B.parent.child2 = iB;
|
759
|
+
}
|
760
|
+
} else {
|
761
|
+
m_root = iB;
|
762
|
+
}
|
763
|
+
|
764
|
+
// Rotate
|
765
|
+
if (D.height > E.height) {
|
766
|
+
B.child2 = iD;
|
767
|
+
A.child1 = iE;
|
768
|
+
E.parent = iA;
|
769
|
+
A.aabb.combine(C.aabb, E.aabb);
|
770
|
+
B.aabb.combine(A.aabb, D.aabb);
|
771
|
+
|
772
|
+
A.height = 1 + MathUtils.max(C.height, E.height);
|
773
|
+
B.height = 1 + MathUtils.max(A.height, D.height);
|
774
|
+
} else {
|
775
|
+
B.child2 = iE;
|
776
|
+
A.child1 = iD;
|
777
|
+
D.parent = iA;
|
778
|
+
A.aabb.combine(C.aabb, D.aabb);
|
779
|
+
B.aabb.combine(A.aabb, E.aabb);
|
780
|
+
|
781
|
+
A.height = 1 + MathUtils.max(C.height, D.height);
|
782
|
+
B.height = 1 + MathUtils.max(A.height, E.height);
|
783
|
+
}
|
784
|
+
|
785
|
+
return iB;
|
786
|
+
}
|
787
|
+
|
788
|
+
return iA;
|
789
|
+
}
|
790
|
+
|
791
|
+
private void validateStructure(DynamicTreeNode node) {
|
792
|
+
if (node == null) {
|
793
|
+
return;
|
794
|
+
}
|
795
|
+
assert (node == m_nodes[node.id]);
|
796
|
+
|
797
|
+
if (node == m_root) {
|
798
|
+
assert (node.parent == null);
|
799
|
+
}
|
800
|
+
|
801
|
+
DynamicTreeNode child1 = node.child1;
|
802
|
+
DynamicTreeNode child2 = node.child2;
|
803
|
+
|
804
|
+
if (node.child1 == null) {
|
805
|
+
assert (child1 == null);
|
806
|
+
assert (child2 == null);
|
807
|
+
assert (node.height == 0);
|
808
|
+
return;
|
809
|
+
}
|
810
|
+
|
811
|
+
assert (child1 != null && 0 <= child1.id && child1.id < m_nodeCapacity);
|
812
|
+
assert (child2 != null && 0 <= child2.id && child2.id < m_nodeCapacity);
|
813
|
+
|
814
|
+
assert (child1.parent == node);
|
815
|
+
assert (child2.parent == node);
|
816
|
+
|
817
|
+
validateStructure(child1);
|
818
|
+
validateStructure(child2);
|
819
|
+
}
|
820
|
+
|
821
|
+
private void validateMetrics(DynamicTreeNode node) {
|
822
|
+
if (node == null) {
|
823
|
+
return;
|
824
|
+
}
|
825
|
+
|
826
|
+
DynamicTreeNode child1 = node.child1;
|
827
|
+
DynamicTreeNode child2 = node.child2;
|
828
|
+
|
829
|
+
if (node.child1 == null) {
|
830
|
+
assert (child1 == null);
|
831
|
+
assert (child2 == null);
|
832
|
+
assert (node.height == 0);
|
833
|
+
return;
|
834
|
+
}
|
835
|
+
|
836
|
+
assert (child1 != null && 0 <= child1.id && child1.id < m_nodeCapacity);
|
837
|
+
assert (child2 != null && 0 <= child2.id && child2.id < m_nodeCapacity);
|
838
|
+
|
839
|
+
int height1 = child1.height;
|
840
|
+
int height2 = child2.height;
|
841
|
+
int height;
|
842
|
+
height = 1 + MathUtils.max(height1, height2);
|
843
|
+
assert (node.height == height);
|
844
|
+
|
845
|
+
AABB aabb = new AABB();
|
846
|
+
aabb.combine(child1.aabb, child2.aabb);
|
847
|
+
|
848
|
+
assert (aabb.lowerBound.equals(node.aabb.lowerBound));
|
849
|
+
assert (aabb.upperBound.equals(node.aabb.upperBound));
|
850
|
+
|
851
|
+
validateMetrics(child1);
|
852
|
+
validateMetrics(child2);
|
853
|
+
}
|
854
|
+
|
855
|
+
@Override
|
856
|
+
public void drawTree(DebugDraw argDraw) {
|
857
|
+
if (m_root == null) {
|
858
|
+
return;
|
859
|
+
}
|
860
|
+
int height = computeHeight();
|
861
|
+
drawTree(argDraw, m_root, 0, height);
|
862
|
+
}
|
863
|
+
|
864
|
+
private final Color3f color = new Color3f();
|
865
|
+
private final Vec2 textVec = new Vec2();
|
866
|
+
|
867
|
+
public void drawTree(DebugDraw argDraw, DynamicTreeNode node, int spot, int height) {
|
868
|
+
node.aabb.getVertices(drawVecs);
|
869
|
+
|
870
|
+
color.set(1, (height - spot) * 1f / height, (height - spot) * 1f / height);
|
871
|
+
argDraw.drawPolygon(drawVecs, 4, color);
|
872
|
+
|
873
|
+
argDraw.getViewportTranform().getWorldToScreen(node.aabb.upperBound, textVec);
|
874
|
+
argDraw.drawString(textVec.x, textVec.y, node.id + "-" + (spot + 1) + "/" + height, color);
|
875
|
+
|
876
|
+
if (node.child1 != null) {
|
877
|
+
drawTree(argDraw, node.child1, spot + 1, height);
|
878
|
+
}
|
879
|
+
if (node.child2 != null) {
|
880
|
+
drawTree(argDraw, node.child2, spot + 1, height);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
}
|