content: package edu.stanford.rsl.conrad.geometry.shapes.simple;
  
  import edu.stanford.rsl.conrad.geometry.AbstractShape;
  import edu.stanford.rsl.conrad.geometry.Axis;
  import edu.stanford.rsl.conrad.geometry.shapes.compound.CompoundShape;
  import edu.stanford.rsl.conrad.geometry.transforms.AffineTransform;
  import edu.stanford.rsl.conrad.geometry.transforms.Transform;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.numerics.SimpleVector;
  
  
  /**
   * Creates a Sphere.
   * @author Rotimi X Ojo
   */
  public class Sphere extends QuadricSurface {
  
  	private static final long serialVersionUID = 7869742887570580304L;
  	private Axis principalAxis = new Axis(new SimpleVector(1,0,0));
  	
  	public Sphere(){
  	}	
  	
  	public Sphere(double radius, PointND surfaceOrigin){		
  		init(radius, surfaceOrigin);
  	}
  	
  	public Sphere(double radius){		
  		init(radius, new PointND(0,0,0));
  	}
  	
  	public Sphere(Sphere s){
  		super(s);
  		principalAxis = (s.principalAxis != null) ? s.principalAxis.clone() : null; 
  	}
  	
  	protected void init(double radius, PointND surfaceOrigin){
  		SimpleMatrix mat = new SimpleMatrix(3,3);
  		mat.identity();
  		transform = new AffineTransform(mat, surfaceOrigin.getAbstractVector());
  		double cons = 1/(radius*radius);
  		double constArray[][] = {{cons,0,0},{0,cons,0},{0,0,cons}};
  		super.constMatrix = new SimpleMatrix(constArray);
  		super.constant = 1;
  		
  		this.min = new PointND(surfaceOrigin.get(0) - radius, surfaceOrigin.get(1) - radius, surfaceOrigin.get(2) - radius);
  		this.max = new PointND(surfaceOrigin.get(0) + radius, surfaceOrigin.get(1) + radius, surfaceOrigin.get(2) + radius);
  	}
  
  	@Override
  	public boolean isBounded() {
  		return true;
  	}
  
  	@Override
  	public PointND[] getRasterPoints(int number) {
  		// TODO Auto-generated method stub
  		return null;
  	}
  
  	@Override
  	public Axis getPrincipalAxis() {
  		return principalAxis;
  	}
  	
  	@Override
  	public void applyTransform(Transform t) {
  		super.applyTransform(t);
  		//this.min = t.transform(this.min);
  		//this.max = t.transform(this.max);
  	}
  
  	@Override
  	public AbstractShape tessellate(double accuracy) {
  		
  		PointND center = (new PointND((min.get(0)+max.get(0))/2, (min.get(1)+max.get(1))/2,(min.get(2)+max.get(2))/2));
  		double radius = (max.get(0)-min.get(0))/2;
  		
  		
  		int pointsOnCurve = (int) (accuracy * 50); 
  		int nextCurve = pointsOnCurve + 1;
  		double angle;
  		double angleIncrement = (2*Math.PI)/pointsOnCurve;
  		int slices = (int) (accuracy*40);
  		if (slices%2 != 0) slices++; //uneven number of slices to include equator curve
  		double curveDistance = (radius*2)/(slices);
  		
  		PointND[] curve = new PointND[(pointsOnCurve+1)*(slices + 1)]; //last value of each slice's curve equals first value. Total number of curves is number of slices + 1.
  
  
  		/*
  		 * Create all layers
  		 */
  		CompoundShape 	shape = new CompoundShape();
  		double scalingCurve;
  		for (int z = 0; z <= slices; z++) {
  			scalingCurve = Math.sqrt(Math.pow(radius, 2) - Math.pow((radius-z*curveDistance), 2));
  				angle = 0;
  				for (int i = 0+z*nextCurve; i < pointsOnCurve+z*nextCurve; i++) {
  					curve[i] = (new PointND(center.get(0)+scalingCurve*Math.cos(angle), center.get(1)+scalingCurve*Math.sin(angle), center.get(2)+radius-z*curveDistance));
  					angle += angleIncrement;
  				}
  				curve[pointsOnCurve+z*nextCurve] = curve[0+z*nextCurve];
  		}
  		for (int z = 0; z < slices; z++) {
  			for (int i = 0+z*nextCurve; i < pointsOnCurve+z*nextCurve; i++) {
  				if (i > pointsOnCurve) shape.add(new Triangle(curve[i], curve[i+nextCurve], curve[i+1]));
  				if (i < pointsOnCurve+(slices-2)*nextCurve) shape.add(new Triangle(curve[i+nextCurve], curve[i+1+nextCurve], curve[i+1]));
  			}
  		}
  		return shape;
  	}
  	public double getRadius(){
  		return (max.get(0)-min.get(0))/2;
  	}
  	
  	public PointND getCenter(){
  		return new PointND((min.get(0)+max.get(0))/2, (min.get(1)+max.get(1))/2,(min.get(2)+max.get(2))/2);
  	}
  	
  	@Override
  	public float[] getRasterPoints(int elementCountU, int elementCountV) {
  
  		if (elementCountU < 2){
  			System.out.println("Error! valuesU has to be higher than 1");
  			return null;
  		}
  
  		PointND center = (new PointND((min.get(0)+max.get(0))/2, (min.get(1)+max.get(1))/2,(min.get(2)+max.get(2))/2));
  		double radius = (max.get(0)-min.get(0))/2;
  		
  		
  		int pointsOnCurve = elementCountU; 
  		double angle;
  		double angleIncrement = (2*Math.PI)/(pointsOnCurve-1);
  		int numberOfCurves = elementCountV;
  		
  		double curveDistance = (radius*2)/(numberOfCurves-1);
  		
  		float[] curve = new float[(pointsOnCurve)*(numberOfCurves)*3];
  
  		
  		double scalingCurve;
  		for (int i = 0; i < pointsOnCurve; i++) {
  				//for (int j = 0+i*nextCurve; j < pointsOnCurve+i*nextCurve; j++) {
  				for (int j = 0; j < numberOfCurves; j++){
  					angle = i * angleIncrement;
  					scalingCurve = Math.sqrt(Math.pow(radius, 2) - Math.pow((radius-j*curveDistance), 2));
  					curve[(j*pointsOnCurve+i)*3] = (float) (center.get(0)+scalingCurve*Math.cos(angle));
  					curve[(j*pointsOnCurve+i)*3+1] = (float) (center.get(1)+scalingCurve*Math.sin(angle));
  					curve[(j*pointsOnCurve+i)*3+2] = (float) (center.get(2)-radius+j*curveDistance);
  				}
  		}
  
  		return curve;
  	}
  
  	@Override
  	public AbstractShape clone() {
  		return new Sphere(this);
  	}
  
  
  }
  /*
   * Copyright (C) 2010-2014 Andreas Maier, Rotimi X Ojo 
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */