content: package edu.stanford.rsl.conrad.geometry.shapes.simple;
  
  import java.util.ArrayList;
  
  import edu.stanford.rsl.apps.gui.opengl.PointCloudViewer;
  import edu.stanford.rsl.conrad.geometry.AbstractCurve;
  import edu.stanford.rsl.conrad.geometry.AbstractShape;
  import edu.stanford.rsl.conrad.geometry.Axis;
  import edu.stanford.rsl.conrad.geometry.General;
  import edu.stanford.rsl.conrad.geometry.shapes.compound.CompoundShape;
  import edu.stanford.rsl.conrad.geometry.transforms.AffineTransform;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  
  /**
   * Creates a Box.
   */
  public class Box extends SimpleSurface {
  
  
  	public static final long serialVersionUID = 1760483762488429473L;
  	private Axis principalAxis = new Axis(new SimpleVector(1,0,0));
  	//protected PointND lowerCorner;
  	public PointND lowerCorner;    // invisible in OpenCLBox, the suggestion is to change to 'public'
  	//protected PointND upperCorner;
  	public PointND upperCorner;
  	
  	public Box(){
  		SimpleMatrix mat = new SimpleMatrix(3,3);	
  		mat.identity();
  		transform = new AffineTransform(mat, new SimpleVector(0,0,0));
  	}
  	
  	/**
  	 * Generates a box of size dx*dy*dz at between (0,0,0) and (dx,dy,dz)
  	 * @param dx
  	 * @param dy
  	 * @param dz
  	 */
  	public Box(double dx, double dy, double dz){
  		this();
  		init((new PointND(0,0,0)),dx, dy, dz);
  	}
  
  	public Box(Box b){
  		super(b);
  		principalAxis = b.principalAxis!=null ? b.principalAxis.clone() : null;
  		lowerCorner = b.lowerCorner != null ? b.lowerCorner.clone() : null;
  		upperCorner = b.upperCorner != null ? b.upperCorner.clone() : null;
  	}
  	
  	protected void init(PointND origin, double dx, double dy,	double dz) {
  		min = origin;
  		lowerCorner = min;
  		max = new PointND(origin.get(0)+ dx,origin.get(1)+dy,origin.get(2)+dz);
  		upperCorner = max;
  		generateBoundingPlanes();		
  	}
  	
  	@Override
  	public boolean isMember(PointND point, boolean pointTransformed) {
  		// TODO: Correct for new boundary conditions
  		// Now, only the bounding box is checked.
  		for (int j=0; j < point.getDimension(); j++){
  			double coord = point.get(j);
  			if (coord > max.get(j) + CONRAD.SMALL_VALUE || coord < min.get(j) - CONRAD.SMALL_VALUE) {
  				return false;
  			}
  		}
  		return true;
  	}
  
  
  	@Override
  	public PointND evaluate(PointND u) {
  		return null;
  	}
  
  	@Override
  	public int getDimension() {
  		return 3;
  	}
  
  	@Override
  	public int getInternalDimension() {
  		return 0;
  	}
  
  	@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 = General.intersectRayWithCuboid(line, lowerCorner, upperCorner);
  		return getCorrectedHits(hitsOnShape);	
  	}
  
  	@Override
  	public boolean isBounded() {
  		return true;
  	}
  
  
  	@Override
  	public PointND[] getRasterPoints(int number) {
  		PointND one = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		PointND two = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		PointND three = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		PointND four = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		PointND five = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		PointND six = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		PointND seven = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		PointND eight = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		int subNumber = number /12;
  		Edge e01 = new Edge(one, two);
  		Edge e02 = new Edge(one, three);
  		Edge e03 = new Edge(one, five);
  		Edge e04 = new Edge(two, six);
  		Edge e05 = new Edge(two, four);
  		Edge e06 = new Edge(three, four);
  		Edge e07 = new Edge(seven, three);
  		Edge e08 = new Edge(four, eight);
  		Edge e09 = new Edge(five, six);
  		Edge e10 = new Edge(five, seven);
  		Edge e11 = new Edge(six, eight);
  		Edge e12 = new Edge(seven, eight);
  		PointND [] points01 = e01.getRasterPoints(subNumber);
  		PointND [] points02 = e02.getRasterPoints(subNumber);
  		PointND [] points03 = e03.getRasterPoints(subNumber);
  		PointND [] points04 = e04.getRasterPoints(subNumber);
  		PointND [] points05 = e05.getRasterPoints(subNumber);
  		PointND [] points06 = e06.getRasterPoints(subNumber);
  		PointND [] points07 = e07.getRasterPoints(subNumber);
  		PointND [] points08 = e08.getRasterPoints(subNumber);
  		PointND [] points09 = e09.getRasterPoints(subNumber);
  		PointND [] points10 = e10.getRasterPoints(subNumber);
  		PointND [] points11 = e11.getRasterPoints(subNumber);
  		PointND [] points12 = e12.getRasterPoints(subNumber);
  		PointND [] points = new PointND[12 * subNumber];
  		int offset = 0;
  		System.arraycopy(points01, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points02, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points03, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points04, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points05, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points06, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points07, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points08, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points09, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points10, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points11, 0, points, offset, subNumber);
  		offset += subNumber;
  		System.arraycopy(points12, 0, points, offset, subNumber);
  		return points;
  	}
  
  	@Override
  	public ArrayList<PointND> getHits(AbstractCurve other) {
  		// TODO Auto-generated method stub
  		return null;
  	}
  
  	@Override
  	public Axis getPrincipalAxis() {
  		return principalAxis ;
  	}
  
  	@Override
  	public PointND evaluate(double u, double v) {
  		// TODO Auto-generated method stub
  		return null;
  	}
  
  	@Override
  	public AbstractShape tessellate(double accuracy) {
  		PointND one = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		PointND two = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		PointND three = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		PointND four = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		PointND five = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		PointND six = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		PointND seven = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		PointND eight = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		CompoundShape shape = new CompoundShape();
  		shape.add(new Triangle(one, two, four));
  		shape.add(new Triangle(one, three, four));
  		
  		shape.add(new Triangle(one, two, six));
  		shape.add(new Triangle(one, five, six));
  		
  		shape.add(new Triangle(two, four, eight));
  		shape.add(new Triangle(two, six, eight));
  		
  		shape.add(new Triangle(one, three, seven));
  		shape.add(new Triangle(one, five, seven));
  		
  		shape.add(new Triangle(six, five, seven));
  		shape.add(new Triangle(six, seven, eight));
  		
  		shape.add(new Triangle(three, four, eight));
  		shape.add(new Triangle(three, seven, eight));
  		
  		return shape;
  	}
  
  
  	public float[] getRasterPointsOld(int elementCountU, int elementCountV) {
  		//if (elementCountU % 8 != 0 || elementCountV % 8 != 0){
  		//	System.out.println("Error! valuesU and valuesV have to be a multiple of 4");
  		//	return null;
  		//}
  		int pointsOnCurve = elementCountU;
  		int numberOfCurves = elementCountV;
  		int extraValuesPerEdge = (elementCountU-4)/4;
  		
  		PointND[] points = new PointND[8];
  		
  		points[0] = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		points[1] = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		points[2] = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		points[3] = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		
  		points[4] = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		points[5] = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		points[6] = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		points[7] = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		
  		float[] curve = new float[elementCountU*elementCountV*3];
  		
  	/*	for (int i = 0; i < pointsOnCurve; i++) {
  			for (int j = 0; j < numberOfCurves; j++){
  				if (i%(extraValuesPerEdge+1) == 0) { //regular corner point
  					curve[(j*pointsOnCurve+i)*3] = (float) points[i%(extraValuesPerEdge)].get(0);
  					curve[(j*pointsOnCurve+i)*3+1] = (float) points[i%4].get(1);
  				}
  				curve[(j*pointsOnCurve+i)*3] = (float) points[i%4].get(0);
  				curve[(j*pointsOnCurve+i)*3+1] = (float) points[i%4].get(1);
  				//curve[(j*pointsOnCurve+i)*3+2] = (float) (points[i%4].get(2)-points[];
  				//curve[i] = (new PointND(((min.get(0)+ max.get(0))/2)+0.5*(max.get(0)-min.get(0))*Math.cos(angle), ((min.get(1)+ max.get(1))/2)+0.5*(max.get(1)-min.get(1))*Math.sin(angle), max.get(2)));
  				//lowerCurve[i] = (new PointND(((min.get(0)+ max.get(0))/2)+0.5*(max.get(0)-min.get(0))*Math.cos(angle), ((min.get(1)+ max.get(1))/2)+0.5*(max.get(1)-min.get(1))*Math.sin(angle), min.get(2)));
  				
  			}
  		} */
  		int k = 0;
  		
  		for (int i = 0; i < pointsOnCurve; i++) {
  			for (int j = 0; j < numberOfCurves; j++){
  				k = (4*i)/pointsOnCurve;
  				
  				curve[(i*numberOfCurves+j)*3] = (float) points[k%4].get(0);
  				curve[(i*numberOfCurves+j)*3+1] = (float) points[k%4].get(1);
  				curve[(i*numberOfCurves+j)*3+2] = (float) (points[k%4].get(2) - (points[k%4].get(2)-points[k%4+4].get(2)) * ((float) j/(numberOfCurves-1)));
  				
  				//curve[i] = (new PointND(((min.get(0)+ max.get(0))/2)+0.5*(max.get(0)-min.get(0))*Math.cos(angle), ((min.get(1)+ max.get(1))/2)+0.5*(max.get(1)-min.get(1))*Math.sin(angle), max.get(2)));
  				//lowerCurve[i] = (new PointND(((min.get(0)+ max.get(0))/2)+0.5*(max.get(0)-min.get(0))*Math.cos(angle), ((min.get(1)+ max.get(1))/2)+0.5*(max.get(1)-min.get(1))*Math.sin(angle), min.get(2)));
  			}
  		}
  		return curve;
  	}
  	
  	public PointND interpolateU(double u, PointND [] points, int offset){
  		PointND first;
  		PointND second;
  		if (u <= 0.25){
  			first = points[0+offset];
  			second = points[1+offset];
  		} else if (u <= 0.5){
  			u -= 0.25;
  			first = points[1+offset];
  			second = points[2+offset];
  		} else if (u <= 0.75){
  			u -= 0.5;
  			first = points[2+offset];
  			second = points[3+offset];
  		} else {
  			u -= 0.75;
  			first = points[3+offset];
  			second = points[0+offset];
  		}
  		return interpolate(first, second, u*4.0);
  	}
  	
  	public PointND interpolate(PointND first, PointND second, double factor){
  		SimpleVector resultVector = first.getAbstractVector().multipliedBy(1.0-factor);
  		resultVector.add(second.getAbstractVector().multipliedBy(factor)); 
  		return new PointND(resultVector);
  	}
  	
  	@Override
  	public float[] getRasterPoints(int elementCountU, int elementCountV) {
  		
  		PointND[] points = new PointND[8];
  		
  		points[0] = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		points[1] = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), upperCorner.get(2)));
  		points[2] = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		points[3] = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), upperCorner.get(2)));
  		
  		points[4] = transform.transform(new PointND(lowerCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		points[5] = transform.transform(new PointND(upperCorner.get(0), lowerCorner.get(1), lowerCorner.get(2)));
  		points[6] = transform.transform(new PointND(upperCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		points[7] = transform.transform(new PointND(lowerCorner.get(0), upperCorner.get(1), lowerCorner.get(2)));
  		
  		float[] curve = new float[elementCountU*elementCountV*3];
  		
  		
  		for (int j = 0; j < elementCountV; j++){
  			double v = ((double)j)/(elementCountV-1);
  			for (int i = 0; i < elementCountU; i++) {
  				double u = ((double)i)/(elementCountU);
  				
  				PointND top = interpolateU(u, points, 0);
  				PointND bottom = interpolateU(u, points, 4);
  				PointND gridPoint = interpolate(bottom, top, v);
  				
  				curve[(j*elementCountU+i)*3] = (float) gridPoint.get(0);
  				curve[(j*elementCountU+i)*3+1] = (float) gridPoint.get(1);
  				curve[(j*elementCountU+i)*3+2] = (float) gridPoint.get(2);
  			}
  		} 
  		return curve;
  	}
  	
  	/*public float[] getRasterPointsNew(int elementCountU, int elementCountV){
  		
  		int pointsOnCurve = elementCountU;
  		int numberOfCurves = elementCountV;
  		int extraValuesPerEdge = (elementCountU-4)/4;
  		float[] curve = new float[elementCountU*elementCountV*3];
  		
  		for (int j = 0; j < numberOfCurves; j++) {
  			for (int i = 0; i < pointsOnCurve; i++){
  				for (int k = 0; k < (int)pointsOnCurve/4; k++){ 
  				curve[(j*pointsOnCurve+i+k)*3] = (float) (lowerCorner.get(0) + i*(upperCorner.get(0)-lowerCorner.get(0))/(pointsOnCurve-1));
  				}
  				curve[(j*pointsOnCurve+i+k)*3+1] = (float) (lowerCorner.get(1) + i*(upperCorner.get(1)-lowerCorner.get(1))/(pointsOnCurve-1));
  				curve[(j*pointsOnCurve+i+k)*3+2] = (float) (lowerCorner.get(2) + j*(upperCorner.get(2)-lowerCorner.get(2))/(numberOfCurves-1));
  			}
  		}
  		return curve;
  	}*/
  
  	@Override
  	public AbstractShape clone() {
  		return new Box(this);
  	}
  
  	/**
  	 * @return the lowerCorner
  	 */
  	public PointND getLowerCorner() {
  		return lowerCorner;
  	}
  
  	/**
  	 * @param lowerCorner the lowerCorner to set
  	 */
  	public void setLowerCorner(PointND lowerCorner) {
  		this.lowerCorner = lowerCorner;
  	}
  
  	/**
  	 * @return the upperCorner
  	 */
  	public PointND getUpperCorner() {
  		return upperCorner;
  	}
  
  	/**
  	 * @param upperCorner the upperCorner to set
  	 */
  	public void setUpperCorner(PointND upperCorner) {
  		this.upperCorner = upperCorner;
  	}
  
  	/**
  	 * @param principalAxis the principalAxis to set
  	 */
  	public void setPrincipalAxis(Axis principalAxis) {
  		this.principalAxis = principalAxis;
  	}
  	
  	public ArrayList<PointND> getPointCloud(int u, int v){
  		float[] rasterPoints = this.getRasterPoints(u, v);
  		//PointND[] points = box.getRasterPoints(256);
  		
  	    ArrayList<PointND> pointsList= new ArrayList<PointND>();
  		
  	    for (int i= 0; i < rasterPoints.length/3; i++){
  
  			PointND point = new PointND(rasterPoints[(i*3)],rasterPoints[(i*3)+1],rasterPoints[(i*3)+2]);
  			//	PointND point = new PointND(points[i].get(0),points[i].get(1),points[i].get(2));
  
  	    		pointsList.add(point);
  	    	
  		}
  	    return pointsList;
  	}
  	
  /*	public static void main(String [] args){
  		Box box = new Box (1,1,1);
  		
  		ArrayList<PointND> pointsList=box.getPointCloud(40, 10);
  		
  	    PointCloudViewer pcv = new PointCloudViewer("Box Visualization", pointsList);
  		pcv.setVisible(true);
  	}*/
  
  }
  
  /*
   * Copyright (C) 2010-2014 Andreas Maier, Rotimi X Ojo, Zijia Guo 
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  
  