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.GeoDirectionND; import org.geogebra.common.kernel.kernelND.GeoPointND; /** * @author ggb3D * * Creates a new GeoPolyhedron * */ public class AlgoArchimedeanSolid extends AlgoPolyhedron { protected OutputHandler<GeoPolygon3D> outputPolygons; protected OutputHandler<GeoSegment3D> outputSegments; private GeoPointND A, B; private GeoDirectionND v; 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 AlgoArchimedeanSolid(Construction c, String[] labels, GeoPointND A, GeoPointND B, GeoDirectionND v, 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.v = v; 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 GeoPointND getA() { return A; } protected GeoPointND getB() { return B; } protected Coords getDirection() { return v.getDirectionInD3(); } protected void setInput() { input = new GeoElement[3]; input[0] = (GeoElement) A; input[1] = (GeoElement) B; input[2] = (GeoElement) v; } @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 - 2, false); if (getPolyhedron().allLabelsAreSet()) { outputPoints.setLabels(null); } // coords coords = solidDescription.getVertices(); // points GeoPointND[] points = new GeoPointND[vertexCount]; points[0] = getA(); points[1] = getB(); for (int i = 2; i < vertexCount; i++) { GeoPoint3D point = outputPoints.getElement(i - 2); 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(); } } @Override public void compute() { polyhedron.setDefined(); Coords o = getA().getInhomCoordsInD3(); Coords v1l = getB().getInhomCoordsInD3().sub(o); // check if A!=B if (v1l.equalsForKernel(0, Kernel.STANDARD_PRECISION)) { setUndefined(); return; } v1l.calcNorm(); double l = v1l.getNorm(); Coords v1 = v1l.mul(1 / l); // check if vn!=0 Coords vn = getDirection(); if (vn.equalsForKernel(0, Kernel.STANDARD_PRECISION)) { setUndefined(); return; } // check if vn is ortho to AB if (!Kernel.isZero(vn.dotproduct(v1))) { setUndefined(); return; } Coords v2 = getDirection().crossProduct(v1); v2.normalize(); Coords v3 = v1.crossProduct(v2); matrix.setOrigin(o); matrix.setVx(v1l); matrix.setVy(v2.mul(l)); matrix.setVz(v3.mul(l)); for (int i = 0; i < coords.length - 2; i++) { outputPoints.getElement(i).setCoords(matrix.mul(coords[i + 2]), 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; } }