package drawing3D;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.ProgressMonitor;
import math3D.Window3D;
import primitives3D.Object3D;
/**
* Manages a collection of 3D objects for drawing. This class is responsible for
* Z-Sorting.
*
* @author Curran Kelleher
*
*/
public class Object3DViewer {
/**
* The 3D objects to Z-Sort and draw.
*/
private Vector<Object3D> objects = new Vector<Object3D>();
/**
* The Window3D to use for calculating rotations.
*/
public Window3D window;
/**
* The comparator to use for Z-Sorting.
*/
private Comparator<Object3D> depthComparator;
/**
* The color of the background used in depth shading
*/
public static Color backgroundColor = Color.black;
/**
* Temporary variables used in calculation of depth shading.
*/
static float[] colorComponents = { 0, 0, 0 }, bkgColorComponents = { 0, 0,
0 };
/**
* Construct an Object3DViewer which will use the default Window3D to
* calculate rotations.
*/
public Object3DViewer() {
this(new Window3D(0, 0));
}
/**
* Construct an Object3DViewer which will use the specified Window3D to
* calculate rotations.
*
* @param window
* the window to use when calculating rotations.
*/
public Object3DViewer(Window3D window) {
depthComparator = new Comparator<Object3D>() {
public int compare(Object3D a, Object3D b) {
return a.centerPoint.z > b.centerPoint.z ? 1 : -1;
}
};
this.window = window;
}
/**
* Adds an Object3D to the collection of objects to draw.
*
* @param o
* the Object3D to add to the collection of objects to draw.
*/
public void add3DObject(Object3D o) {
objects.add(o);
}
/**
* Adds an array of 3D objects to the collection of objects to draw.
*
* @param objects
* the array of 3D objects to add to the collection of objects to
* draw.
*/
public void add3DObjects(Object3D[] objects) {
for (int i = 0; i < objects.length; i++)
add3DObject(objects[i]);
}
/**
* Adds a collection of 3D objects to the collection of objects to draw.
*
* @param objects
* the collection of 3D objects to add to the collection of
* objects to draw.
*/
public void add3DObjects(Collection<Object3D> objects) {
this.objects.addAll(objects);
}
/**
* Removes all Object3Ds from the collection of objects to draw.
*
*/
public void clear3DObjects() {
objects.clear();
}
/**
* Z-Sorts and draws all 3D objects which have been added to the collection
* of objects to draw via the add3DObject() method.
*
* @param g
* the Graphics on which to draw the 3D objects.
*/
public void drawObjectsOnThis(Graphics g) {
synchronized (objects) {
// calculate all rotations
for (Iterator<Object3D> it = objects.iterator(); it.hasNext();) {
Object3D currentObject = it.next();
// very rarely, when the "objects" list is being modified by
// another thread, "currentObject" is null here
if (currentObject != null)
currentObject.calculateRotation(window);
else
// if we are here, then that strange thread error has
// occurred, and it causes an exception to be thrown
// when
// Collections.sort() is called later, so just
// return now to prevent further errors
return;
}
// Z-Sort
Collections.sort(objects, depthComparator);
// Draw all objects
Graphics2D g2D = (Graphics2D) g;
for (Iterator<Object3D> it = objects.iterator(); it.hasNext();)
it.next().drawOnThis(g2D);
}
}
/**
* Z-Sorts and draws all 3D objects which have been added to the collection
* of objects to draw via the add3DObject() method. The progress of the
* drawing is instrumented with the specified progress monitor.
*
* @param g
* the Graphics on which to draw the 3D objects.
* @param progressMonitor
* the progress monitor which will be updated with the progress
* of drawing the objects. If the progress dialog is cancelled,
* then the drawing stops.
*/
public void drawObjectsOnThis(Graphics g, ProgressMonitor progressMonitor) {
synchronized (objects) {
progressMonitor.setProgress(1);
progressMonitor.setNote("Calculating rotations...");
for (Iterator<Object3D> it = objects.iterator(); it.hasNext();) {
Object3D currentObject = it.next();
// check for cancellation
if (progressMonitor.isCanceled())
return;
// very rarely, when the "objects" list is being modified by
// another thread, "currentObject" here is null
if (currentObject != null)
currentObject.calculateRotation(window);
else
// if we are here, then that strange thread error has
// occurred, and it causes an exception to be thrown when
// Collections.sort() is called later, so just
// return now to prevent further errors
return;
}
progressMonitor.setNote("Z-Sorting...");
// Z-Sort
Collections.sort(objects, depthComparator);
// Draw all objects
Graphics2D g2D = (Graphics2D) g;
double numberOfObjects = (double) objects.size();
double currentIndex = 1;
progressMonitor.setNote("Drawing objects...");
int maxProgress = progressMonitor.getMaximum();
for (Iterator<Object3D> it = objects.iterator(); it.hasNext();) {
// check for cancellation
if (progressMonitor.isCanceled())
return;
else {
progressMonitor.setProgress((int) (currentIndex++
/ numberOfObjects * maxProgress));
it.next().drawOnThis(g2D);
}
}
}
}
/**
* Shades a color; darker with distance.
*
* @param originalColor
* the original color
* @param z
* the z position of the color
* @return the shaded color
*/
public static Color shadeColor(Color originalColor, double z) {
originalColor.getColorComponents(colorComponents);
backgroundColor.getColorComponents(bkgColorComponents);
double percentOriginal = (z / 20 + 0.8);
double percentBackground = 1 - percentOriginal;
double x;
for (int i = 0; i < 3; i++) {
x = colorComponents[i] * percentOriginal + bkgColorComponents[i]
* percentBackground;
colorComponents[i] = (float) (x > 1 ? 1 : x < 0 ? 0 : x);
}
return new Color(colorComponents[0], colorComponents[1],
colorComponents[2]);
}
/**
* Figures out the object which, when drawn on the screen, the specified
* point is "inside"
*
* @param point
* the point to look for objects under
* @return the object which, when drawn on the screen, the specified point
* is "inside", or null if the point is not over an object.
*/
public Object3D getObjectWhichContainsPoint(Point point) {
for (Iterator<Object3D> it = objects.iterator(); it.hasNext();) {
Object3D currentObject = it.next();
if(currentObject.contains(point))
return currentObject;
}
return null;
}
}