package org.geogebra.common.geogebra3D.euclidian3D.draw; import java.util.ArrayList; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.euclidian.Previewable; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.euclidian3D.Hitting; import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterBrush; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer.PickingType; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoPolyhedronPoints; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoPolyhedronPointsPrism; import org.geogebra.common.geogebra3D.kernel3D.algos.AlgoPolyhedronPointsPyramid; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPolyhedron; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoSegmentND; import org.geogebra.common.main.Feature; /** * Class for drawing 3D polygons. * * @author matthieu * */ public class DrawPolyhedron3D extends Drawable3DSurfaces implements Previewable { /** * Common constructor * * @param a_view3D * @param poly */ public DrawPolyhedron3D(EuclidianView3D a_view3D, GeoPolyhedron poly) { super(a_view3D, poly); } private int previewMode; private DrawPolygon3D drawPolygon3D; private ArrayList<GeoPointND> selectedPoints; private ArrayList<GeoPolygon> selectedPolygons; public DrawPolyhedron3D(EuclidianView3D a_view3D, ArrayList<GeoPointND> selectedPoints, ArrayList<GeoPolygon> selectedPolygons, int mode) { super(a_view3D); drawPolygon3D = new DrawPolygon3D(a_view3D, selectedPoints); this.selectedPoints = selectedPoints; this.selectedPolygons = selectedPolygons; previewMode = mode; // updatePreview(); } // drawing @Override public void drawGeometry(Renderer renderer) { renderer.setLayer(getLayer()); // +0f for z-fighting with planes renderer.getGeometryManager().draw(getGeometryIndex()); renderer.setLayer(0); } @Override public void drawOutline(Renderer renderer) { if (isVisible()) { setHighlightingColor(); renderer.getTextures() .setDashFromLineType(getGeoElement().getLineType()); drawGeometry(renderer); } drawTracesOutline(renderer, false); } @Override public void drawGeometryHiding(Renderer renderer) { drawSurfaceGeometry(renderer); } @Override public void drawGeometryHidden(Renderer renderer) { drawGeometry(renderer); } @Override protected void drawGeometryForPicking(Renderer renderer, PickingType type) { if (type == PickingType.POINT_OR_CURVE) { drawGeometry(renderer); } else { if (getAlpha() > 0) { // surface is pickable only if not totally // transparent drawSurfaceGeometry(renderer); } } } @Override protected void drawSurfaceGeometry(Renderer renderer) { renderer.setLayer(getLayer()); // +0f to avoid z-fighting with planes renderer.getGeometryManager().draw(getSurfaceIndex()); renderer.setLayer(0); } @Override public int getPickOrder() { return DRAW_PICK_ORDER_SURFACE; } @Override public void addToDrawable3DLists(Drawable3DLists lists) { addToDrawable3DLists(lists, DRAW_TYPE_CLOSED_SURFACES_NOT_CURVED); addToDrawable3DLists(lists, DRAW_TYPE_CURVES); } @Override public void removeFromDrawable3DLists(Drawable3DLists lists) { removeFromDrawable3DLists(lists, DRAW_TYPE_CLOSED_SURFACES_NOT_CURVED); removeFromDrawable3DLists(lists, DRAW_TYPE_CURVES); } private Coords[] vertices = new Coords[0]; @Override protected boolean updateForItSelf() { Renderer renderer = getView3D().getRenderer(); // outline updateOutline(renderer); // surface updateSurface(renderer); return true; } private void updateSurface(Renderer renderer) { int index = renderer.startPolygons(getReusableSurfaceIndex()); for (GeoPolygon p : ((GeoPolyhedron) getGeoElement()) .getPolygonsLinked()) { drawPolygon(renderer, p); } for (GeoPolygon p : ((GeoPolyhedron) getGeoElement()).getPolygons()) { drawPolygon(renderer, p); } renderer.endPolygons(); setSurfaceIndex(index); } private void updateOutline(Renderer renderer) { GeoPolyhedron poly = (GeoPolyhedron) getGeoElement(); PlotterBrush brush = renderer.getGeometryManager().getBrush(); brush.start(getReusableGeometryIndex()); brush.setThickness(poly.getLineThickness(), (float) getView3D().getScale()); for (GeoPolygon p : ((GeoPolyhedron) getGeoElement()) .getPolygonsLinked()) { // draw segments for polygons that have no label if (p.isEuclidianVisible() && !p.isLabelSet()) { for (GeoSegmentND seg : p.getSegments()) { drawSegment(brush, seg); } } } for (GeoSegmentND seg : poly.getSegments()) { drawSegment(brush, seg); } setGeometryIndex(brush.end()); } private static void drawSegment(PlotterBrush brush, GeoSegmentND seg) { // draw only segments that have no label if (!seg.isEuclidianVisible() || seg.isLabelSet()) { return; } brush.setAffineTexture(0.5f, 0.25f); brush.segment(seg.getStartInhomCoords(), seg.getEndInhomCoords()); } private void drawPolygon(Renderer renderer, GeoPolygon polygon) { // draw only polygons that have no label if (!polygon.isEuclidianVisible() || polygon.isLabelSet()) { return; } int pointLength = polygon.getPointsLength(); if (pointLength < 3) { // no polygon return; } if (vertices.length < pointLength) { vertices = new Coords[pointLength]; for (int i = 0; i < pointLength; i++) { vertices[i] = new Coords(3); } } for (int i = 0; i < pointLength; i++) { vertices[i].setValues(polygon.getPoint3D(i), 3); } DrawPolygon3D.drawPolygon(renderer, polygon, vertices, polygon.getPointsLength()); } @Override protected void updateForView() { if (getView3D().viewChangedByZoom()) { Renderer renderer = getView3D().getRenderer(); // outline updateOutline(renderer); if (getView3D().getApplication() .has(Feature.DIFFERENT_AXIS_RATIO_3D)) { // surface updateSurface(renderer); } recordTrace(); } } @Override public void updatePreview() { if (previewBasisIsFinished) { getView3D().getCursor3D().updateCascade(); return; } if (selectedPolygons.size() == 1) { previewBasisIsFinished = true; Construction cons = getView3D().getKernel().getConstruction(); switch (previewMode) { default: case EuclidianConstants.MODE_PYRAMID: previewAlgo = new AlgoPolyhedronPointsPyramid(cons, null, selectedPolygons.get(0), getView3D().getCursor3D()); break; case EuclidianConstants.MODE_PRISM: previewAlgo = new AlgoPolyhedronPointsPrism(cons, null, selectedPolygons.get(0), getView3D().getCursor3D()); break; } // set visibilities previewAlgo.removeOutputFromAlgebraView(); previewAlgo.removeOutputFromPicking(); previewAlgo.setOutputPointsEuclidianVisible(false); previewAlgo.notifyUpdateOutputPoints(); // ensure correct drawing of visible parts of the previewable previewAlgo.setOutputOtherEuclidianVisible(true); previewAlgo.notifyUpdateOutputOther(); } else { drawPolygon3D.updatePreview(); } } private AlgoPolyhedronPoints previewAlgo; @Override public void disposePreview() { super.disposePreview(); if (previewBasisIsFinished) { previewBasisIsFinished = false; if (previewAlgo != null) { previewAlgo.remove(); previewAlgo = null; } } else { drawPolygon3D.disposePreview(); } } // public void hidePreview(){ // if (previewBasisIsFinished){ // previewBasisIsFinished = false; // // if (previewAlgo != null){ // previewAlgo.remove(); // previewAlgo = null; // } // }else{ // drawPolygon3D.hidePreview(); // } // } @Override public void updateMousePos(double x, double y) { // TODO Auto-generated method stub } private boolean previewBasisIsFinished = false; /** * tells that the preview basis is done */ public void previewBasisIsFinished() { previewBasisIsFinished = true; // dispose polygon preview drawPolygon3D.disposePreview(); // create polyhedron GeoPointND[] points = new GeoPointND[selectedPoints.size() + 1]; for (int i = 0; i < selectedPoints.size(); i++) { points[i] = selectedPoints.get(i); } points[selectedPoints.size()] = getView3D().getCursor3D(); Construction cons = getView3D().getKernel().getConstruction(); switch (previewMode) { default: case EuclidianConstants.MODE_PYRAMID: previewAlgo = new AlgoPolyhedronPointsPyramid(cons, null, points); break; case EuclidianConstants.MODE_PRISM: previewAlgo = new AlgoPolyhedronPointsPrism(cons, null, points); break; } // set visibilities previewAlgo.removeOutputFromAlgebraView(); previewAlgo.removeOutputFromPicking(); previewAlgo.setOutputPointsEuclidianVisible(false); previewAlgo.notifyUpdateOutputPoints(); // ensure correct drawing of visible parts of the previewable previewAlgo.setOutputOtherEuclidianVisible(true); previewAlgo.notifyUpdateOutputOther(); } private Coords globalCoords, inPlaneCoords; @Override public boolean hit(Hitting hitting) { if (waitForReset) { // prevent NPE return false; } // project hitting origin on polygon plane if (globalCoords == null) { globalCoords = new Coords(4); inPlaneCoords = new Coords(4); } double d = Double.NaN; for (GeoPolygon p : ((GeoPolyhedron) getGeoElement()) .getPolygonsLinked()) { d = hitPolygon(d, hitting, p, globalCoords, inPlaneCoords); } for (GeoPolygon p : ((GeoPolyhedron) getGeoElement()).getPolygons()) { d = hitPolygon(d, hitting, p, globalCoords, inPlaneCoords); } if (!Double.isNaN(d)) { setZPick(d, d); setPickingType(PickingType.SURFACE); return true; } return false; } static private double hitPolygon(double currentDistance, Hitting hitting, GeoPolygon polygon, Coords globalCoords, Coords inPlaneCoords) { if (!polygon.isEuclidianVisible() || polygon.isLabelSet()) { return currentDistance; } hitting.origin.projectPlaneThruVIfPossible( polygon.getCoordSys().getMatrixOrthonormal(), hitting.direction, globalCoords, inPlaneCoords); if (!hitting.isInsideClipping(globalCoords)) { return currentDistance; } // check if hitting projection hits the polygon if (polygon.isInRegion(inPlaneCoords.getX(), inPlaneCoords.getY())) { double parameterOnHitting = inPlaneCoords.getZ();// TODO use // other for // non-parallel // projection // : // -hitting.origin.distance(project[0]); if (parameterOnHitting < currentDistance) { // currentDistance may // be NaN return currentDistance; } return parameterOnHitting; } return currentDistance; } }