package org.geogebra.common.geogebra3D.euclidian3D.draw; import java.util.ArrayList; import org.geogebra.common.euclidian.EuclidianController; 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.PlotterSurface; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer.PickingType; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPlane3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3DPart; import org.geogebra.common.kernel.PathNormalizer; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.GProperty; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoQuadricNDConstants; import org.geogebra.common.main.Feature; /** * Class for drawing quadrics. * * @author mathieu * */ public class DrawQuadric3D extends Drawable3DSurfaces implements Previewable { /** * common constructor * * @param a_view3d * view * @param a_quadric * quadric */ public DrawQuadric3D(EuclidianView3D a_view3d, GeoQuadric3D a_quadric) { super(a_view3d, a_quadric); } @Override public void drawGeometry(Renderer renderer) { switch (((GeoQuadric3D) getGeoElement()).getType()) { case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: drawPlanes[0].drawGeometry(renderer); drawPlanes[1].drawGeometry(renderer); break; case GeoQuadricNDConstants.QUADRIC_PLANE: drawPlanes[0].drawGeometry(renderer); break; case GeoQuadricNDConstants.QUADRIC_LINE: // not used: see drawOutline() and drawGeometryHidden() break; default: renderer.setLayer(getLayer()); renderer.getGeometryManager().draw(getSurfaceIndex()); renderer.setLayer(0); break; } } @Override protected void drawSurfaceGeometry(Renderer renderer) { drawGeometry(renderer); } @Override void drawGeometryHiding(Renderer renderer) { drawSurfaceGeometry(renderer); } @Override public void drawGeometryHidden(Renderer renderer) { switch (((GeoQuadric3D) getGeoElement()).getType()) { default: // do nothing break; case GeoQuadricNDConstants.QUADRIC_LINE: drawLine.drawGeometryHidden(renderer); break; } } @Override public void drawOutline(Renderer renderer) { if (isVisible()) { switch (((GeoQuadric3D) getGeoElement()).getType()) { default: // do nothing break; case GeoQuadricNDConstants.QUADRIC_LINE: drawLine.drawOutline(renderer); break; } } drawTracesOutline(renderer, false); } @Override public void drawHidden(Renderer renderer) { super.drawHidden(renderer); drawTracesOutline(renderer, true); } @Override protected void drawTracesOutline(Renderer renderer, boolean hidden) { if ((((GeoQuadric3D) getGeoElement()) .getType() != GeoQuadricNDConstants.QUADRIC_LINE || hidden) && drawLine != null) { drawLine.drawTracesOutline(renderer, hidden); } } @Override protected void drawTracesNotTranspSurface(Renderer renderer) { if (drawPlanes != null) { drawPlanes[0].drawTracesNotTranspSurface(renderer); if (drawPlanes[1] != null) { drawPlanes[1].drawTracesNotTranspSurface(renderer); } } super.drawTracesNotTranspSurface(renderer); } @Override protected void drawTracesHidingSurface(Renderer renderer) { if (drawPlanes != null) { drawPlanes[0].drawTracesHidingSurface(renderer); if (drawPlanes[1] != null) { drawPlanes[1].drawTracesHidingSurface(renderer); } } super.drawTracesHidingSurface(renderer); } @Override protected void drawTracesTranspSurface(Renderer renderer) { if (drawPlanes != null) { drawPlanes[0].drawTracesTranspSurface(renderer); if (drawPlanes[1] != null) { drawPlanes[1].drawTracesTranspSurface(renderer); } } super.drawTracesTranspSurface(renderer); } @Override protected void drawGeometryForPicking(Renderer renderer, PickingType type) { if (((GeoQuadric3D) getGeoElement()) .getType() == GeoQuadricNDConstants.QUADRIC_LINE) { drawOutline(renderer); } else { drawGeometry(renderer); } } @Override protected void updateColors() { super.updateColors(); GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); switch (quadric.getType()) { default: // do nothing break; case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: initDrawPlanes(quadric); drawPlanes[0].updateColors(); drawPlanes[1].updateColors(); break; case GeoQuadricNDConstants.QUADRIC_PLANE: initDrawPlanes(quadric); drawPlanes[0].updateColors(); break; case GeoQuadricNDConstants.QUADRIC_LINE: initDrawLine(quadric); drawLine.updateColors(); break; } } /** * Last longitude used for painting; helps avoiding updates */ protected int longitude = 0; private double scale; private double alpha, beta; /** * Visibility flag * * @author mathieu * */ private static enum Visible { /** the quadric is totally outside */ TOTALLY_OUTSIDE, /** the quadric is totally inside */ TOTALLY_INSIDE, /** the quadric is partly inside, center outside */ CENTER_OUTSIDE, /** the quadric is partly inside, center inside */ CENTER_INSIDE } private Visible visible = Visible.TOTALLY_OUTSIDE; /** * check if the sphere is (at least partially) visible * * @param center * sphere center * @param radius * sphere radius */ private void checkSphereVisible(Coords center, double radius) { double frustumRadius = getView3D().getFrustumRadius(); Coords origin = getView3D().getCenter(); Coords v = origin.sub(center); v.calcNorm(); double centersDistance = v.getNorm(); if (centersDistance > radius + frustumRadius) { // sphere totally // outside the frustum visible = Visible.TOTALLY_OUTSIDE; } else if (centersDistance + frustumRadius < radius) { // frustum // totally // inside the // sphere visible = Visible.TOTALLY_OUTSIDE; } else if (centersDistance + radius < frustumRadius) { // totally inside visible = Visible.TOTALLY_INSIDE; } else if (centersDistance < frustumRadius) { // center inside visible = Visible.CENTER_INSIDE; } else { // calc angles to draw minimum longitudes double horizontalDistance = Math .sqrt(v.getX() * v.getX() + v.getY() * v.getY()); if (horizontalDistance > frustumRadius) { alpha = Math.asin(frustumRadius / horizontalDistance); beta = Math.atan2(v.getY(), v.getX()); // Log.debug("alpha = "+(alpha*180/Math.PI)+"degrees, beta = // "+(beta*180/Math.PI)+"degrees"); visible = Visible.CENTER_OUTSIDE; // center outside } else { visible = Visible.CENTER_INSIDE; // do as if center inside } } } private void drawSphere(PlotterSurface surface, Coords center, double radius) { if (visible == Visible.CENTER_OUTSIDE) { int longitudeAlpha = 8; while (longitudeAlpha * Math.PI < alpha * longitude) { longitudeAlpha *= 2; } // Log.debug(longitudeAlpha+""); surface.drawSphere(center, radius, longitude, beta - longitudeAlpha * Math.PI / longitude, longitudeAlpha); } else { surface.drawSphere(center, radius, longitude); } } private Coords boundsMin = new Coords(3), boundsMax = new Coords(3); @Override public void enlargeBounds(Coords min, Coords max) { switch (((GeoQuadric3D) getGeoElement()).getType()) { default: // do nothing break; case GeoQuadricNDConstants.QUADRIC_SPHERE: case GeoQuadricNDConstants.QUADRIC_SINGLE_POINT: enlargeBounds(min, max, boundsMin, boundsMax); break; case GeoQuadricNDConstants.QUADRIC_CONE: case GeoQuadricNDConstants.QUADRIC_CYLINDER: if (getGeoElement() instanceof GeoQuadric3DPart) { enlargeBounds(min, max, boundsMin, boundsMax); } break; } } private double[] uMinMax, vMinMax; @Override protected boolean updateForItSelf() { Renderer renderer = getView3D().getRenderer(); GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); PlotterSurface surface; int type = quadric.getType(); double min, max; switch (type) { case GeoQuadricNDConstants.QUADRIC_SPHERE: Coords center = quadric.getMidpoint3D(); double radius = quadric.getHalfAxis(0); boundsMin.setValues(center, 3); boundsMax.setValues(center, 3); boundsMin.addInside(-radius); boundsMax.addInside(radius); checkSphereVisible(center, radius); if (visible != Visible.TOTALLY_OUTSIDE) { surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); scale = getView3D().getMaxScale(); longitude = surface.calcSphereLongitudesNeeded(radius, scale); drawSphere(surface, center, radius); setSurfaceIndex(surface.end()); } else { setSurfaceIndex(-1); } break; case GeoQuadricNDConstants.QUADRIC_ELLIPSOID: center = quadric.getMidpoint3D(); double r0 = quadric.getHalfAxis(0); double r1 = quadric.getHalfAxis(1); double r2 = quadric.getHalfAxis(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); scale = getView3D().getMaxScale(); radius = Math.max(r0, Math.max(r1, r2)); longitude = surface.calcSphereLongitudesNeeded(radius, scale); Coords ev0 = quadric.getEigenvec3D(0); Coords ev1 = quadric.getEigenvec3D(1); Coords ev2 = quadric.getEigenvec3D(2); surface.drawEllipsoid(center, ev0, ev1, ev2, r0, r1, r2, longitude); setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_ONE_SHEET: center = quadric.getMidpoint3D(); r0 = quadric.getHalfAxis(0); r1 = quadric.getHalfAxis(1); r2 = quadric.getHalfAxis(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev2.mul(r2)); scale = getView3D().getMaxScale(); // get radius at max radius = Math.max(r0, r1) * Math.max(Math.abs(vMinMax[0]), Math.max(Math.abs(vMinMax[1]), 1)) / r2; longitude = surface.calcSphereLongitudesNeeded(radius, scale); min = DrawConic3D.asinh(vMinMax[0]); max = DrawConic3D.asinh(vMinMax[1]); surface.drawHyperboloidOneSheet(center, ev0, ev1, ev2, r0, r1, r2, longitude, min, max, !getView3D().useClippingCube()); setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_TWO_SHEETS: center = quadric.getMidpoint3D(); r0 = quadric.getHalfAxis(0); r1 = quadric.getHalfAxis(1); r2 = quadric.getHalfAxis(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev2.mul(r2)); scale = getView3D().getMaxScale(); // get radius at max radius = Math.max(r0, r1) * Math.max(Math.abs(vMinMax[0]), Math.abs(vMinMax[1])) / r2; longitude = surface.calcSphereLongitudesNeeded(radius, scale); if (vMinMax[0] < -1) { // bottom exists min = -DrawConic3D.acosh(-vMinMax[0]); } else if (vMinMax[0] <= 1) { // top ends at pole min = 0; } else { // top pole is cut min = DrawConic3D.acosh(vMinMax[0]); } if (vMinMax[1] > 1) { // top exists max = DrawConic3D.acosh(vMinMax[1]); } else if (vMinMax[1] >= -1) { // bottom ends at pole max = 0; } else { // bottom pole is cut max = -DrawConic3D.acosh(-vMinMax[1]); } surface.drawHyperboloidTwoSheets(center, ev0, ev1, ev2, r0, r1, r2, longitude, min, max, !getView3D().useClippingCube()); setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_PARABOLOID: center = quadric.getMidpoint3D(); r0 = quadric.getHalfAxis(0); r1 = quadric.getHalfAxis(1); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); if (quadric.getHalfAxis(2) < 0) { ev0 = ev0.mul(-1); ev2 = ev2.mul(-1); } if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev2); if (vMinMax[1] < 0) { // nothing to draw setSurfaceIndex(surface.end()); } else { scale = getView3D().getMaxScale(); // get radius at max if (vMinMax[0] <= 0) { vMinMax[0] = 0; } else { vMinMax[0] = Math.sqrt(vMinMax[0]); } vMinMax[1] = Math.sqrt(vMinMax[1]); radius = Math.max(r0, r1) * vMinMax[1]; longitude = surface.calcSphereLongitudesNeeded(radius, scale); surface.drawParaboloid(center, ev0, ev1, ev2, r0, r1, longitude, vMinMax[0], vMinMax[1], !getView3D().useClippingCube()); setSurfaceIndex(surface.end()); } break; case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_PARABOLOID: center = quadric.getMidpoint3D(); r0 = quadric.getHalfAxis(0); r1 = quadric.getHalfAxis(1); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); if (uMinMax == null) { uMinMax = new double[2]; } uMinMax[0] = Double.POSITIVE_INFINITY; uMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(uMinMax, center, ev0); if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev1); surface.drawHyperbolicParaboloid(center, ev0, ev1, ev2, r0, r1, uMinMax[0], uMinMax[1], vMinMax[0], vMinMax[1], !getView3D().useClippingCube()); setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_PARABOLIC_CYLINDER: center = quadric.getMidpoint3D(); r2 = quadric.getHalfAxis(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev0); if (quadric instanceof GeoQuadric3DPart) { // simple cylinder radius = quadric.getHalfAxis(0); if (vMinMax[1] < 0) { // nothing to draw } else { if (vMinMax[0] <= 0) { vMinMax[0] = 0; } else { vMinMax[0] = Math.sqrt(vMinMax[0]); } vMinMax[1] = Math.sqrt(vMinMax[1]); surface.drawParabolicCylinder(center, ev0, ev1, ev2, r2, vMinMax[0], vMinMax[1], quadric.getMinParameter(1), quadric.getMaxParameter(1), false); boundsMin.set(Double.POSITIVE_INFINITY); boundsMax.set(Double.NEGATIVE_INFINITY); } } else { if (uMinMax == null) { uMinMax = new double[2]; } uMinMax[0] = Double.POSITIVE_INFINITY; uMinMax[1] = Double.NEGATIVE_INFINITY; if (vMinMax[1] < 0) { // nothing to draw } else { scale = getView3D().getMaxScale(); // get radius at max if (vMinMax[0] <= 0) { vMinMax[0] = 0; } else { vMinMax[0] = Math.sqrt(vMinMax[0]); } vMinMax[1] = Math.sqrt(vMinMax[1]); getView3D().getMinIntervalOutsideClipping(uMinMax, center, ev1); surface.drawParabolicCylinder(center, ev0, ev1, ev2, r2, vMinMax[0], vMinMax[1], uMinMax[0], uMinMax[1], !getView3D().useClippingCube()); } } setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_CYLINDER: center = quadric.getMidpoint3D(); r0 = quadric.getHalfAxis(0); r1 = quadric.getHalfAxis(1); ev0 = quadric.getEigenvec3D(0); ev1 = quadric.getEigenvec3D(1); ev2 = quadric.getEigenvec3D(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); if (uMinMax == null) { uMinMax = new double[2]; } uMinMax[0] = Double.POSITIVE_INFINITY; uMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(uMinMax, center, ev0.mul(r0)); if (uMinMax[0] < -1) { // bottom exists min = -DrawConic3D.acosh(-uMinMax[0]); } else if (uMinMax[0] <= 1) { // top ends at pole min = 0; } else { // top pole is cut min = DrawConic3D.acosh(uMinMax[0]); } if (uMinMax[1] > 1) { // top exists max = DrawConic3D.acosh(uMinMax[1]); } else if (uMinMax[1] >= -1) { // bottom ends at pole max = 0; } else { // bottom pole is cut max = -DrawConic3D.acosh(-uMinMax[1]); } if (quadric instanceof GeoQuadric3DPart) { // simple cylinder radius = quadric.getHalfAxis(0); double radius2 = quadric.getHalfAxis(1); longitude = renderer.getGeometryManager().getLongitude(radius, getView3D().getMaxScale()); if (min < 0) { surface.drawHyperbolicCylinder(center, ev0.mul(-1), ev1, ev2, radius, radius2, -max, -min, quadric.getMinParameter(1), quadric.getMaxParameter(1), false); } if (max > 0) { surface.drawHyperbolicCylinder(center, ev0, ev1, ev2, radius, radius2, min, max, quadric.getMinParameter(1), quadric.getMaxParameter(1), false); } boundsMin.set(Double.POSITIVE_INFINITY); boundsMax.set(Double.NEGATIVE_INFINITY); enlargeBoundsToDiagonal(boundsMin, boundsMax, center, ev1, ev2, radius, radius); } else { scale = getView3D().getMaxScale(); if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getView3D().getMinIntervalOutsideClipping(vMinMax, center, ev2); if (min < 0) { surface.drawHyperbolicCylinder(center, ev0.mul(-1), ev1.mul(-1), ev2, r0, r1, -max, -min, vMinMax[0], vMinMax[1], !getView3D().useClippingCube()); } if (max > 0) { surface.drawHyperbolicCylinder(center, ev0, ev1, ev2, r0, r1, min, max, vMinMax[0], vMinMax[1], !getView3D().useClippingCube()); } uMinMax[0] = Math.sinh(min); uMinMax[1] = Math.sinh(max); } setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_CONE: surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); if (quadric instanceof GeoQuadric3DPart) { // simple cone double height = ((GeoQuadric3DPart) quadric) .getBottomParameter() - ((GeoQuadric3DPart) quadric).getTopParameter(); Coords top = quadric.getMidpoint3D(); ev1 = quadric.getEigenvec3D(0); ev2 = quadric.getEigenvec3D(1); radius = quadric.getHalfAxis(0); double radius2 = quadric.getHalfAxis(1); Coords bottomCenter = surface.cone(top, ev1, ev2, quadric.getEigenvec3D(2), radius, radius2, 0, 2 * Math.PI, height, 1f); boundsMin.setValues(top, 3); boundsMax.setValues(top, 3); radius *= height; enlargeBoundsToDiagonal(boundsMin, boundsMax, bottomCenter, ev1, ev2, radius, radius2); } else { // infinite cone if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getMinMax(vMinMax); min = vMinMax[0]; max = vMinMax[1]; // min -= delta; // max += delta; // Log.debug(min+","+max); center = quadric.getMidpoint3D(); ev1 = quadric.getEigenvec3D(0); ev2 = quadric.getEigenvec3D(1); Coords ev3 = quadric.getEigenvec3D(2); r1 = quadric.getHalfAxis(0); r2 = quadric.getHalfAxis(1); if (min * max < 0) { if (getView3D().useClippingCube()) { surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, 1f); surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, max, 1f); } else { surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, (float) ((-9 * min - max) / (min - max))); surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, max, (float) ((-9 * max - min) / (max - min))); } } else { if (getView3D().useClippingCube()) { surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, max, false, false); } else { double delta = (max - min) / 10; surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min + delta, max - delta, false, false); surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, min + delta, true, false); surface.cone(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, max - delta, max, false, true); } } } setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_CYLINDER: center = quadric.getMidpoint3D(); ev1 = quadric.getEigenvec3D(0); ev2 = quadric.getEigenvec3D(1); Coords ev3 = quadric.getEigenvec3D(2); surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); if (quadric instanceof GeoQuadric3DPart) { // simple cylinder radius = quadric.getHalfAxis(0); double radius2 = quadric.getHalfAxis(1); longitude = renderer.getGeometryManager().getLongitude(radius, getView3D().getMaxScale()); Coords bottomCenter = surface.cylinder(center, ev1, ev2, ev3, radius, radius2, 0, 2 * Math.PI, quadric.getMinParameter(1), quadric.getMaxParameter(1), false, false, longitude); boundsMin.set(Double.POSITIVE_INFINITY); boundsMax.set(Double.NEGATIVE_INFINITY); enlargeBoundsToDiagonal(boundsMin, boundsMax, center, ev1, ev2, radius, radius); enlargeBoundsToDiagonal(boundsMin, boundsMax, bottomCenter, ev1, ev2, radius, radius); } else { if (vMinMax == null) { vMinMax = new double[2]; } vMinMax[0] = Double.POSITIVE_INFINITY; vMinMax[1] = Double.NEGATIVE_INFINITY; getMinMax(vMinMax); min = vMinMax[0]; max = vMinMax[1]; r1 = quadric.getHalfAxis(0); r2 = quadric.getHalfAxis(1); radius = Math.max(r1, r2); longitude = renderer.getGeometryManager().getLongitude(radius, getView3D().getMaxScale()); if (getView3D().useClippingCube()) { surface.cylinder(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, max, false, false, longitude); } else { double delta = (max - min) / 10; surface.cylinder(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min + delta, max - delta, false, false, longitude); surface.cylinder(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, min, min + delta, true, false, longitude); surface.cylinder(center, ev1, ev2, ev3, r1, r2, 0, 2 * Math.PI, max - delta, max, false, true, longitude); } } setSurfaceIndex(surface.end()); break; case GeoQuadricNDConstants.QUADRIC_SINGLE_POINT: surface = renderer.getGeometryManager().getSurface(); surface.start(getReusableSurfaceIndex()); Coords m = quadric.getMidpoint3D(); double thickness = quadric.getLineThickness() / getView3D().getScale() * DrawPoint3D.DRAW_POINT_FACTOR; surface.drawSphere(quadric.getLineThickness(), m, thickness); setSurfaceIndex(surface.end()); boundsMin.setValues(m, 3); boundsMax.setValues(m, 3); boundsMin.addInside(-thickness); boundsMax.addInside(thickness); break; case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: initDrawPlanes(quadric); drawPlanes[0].updateForItSelf(); drawPlanes[1].updateForItSelf(); break; case GeoQuadricNDConstants.QUADRIC_PLANE: initDrawPlanes(quadric); drawPlanes[0].updateForItSelf(); break; case GeoQuadricNDConstants.QUADRIC_LINE: initDrawLine(quadric); drawLine.updateForItSelf(); break; default: setSurfaceIndex(-1); } return true; } private DrawPlane3D[] drawPlanes; private void initDrawPlanes(GeoQuadric3D quadric) { if (drawPlanes == null) { drawPlanes = new DrawPlane3DForQuadrics[2]; GeoPlane3D[] planes = quadric.getPlanes(); drawPlanes[0] = new DrawPlane3DForQuadrics(getView3D(), planes[0], quadric); drawPlanes[1] = new DrawPlane3DForQuadrics(getView3D(), planes[1], quadric); } } private DrawLine3D drawLine; private void initDrawLine(GeoQuadric3D quadric) { if (drawLine == null) { drawLine = new DrawLine3DForQuadrics(getView3D(), quadric.getLine(), quadric); } } /** * * @return min and max value along the axis of the quadric */ protected double[] getMinMax() { GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); double[] minmax = { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY }; getView3D().getMinIntervalOutsideClipping(minmax, quadric.getMidpoint3D(), quadric.getEigenvec3D(2)); // Log.debug(minmax[0]+","+minmax[1]); return minmax; } /** * set min and max value along the axis of the quadric * * @param minmax * min/max values */ protected void getMinMax(double[] minmax) { GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); getView3D().getMinIntervalOutsideClipping(minmax, quadric.getMidpoint3D(), quadric.getEigenvec3D(2)); } private boolean updateNeededForNewScale(double s) { if (getView3D().getApplication().has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return true; } return scale < s; } @Override protected void updateForView() { GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); int type = quadric.getType(); switch (type) { case GeoQuadricNDConstants.QUADRIC_SPHERE: if (getView3D().viewChangedByZoom()) { Renderer renderer = getView3D().getRenderer(); PlotterSurface surface = renderer.getGeometryManager() .getSurface(); double s = scale; scale = getView3D().getMaxScale(); // check if longitude length changes double radius = quadric.getHalfAxis(0); int l = surface.calcSphereLongitudesNeeded(radius, scale); // redraw if sphere was not visible, or if new longitude length, // or if negative zoom occured if (visible == Visible.TOTALLY_OUTSIDE || l != longitude || updateNeededForNewScale(s)) { Coords center = quadric.getMidpoint3D(); checkSphereVisible(center, radius); if (visible != Visible.TOTALLY_OUTSIDE) { // Log.debug(l+","+longitude); longitude = l; surface.start(getReusableSurfaceIndex()); drawSphere(surface, center, radius); setSurfaceIndex(surface.end()); recordTrace(); } else { setSurfaceIndex(-1); } } } else if (visible != Visible.TOTALLY_INSIDE && getView3D().viewChangedByTranslate()) { Renderer renderer = getView3D().getRenderer(); PlotterSurface surface = renderer.getGeometryManager() .getSurface(); Coords center = quadric.getMidpoint3D(); double radius = quadric.getHalfAxis(0); checkSphereVisible(center, radius); if (visible != Visible.TOTALLY_OUTSIDE) { surface.start(getReusableSurfaceIndex()); drawSphere(surface, center, radius); setSurfaceIndex(surface.end()); recordTrace(); } else { setSurfaceIndex(-1); } } break; case GeoQuadricNDConstants.QUADRIC_ELLIPSOID: case GeoQuadricNDConstants.QUADRIC_CONE: case GeoQuadricNDConstants.QUADRIC_CYLINDER: case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_ONE_SHEET: case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_TWO_SHEETS: case GeoQuadricNDConstants.QUADRIC_PARABOLOID: case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_PARABOLOID: case GeoQuadricNDConstants.QUADRIC_PARABOLIC_CYLINDER: case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_CYLINDER: case GeoQuadricNDConstants.QUADRIC_SINGLE_POINT: if (getView3D().viewChangedByZoom() || getView3D().viewChangedByTranslate()) { updateForItSelf(); } break; case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: if (getView3D().viewChanged()) { initDrawPlanes(quadric); drawPlanes[0].updateForView(); drawPlanes[1].updateForView(); super.setWaitForUpdate(); } break; case GeoQuadricNDConstants.QUADRIC_PLANE: if (getView3D().viewChanged()) { initDrawPlanes(quadric); drawPlanes[0].updateForView(); super.setWaitForUpdate(); } break; case GeoQuadricNDConstants.QUADRIC_LINE: if (getView3D().viewChanged()) { initDrawLine(quadric); drawLine.updateForView(); super.setWaitForUpdate(); } break; default: // do nothing break; } } @Override public void setWaitForUpdateVisualStyle(GProperty prop) { GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); switch (quadric.getType()) { case GeoQuadricNDConstants.QUADRIC_SINGLE_POINT: super.setWaitForUpdate(); break; case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: initDrawPlanes(quadric); drawPlanes[0].setWaitForUpdateVisualStyle(prop); drawPlanes[1].setWaitForUpdateVisualStyle(prop); super.setWaitForUpdate(); break; case GeoQuadricNDConstants.QUADRIC_PLANE: initDrawPlanes(quadric); drawPlanes[0].setWaitForUpdateVisualStyle(prop); super.setWaitForUpdate(); break; case GeoQuadricNDConstants.QUADRIC_LINE: initDrawLine(quadric); drawLine.setWaitForUpdateVisualStyle(prop); super.setWaitForUpdate(); break; default: // do nothing break; } super.setWaitForUpdateVisualStyle(prop); } @Override protected void recordTrace() { GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); switch (quadric.getType()) { case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: initDrawPlanes(quadric); drawPlanes[0].recordTrace(); drawPlanes[1].recordTrace(); break; case GeoQuadricNDConstants.QUADRIC_PLANE: initDrawPlanes(quadric); drawPlanes[0].recordTrace(); break; case GeoQuadricNDConstants.QUADRIC_LINE: initDrawLine(quadric); drawLine.recordTrace(); break; default: super.recordTrace(); break; } } @Override protected void clearTraceForViewChangedByZoomOrTranslate() { if (drawPlanes != null) { drawPlanes[0].clearTraceForViewChanged(); if (drawPlanes[1] != null) { drawPlanes[1].clearTraceForViewChanged(); } } if (drawLine != null) { drawLine.clearTraceForViewChanged(); } super.clearTraceForViewChangedByZoomOrTranslate(); } @Override public void setWaitForUpdate() { super.setWaitForUpdate(); GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); int type = quadric.getType(); switch (type) { default: // do nothing break; case GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES: case GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES: initDrawPlanes(quadric); drawPlanes[0].setWaitForUpdate(); drawPlanes[1].setWaitForUpdate(); break; case GeoQuadricNDConstants.QUADRIC_PLANE: initDrawPlanes(quadric); drawPlanes[0].setWaitForUpdate(); break; case GeoQuadricNDConstants.QUADRIC_LINE: initDrawLine(quadric); drawLine.setWaitForUpdate(); break; } } @Override public int getPickOrder() { if (getPickingType() == PickingType.POINT_OR_CURVE) { return DRAW_PICK_ORDER_POINT; } return DRAW_PICK_ORDER_SURFACE; } @Override public boolean isTransparent() { if (getPickingType() == PickingType.SURFACE) { return getAlpha() <= EuclidianController.MAX_TRANSPARENT_ALPHA_VALUE_INT; } return false; } private int surfaceDrawTypeAdded; @Override public void addToDrawable3DLists(Drawable3DLists lists) { switch (((GeoQuadric3D) getGeoElement()).getType()) { case GeoQuadricNDConstants.QUADRIC_SPHERE: case GeoQuadricNDConstants.QUADRIC_ELLIPSOID: case GeoQuadricNDConstants.QUADRIC_CONE: case GeoQuadricNDConstants.QUADRIC_CYLINDER: case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_ONE_SHEET: addToDrawable3DLists(lists, DRAW_TYPE_CLOSED_SURFACES_CURVED); surfaceDrawTypeAdded = DRAW_TYPE_CLOSED_SURFACES_CURVED; break; default: addToDrawable3DLists(lists, DRAW_TYPE_SURFACES); surfaceDrawTypeAdded = DRAW_TYPE_SURFACES; break; } addToDrawable3DLists(lists, DRAW_TYPE_CURVES); } @Override public void removeFromDrawable3DLists(Drawable3DLists lists) { removeFromDrawable3DLists(lists, surfaceDrawTypeAdded); removeFromDrawable3DLists(lists, DRAW_TYPE_CURVES); } // ////////////////////////////// // Previewable interface private ArrayList<GeoPointND> selectedPoints; /** * constructor for previewable * * @param view3D * view * @param selectedPoints * points defining preview * @param type * quadric type */ public DrawQuadric3D(EuclidianView3D view3D, ArrayList<GeoPointND> selectedPoints, int type) { super(view3D); GeoQuadric3D q = new GeoQuadric3D(view3D.getKernel().getConstruction()); setGeoElement(q); q.setIsPickable(false); q.setType(type); // setGeoElement(q); setPickingType(PickingType.SURFACE); this.selectedPoints = selectedPoints; updatePreview(); } @Override public void updateMousePos(double xRW, double yRW) { // not needed } @Override public void updatePreview() { GeoPointND firstPoint = null; GeoPointND secondPoint = null; if (selectedPoints.size() >= 1) { firstPoint = selectedPoints.get(0); if (selectedPoints.size() == 2) { secondPoint = selectedPoints.get(1); } else { secondPoint = getView3D().getCursor3D(); } } if (selectedPoints.size() >= 1) { ((GeoQuadric3D) getGeoElement()).setSphereND(firstPoint, secondPoint); getGeoElement().setEuclidianVisible(true); setWaitForUpdate(); } else { getGeoElement().setEuclidianVisible(false); } } @Override public boolean hit(Hitting hitting) { if (waitForReset) { // prevent NPE return false; } GeoQuadric3D quadric = (GeoQuadric3D) getGeoElement(); quadric.resetLastHitParameters(); if (quadric.getType() == GeoQuadricNDConstants.QUADRIC_NOT_CLASSIFIED) { return false; } if (quadric.getType() == GeoQuadricNDConstants.QUADRIC_SINGLE_POINT) { if (DrawPoint3D.hit(hitting, quadric.getMidpoint3D(), this, quadric.getLineThickness(), project, parameters, false)) { setPickingType(PickingType.POINT_OR_CURVE); return true; } return false; } if (quadric.getType() == GeoQuadricNDConstants.QUADRIC_LINE) { if (drawLine.hit(hitting)) { setZPick(drawLine.getZPickNear(), drawLine.getZPickFar()); setPickingType(PickingType.POINT_OR_CURVE); return true; } return false; } if (getGeoElement() .getAlphaValue() < EuclidianController.MIN_VISIBLE_ALPHA_VALUE) { return false; } if (quadric.getType() == GeoQuadricNDConstants.QUADRIC_PARALLEL_PLANES || quadric .getType() == GeoQuadricNDConstants.QUADRIC_INTERSECTING_PLANES) { double z1 = Double.NEGATIVE_INFINITY, z2 = Double.NEGATIVE_INFINITY; if (drawPlanes[0].hit(hitting, p1, project)) { z1 = drawPlanes[0].getZPickNear(); } if (drawPlanes[1].hit(hitting, p2, project)) { z2 = drawPlanes[1].getZPickNear(); } int planeIndex = 0; // keep highest value (closest to eye) if (z1 < z2) { z1 = z2; planeIndex = 1; } // if both negative infinity : not hitted if (Double.isInfinite(z1)) { quadric.resetLastHitParameters(); return false; } // project with ortho matrix to get correct parameters hitting.origin.projectPlaneThruVIfPossible( quadric.getPlanes()[planeIndex].getCoordSys() .getMatrixOrthonormal(), hitting.direction, p1, project); parameters1[0] = PathNormalizer.inverseInfFunction(project.getX()) + 2 * planeIndex; parameters1[1] = project.getY(); quadric.setLastHitParameters(parameters1); // hitted setZPick(z1, z1); setPickingType(PickingType.SURFACE); return true; } if (quadric.getType() == GeoQuadricNDConstants.QUADRIC_PLANE) { if (drawPlanes[0].hit(hitting)) { setZPick(drawPlanes[0].getZPickNear(), drawPlanes[0].getZPickFar()); setPickingType(PickingType.SURFACE); return true; } return false; } quadric.getProjections(null, hitting.origin, hitting.direction, p1, parameters1, p2, parameters2); double z1 = Double.NEGATIVE_INFINITY, z2 = Double.NEGATIVE_INFINITY; // check first point if (hitting.isInsideClipping(p1) && arePossibleParameters(parameters1[0], parameters1[1])) { // check distance to hitting line p1.projectLine(hitting.origin, hitting.direction, project, parameters); double d = getView3D().getScaledDistance(p1, project); if (d <= hitting.getThreshold()) { z1 = -parameters[0]; } } // check second point (if defined) if (p2.isDefined() && hitting.isInsideClipping(p2) && arePossibleParameters(parameters2[0], parameters2[1])) { // check distance to hitting line p2.projectLine(hitting.origin, hitting.direction, project, parameters); double d = getView3D().getScaledDistance(p2, project); if (d <= hitting.getThreshold()) { z2 = -parameters[0]; } } // keep highest value (closest to eye) if (z1 < z2) { z1 = z2; quadric.setLastHitParameters(parameters2); } else { quadric.setLastHitParameters(parameters1); } // if both negative infinity : not hitted if (Double.isInfinite(z1)) { quadric.resetLastHitParameters(); return false; } // hitted setZPick(z1, z1); setPickingType(PickingType.SURFACE); return true; } private boolean arePossibleParameters(double u, double v) { if (getGeoElement() instanceof GeoQuadric3DPart) { return true; // no limitation in parameters } switch (((GeoQuadric3D) getGeoElement()).getType()) { case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_PARABOLOID: return isPossibleU(u) && isPossibleV(v); case GeoQuadricNDConstants.QUADRIC_PARABOLIC_CYLINDER: return isPossibleU(u) && isPossibleV(Math.abs(v)); case GeoQuadricNDConstants.QUADRIC_HYPERBOLIC_CYLINDER: double u0; if (u > 1) { u0 = Math.abs(PathNormalizer.infFunction(u - 2)); } else { u0 = -Math.abs(PathNormalizer.infFunction(u)); } return isPossibleU(u0) && isPossibleV(v); case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_ONE_SHEET: case GeoQuadricNDConstants.QUADRIC_HYPERBOLOID_TWO_SHEETS: case GeoQuadricNDConstants.QUADRIC_PARABOLOID: case GeoQuadricNDConstants.QUADRIC_CONE: case GeoQuadricNDConstants.QUADRIC_CYLINDER: return isPossibleV(v); default: return true; } } private boolean isPossibleU(double u) { if (u < uMinMax[0]) { return false; } if (u > uMinMax[1]) { return false; } return true; } private boolean isPossibleV(double v) { if (v < vMinMax[0]) { return false; } if (v > vMinMax[1]) { return false; } return true; } private Coords project = Coords.createInhomCoorsInD3(), p1 = Coords.createInhomCoorsInD3(), p2 = Coords.createInhomCoorsInD3(); private double[] parameters = new double[2]; private double[] parameters1 = new double[2]; private double[] parameters2 = new double[2]; @Override public Drawable3D drawForPicking(Renderer renderer, boolean intersection, PickingType type) { switch (type) { case POINT_OR_CURVE: int quadricType = ((GeoQuadric3D) getGeoElement()).getType(); if (quadricType == GeoQuadricNDConstants.QUADRIC_SINGLE_POINT || quadricType == GeoQuadricNDConstants.QUADRIC_LINE) { return super.drawForPicking(renderer, intersection, type); } return null; case SURFACE: quadricType = ((GeoQuadric3D) getGeoElement()).getType(); if (quadricType == GeoQuadricNDConstants.QUADRIC_SINGLE_POINT || quadricType == GeoQuadricNDConstants.QUADRIC_LINE) { return null; } return super.drawForPicking(renderer, intersection, type); case LABEL: return super.drawForPicking(renderer, intersection, type); default: return null; } } }