/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008). * Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org). * This source code is released under a BSD License, which allows redistribution * in source and binary form, with or without modification, provided copyright * and license information are included, and with no warranty or guarantee of * any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt */ package vmm.polyhedron; import java.awt.event.ActionEvent; import java.util.ArrayList; import vmm.actions.AbstractActionVMM; import vmm.actions.ActionList; import vmm.actions.ActionRadioGroup; import vmm.core.TimerAnimation; import vmm.core.I18n; import vmm.core.Parameter; import vmm.core.RealParamAnimateable; import vmm.core.VMMSave; import vmm.core.View; import vmm.core3D.Transform3D; import vmm.core3D.Vector3D; import vmm.core3D.View3D; /** * Represents a regular polyhedron, with a parameter that allows the * corners to be "truncated" by various amounts. * In fact, this class can also be used for many * non-regular polyhedra. The main requirement is that the algorithm that * is used for truncating the corners of the polyhedron makes the following * assumption: For each vertex, the opposite endpoints of the set of edges * that are incident on that vertex must be co-planar (otherwise, the * facets that are produced when the corners are truncated will not be * planar polygons). Also, the faces should not have any colinear vertices; * that is, all the vertices of each face should be real corners of that face. *
This class is abstract, even though it has no abstract methods, because
* this class does not define any data for any polyhedron. The data must be
* provided by any concrete subclass, probably in the constructor of the
* subclass, but in any case before the computeDrawData3D() method in this class
* is called.
*/
abstract public class RegularPolyhedron extends IFS {
/**
* A list of the vertices of the polyhedron. The value of this variable in this
* class is null. A valid vertex list must be provided by any concrete subclass.
* This list contains the vertices of the basic, untruncated polyhedron. The value
* of this variable should not be modified after the object is constructed.
*/
protected Vector3D[] polyhedronVertices;
/**
* Data for the faces of the polyhedron. The value of this variable in this
* class is null. Valid face must be provided by any concrete subclass.
* This list describes the faces of the basic, untruncated polyhedron.
* polyhedronFaces[i] is a list of the vertices of face number i, where
* each vertex is specified as an index into the vertex array. The vertices
* must be listed in counterclockwise order, when viewed from the front of
* the face. The value of this variable should not be modified after the
* object is constructed.
*/
protected int[][] polyhedronFaces;
private int[][] edgeList; // This is computed in makeEdgeList the first time computeDrawData3D() is called.
// edgeList[i] contains info about edges that have vertex i as an endpoint; it
// is an array consisting of integers j such that there is an edge between vertex
// i and vertex j. Furthermore, the j's are ordered so that when vertex i is
// truncated, the j's are in the order of the edges arounded the new face that
// is produced by the truncation.
private Vector3D[] mangledPolyhedronVertices; // Vertex for the truncated or stellated polhedron,
// computed in makeTruncatedPolyhedron()/makeStellatedPolyhedron()
private int[][] mangledPolyhedronFaces; // Face data for the truncated or stellated polhedron
private boolean mangledPolyhedronIsStellated = false;
@VMMSave private boolean stellationWanted = false;
private double currentTruncation = -1; // Truncation amount for which the data in truncatedPolyhedronVertices and
// truncatedPolyhedronVertices was computed. If the value is zero, then
// the vertex and face data are null, and the basic data in polyhedronVertices and
// polyhedronFaces is used instead. The value of -1 is there to force the
// polyhedron data to be set in the superclass the first time
// computeDrawData3D() is called.
protected double stellationScale = 0.5; // To create the stellated polyhedron, a copy of the original
// a copy of the original polyhedron, scaled by this amount is used.
// This can be changed in the constructor of a subclass.
protected double stellationHeight = 1; // The height of the pyramids on the stellated polyhedron, BEFORE scaling.
// This can be changed in the consrtuctor of a subclass.
private double stellationDegree = 1; //used only during the stellation morph to morph the height of the pyramids.
private double stellationDegreeUsed = -1;
protected RealParamAnimateable truncation; // A parameter for setting the truncation amount. The value of this
// parameter varies from 1 to 0.5, with 1 indicating no truncation, but
// the trunctaion amount used for computation in this class is actually
// 1 - truncation.getValue(), which varies from 0 to 0.5.
/**
* A radio group that appears in the Action menu that allows the user to select one of the
* standard truncations.
*/
protected ActionRadioGroup truncationSelect = new ActionRadioGroup(
new String[] { I18n.tr("vmm.polyhedron.RegularPolyhedron.NoTruncation"),
I18n.tr("vmm.polyhedron.RegularPolyhedron.StandardTruncation"),
I18n.tr("vmm.polyhedron.RegularPolyhedron.MidpointTruncation"),
I18n.tr("vmm.polyhedron.RegularPolyhedron.Stellated")},
0 ) {
public void optionSelected(int selectedIndex) {
if (selectedIndex == 0)
truncation.setValue(1);
else if (selectedIndex == 1)
truncation.setValueFromString("2/3");
else if (selectedIndex == 2)
truncation.setValueFromString("1/2");
setStellationWanted(selectedIndex == 3);
}
};
/**
* Sets up a RegularPolyhedron with default viewpoint (10,-10,10) and default xy-window
* (-2,2,-2,2), and adds the {@link #truncation} parameter.
*/
protected RegularPolyhedron() {
setDefaultViewpoint(new Vector3D(10,-10,10));
setDefaultWindow(-2,2,-2,2);
truncation = new RealParamAnimateable("vmm.polyhedron.RegularPolyhedron.truncation","1","1","1/2");
truncation.setMinimumValueForInput(0.5);
truncation.setMaximumValueForInput(1);
addParameter(truncation);
}
public boolean getStellationWanted() {
return stellationWanted;
}
public void setStellationWanted(boolean stellationWanted) {
if (this.stellationWanted == stellationWanted)
return;
this.stellationWanted = stellationWanted;
if (stellationWanted) {
truncationSelect.setSelectedIndex(3);
}
else {
double val = truncation.getValue();
if (val > 0.995)
truncationSelect.setSelectedIndex(0);
else if (Math.abs(val - 2.0/3.0) < 0.005)
truncationSelect.setSelectedIndex(1);
else if (val < 0.505)
truncationSelect.setSelectedIndex(2);
else
truncationSelect.setSelectedIndex(-1);
}
forceRedraw();
}
protected void computeDrawData3D(View3D view, boolean exhibitNeedsRedraw, Transform3D previousTransform3D, Transform3D newTransform3D) {
double trunc = 1 - truncation.getValue();
if (trunc <= 0.005)
trunc = 0;
else if (trunc >= 0.495)
trunc = 0.5;
if (stellationWanted) {
if (!mangledPolyhedronIsStellated || stellationDegree != stellationDegreeUsed) {
makeStellatedPolyhedron();
mangledPolyhedronIsStellated = true;
setIFSData(mangledPolyhedronVertices, mangledPolyhedronFaces);
}
}
else if (mangledPolyhedronIsStellated || trunc != currentTruncation) {
// Note that the condition is always true the first time this method is called.
if (trunc == 0) { // Use the data for the untruncated polyhedron.
mangledPolyhedronFaces = null;
mangledPolyhedronVertices = null;
setIFSData(polyhedronVertices, polyhedronFaces);
}
else { // Compute the data for truncated polyhedron and install it so that it will be used for drawing.
if (edgeList == null)
makeEdgeList();
makeTruncatedPolyhedron(trunc);
setIFSData(mangledPolyhedronVertices, mangledPolyhedronFaces);
}
currentTruncation = trunc;
mangledPolyhedronIsStellated = false;
}
super.computeDrawData3D(view, exhibitNeedsRedraw, previousTransform3D,
newTransform3D);
}
/**
* Adds {@link #truncationSelect} to the actions from the superclass.
*/
public ActionList getActionsForView(View view) {
ActionList actions = super.getActionsForView(view);
actions.add(null);
actions.add(truncationSelect);
return actions;
}
/**
* This is overridden to keep the selected item in the action group
* {@link #truncationSelect} in sync with the value of the parameter {@link #truncation}.
*/
public void parameterChanged(Parameter param, Object oldValue, Object newValue) {
super.parameterChanged(param, oldValue, newValue);
if (param == truncation) {
stellationWanted = false;
double val = truncation.getValue();
if (val > 0.995)
truncationSelect.setSelectedIndex(0);
else if (Math.abs(val - 2.0/3.0) < 0.005)
truncationSelect.setSelectedIndex(1);
else if (val < 0.505)
truncationSelect.setSelectedIndex(2);
else
truncationSelect.setSelectedIndex(-1);
}
}
public ActionList getAdditionalAnimationsForView(final View view) {
ActionList actions = super.getAdditionalAnimationsForView(view);
actions.add(new AbstractActionVMM(I18n.tr("vmm.polyhedron.RegularPolyhedron.StellationMorph")) {
public void actionPerformed(ActionEvent evt) {
TimerAnimation animation = new TimerAnimation() {
protected void animationStarting() {
stellationDegree = 0;
truncation.setValue(1);
setStellationWanted(true);
forceRedraw();
}
protected void animationEnding() {
stellationDegree = 1;
forceRedraw();
}
protected void drawFrame() {
stellationDegree = (double)getFrameNumber()/getFrames();
forceRedraw();
}
};
animation.setLooping(TimerAnimation.OSCILLATE);
animation.setFrames(getFramesForMorphing());
view.getDisplay().installAnimation(animation);
}
});
return actions;
}
/**
* Computes the essential edge list data that is needed for making the truncated
* polyhedra. The object is to have a list of the edges that come out of each
* vertex of the original polyhedron; the data actually contains a list of
* the opposite endpoints of each of those edges. The hard part is putting
* the list of edges into the right order -- counterclockwise as seen when
* looking down on the vertex from outside the polyhedron.
*/
private void makeEdgeList() {
edgeList = new int[polyhedronVertices.length][];
for (int v = 0; v < polyhedronVertices.length; v++) {
ArrayList