package org.geogebra.common.geogebra3D.kernel3D.algos;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPoint3D;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPolygon3D;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPolyhedron;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoSegment3D;
import org.geogebra.common.geogebra3D.kernel3D.solid.PlatonicSolid;
import org.geogebra.common.geogebra3D.kernel3D.solid.PlatonicSolidsFactory;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Matrix.CoordMatrix4x4;
import org.geogebra.common.kernel.Matrix.Coords;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.kernelND.GeoPointND;
/**
* @author ggb3D
*
* Creates a new GeoPolyhedron
*
*/
public class AlgoArchimedeanSolidThreePoints extends AlgoPolyhedron {
protected OutputHandler<GeoPolygon3D> outputPolygons;
protected OutputHandler<GeoSegment3D> outputSegments;
private GeoPointND A, B, C;
protected CoordMatrix4x4 matrix;
private Coords[] coords;
private Commands name;
private PlatonicSolid solidDescription;
/**
* creates an archimedean solid
*
* @param c
* construction
* @param labels
* @param A
* @param B
* @param v
* @param name
*/
public AlgoArchimedeanSolidThreePoints(Construction c, String[] labels,
GeoPointND A, GeoPointND B, GeoPointND C, Commands name) {
super(c);
this.name = name;
// set polyhedron type
switch (name) {
default:
case Tetrahedron:
polyhedron.setType(GeoPolyhedron.TYPE_TETRAHEDRON);
solidDescription = PlatonicSolidsFactory.getTetrahedron();
break;
case Cube:
polyhedron.setType(GeoPolyhedron.TYPE_CUBE);
solidDescription = PlatonicSolidsFactory.getCube();
break;
case Octahedron:
polyhedron.setType(GeoPolyhedron.TYPE_OCTAHEDRON);
solidDescription = PlatonicSolidsFactory.getOctahedron();
break;
case Dodecahedron:
polyhedron.setType(GeoPolyhedron.TYPE_DODECAHEDRON);
solidDescription = PlatonicSolidsFactory.getDodecahedron();
break;
case Icosahedron:
polyhedron.setType(GeoPolyhedron.TYPE_ICOSAHEDRON);
solidDescription = PlatonicSolidsFactory.getIcosahedron();
break;
}
setVolumeAreaAndHeightFactors();
this.A = A;
this.B = B;
this.C = C;
matrix = new CoordMatrix4x4();
createPolyhedron();
compute();
// input
setInput();
addAlgoToInput();
polyhedron.createFaces();
// faces are oriented to the inside
polyhedron.setReverseNormals();
setOutput();
setLabels(labels);
update();
}
/**
* set the labels
*
* @param labels
* lables
*/
protected void setLabels(String[] labels) {
if (labels == null || labels.length <= 1) {
polyhedron.initLabels(labels);
} else {
polyhedron.setAllLabelsAreSet(true);
for (int i = 0; i < labels.length; i++) {
getOutput(i).setLabel(labels[i]);
}
}
}
protected void setInput() {
input = new GeoElement[3];
input[0] = (GeoElement) A;
input[1] = (GeoElement) B;
input[2] = (GeoElement) C;
}
@Override
protected void createOutputSegments() {
outputSegments = createOutputSegmentsHandler();
}
@Override
protected void createOutputPolygons() {
outputPolygons = createOutputPolygonsHandler();
}
@Override
protected void updateOutput() {
// add polyhedron's segments and polygons, without setting this algo as
// algoparent
outputPolygons.addOutput(polyhedron.getFaces3D(), false, false);
outputSegments.addOutput(polyhedron.getSegments3D(), false, true);
}
/**
* create the polyhedron (faces and edges)
*
* @param polyhedron
*/
protected void createPolyhedron() {
int vertexCount = solidDescription.getVertexCount();
outputPoints.augmentOutputSize(vertexCount - 3, false);
if (getPolyhedron().allLabelsAreSet()) {
outputPoints.setLabels(null);
}
// coords
coords = solidDescription.getVertices();
// points
GeoPointND[] points = new GeoPointND[vertexCount];
points[0] = A;
points[1] = B;
points[2] = C;
for (int i = 3; i < vertexCount; i++) {
GeoPoint3D point = outputPoints.getElement(i - 3);
points[i] = point;
point.setCoords(coords[i]);
polyhedron.addPointCreated(point);
}
// faces
int[][] faces = solidDescription.getFaces();
for (int i = 0; i < faces.length; i++) {
polyhedron.startNewFace();
for (int j = 0; j < faces[i].length; j++) {
polyhedron.addPointToCurrentFace(points[faces[i][j]]);
}
polyhedron.endCurrentFace();
}
}
private Coords v1l = new Coords(4), v2l = new Coords(4),
vnl = new Coords(4), tmpCoords = new Coords(4);
@Override
public void compute() {
polyhedron.setDefined();
Coords o = A.getInhomCoordsInD3();
// check if A!=B
Coords cB = B.getInhomCoordsInD3();
v1l.setSub(cB, o);
if (v1l.equalsForKernel(0, Kernel.STANDARD_PRECISION)) {
setUndefined();
return;
}
// check if B!=C
Coords cC = C.getInhomCoordsInD3();
v2l.setSub(cC, cB); // use v2l as temp memory
if (v2l.equalsForKernel(0, Kernel.STANDARD_PRECISION)) {
setUndefined();
return;
}
// check if A, B, C are aligned
vnl.setCrossProduct(v1l, v2l);
if (vnl.equalsForKernel(0, Kernel.STANDARD_PRECISION)) {
setUndefined();
return;
}
// set lengths
v1l.calcNorm();
double l = v1l.getNorm();
vnl.normalize();
v2l.setCrossProduct(vnl, v1l);
vnl.mulInside3(l);
// set matrix
matrix.setOrigin(o);
matrix.setVx(v1l);
matrix.setVy(v2l);
matrix.setVz(vnl);
// check C is third point
if (!cC.equalsForKernel(tmpCoords.setMul(matrix, coords[2]),
Kernel.STANDARD_PRECISION)) {
setUndefined();
return;
}
// set points
for (int i = 0; i < coords.length - 3; i++) {
outputPoints.getElement(i)
.setCoords(tmpCoords.setMul(matrix, coords[i + 3]), true);
}
// update volume
polyhedron.setVolume(l * l * l * volumeFactor);
// update area
polyhedron.setArea(l * l * areaFactor);
// Log.debug("Aire "+polyhedron.getArea());
// update height
polyhedron.setOrientedHeight(l * heightFactor);
}
/**
* factor to calculate the volume
*/
private double volumeFactor;
/**
* factor to calculate the height
*/
private double heightFactor;
/**
* factor to calculate the area
*/
private double areaFactor;
private void setVolumeAreaAndHeightFactors() {
switch (name) {
default:
case Tetrahedron:
volumeFactor = Math.sqrt(2) / 12;
heightFactor = Math.sqrt(2. / 3.);
areaFactor = Math.sqrt(3);
break;
case Cube:
volumeFactor = 1;
heightFactor = 1;
areaFactor = 6;
break;
case Octahedron:
volumeFactor = Math.sqrt(2) / 3;
heightFactor = Math.sqrt(2. / 3.);
areaFactor = 2 * Math.sqrt(3);
break;
case Dodecahedron:
volumeFactor = (15 + 7 * Math.sqrt(5)) / 4;
heightFactor = Math.sqrt(2.5 + 1.1 * Math.sqrt(5));
areaFactor = 3 * Math.sqrt(25 + 10 * Math.sqrt(5));
break;
case Icosahedron:
volumeFactor = (15 + 5 * Math.sqrt(5)) / 12;
heightFactor = (3 + Math.sqrt(5)) / (2 * Math.sqrt(3));
areaFactor = 5 * Math.sqrt(3);
break;
}
}
private void setUndefined() {
polyhedron.setUndefined();
for (int i = 0; i < outputPoints.size(); i++) {
outputPoints.getElement(i).setUndefined();
}
}
// ///////////////////////////////////////////
// END OF THE CONSTRUCTION
// //////////////////////////////////////////
@Override
protected void updateDependentGeos() {
super.updateDependentGeos();
outputPoints.update();
// force update of segments and polygons when e.g. in a list
if (!getPolyhedron().allLabelsAreSet()) {
outputSegments.updateParentAlgorithm();
outputPolygons.updateParentAlgorithm();
}
}
@Override
public Commands getClassName() {
return name;
}
@Override
final protected boolean isFirstInputPointVisible() {
return true;
}
@Override
final protected boolean isFirstInputPointLabelVisible() {
return true;
}
}