content: package edu.stanford.rsl.conrad.geometry.shapes.simple;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Iterator;
  
  import edu.stanford.rsl.conrad.geometry.AbstractCurve;
  import edu.stanford.rsl.conrad.geometry.AbstractSurface;
  import edu.stanford.rsl.conrad.geometry.Axis;
  import edu.stanford.rsl.conrad.geometry.General;
  import edu.stanford.rsl.conrad.geometry.bounds.AbstractBoundingCondition;
  import edu.stanford.rsl.conrad.geometry.transforms.ComboTransform;
  import edu.stanford.rsl.conrad.geometry.transforms.Transform;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  
  /**
   * <p> CONRAD Class to model surfaces that can efficiently determine the membership of an arbitrary point.
   * <p> The main features provided by this class are point and curve membership verification and bounds checking.
   * 
   *
   * @author Rotimi X Ojo
   *
   */
  public abstract class SimpleSurface extends AbstractSurface {
  	private static final long serialVersionUID = 1165067022561672962L;
  	protected Transform transform;
  	protected ArrayList<AbstractBoundingCondition> boundingConditions = new ArrayList<AbstractBoundingCondition>();
  
  	public SimpleSurface(){
  		super();
  	}
  	
  	public SimpleSurface(SimpleSurface surf){
  		super(surf);
  		
  		// deep copy the bounding conditions list
  		if (surf.boundingConditions != null){
  			Iterator<AbstractBoundingCondition> it = surf.boundingConditions.iterator();
  			boundingConditions = new ArrayList<AbstractBoundingCondition>();
  			while (it.hasNext()) {
  				AbstractBoundingCondition cond = it.next();
  				boundingConditions.add((cond!=null) ? cond.clone() : null);
  			}
  		}
  		else {
  			boundingConditions = null;
  		}
  		
  		// copy the underlying transform
  		transform = (surf.transform != null) ? surf.transform.clone() : null;
  
  	}
  
  	@Override
  	public ArrayList<PointND> intersect(AbstractCurve other) {
  		StraightLine buff = (StraightLine) other;
  		StraightLine line = new StraightLine(buff.getPoint().clone(), buff.getDirection().clone());
  		line.applyTransform(transform.inverse());		
  		ArrayList<PointND> hitsOnShape = getHits(line);
  		if(hitsOnShape.size() > 0 && boundingConditions.size() > 0){
  			hitsOnShape = removeOBHits(hitsOnShape, line);
  			hitsOnShape = addBoundingHits(hitsOnShape, line);
  		}
  		return getCorrectedHits(hitsOnShape);		
  	}
  
  
  	public abstract ArrayList<PointND> getHits(AbstractCurve other);
  
  
  	/**
  	 * After having obtained the object hits we need to add the bounding condition hits
  	 * @param hitsOnBaseShape
  	 * @param other
  	 * @return
  	 */
  	protected ArrayList<PointND> addBoundingHits(ArrayList<PointND> hitsOnBaseShape, AbstractCurve other){
  		Iterator<AbstractBoundingCondition> boundingclips = boundingConditions.iterator();
  		while (boundingclips.hasNext()) {
  			AbstractBoundingCondition currClip = boundingclips.next();
  			ArrayList<PointND> hitsarr = null;
  			try {
  				hitsarr = currClip.getBoundingSurface().intersect(other);
  			} catch (Exception e) {
  				continue;
  			}
  			Iterator<PointND> cliphits = hitsarr.iterator();
  			while (cliphits.hasNext()) {
  				PointND currHit = cliphits.next();
  				if (isMember(currHit, true)) {
  					hitsOnBaseShape.add(currHit);
  				}
  			}
  		}		
  		return hitsOnBaseShape;
  	}
  	
  	
  	/**
  	 * Remove out of bounds hits
  	 * @param hitsOnBaseShape
  	 * @param other
  	 * @return the list without out-of-bound hits
  	 */
  	protected ArrayList<PointND> removeOBHits(ArrayList<PointND> hitsOnBaseShape, AbstractCurve other) {
  		Iterator<PointND> baseHits =  hitsOnBaseShape.iterator();
  		while(baseHits.hasNext()){
  			PointND currHit = baseHits.next();
  			Iterator<AbstractBoundingCondition> boundingclips = boundingConditions.iterator();
  			while(boundingclips.hasNext()){
  				if(!boundingclips.next().isSatisfiedBy(currHit)){
  					baseHits.remove();
  					break;
  				}
  			}
  		}
  		return hitsOnBaseShape;
  	}
  
  	/**
  	 * Calculates matrix for rotating shape from a principal axis to new axis.
  	 * Special case of change of coordinate.
  	 * @param newAxis
  	 * @return the matrix
  	 */
  	public SimpleMatrix getChangeOfAxisMatrix(Axis newAxis) {
  		if(newAxis == null || General.areColinear(getPrincipalAxis().getAxisVector(), newAxis.getAxisVector(), CONRAD.FLOAT_EPSILON)){
  			SimpleMatrix rotator = new SimpleMatrix(getPrincipalAxis().dimension(),getPrincipalAxis().dimension());
  			rotator.identity();
  			return rotator;
  		}
  
  		SimpleVector Z = newAxis.getAxisVector();
  		SimpleVector Y = new Axis(General.crossProduct(Z, getPrincipalAxis().getAxisVector())).getAxisVector();
  		SimpleVector X = General.crossProduct(Z, Y);
  
  		double [][] data = {X.copyAsDoubleArray(),Y.copyAsDoubleArray(),Z.copyAsDoubleArray()};
  		return new SimpleMatrix(data).transposed();
  	}
  
  	/**
  	 * Transforms given point (usually from object space to world space).
  	 * @param hits hits to be transformed by tranformer.
  	 * @return ArrayList of hits in new space
  	 */
  	protected ArrayList<PointND> getCorrectedHits(ArrayList<PointND> hits){
  		ArrayList<PointND> correctedHits = new ArrayList<PointND>(2);
  		Iterator<PointND> quadHits = hits.iterator();
  
  		while (quadHits.hasNext()) {
  			PointND val = transform.transform(quadHits.next());
  			correctedHits.add(val);
  		}		
  
  		return correctedHits;
  	}
  
  	/**
  	 * Determines if the given point in within the bounds of shape;
  	 * @param point The point needs to be non-transformed, hence a world coordinate system point
  	 * @return true if the point is within the surface
  	 */
  	public boolean isMember(PointND point){
  		return isMember(point, false);
  	}
  	
  	/**
  	 * Determines if the given point in within the bounds of shape;
  	 * @param point
  	 * @param pointTransformed if true, the point is already in a transformed state, otherwise it needs to be transformed
  	 * @return true if the point is within the surface
  	 */
  	public abstract boolean isMember(PointND point, boolean pointTransformed);
  
  	/**
  	 * Adds a bounding condition to the top of the "stack". 
  	 * @param condition
  	 */
  	public void addBoundingCondition(AbstractBoundingCondition condition){
  		boundingConditions.add(0,condition);
  	}
  
  
  	public abstract Axis getPrincipalAxis();
  
  	/**
  	 * Adds a collection of bounding conditions
  	 * @param conditions
  	 */
  	public void addAllBoundingConditions(Collection<? extends AbstractBoundingCondition> conditions){
  		boundingConditions.addAll(conditions);
  	}
  
  	@Override
  	public void applyTransform(Transform t) {
  		transform = new ComboTransform(transform, t);
  		// transform the eight corners:
  		PointND [] corners = new PointND[8];
  		corners[0] = new PointND(min.get(0), min.get(1), min.get(2));
  		corners[1] = new PointND(min.get(0), min.get(1), max.get(2));
  		corners[2] = new PointND(min.get(0), max.get(1), min.get(2));
  		corners[3] = new PointND(min.get(0), max.get(1), max.get(2));
  		corners[4] = new PointND(max.get(0), min.get(1), min.get(2));
  		corners[5] = new PointND(max.get(0), min.get(1), max.get(2));
  		corners[6] = new PointND(max.get(0), max.get(1), min.get(2));
  		corners[7] = new PointND(max.get(0), max.get(1), max.get(2));
  		min = new PointND(Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE);
  		max = new PointND(-Double.MAX_VALUE,-Double.MAX_VALUE,-Double.MAX_VALUE);
  		for (int i=0; i < 8; i++){
  			PointND transf = t.transform(corners[i]);
  			min.updateIfLower(transf);
  			max.updateIfHigher(transf);
  		}
  	}
  
  	@Override
  	public int getInternalDimension() {
  		return 0;
  	}
  
  	@Override
  	public PointND evaluate(PointND u) {
  		return null;
  	}
  
  	public float[] getRasterPoints(int elementCountU, int elementCountV) {
  		return null;
  	}
  
  	/**
  	 * Returns cloned copy of shape's affine transform
  	 * @return the transform
  	 */
  	public Transform getTransform() {	
  		return transform.clone();
  	}
  
  
  }
  /*
   * Copyright (C) 2010-2014 Andreas Maier, Rotimi X Ojo 
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
   */