Package org.jbox2d.collision.broadphase

Source Code of org.jbox2d.collision.broadphase.DynamicTreeFlatNodes

/*******************************************************************************
* Copyright (c) 2013, Daniel Murphy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*  * Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
*  * Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package org.jbox2d.collision.broadphase;

import org.jbox2d.callbacks.DebugDraw;
import org.jbox2d.callbacks.TreeCallback;
import org.jbox2d.callbacks.TreeRayCastCallback;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.RayCastInput;
import org.jbox2d.common.BufferUtils;
import org.jbox2d.common.Color3f;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;

public class DynamicTreeFlatNodes implements BroadPhaseStrategy {
  public static final int MAX_STACK_SIZE = 64;
  public static final int NULL_NODE = -1;
  public static final int INITIAL_BUFFER_LENGTH = 16;

  public int m_root;
  public AABB[] m_aabb;
  public Object[] m_userData;
  protected int[] m_parent;
  protected int[] m_child1;
  protected int[] m_child2;
  protected int[] m_height;

  private int m_nodeCount;
  private int m_nodeCapacity;

  private int m_freeList;

  private final Vec2[] drawVecs = new Vec2[4];

  public DynamicTreeFlatNodes() {
    m_root = NULL_NODE;
    m_nodeCount = 0;
    m_nodeCapacity = 16;
    expandBuffers(0, m_nodeCapacity);

    for (int i = 0; i < drawVecs.length; i++) {
      drawVecs[i] = new Vec2();
    }
  }

  private void expandBuffers(int oldSize, int newSize) {
    m_aabb = BufferUtils.reallocateBuffer(AABB.class, m_aabb, oldSize, newSize);
    m_userData = BufferUtils.reallocateBuffer(Object.class, m_userData, oldSize, newSize);
    m_parent = BufferUtils.reallocateBuffer(m_parent, oldSize, newSize);
    m_child1 = BufferUtils.reallocateBuffer(m_child1, oldSize, newSize);
    m_child2 = BufferUtils.reallocateBuffer(m_child2, oldSize, newSize);
    m_height = BufferUtils.reallocateBuffer(m_height, oldSize, newSize);

    // Build a linked list for the free list.
    for (int i = oldSize; i < newSize; i++) {
      m_aabb[i] = new AABB();
      m_parent[i] = (i == newSize - 1) ? NULL_NODE : i + 1;
      m_height[i] = -1;
      m_child1[i] = -1;
      m_child2[i] = -1;
    }
    m_freeList = oldSize;
  }

  @Override
  public final int createProxy(final AABB aabb, Object userData) {
    final int node = allocateNode();
    // Fatten the aabb
    final AABB nodeAABB = m_aabb[node];
    nodeAABB.lowerBound.x = aabb.lowerBound.x - Settings.aabbExtension;
    nodeAABB.lowerBound.y = aabb.lowerBound.y - Settings.aabbExtension;
    nodeAABB.upperBound.x = aabb.upperBound.x + Settings.aabbExtension;
    nodeAABB.upperBound.y = aabb.upperBound.y + Settings.aabbExtension;
    m_userData[node] = userData;

    insertLeaf(node);

    return node;
  }

  @Override
  public final void destroyProxy(int proxyId) {
    assert (0 <= proxyId && proxyId < m_nodeCapacity);
    assert (m_child1[proxyId] == NULL_NODE);

    removeLeaf(proxyId);
    freeNode(proxyId);
  }

  @Override
  public final boolean moveProxy(int proxyId, final AABB aabb, Vec2 displacement) {
    assert (0 <= proxyId && proxyId < m_nodeCapacity);
    final int node = proxyId;
    assert (m_child1[node] == NULL_NODE);

    final AABB nodeAABB = m_aabb[node];
    // if (nodeAABB.contains(aabb)) {
    if (nodeAABB.lowerBound.x <= aabb.lowerBound.x && nodeAABB.lowerBound.y <= aabb.lowerBound.y
        && aabb.upperBound.x <= nodeAABB.upperBound.x && aabb.upperBound.y <= nodeAABB.upperBound.y) {
      return false;
    }

    removeLeaf(node);

    // Extend AABB
    final Vec2 lowerBound = nodeAABB.lowerBound;
    final Vec2 upperBound = nodeAABB.upperBound;
    lowerBound.x = aabb.lowerBound.x - Settings.aabbExtension;
    lowerBound.y = aabb.lowerBound.y - Settings.aabbExtension;
    upperBound.x = aabb.upperBound.x + Settings.aabbExtension;
    upperBound.y = aabb.upperBound.y + Settings.aabbExtension;

    // Predict AABB displacement.
    final float dx = displacement.x * Settings.aabbMultiplier;
    final float dy = displacement.y * Settings.aabbMultiplier;
    if (dx < 0.0f) {
      lowerBound.x += dx;
    } else {
      upperBound.x += dx;
    }

    if (dy < 0.0f) {
      lowerBound.y += dy;
    } else {
      upperBound.y += dy;
    }

    insertLeaf(proxyId);
    return true;
  }

  @Override
  public final Object getUserData(int proxyId) {
    assert (0 <= proxyId && proxyId < m_nodeCount);
    return m_userData[proxyId];
  }

  @Override
  public final AABB getFatAABB(int proxyId) {
    assert (0 <= proxyId && proxyId < m_nodeCount);
    return m_aabb[proxyId];
  }

  private int[] nodeStack = new int[20];
  private int nodeStackIndex;

  @Override
  public final void query(TreeCallback callback, AABB aabb) {
    nodeStackIndex = 0;
    nodeStack[nodeStackIndex++] = m_root;

    while (nodeStackIndex > 0) {
      int node = nodeStack[--nodeStackIndex];
      if (node == NULL_NODE) {
        continue;
      }

      if (AABB.testOverlap(m_aabb[node], aabb)) {
        int child1 = m_child1[node];
        if (child1 == NULL_NODE) {
          boolean proceed = callback.treeCallback(node);
          if (!proceed) {
            return;
          }
        } else {
          if (nodeStack.length - nodeStackIndex - 2 <= 0) {
            nodeStack =
                BufferUtils.reallocateBuffer(nodeStack, nodeStack.length, nodeStack.length * 2);
          }
          nodeStack[nodeStackIndex++] = child1;
          nodeStack[nodeStackIndex++] = m_child2[node];
        }
      }
    }
  }

  private final Vec2 r = new Vec2();
  private final AABB aabb = new AABB();
  private final RayCastInput subInput = new RayCastInput();

  @Override
  public void raycast(TreeRayCastCallback callback, RayCastInput input) {
    final Vec2 p1 = input.p1;
    final Vec2 p2 = input.p2;
    float p1x = p1.x, p2x = p2.x, p1y = p1.y, p2y = p2.y;
    float vx, vy;
    float rx, ry;
    float absVx, absVy;
    float cx, cy;
    float hx, hy;
    float tempx, tempy;
    r.x = p2x - p1x;
    r.y = p2y - p1y;
    assert ((r.x * r.x + r.y * r.y) > 0f);
    r.normalize();
    rx = r.x;
    ry = r.y;

    // v is perpendicular to the segment.
    vx = -1f * ry;
    vy = 1f * rx;
    absVx = MathUtils.abs(vx);
    absVy = MathUtils.abs(vy);

    // Separating axis for segment (Gino, p80).
    // |dot(v, p1 - c)| > dot(|v|, h)

    float maxFraction = input.maxFraction;

    // Build a bounding box for the segment.
    final AABB segAABB = aabb;
    // Vec2 t = p1 + maxFraction * (p2 - p1);
    // before inline
    // temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
    // Vec2.minToOut(p1, temp, segAABB.lowerBound);
    // Vec2.maxToOut(p1, temp, segAABB.upperBound);
    tempx = (p2x - p1x) * maxFraction + p1x;
    tempy = (p2y - p1y) * maxFraction + p1y;
    segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
    segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
    segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
    segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
    // end inline

    nodeStackIndex = 0;
    nodeStack[nodeStackIndex++] = m_root;
    while (nodeStackIndex > 0) {
      int node = nodeStack[--nodeStackIndex] = m_root;
      if (node == NULL_NODE) {
        continue;
      }

      final AABB nodeAABB = m_aabb[node];
      if (!AABB.testOverlap(nodeAABB, segAABB)) {
        continue;
      }

      // Separating axis for segment (Gino, p80).
      // |dot(v, p1 - c)| > dot(|v|, h)
      // node.aabb.getCenterToOut(c);
      // node.aabb.getExtentsToOut(h);
      cx = (nodeAABB.lowerBound.x + nodeAABB.upperBound.x) * .5f;
      cy = (nodeAABB.lowerBound.y + nodeAABB.upperBound.y) * .5f;
      hx = (nodeAABB.upperBound.x - nodeAABB.lowerBound.x) * .5f;
      hy = (nodeAABB.upperBound.y - nodeAABB.lowerBound.y) * .5f;
      tempx = p1x - cx;
      tempy = p1y - cy;
      float separation = MathUtils.abs(vx * tempx + vy * tempy) - (absVx * hx + absVy * hy);
      if (separation > 0.0f) {
        continue;
      }

      int child1 = m_child1[node];
      if (child1 == NULL_NODE) {
        subInput.p1.x = p1x;
        subInput.p1.y = p1y;
        subInput.p2.x = p2x;
        subInput.p2.y = p2y;
        subInput.maxFraction = maxFraction;

        float value = callback.raycastCallback(subInput, node);

        if (value == 0.0f) {
          // The client has terminated the ray cast.
          return;
        }

        if (value > 0.0f) {
          // Update segment bounding box.
          maxFraction = value;
          // temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
          // Vec2.minToOut(p1, temp, segAABB.lowerBound);
          // Vec2.maxToOut(p1, temp, segAABB.upperBound);
          tempx = (p2x - p1x) * maxFraction + p1x;
          tempy = (p2y - p1y) * maxFraction + p1y;
          segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
          segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
          segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
          segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
        }
      } else {
        nodeStack[nodeStackIndex++] = child1;
        nodeStack[nodeStackIndex++] = m_child2[node];
      }
    }
  }

  @Override
  public final int computeHeight() {
    return computeHeight(m_root);
  }

  private final int computeHeight(int node) {
    assert (0 <= node && node < m_nodeCapacity);

    if (m_child1[node] == NULL_NODE) {
      return 0;
    }
    int height1 = computeHeight(m_child1[node]);
    int height2 = computeHeight(m_child2[node]);
    return 1 + MathUtils.max(height1, height2);
  }

  /**
   * Validate this tree. For testing.
   */
  public void validate() {
    validateStructure(m_root);
    validateMetrics(m_root);

    int freeCount = 0;
    int freeNode = m_freeList;
    while (freeNode != NULL_NODE) {
      assert (0 <= freeNode && freeNode < m_nodeCapacity);
      freeNode = m_parent[freeNode];
      ++freeCount;
    }

    assert (getHeight() == computeHeight());
    assert (m_nodeCount + freeCount == m_nodeCapacity);
  }

  @Override
  public int getHeight() {
    if (m_root == NULL_NODE) {
      return 0;
    }
    return m_height[m_root];
  }

  @Override
  public int getMaxBalance() {
    int maxBalance = 0;
    for (int i = 0; i < m_nodeCapacity; ++i) {
      if (m_height[i] <= 1) {
        continue;
      }

      assert (m_child1[i] != NULL_NODE);

      int child1 = m_child1[i];
      int child2 = m_child2[i];
      int balance = MathUtils.abs(m_height[child2] - m_height[child1]);
      maxBalance = MathUtils.max(maxBalance, balance);
    }

    return maxBalance;
  }

  @Override
  public float getAreaRatio() {
    if (m_root == NULL_NODE) {
      return 0.0f;
    }

    final int root = m_root;
    float rootArea = m_aabb[root].getPerimeter();

    float totalArea = 0.0f;
    for (int i = 0; i < m_nodeCapacity; ++i) {
      if (m_height[i] < 0) {
        // Free node in pool
        continue;
      }

      totalArea += m_aabb[i].getPerimeter();
    }

    return totalArea / rootArea;
  }

  // /**
  // * Build an optimal tree. Very expensive. For testing.
  // */
  // public void rebuildBottomUp() {
  // int[] nodes = new int[m_nodeCount];
  // int count = 0;
  //
  // // Build array of leaves. Free the rest.
  // for (int i = 0; i < m_nodeCapacity; ++i) {
  // if (m_nodes[i].height < 0) {
  // // free node in pool
  // continue;
  // }
  //
  // DynamicTreeNode node = m_nodes[i];
  // if (node.isLeaf()) {
  // node.parent = null;
  // nodes[count] = i;
  // ++count;
  // } else {
  // freeNode(node);
  // }
  // }
  //
  // AABB b = new AABB();
  // while (count > 1) {
  // float minCost = Float.MAX_VALUE;
  // int iMin = -1, jMin = -1;
  // for (int i = 0; i < count; ++i) {
  // AABB aabbi = m_nodes[nodes[i]].aabb;
  //
  // for (int j = i + 1; j < count; ++j) {
  // AABB aabbj = m_nodes[nodes[j]].aabb;
  // b.combine(aabbi, aabbj);
  // float cost = b.getPerimeter();
  // if (cost < minCost) {
  // iMin = i;
  // jMin = j;
  // minCost = cost;
  // }
  // }
  // }
  //
  // int index1 = nodes[iMin];
  // int index2 = nodes[jMin];
  // DynamicTreeNode child1 = m_nodes[index1];
  // DynamicTreeNode child2 = m_nodes[index2];
  //
  // DynamicTreeNode parent = allocateNode();
  // parent.child1 = child1;
  // parent.child2 = child2;
  // parent.height = 1 + MathUtils.max(child1.height, child2.height);
  // parent.aabb.combine(child1.aabb, child2.aabb);
  // parent.parent = null;
  //
  // child1.parent = parent;
  // child2.parent = parent;
  //
  // nodes[jMin] = nodes[count - 1];
  // nodes[iMin] = parent.id;
  // --count;
  // }
  //
  // m_root = m_nodes[nodes[0]];
  //
  // validate();
  // }

  private final int allocateNode() {
    if (m_freeList == NULL_NODE) {
      assert (m_nodeCount == m_nodeCapacity);
      m_nodeCapacity *= 2;
      expandBuffers(m_nodeCount, m_nodeCapacity);
    }
    assert (m_freeList != NULL_NODE);
    int node = m_freeList;
    m_freeList = m_parent[node];
    m_parent[node] = NULL_NODE;
    m_child1[node] = NULL_NODE;
    m_height[node] = 0;
    ++m_nodeCount;
    return node;
  }

  /**
   * returns a node to the pool
   */
  private final void freeNode(int node) {
    assert (node != NULL_NODE);
    assert (0 < m_nodeCount);
    m_parent[node] = m_freeList != NULL_NODE ? m_freeList : NULL_NODE;
    m_height[node] = -1;
    m_freeList = node;
    m_nodeCount--;
  }

  private final AABB combinedAABB = new AABB();

  private final void insertLeaf(int leaf) {
    if (m_root == NULL_NODE) {
      m_root = leaf;
      m_parent[m_root] = NULL_NODE;
      return;
    }

    // find the best sibling
    AABB leafAABB = m_aabb[leaf];
    int index = m_root;
    while (m_child1[index] != NULL_NODE) {
      final int node = index;
      int child1 = m_child1[node];
      int child2 = m_child2[node];
      final AABB nodeAABB = m_aabb[node];
      float area = nodeAABB.getPerimeter();

      combinedAABB.combine(nodeAABB, leafAABB);
      float combinedArea = combinedAABB.getPerimeter();

      // Cost of creating a new parent for this node and the new leaf
      float cost = 2.0f * combinedArea;

      // Minimum cost of pushing the leaf further down the tree
      float inheritanceCost = 2.0f * (combinedArea - area);

      // Cost of descending into child1
      float cost1;
      AABB child1AABB = m_aabb[child1];
      if (m_child1[child1] == NULL_NODE) {
        combinedAABB.combine(leafAABB, child1AABB);
        cost1 = combinedAABB.getPerimeter() + inheritanceCost;
      } else {
        combinedAABB.combine(leafAABB, child1AABB);
        float oldArea = child1AABB.getPerimeter();
        float newArea = combinedAABB.getPerimeter();
        cost1 = (newArea - oldArea) + inheritanceCost;
      }

      // Cost of descending into child2
      float cost2;
      AABB child2AABB = m_aabb[child2];
      if (m_child1[child2] == NULL_NODE) {
        combinedAABB.combine(leafAABB, child2AABB);
        cost2 = combinedAABB.getPerimeter() + inheritanceCost;
      } else {
        combinedAABB.combine(leafAABB, child2AABB);
        float oldArea = child2AABB.getPerimeter();
        float newArea = combinedAABB.getPerimeter();
        cost2 = newArea - oldArea + inheritanceCost;
      }

      // Descend according to the minimum cost.
      if (cost < cost1 && cost < cost2) {
        break;
      }

      // Descend
      if (cost1 < cost2) {
        index = child1;
      } else {
        index = child2;
      }
    }

    int sibling = index;
    int oldParent = m_parent[sibling];
    final int newParent = allocateNode();
    m_parent[newParent] = oldParent;
    m_userData[newParent] = null;
    m_aabb[newParent].combine(leafAABB, m_aabb[sibling]);
    m_height[newParent] = m_height[sibling] + 1;

    if (oldParent != NULL_NODE) {
      // The sibling was not the root.
      if (m_child1[oldParent] == sibling) {
        m_child1[oldParent] = newParent;
      } else {
        m_child2[oldParent] = newParent;
      }

      m_child1[newParent] = sibling;
      m_child2[newParent] = leaf;
      m_parent[sibling] = newParent;
      m_parent[leaf] = newParent;
    } else {
      // The sibling was the root.
      m_child1[newParent] = sibling;
      m_child2[newParent] = leaf;
      m_parent[sibling] = newParent;
      m_parent[leaf] = newParent;
      m_root = newParent;
    }

    // Walk back up the tree fixing heights and AABBs
    index = m_parent[leaf];
    while (index != NULL_NODE) {
      index = balance(index);

      int child1 = m_child1[index];
      int child2 = m_child2[index];

      assert (child1 != NULL_NODE);
      assert (child2 != NULL_NODE);

      m_height[index] = 1 + MathUtils.max(m_height[child1], m_height[child2]);
      m_aabb[index].combine(m_aabb[child1], m_aabb[child2]);

      index = m_parent[index];
    }
    // validate();
  }

  private final void removeLeaf(int leaf) {
    if (leaf == m_root) {
      m_root = NULL_NODE;
      return;
    }

    int parent = m_parent[leaf];
    int grandParent = m_parent[parent];
    int parentChild1 = m_child1[parent];
    int parentChild2 = m_child2[parent];
    int sibling;
    if (parentChild1 == leaf) {
      sibling = parentChild2;
    } else {
      sibling = parentChild1;
    }

    if (grandParent != NULL_NODE) {
      // Destroy parent and connect sibling to grandParent.
      if (m_child1[grandParent] == parent) {
        m_child1[grandParent] = sibling;
      } else {
        m_child2[grandParent] = sibling;
      }
      m_parent[sibling] = grandParent;
      freeNode(parent);

      // Adjust ancestor bounds.
      int index = grandParent;
      while (index != NULL_NODE) {
        index = balance(index);

        int child1 = m_child1[index];
        int child2 = m_child2[index];

        m_aabb[index].combine(m_aabb[child1], m_aabb[child2]);
        m_height[index] = 1 + MathUtils.max(m_height[child1], m_height[child2]);

        index = m_parent[index];
      }
    } else {
      m_root = sibling;
      m_parent[sibling] = NULL_NODE;
      freeNode(parent);
    }

    // validate();
  }

  // Perform a left or right rotation if node A is imbalanced.
  // Returns the new root index.
  private int balance(int iA) {
    assert (iA != NULL_NODE);

    int A = iA;
    if (m_child1[A] == NULL_NODE || m_height[A] < 2) {
      return iA;
    }

    int iB = m_child1[A];
    int iC = m_child2[A];
    assert (0 <= iB && iB < m_nodeCapacity);
    assert (0 <= iC && iC < m_nodeCapacity);

    int B = iB;
    int C = iC;

    int balance = m_height[C] - m_height[B];

    // Rotate C up
    if (balance > 1) {
      int iF = m_child1[C];
      int iG = m_child2[C];
      int F = iF;
      int G = iG;
      // assert (F != null);
      // assert (G != null);
      assert (0 <= iF && iF < m_nodeCapacity);
      assert (0 <= iG && iG < m_nodeCapacity);

      // Swap A and C
      m_child1[C] = iA;
      int cParent = m_parent[C] = m_parent[A];
      m_parent[A] = iC;

      // A's old parent should point to C
      if (cParent != NULL_NODE) {
        if (m_child1[cParent] == iA) {
          m_child1[cParent] = iC;
        } else {
          assert (m_child2[cParent] == iA);
          m_child2[cParent] = iC;
        }
      } else {
        m_root = iC;
      }

      // Rotate
      if (m_height[F] > m_height[G]) {
        m_child2[C] = iF;
        m_child2[A] = iG;
        m_parent[G] = iA;
        m_aabb[A].combine(m_aabb[B], m_aabb[G]);
        m_aabb[C].combine(m_aabb[A], m_aabb[F]);

        m_height[A] = 1 + MathUtils.max(m_height[B], m_height[G]);
        m_height[C] = 1 + MathUtils.max(m_height[A], m_height[F]);
      } else {
        m_child2[C] = iG;
        m_child2[A] = iF;
        m_parent[F] = iA;
        m_aabb[A].combine(m_aabb[B], m_aabb[F]);
        m_aabb[C].combine(m_aabb[A], m_aabb[G]);

        m_height[A] = 1 + MathUtils.max(m_height[B], m_height[F]);
        m_height[C] = 1 + MathUtils.max(m_height[A], m_height[G]);
      }

      return iC;
    }

    // Rotate B up
    if (balance < -1) {
      int iD = m_child1[B];
      int iE = m_child2[B];
      int D = iD;
      int E = iE;
      assert (0 <= iD && iD < m_nodeCapacity);
      assert (0 <= iE && iE < m_nodeCapacity);

      // Swap A and B
      m_child1[B] = iA;
      int Bparent = m_parent[B] = m_parent[A];
      m_parent[A] = iB;

      // A's old parent should point to B
      if (Bparent != NULL_NODE) {
        if (m_child1[Bparent] == iA) {
          m_child1[Bparent] = iB;
        } else {
          assert (m_child2[Bparent] == iA);
          m_child2[Bparent] = iB;
        }
      } else {
        m_root = iB;
      }

      // Rotate
      if (m_height[D] > m_height[E]) {
        m_child2[B] = iD;
        m_child1[A] = iE;
        m_parent[E] = iA;
        m_aabb[A].combine(m_aabb[C], m_aabb[E]);
        m_aabb[B].combine(m_aabb[A], m_aabb[D]);

        m_height[A] = 1 + MathUtils.max(m_height[C], m_height[E]);
        m_height[B] = 1 + MathUtils.max(m_height[A], m_height[D]);
      } else {
        m_child2[B] = iE;
        m_child1[A] = iD;
        m_parent[D] = iA;
        m_aabb[A].combine(m_aabb[C], m_aabb[D]);
        m_aabb[B].combine(m_aabb[A], m_aabb[E]);

        m_height[A] = 1 + MathUtils.max(m_height[C], m_height[D]);
        m_height[B] = 1 + MathUtils.max(m_height[A], m_height[E]);
      }

      return iB;
    }

    return iA;
  }

  private void validateStructure(int node) {
    if (node == NULL_NODE) {
      return;
    }

    if (node == m_root) {
      assert (m_parent[node] == NULL_NODE);
    }

    int child1 = m_child1[node];
    int child2 = m_child2[node];

    if (child1 == NULL_NODE) {
      assert (child1 == NULL_NODE);
      assert (child2 == NULL_NODE);
      assert (m_height[node] == 0);
      return;
    }

    assert (child1 != NULL_NODE && 0 <= child1 && child1 < m_nodeCapacity);
    assert (child2 != NULL_NODE && 0 <= child2 && child2 < m_nodeCapacity);

    assert (m_parent[child1] == node);
    assert (m_parent[child2] == node);

    validateStructure(child1);
    validateStructure(child2);
  }

  private void validateMetrics(int node) {
    if (node == NULL_NODE) {
      return;
    }

    int child1 = m_child1[node];
    int child2 = m_child2[node];

    if (child1 == NULL_NODE) {
      assert (child1 == NULL_NODE);
      assert (child2 == NULL_NODE);
      assert (m_height[node] == 0);
      return;
    }

    assert (child1 != NULL_NODE && 0 <= child1 && child1 < m_nodeCapacity);
    assert (child2 != child1 && 0 <= child2 && child2 < m_nodeCapacity);

    int height1 = m_height[child1];
    int height2 = m_height[child2];
    int height;
    height = 1 + MathUtils.max(height1, height2);
    assert (m_height[node] == height);

    AABB aabb = new AABB();
    aabb.combine(m_aabb[child1], m_aabb[child2]);

    assert (aabb.lowerBound.equals(m_aabb[node].lowerBound));
    assert (aabb.upperBound.equals(m_aabb[node].upperBound));

    validateMetrics(child1);
    validateMetrics(child2);
  }

  @Override
  public void drawTree(DebugDraw argDraw) {
    if (m_root == NULL_NODE) {
      return;
    }
    int height = computeHeight();
    drawTree(argDraw, m_root, 0, height);
  }

  private final Color3f color = new Color3f();
  private final Vec2 textVec = new Vec2();

  public void drawTree(DebugDraw argDraw, int node, int spot, int height) {
    AABB a = m_aabb[node];
    a.getVertices(drawVecs);

    color.set(1, (height - spot) * 1f / height, (height - spot) * 1f / height);
    argDraw.drawPolygon(drawVecs, 4, color);

    argDraw.getViewportTranform().getWorldToScreen(a.upperBound, textVec);
    argDraw.drawString(textVec.x, textVec.y, node + "-" + (spot + 1) + "/" + height, color);

    int c1 = m_child1[node];
    int c2 = m_child2[node];
    if (c1 != NULL_NODE) {
      drawTree(argDraw, c1, spot + 1, height);
    }
    if (c2 != NULL_NODE) {
      drawTree(argDraw, c2, spot + 1, height);
    }
  }
}
TOP

Related Classes of org.jbox2d.collision.broadphase.DynamicTreeFlatNodes

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.