Package jray.struct

Source Code of jray.struct.Node

package jray.struct;

import java.util.ArrayList;
import java.util.List;

import jray.common.Object3D;
import jray.common.Sphere;
import jray.common.Vect3;
import jray.math.intersections.CubeSphere;
import jray.math.intersections.PointCube;

/**
* A node of an octree located at <code>center +/- width/2</code> (a cube).<p/>
*
* If a point is contained in a leaf-node, it is guaranteed that any object intersecting this point can be found in the leaf node or it's parents.
* It is NOT guaranteed that each object is stored only once in an octree (although a TreeInsertStrategy may try to do this to save memory).
*/
public class Node{
  public static TreeInsertStrategy strategy = TreeInsertStrategy.DYNAMIC_TEST;
  public static final double MIN_NODE_WIDTH = 1e-9;
 
  private final Node parent; //parent node
  private Node[] child; //child nodes
  List<Object3D> content = new ArrayList<Object3D>(); //objects contained in this node
  final Vect3 center; //central point of the nodes cube
  final double width; //node reaches from center-width/2 to center+width/2
 
  public Node(Vect3 center, Node parent, double width){
    this.parent = parent;
    this.width = width;
    this.center = center;
   
    if(width<MIN_NODE_WIDTH)
      throw new RuntimeException("Node width too small: "+width);
  }
 
  /**
   * @param v a <code>Vect3</code> representing a point
   * @return true if, and only if, <code>v</code> is enclosed by this subtree.
   */
  public boolean encloses(Vect3 v){
    return PointCube.encloses(center, width/2 + TreeInsertStrategy.EPS, v);
  }
 
  /**
   * Inserts an object into the subtree. The actual algorithms for building the tree are specified by TreeInsertStrategy.
   *
   * @param o the object to be added.
   * @param s the bounding sphere of the object - precomputed for memory and performance reasons
   * @return false if the object does not intersect/or is not enclosed by this subtree, true otherwise.
   */
  public boolean insert(Object3D o, Sphere s){
    if(s==null){ //objects without bounding sphere will potentially intersect every ray
      content.add(o);
      return true;
    }
   
    switch(strategy){
    case LEAF_ONLY:
      if(!CubeSphere.isSphereIntersectingCube(center, width/2, s.getPosition(), s.getRadius())){
        return false;
      }
     
      if(child!=null){
        child[0].insert(o,s);
        child[1].insert(o,s);
        child[2].insert(o,s);
        child[3].insert(o,s);
        child[4].insert(o,s);
        child[5].insert(o,s);
        child[6].insert(o,s);
        child[7].insert(o,s);
      }else{
        content.add(o);
      }
     
      if(child==null&&content.size()>TreeInsertStrategy.MAX_ELEMENTS&&width>TreeInsertStrategy.MIN_WIDTH){
        split();
      }
     
      return true;
    case FIT_INTO_BOX:
      if(!o.isEnclosedByCube(center, width/2))
        return false;
     
      if(child!=null){
        if(child[0].insert(o,s)||
           child[1].insert(o,s)||
           child[2].insert(o,s)||
           child[3].insert(o,s)||
           child[4].insert(o,s)||
           child[5].insert(o,s)||
           child[6].insert(o,s)||
           child[7].insert(o,s))
          return true;
      }
     
      content.add(o);
     
      if(child==null&&content.size()>TreeInsertStrategy.MAX_ELEMENTS&&width>TreeInsertStrategy.MIN_WIDTH)
        split();
     
      return true;
    case DYNAMIC:
      //if sphere is not touching cube -> error
      if(!CubeSphere.isSphereIntersectingCube(center, width/2, s.getPosition(), s.getRadius())){
        return false;
      }
     
      //if object is enclosed by any child, add it there
      if(child != null){
        for(Node n : child){
          if(o.isEnclosedByCube(n.center, n.width/2))
            return n.insert(o, s);
        }
      }
     
      //if object is very small (in relation to the box) - duplicate it to child nodes
      //add it to this node otherwise
      if(child!=null && s.getRadius()/width<TreeInsertStrategy.DYNAMIC_DUPLICATE_MAX_SIZE_RATIO){
        child[0].insert(o,s);
        child[1].insert(o,s);
        child[2].insert(o,s);
        child[3].insert(o,s);
        child[4].insert(o,s);
        child[5].insert(o,s);
        child[6].insert(o,s);
        child[7].insert(o,s);
      }else{
        content.add(o);
      }
     
      //if node too full split it
      if(child==null&&content.size()>2/*&&width>TreeInsertStrategy.DYNAMIC_MIN_WIDTH*/){
        split();
      }
     
      return true;
    case DYNAMIC_TEST:
      //if sphere is not touching cube -> error
      if(!CubeSphere.isSphereIntersectingCube(center, width/2, s.getPosition(), s.getRadius())){
        return false;
      }
     
      //if object is enclosed by any child, add it there
      if(child != null){
        for(Node n : child){
          if(o.isEnclosedByCube(n.center, n.width/2))
            return n.insert(o, s);
        }
       
        boolean i0 = CubeSphere.isSphereIntersectingCube(child[0].center, child[0].width/2, s.getPosition(), s.getRadius()),
            i1 = CubeSphere.isSphereIntersectingCube(child[1].center, child[1].width/2, s.getPosition(), s.getRadius()),
            i2 = CubeSphere.isSphereIntersectingCube(child[2].center, child[2].width/2, s.getPosition(), s.getRadius()),
            i3 = CubeSphere.isSphereIntersectingCube(child[3].center, child[3].width/2, s.getPosition(), s.getRadius()),
            i4 = CubeSphere.isSphereIntersectingCube(child[4].center, child[4].width/2, s.getPosition(), s.getRadius()),
            i5 = CubeSphere.isSphereIntersectingCube(child[5].center, child[5].width/2, s.getPosition(), s.getRadius()),
            i6 = CubeSphere.isSphereIntersectingCube(child[6].center, child[6].width/2, s.getPosition(), s.getRadius()),
            i7 = CubeSphere.isSphereIntersectingCube(child[7].center, child[7].width/2, s.getPosition(), s.getRadius());
   
        int intersectionCount=0;
       
        if(i0) intersectionCount++;
        if(i1) intersectionCount++;
        if(i2) intersectionCount++;
        if(i3) intersectionCount++;
        if(i4) intersectionCount++;
        if(i5) intersectionCount++;
        if(i6) intersectionCount++;
        if(i7) intersectionCount++;
       
        if(intersectionCount==1||intersectionCount*s.getRadius()/width<0.35){
          if(i0) child[0].insert(o, s);
          if(i1) child[1].insert(o, s);
          if(i2) child[2].insert(o, s);
          if(i3) child[3].insert(o, s);
          if(i4) child[4].insert(o, s);
          if(i5) child[5].insert(o, s);
          if(i6) child[6].insert(o, s);
          if(i7) child[7].insert(o, s);
         
          return true;
        }
      }
     
      content.add(o);
     
      //if node too full split it
      if(child==null&&content.size()>2){
        split();
      }
     
      return true;
    case FAST_BUILD_TEST:
      //if sphere is not touching cube -> error
      if(!CubeSphere.isSphereIntersectingCube(center, width/2, s.getPosition(), s.getRadius())){
        return false;
      }
     
      //if object is enclosed by any child, add it there
      if(child != null){
        boolean i0 = CubeSphere.isSphereIntersectingCube(child[0].center, child[0].width/2, s.getPosition(), s.getRadius()),
                i1 = CubeSphere.isSphereIntersectingCube(child[1].center, child[1].width/2, s.getPosition(), s.getRadius()),
                i2 = CubeSphere.isSphereIntersectingCube(child[2].center, child[2].width/2, s.getPosition(), s.getRadius()),
                i3 = CubeSphere.isSphereIntersectingCube(child[3].center, child[3].width/2, s.getPosition(), s.getRadius()),
                i4 = CubeSphere.isSphereIntersectingCube(child[4].center, child[4].width/2, s.getPosition(), s.getRadius()),
                i5 = CubeSphere.isSphereIntersectingCube(child[5].center, child[5].width/2, s.getPosition(), s.getRadius()),
                i6 = CubeSphere.isSphereIntersectingCube(child[6].center, child[6].width/2, s.getPosition(), s.getRadius()),
                i7 = CubeSphere.isSphereIntersectingCube(child[7].center, child[7].width/2, s.getPosition(), s.getRadius());
       
        int intersectionCount=0;
       
        if(i0) intersectionCount++;
        if(i1) intersectionCount++;
        if(i2) intersectionCount++;
        if(i3) intersectionCount++;
        if(i4) intersectionCount++;
        if(i5) intersectionCount++;
        if(i6) intersectionCount++;
        if(i7) intersectionCount++;
       
        if(intersectionCount==1||intersectionCount*s.getRadius()/width<0.5){
          if(i0) child[0].insert(o, s);
          if(i1) child[1].insert(o, s);
          if(i2) child[2].insert(o, s);
          if(i3) child[3].insert(o, s);
          if(i4) child[4].insert(o, s);
          if(i5) child[5].insert(o, s);
          if(i6) child[6].insert(o, s);
          if(i7) child[7].insert(o, s);
         
          return true;
        }
      }
     
      content.add(o);
   
      //if node too full split it
      if(child==null&&content.size()>TreeInsertStrategy.MAX_ELEMENTS&&width>TreeInsertStrategy.DYNAMIC_MIN_WIDTH){
        split();
      }
     
      return true;
    default:
      throw new RuntimeException("Mode not implemented: "+strategy);
    }
  }
 
  /**
   * Splits this node into 8 child-nodes and re-inserts the content
   */
  private void split(){
    if(child!=null)
      throw new RuntimeException("double split detected");
   
    child = new Node[8];
    double w2 = width/2;
    double w4 = width/4;
   
    child[0] = new Node(new Vect3(center.data[0] + w4, center.data[1] + w4, center.data[2] + w4),this,w2);
    child[1] = new Node(new Vect3(center.data[0] + w4, center.data[1] + w4, center.data[2] - w4),this,w2);
    child[2] = new Node(new Vect3(center.data[0] + w4, center.data[1] - w4, center.data[2] + w4),this,w2);
    child[3] = new Node(new Vect3(center.data[0] + w4, center.data[1] - w4, center.data[2] - w4),this,w2);
    child[4] = new Node(new Vect3(center.data[0] - w4, center.data[1] + w4, center.data[2] + w4),this,w2);
    child[5] = new Node(new Vect3(center.data[0] - w4, center.data[1] + w4, center.data[2] - w4),this,w2);
    child[6] = new Node(new Vect3(center.data[0] - w4, center.data[1] - w4, center.data[2] + w4),this,w2);
    child[7] = new Node(new Vect3(center.data[0] - w4, center.data[1] - w4, center.data[2] - w4),this,w2);
   
    List<Object3D> oldContent = content;
    content = new ArrayList<Object3D>();
   
    for(Object3D o : oldContent){
      if(!insert(o, o.getBoundingSphere())){
        throw new RuntimeException("could not insert: "+o);
      }
    }
  }
 
  /**
   * March through Octree checking collisions. Every node on the way (when traversing to leaf direction) is checked for hits.
   *
   * @param v point to search for
   * @param c collision details
   * @return smallest node containing v, or null if (and only if) v is not inside the tree
   */
  public Node marchToCheckingCollisions(Vect3 v, CollisionDetails c){
    if(encloses(v)){
      if(child==null){
        return this;
      }
     
      for(Node n : child){
        if(n.encloses(v)){
          c.checkCollisionSet(n.content);
          return n.marchToCheckingCollisions(v, c);
        }
      }
     
      return this;
    }else{
      if(parent!=null)
        return parent.marchToCheckingCollisions(v, c);
      else
        return null;
    }
  }
 
  @Override
  public String toString(){
    return center+"+/-"+width/2;
  }
 
  public void addStringRep(StringBuilder sb, int layer){
    for(int i=0;i<layer;i++)
      sb.append("  ");
    sb.append(toString());
    sb.append(":");
    sb.append(content);
    sb.append("\n");
   
    if(child!=null)
      for(Node n : child){
        n.addStringRep(sb, layer+1);
      }
  }
 
  /**
   * @return the number of objects stored in this subtree (duplicates are count multiple times)
   */
  public int getSize(){
    int size = content.size();
   
    if(child!=null)
      for(Node n : child)
        size += n.getSize();
     
    return size;
  }
 
  /**
   * Calculates: <code>s = sum(layer)</code> where sum is the sum over all objects stored in
   * this subtree and layer at which the object is stored (the root has layer 0).
   * <p/>
   * <code>s/getSize()</code> defines the average layer of the object. 
   * @param layer
   * @return s
   */
  public long getContentDepthSum(int layer){
    long sum = 0;
   
    sum += content.size()*(long)layer;
   
    if(child!=null)
      for(Node n : child)
        sum += n.getContentDepthSum(layer+1);
   
    return sum;
  }
 
  /**
   * Removes child nodes in case none of them has any content
   */
  public void compress(){
    if(getSize()-content.size()==0&&child!=null){
      child=null;
    }
   
    if(child!=null){
      for(Node n : child)
        n.compress();
    }
  }
 
  /**
   * @return number of nodes in this subtree
   */
  public int getNodeCount(){
    int nc=1;
   
    if(child!=null)
      for(Node n : child)
        nc += n.getNodeCount();
   
    return nc;
  }
}
TOP

Related Classes of jray.struct.Node

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.