Package ca.eandb.jmist.framework.accel

Source Code of ca.eandb.jmist.framework.accel.BoundingBoxHierarchy3$Node

/**
* Java Modular Image Synthesis Toolkit (JMIST)
* Copyright (C) 2008-2013 Bradley W. Kimmel
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package ca.eandb.jmist.framework.accel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import ca.eandb.jmist.framework.Bounded3;
import ca.eandb.jmist.framework.BoundingBoxBuilder3;
import ca.eandb.jmist.framework.RayTraversalStrategy3;
import ca.eandb.jmist.framework.Visitor;
import ca.eandb.jmist.math.Box3;
import ca.eandb.jmist.math.Interval;
import ca.eandb.jmist.math.Point3;
import ca.eandb.jmist.math.Ray3;

/**
* Stores bounded objects in a tree structure so that intersection tests may be
* accelerated.
* @author Brad Kimmel
*/
public final class BoundingBoxHierarchy3 implements RayTraversalStrategy3 {

  /** Serialization version ID. */
  private static final long serialVersionUID = -7768577656591759503L;

  /**
   * Creates a new <code>BoundingBoxHierarchy3</code>.
   */
  public BoundingBoxHierarchy3() {
  }

  /**
   * Adds a new item to this <code>BoundingBoxHierarchy3</code>.
   * @param item The <code>Bounded3</item> item to add.
   * @return A reference to this <code>BoundingBoxHierarchy3</code> so that
   *     calls to this method may be chained.
   */
  public BoundingBoxHierarchy3 addItem(Bounded3 item) {
    return addItem(item, item.boundingBox());
  }

  /**
   * Adds a new item to this <code>BoundingBoxHierarchy3</code>.
   * @param item The <code>Object</code> to add.
   * @param bound The bounding <code>Box3</code> for the item.
   * @return A reference to this <code>BoundingBoxHierarchy3</code> so that
   *     calls to this method may be chained.
   */
  public BoundingBoxHierarchy3 addItem(Object item, Box3 bound) {

    /* Invalidate the tree if we've already built it. */
    this.root = null;

    /* Add the new leaf node. */
    this.leaves.add(new Node(item, bound));

    return this;

  }

  /* (non-Javadoc)
   * @see ca.eandb.jmist.framework.RayTraversalStrategy3#intersect(ca.eandb.jmist.toolkit.Ray3, ca.eandb.jmist.toolkit.Interval, ca.eandb.jmist.framework.Visitor)
   */
  public boolean intersect(Ray3 ray, Interval I, Visitor visitor) {

    if (this.leaves.size() > 0) {

      /* Rebuild the tree if necessary. */
      this.ensureReady();

      return this.root.intersect(ray, I, visitor);

    }

    return true;

  }

  /**
   * Rebuilds the tree if necessary.
   */
  private synchronized void ensureReady() {
    if (this.root == null) {
      this.rebuild();
    }
  }

  /**
   * Gets the outer bounding <code>Box3</code> of this
   * <code>BoundingBoxHierarchy3</code>.
   * @return The smallest <code>Box3</code> containing all of the bounding
   *     boxes that were added to this <code>BoundingBoxHierarchy3</code>.
   */
  public Box3 getBoundingBox() {
    if (this.leaves.size() > 0) {
      this.ensureReady();
      return this.root.bound;
    }
    return Box3.EMPTY;
  }

  /**
   * Rebuilds the bounding box tree from the leaves.
   */
  private void rebuild() {
    Node[] nodes = new Node[this.leaves.size()];
    this.leaves.toArray(nodes);
    this.root = this.rebuild(nodes, 0, nodes.length - 1);
  }

  /**
   * Rebuilds the bounding box tree for the specified range of leaves
   * in an array.
   * @param nodes The array containing the leaf <code>Node</code>s to build
   *     the subtree for.
   * @param fromIndex The index of the first node of the range to build the
   *     subtree for.
   * @param toIndex The index of the last node of the range to build the
   *     subtree for.
   * @return The root <code>Node</code> of the subtree containing the
   *     specified leaf <code>Node</code>s.
   */
  private Node rebuild(Node[] nodes, int fromIndex, int toIndex) {

    assert(toIndex >= fromIndex);

    int len = toIndex - fromIndex + 1;

    if (len > 2) {

      /* This is a binary tree, so we deal with more than two nodes by
       * splitting the list in half and a subtree for each half.
       */
      int midIndex = (fromIndex + toIndex) / 2;
      Arrays.sort(nodes, fromIndex, toIndex, this.getComparator(nodes, fromIndex, toIndex));

      return new Node(
          this.rebuild(nodes, fromIndex, midIndex),
          this.rebuild(nodes, midIndex + 1, toIndex)
      );

    } else if (len == 2) {

      /* If there are two nodes, create a single node with the two leaves
       * as its children.
       */
      return new Node(nodes[fromIndex], nodes[toIndex]);

    } else { /* len == 1 */

      /* If there is only one node, then it is the subtree. */
      assert(len == 1);
      return nodes[fromIndex];

    }

  }

  /**
   * Gets the <code>Comparator</code> to use to sort the specified list of
   * <code>Node</code>s.
   * @param nodes The array of <code>Node</code>s to be sorted.
   * @param fromIndex The first index of the range of <code>Node</code>s to
   *     be sorted.
   * @param toIndex The last index of the range of <code>Node</code>s to be
   *     sorted.
   * @return The <code>Comparator</code> that will sort the
   *     <code>Node</code>s by the centers of their bounding boxes in the
   *     direction in which the bounding box of all the nodes is the
   *     longest.
   */
  private Comparator<Node> getComparator(Node[] nodes, int fromIndex, int toIndex) {

    Box3 bound = getBoundingBox(nodes, fromIndex, toIndex);

    if (bound.lengthX() > bound.lengthY() && bound.lengthX() > bound.lengthZ()) {
      return COMPARATORS[NodeComparator.X_AXIS];
    } else if (bound.lengthY() > bound.lengthZ()) {
      return COMPARATORS[NodeComparator.Y_AXIS];
    } else {
      return COMPARATORS[NodeComparator.Z_AXIS];
    }

  }

  /**
   * Gets the bounding box of a collection of <code>Node</code>s.
   * @param nodes The array containing the <code>Node</code>s to get the
   *     bounding box for.
   * @param fromIndex The first index into <code>nodes</code> of the range to
   *     get the bounding box for.
   * @param toIndex The last index into <code>nodes</code> of the range to
   *     get the bounding box for.
   * @return The smallest <code>Box3</code> that contains all of the bounding
   *     boxes of the specified <code>Node</code>s.
   */
  private Box3 getBoundingBox(Node[] nodes, int fromIndex, int toIndex) {

    BoundingBoxBuilder3 builder = new BoundingBoxBuilder3();

    for (int i = fromIndex; i <= toIndex; i++) {
      builder.add(nodes[i].bound);
    }

    return builder.getBoundingBox();

  }

  /**
   * A node in the bounding box tree.
   * @author Brad Kimmel
   */
  private static final class Node {

    /** The <code>Object</code> contained at this <code>Node</code>. */
    public final Object item;

    /** The bounding <code>Box3</code> of this <code>Node</code>. */
    public final Box3 bound;

    /** The root <code>Node</code> of the first subtree. */
    public final Node a;

    /** The root <code>Node</code> of the second subtree. */
    public final Node b;

    /**
     * Creates a leaf <code>Node</code>.
     * @param item The <code>Object</code> to be stored at the new
     *    <code>Node</code>.
     * @param bound The bounding <code>Box3</code> of the new
     *     <code>Node</code>.
     */
    public Node(Object item, Box3 bound) {
      this.item = item;
      this.bound = bound;
      this.a = null;
      this.b = null;
    }

    /**
     * Creates an internal <code>Node</code>.
     * @param a The root <code>Node</code> of the first subtree.
     * @param b The root <code>Node</code> of the second subtree.
     */
    public Node(Node a, Node b) {
      this.item = null;
      this.bound = Box3.smallestContaining(a.bound, b.bound);
      this.a = a;
      this.b = b;
    }

    /**
     * Intersects a <code>Ray3</code> with the subtree rooted at this
     * <code>Node</code>.
     * @param ray The <code>Ray3</code> to intersect with this subtree.
     * @param I The <code>Interval</code> along the ray to consider.
     * @param visitor The <code>Visitor</code> to notify when a
     *     <code>Node</code> containing an item is hit.
     * @return A value indicating whether the operation was completed
     *     without being cancelled.
     */
    public boolean intersect(Ray3 ray, Interval I, Visitor visitor) {

      if (this.bound.intersects(ray, I)) {

        if (item != null && !visitor.visit(item)) {
          return false;
        }

        if (a != null && !a.intersect(ray, I, visitor)) {
          return false;
        }

        if (b != null && !b.intersect(ray, I, visitor)) {
          return false;
        }

      }

      return true;

    }

  }

  /**
   * Compares <code>Node</code>s according to the centers of their bounding
   * boxes along a particular axis.
   * @author Brad Kimmel
   */
  private static final class NodeComparator implements Comparator<Node> {

    /**
     * Indicates that this <code>NodeComparator</code> should compare the
     * x-coordinates of the centers of the bounding boxes.
     */
    public static final int X_AXIS = 0;

    /**
     * Indicates that this <code>NodeComparator</code> should compare the
     * y-coordinates of the centers of the bounding boxes.
     */
    public static final int Y_AXIS = 1;

    /**
     * Indicates that this <code>NodeComparator</code> should compare the
     * z-coordinates of the centers of the bounding boxes.
     */
    public static final int Z_AXIS = 2;

    /**
     * Creates a new <code>NodeComparator</code>.
     * @param axis A value indicating the axis along which the comparison
     *     should take place.
     */
    public NodeComparator(int axis) {
      this.axis = axis;
    }

    /* (non-Javadoc)
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    public int compare(Node o1, Node o2) {
      Point3 p1 = o1.bound.center();
      Point3 p2 = o2.bound.center();

      switch (this.axis) {
      case X_AXIS: return Double.compare(p1.x(), p2.x());
      case Y_AXIS: return Double.compare(p1.y(), p2.y());
      case Z_AXIS: return Double.compare(p1.z(), p2.z());
      }

      throw new UnsupportedOperationException("invalid axis");
    }

    /**
     * A value indicating the axis along which the comparison should take
     * place.
     */
    private final int axis;

  }

  /** The root node of the bounding box tree. */
  private Node root;

  /** The <code>List</code> of leaf nodes in the bounding box tree. */
  private final List<Node> leaves = new ArrayList<Node>();

  /** The <code>NodeComparator</code>s for each axis. */
  private static final NodeComparator[] COMPARATORS = new NodeComparator[]{
      new NodeComparator(NodeComparator.X_AXIS),
      new NodeComparator(NodeComparator.Y_AXIS),
      new NodeComparator(NodeComparator.Z_AXIS)
  };

}
TOP

Related Classes of ca.eandb.jmist.framework.accel.BoundingBoxHierarchy3$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.