content: package edu.stanford.rsl.conrad.geometry;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Plane3D;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.StraightLine;
  import edu.stanford.rsl.conrad.geometry.transforms.Transform;
  import edu.stanford.rsl.conrad.geometry.transforms.Transformable;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  
  
  /**
   * Class to model any kind of curve or surface.
   * <br><br>
   * <img alt="Saddle Surface" height="150" width="150" src="http://upload.wikimedia.org/wikipedia/commons/2/21/Saddle_pt.jpg"><BR>
   * Example for a 3D surface.
   * <br> 
   * @author akmaier
   *
   */
  public abstract class AbstractShape implements Serializable, Transformable {
  
  	private Plane3D [] boundingPlanes;
  	protected PointND min;
  	protected PointND max;
  	private String name;
  
  
  	public AbstractShape() {
  	}
  
  	/**
  	 * Copy constructor (deep copy)
  	 * @param shape
  	 */
  	public AbstractShape(AbstractShape shape){
  		min = (shape.min != null) ? shape.min.clone() : null;
  		max = (shape.max != null) ? shape.max.clone() : null;
  		name = (shape.name != null) ? new String(shape.name) : null;
  		
  		if (shape.boundingPlanes!=null){
  			boundingPlanes = new Plane3D[shape.boundingPlanes.length];
  			for (int i = 0; i < boundingPlanes.length; i++) {
  				boundingPlanes[i] = (shape.boundingPlanes[i]!=null) ? new Plane3D(shape.boundingPlanes[i]) : null;
  			}
  		}
  		else
  			boundingPlanes=null;
  	}
  
  
  
  	/**
  	 * Returns a deep copy of this shape
  	 * @return Deep copy of this abstract shape
  	 */
  	public abstract AbstractShape clone();
  
  
  	/**
  	 * Returns true if the shape is of limited space
  	 * @return  Boundedness of this shape.
  	 */
  	public abstract boolean isBounded();
  
  	/**
  	 * Evaluates the bounding box and returns true if it is hit. If the object is not bounded, it returns true as default.
  	 * 
  	 * @param curve the curve
  	 * @return true, if the object is hit.
  	 */
  	public ArrayList<PointND> getHitsOnBoundingBox_slow(AbstractCurve curve){
  		if (isBounded()){
  			ArrayList<PointND> hits = new ArrayList<PointND>();
  			for(int i = 0; i< boundingPlanes.length; i++){
  				Plane3D p = boundingPlanes[i];
  				try{
  					ArrayList<PointND> points = p.intersect(curve);
  					for (PointND intersection : points){
  						//System.out.println(intersection);
  						boolean inBound = true;
  						for (int j=0; j < intersection.getDimension(); j++){
  							double coord = intersection.get(j);
  							if (coord > max.get(j) + CONRAD.SMALL_VALUE || coord < min.get(j) - CONRAD.SMALL_VALUE) {
  								inBound = false;
  								break;
  							}
  						}
  						if (inBound) {
  							hits.add(intersection);
  						}
  					}
  				} catch (RuntimeException e){
  					if (!e.getMessage().equals("Line is parallel to plane")) { 
  						System.out.println(e.getLocalizedMessage());
  					} else {
  
  					}
  				} catch (Exception e){
  					e.printStackTrace();
  				}
  			}
  			return hits;
  		} else {
  			throw new RuntimeException("Object is not bounded");
  		}
  	}
  
  	protected void generateBoundingPlanes(PointND min, PointND max){
  		this.min = min;
  		this.max = max;
  		generateBoundingPlanes();
  	}
  
  	public ArrayList<PointND> getHitsOnBoundingBox(AbstractCurve curve){
  		if (isBounded()){
  			ArrayList<PointND> hits = null;
  			if (curve instanceof StraightLine) {
  				StraightLine line = (StraightLine) curve;
  				hits = General.intersectRayWithCuboid(line, min, max);
  			} else {
  				hits = new ArrayList<PointND>();
  				for(int i = 0; i< boundingPlanes.length; i++){
  					Plane3D p = boundingPlanes[i];
  					try{
  						ArrayList<PointND> points = p.intersect(curve);
  						for (PointND intersection : points){
  							//System.out.println(intersection);
  							boolean inBound = true;
  							for (int j=0; j < intersection.getDimension(); j++){
  								double coord = intersection.get(j);
  								if (coord > max.get(j) + CONRAD.SMALL_VALUE || coord < min.get(j) - CONRAD.SMALL_VALUE) {
  									inBound = false;
  									break;
  								}
  							}
  							if (inBound) {
  								hits.add(intersection);
  							}
  						}
  					} catch (RuntimeException e){
  						if (!e.getMessage().equals("Line is parallel to plane")) { 
  							System.out.println(e.getLocalizedMessage());
  						} else {
  
  						}
  					} catch (Exception e){
  						e.printStackTrace();
  					}
  				}
  			}
  			return hits;
  		} else {
  			throw new RuntimeException("Object is not bounded");
  		}
  	}
  
  	protected void generateBoundingPlanes(){
  		boundingPlanes = new Plane3D[6];
  		//PointND e1 = new PointND(1, 0, 0);
  		//PointND e2 = new PointND(0, 1, 0);
  		//PointND e3 = new PointND(0, 0, 1);
  		//boundingPlanes[0] = new Plane3D(min, e1.getAbstractVector());
  		//boundingPlanes[1] = new Plane3D(min, e2.getAbstractVector());
  		//boundingPlanes[2] = new Plane3D(min, e3.getAbstractVector());
  		//boundingPlanes[3] = new Plane3D(max, e1.getAbstractVector());
  		//boundingPlanes[4] = new Plane3D(max, e2.getAbstractVector());
  		//boundingPlanes[5] = new Plane3D(max, e3.getAbstractVector());
  	}
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = 6333594201718418159L;
  
  	/**
  	 * Returns the point on the shape at the internal position u. If the shape is a curve, u is of dimension 1, if the shape is a surface u is of dimension 2, etc.
  	 * @param u the point in the internal parameter dimension
  	 * @return the point on the shape at the internal dimension
  	 */
  	abstract public PointND evaluate(PointND u);
  
  	/**
  	 * Returns the external dimension of the shape.
  	 * @return the dimension
  	 */
  	abstract public int getDimension();
  
  	/**
  	 * returns the internal dimension of the shape, i.e. 1 if it is a curve, 2 if it is a surface, etc.
  	 * @return the internal dimension
  	 */
  	abstract public int getInternalDimension();
  
  	/**
  	 * Returns the intersection points between the curve and the shape. 
  	 * Returns null, if the intersection is empty.
  	 * 
  	 * @param other
  	 * @return the intersection points.
  	 */
  	abstract public ArrayList<PointND> intersect(AbstractCurve other);
  	
  	
  	/**
  	 * For most objects this method simply calls intersect(other);
  	 * For triangles the orientation of the hit (scalar product of ray and triangle's normal vectors) is stored as an additional coordinate
  	 * 
  	 * @param other
  	 * @return the intersection points
  	 */
  	public ArrayList<PointND> intersectWithHitOrientation(AbstractCurve other) {
  		return intersect(other);
  	}
  
  	/**
  	 * Rasters the shape with a given number of points or less. If the shape is not bounded null is returned.
  	 * @param number the number of points
  	 * @return the raster points
  	 */
  	abstract public PointND[] getRasterPoints(int number);
  
  
  	/**
  	 * @return the minimal corner of the bounding box.
  	 */
  	public PointND getMin() {
  		return min;
  	}
  
  
  	/**
  	 * @return the maximal corner of the bounding box.
  	 */
  	public PointND getMax() {
  		return max;
  	}
  
  
  	public abstract void applyTransform(Transform t);
  
  	public String getName() {
  		return name;
  	}
  
  	public void setName(String name){
  		this.name = name;
  	}
  
  
  }
  /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
   */