package org.geogebra.common.geogebra3D.euclidian3D.openGL; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.Matrix.Coords3; import org.geogebra.common.kernel.arithmetic.Functional2Var; /** * Class for drawing surfaces. * * @author Mathieu * */ public class PlotterSurface { /** manager */ protected Manager manager; /** index */ private int index; /** 2-var function */ private Functional2Var functional2Var; /** domain for plotting */ private float uMin, uMax, vMin, vMax; /** number of plotting */ private int uNb, vNb; /** delta for plotting */ private float du, dv; /** fading values */ private float uMinFade, uMaxFade, vMinFade, vMaxFade; private float uMinFadeNb, uMaxFadeNb, vMinFadeNb, vMaxFadeNb; /** texture coord for out (alpha = 0) */ static final private float TEXTURE_FADE_OUT = 0.75f; /** texture coord for in (alpha = 1) */ static final private float TEXTURE_FADE_IN = 0f; /** * default constructor * * @param manager */ public PlotterSurface(Manager manager) { this.manager = manager; } // ////////////////////////////////// // START AND END // ////////////////////////////////// /** * start new surface */ public void start(int old) { index = manager.startNewList(old); } /** * start new surface * * @param fun * function */ public void start(Functional2Var fun, int old) { index = manager.startNewList(old); this.functional2Var = fun; uMinFade = 0; vMinFade = 0; uMaxFade = 0; vMaxFade = 0; } /** * end surface * * @return gl index of the surface */ public int end() { manager.endList(); return index; } // ////////////////////////////////// // DRAWING METHODS // ////////////////////////////////// /** * set domain for u parameter * * @param min * @param max */ public void setU(float min, float max) { this.uMin = min; this.uMax = max; } /** * set domain for v parameter * * @param min * @param max */ public void setV(float min, float max) { this.vMin = min; this.vMax = max; } /** * set number of plot for u * * @param n */ public void setNbU(int n) { this.uNb = n; } /** * set number of plot for v * * @param n */ public void setNbV(int n) { this.vNb = n; } /** * set fading frontiers for u parameter * * @param min * @param max */ public void setUFading(float min, float max) { this.uMinFade = min; this.uMaxFade = max; } /** * set fading frontiers for v parameter * * @param min * @param max */ public void setVFading(float min, float max) { this.vMinFade = min; this.vMaxFade = max; } public void drawTriangle(Coords p1, Coords p2, Coords p3) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); float uT = getTextureCoord(1, uNb, uMinFadeNb, uMaxFadeNb); float vT = getTextureCoord(1, vNb, vMinFadeNb, vMaxFadeNb); manager.texture(uT, vT); manager.vertex(p1); manager.vertex(p3); manager.vertex(p2); manager.endGeometry(); } /** * draw a quadrilateral * * @param p1 * point 1 * @param p2 * point 2 * @param p3 * point 3 * @param p4 * point 4 */ public void drawQuad(Coords p1, Coords p2, Coords p3, Coords p4) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); float uT = getTextureCoord(1, uNb, uMinFadeNb, uMaxFadeNb); float vT = getTextureCoord(1, vNb, vMinFadeNb, vMaxFadeNb); manager.texture(uT, vT); manager.vertex(p1); manager.vertex(p2); manager.vertex(p4); manager.vertex(p3); manager.endGeometry(); } public void drawQuadNoTexture(Coords p1, Coords p2, Coords p3, Coords p4) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); manager.setDummyTexture(); manager.vertex(p1); manager.vertex(p2); manager.vertex(p4); manager.vertex(p3); manager.endGeometry(); } public void drawQuadWireFrame(Coords p1, Coords p2, Coords p3, Coords p4) { // lines manager.startGeometry(Manager.Type.LINE_LOOP); manager.setDummyTexture(); manager.color(0f, 0f, 0f, 1f); manager.vertex(p1); manager.vertex(p2); manager.vertex(p3); manager.vertex(p4); manager.endGeometry(); // surface manager.startGeometry(Manager.Type.TRIANGLE_STRIP); manager.setDummyTexture(); manager.color(1f, 0f, 0f, 0.5f); manager.vertex(p1); manager.vertex(p2); manager.vertex(p4); manager.vertex(p3); manager.endGeometry(); } public void startTrianglesWireFrame() { // lines manager.startGeometry(Manager.Type.LINE_LOOP); manager.setDummyTexture(); manager.color(0f, 0f, 0f, 1f); } public void startTrianglesWireFrameSurface() { manager.startGeometry(Manager.Type.TRIANGLES); manager.setDummyTexture(); manager.color(1f, 0f, 0f, 0.25f); } public void startTrianglesWireFrameSurfaceBoundary() { manager.startGeometry(Manager.Type.TRIANGLES); manager.setDummyTexture(); manager.color(0f, 0f, 1f, 0.25f); } public void startTriangles() { manager.startGeometry(Manager.Type.TRIANGLES); manager.setDummyTexture(); } /** * * draw triangle * * @param p1 * first vertex * @param p2 * second vertex * @param p3 * last vertex * @param norm1 * first normal * @param norm2 * second normal * @param norm3 * third normal */ public void triangle(Coords3 p1, Coords3 p2, Coords3 p3, Coords3 norm1, Coords3 norm2, Coords3 norm3) { manager.normal(norm1.getXd(), norm1.getYd(), norm1.getZd()); manager.vertexToScale(p1.getXd(), p1.getYd(), p1.getZd()); manager.normal(norm2.getXd(), norm2.getYd(), norm2.getZd()); manager.vertexToScale(p2.getXd(), p2.getYd(), p2.getZd()); manager.normal(norm3.getXd(), norm3.getYd(), norm3.getZd()); manager.vertexToScale(p3.getXd(), p3.getYd(), p3.getZd()); } /** * start triangles * * @param size * vertices size */ public void startTriangles(int size) { manager.startGeometryDirect(Manager.Type.TRIANGLES, size); manager.setDummyTexture(); } public void vertexDirect(Coords3 p) { manager.vertexDirect(p); } public void normalDirect(Coords3 n0) { manager.normalDirect(n0); } public void endGeometryDirect() { manager.endGeometryDirect(); } public void endGeometry() { manager.endGeometry(); } /** * * @param radius * radius of the sphere * @param viewScale * view scale * @return longitude length needed to render the sphere */ public int calcSphereLongitudesNeeded(double radius, double viewScale) { int longitude = 8; double size = radius * viewScale; // App.error(""+size); while (longitude * longitude <= 16 * size && longitude < manager.getLongitudeMax()) {// find the correct // longitude size longitude *= 2; } // Log.debug("sphere ==== longitude="+longitude); return longitude; } /** * draw a sphere with center and radius. view scaling is used to know how * many triangles are needed * * @param center * center of the sphere * @param radius * radius of the sphere * @param longitude * longitude length for rendering */ public void drawSphere(Coords center, double radius, int longitude) { drawSphere(center, radius, longitude, 0, longitude); } /** * draw an ellipsoid * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r0 * first half axis * @param r1 * second half axis * @param r2 * third half axis * @param longitude * longitude length for rendering */ public void drawEllipsoid(Coords center, Coords ev0, Coords ev1, Coords ev2, double r0, double r1, double r2, int longitude) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawEllipsoid(center, ev0, ev1, ev2, r0, r1, r2, longitude); } private ManagerElementForGLList managerElements; private PlotterSurfaceElements plotterElements; /** * draw an hyperboloid (one sheet) * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r0 * first half axis * @param r1 * second half axis * @param r2 * third half axis * @param longitude * longitude length for rendering * @param min * minimum parameter for axis * @param max * maximum parameter for axis * @param fading * if we need fading or not * */ public void drawHyperboloidOneSheet(Coords center, Coords ev0, Coords ev1, Coords ev2, double r0, double r1, double r2, int longitude, double min, double max, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawHyperboloidOneSheet(center, ev0, ev1, ev2, r0, r1, r2, longitude, min, max, fading); } /** * draw an hyperboloid (two sheets) * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r0 * first half axis * @param r1 * second half axis * @param r2 * third half axis * @param longitude * longitude length for rendering * @param min * minimum parameter for axis * @param max * maximum parameter for axis * @param fading * if we need fading or not * */ public void drawHyperboloidTwoSheets(Coords center, Coords ev0, Coords ev1, Coords ev2, double r0, double r1, double r2, int longitude, double min, double max, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawHyperboloidTwoSheets(center, ev0, ev1, ev2, r0, r1, r2, longitude, min, max, fading); } /** * draw a paraboloid * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r0 * first half axis * @param r1 * second half axis * @param longitude * longitude length for rendering * @param min * minimum parameter for axis * @param max * maximum parameter for axis * @param fading * if we need fading or not * */ public void drawParaboloid(Coords center, Coords ev0, Coords ev1, Coords ev2, double r0, double r1, int longitude, double min, double max, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawParaboloid(center, ev0, ev1, ev2, r0, r1, longitude, min, max, fading); } /** * draw an hyperbolic paraboloid * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r0 * first half axis * @param r1 * second half axis * @param min0 * minimum parameter for first axis * @param max0 * maximum parameter for first axis * @param min1 * minimum parameter for second axis * @param max1 * maximum parameter for second axis * @param fading * if we need fading or not * */ public void drawHyperbolicParaboloid(Coords center, Coords ev0, Coords ev1, Coords ev2, double r0, double r1, double min0, double max0, double min1, double max1, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawHyperbolicParaboloid(center, ev0, ev1, ev2, r0, r1, min0, max0, min1, max1, fading); } /** * draw a parabolic cylinder * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r * half axis * @param min * minimum parameter for quadric axis * @param max * maximum parameter for quadric axis * @param lineMin * minimum parameter for line axis * @param lineMax * maximum parameter for line axis * @param fading * if we need fading or not * */ public void drawParabolicCylinder(Coords center, Coords ev0, Coords ev1, Coords ev2, double r, double min, double max, double lineMin, double lineMax, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawParabolicCylinder(center, ev0, ev1, ev2, r, min, max, lineMin, lineMax, fading); } /** * draw an hyperbolic cylinder * * @param center * center * @param ev0 * first eigenvector * @param ev1 * second eigenvector * @param ev2 * third eigenvector * @param r1 * half axis * @param r2 * half axis * @param min * minimum parameter for quadric axis * @param max * maximum parameter for quadric axis * @param lineMin * minimum parameter for line axis * @param lineMax * maximum parameter for line axis * @param fading * if we need fading or not * */ public void drawHyperbolicCylinder(Coords center, Coords ev0, Coords ev1, Coords ev2, double r1, double r2, double min, double max, double lineMin, double lineMax, boolean fading) { if (managerElements == null) { managerElements = new ManagerElementForGLList(manager.getRenderer(), manager.getView3D(), manager); plotterElements = new PlotterSurfaceElements(managerElements); } plotterElements.drawHyperbolicCylinder(center, ev0, ev1, ev2, r1, r2, min, max, lineMin, lineMax, fading); } private Coords[] coordsArray = new Coords[0]; /** * draw a sphere with center and radius. view scaling is used to know how * many triangles are needed * * @param center * center of the sphere * @param radius * radius of the sphere * @param longitude * longitude length for rendering, corresponding to 2*PI (must be * power of 2) * @param longitudeStart * for sphere parts, first longitude to draw * @param longitudeLength * for sphere parts, longitude width (must be power of 2) */ public void drawSphere(Coords center, double radius, int longitude, double longitudeStart, int longitudeLength) { drawSphere(center, radius, longitude, longitudeStart, longitudeLength, manager.getView3D().getFrustumRadius()); } public void drawSphere(Coords center, double radius, int longitude, double longitudeStart, int longitudeLength, double frustumRadius) { manager.startGeometry(Manager.Type.TRIANGLES); // set texture to (0,0) manager.setDummyTexture(); int latitude = longitude / 4; // check which parts are visible (latitudes) Coords o = manager.getView3D().getCenter(); double z = center.getZ(); double zMin = o.getZ() - frustumRadius; double zMax = o.getZ() + frustumRadius; int latitudeMaxTop = 0; latitudeMaxTop = latitude; if (Kernel.isGreater(z + radius, zMax)) { double angle = Math.asin((zMax - z) / radius); latitudeMaxTop = (int) (latitude * 2 * angle / Math.PI) + 2; } int latitudeMaxBottom = 0; latitudeMaxBottom = latitude; if (Kernel.isGreater(zMin, z - radius)) { double angle = Math.asin((z - zMin) / radius); latitudeMaxBottom = (int) (latitude * 2 * angle / Math.PI) + 2; } // Log.debug(latitudeMaxBottom+","+latitudeMaxTop); int latitudeMax = Math.max(latitudeMaxTop, latitudeMaxBottom); int latitudeMin = 0; // start on equator if (latitudeMaxTop < 0) { // start below equator latitudeMin = -latitudeMaxTop; } else if (latitudeMaxBottom < 0) { // start above equator latitudeMin = -latitudeMaxBottom; } // check which parts are visible (longitudes) // start drawing if (coordsArray.length <= longitudeLength) { coordsArray = new Coords[longitudeLength + 1]; for (int ui = 0; ui <= longitudeLength; ui++) { coordsArray[ui] = new Coords(4); } } Coords norm1 = new Coords(4), norm2 = new Coords(4), n1b = new Coords(4), n2b = new Coords(4); double[] cosSinV = new double[2]; // equator // cosSinV[0] = 1; // cos(0) // cosSinV[1] = 0; // sin(0) cosSin(latitudeMin, latitude, cosSinV); double lastCos = 1; for (int ui = 0; ui <= longitudeLength; ui++) { sphericalCoords(ui, longitude, longitudeStart, cosSinV, coordsArray[ui]); } // shift for longitude int shift = 1; boolean jumpNeeded = false; for (int vi = latitudeMin + 1; vi < latitudeMax; vi++) { cosSin(vi, latitude, cosSinV); // check if parallel is small enough to make jumps if (2 * cosSinV[0] < lastCos) { lastCos = lastCos / 2; jumpNeeded = true; } else { jumpNeeded = false; } // first values norm2.set(coordsArray[0]); sphericalCoords(0, longitude, longitudeStart, cosSinV, n2b); // first : no jump boolean jump = jumpNeeded; for (int ui = shift; ui <= longitudeLength; ui += shift) { // last latitude values norm1.set(norm2); norm2.set(coordsArray[ui]); // new latitude values and draw triangles n1b.set(n2b); if (jumpNeeded) { if (jump) { // draw edge triangle and center triangle sphericalCoords(ui + shift, longitude, longitudeStart, cosSinV, n2b); if (vi < latitudeMaxTop) {// top triangles drawNCr(norm1, center, radius); drawNCr(norm2, center, radius); drawNCr(n1b, center, radius); drawNCr(n1b, center, radius); drawNCr(norm2, center, radius); drawNCr(n2b, center, radius); } if (vi < latitudeMaxBottom) {// bottom triangles drawNCrm(norm1, center, radius); drawNCrm(n1b, center, radius); drawNCrm(norm2, center, radius); drawNCrm(n1b, center, radius); drawNCrm(n2b, center, radius); drawNCrm(norm2, center, radius); } } else { // draw edge triangle sphericalCoords(ui, longitude, longitudeStart, cosSinV, n2b); if (vi < latitudeMaxTop) {// top triangles drawNCr(norm1, center, radius); drawNCr(norm2, center, radius); drawNCr(n1b, center, radius); } if (vi < latitudeMaxBottom) {// bottom triangles drawNCrm(norm1, center, radius); drawNCrm(n1b, center, radius); drawNCrm(norm2, center, radius); } } } else { // no jump : draw two triangles sphericalCoords(ui, longitude, longitudeStart, cosSinV, n2b); if (vi < latitudeMaxTop) {// top triangles drawNCr(norm1, center, radius); drawNCr(norm2, center, radius); drawNCr(n1b, center, radius); drawNCr(norm2, center, radius); drawNCr(n2b, center, radius); drawNCr(n1b, center, radius); } if (vi < latitudeMaxBottom) {// bottom triangles drawNCrm(norm1, center, radius); drawNCrm(n1b, center, radius); drawNCrm(norm2, center, radius); drawNCrm(norm2, center, radius); drawNCrm(n1b, center, radius); drawNCrm(n2b, center, radius); } } coordsArray[ui].set(n2b); if (jumpNeeded) { jump = !jump; } } // if just jumps done, next shift is twice if (jumpNeeded) { shift = shift * 2; } sphericalCoords(0, longitude, longitudeStart, cosSinV, coordsArray[0]); } if (latitudeMax == latitude) { // pole norm2.set(coordsArray[0]); for (int ui = shift; ui <= longitudeLength; ui += shift) { norm1.set(norm2); norm2.set(coordsArray[ui]); if (latitudeMaxTop == latitude) {// top triangles drawNCr(norm1, center, radius); drawNCr(norm2, center, radius); drawNCr(Coords.VZ, center, radius); } if (latitudeMaxBottom == latitude) {// bottom triangles drawNCrm(norm1, center, radius); drawNCrm(Coords.VZ, center, radius); drawNCrm(norm2, center, radius); } } } manager.endGeometry(); } /** * draw part of the surface */ public void draw() { manager.startGeometry(Manager.Type.TRIANGLES); du = (uMax - uMin) / uNb; dv = (vMax - vMin) / vNb; /* * uMinFadeNb = uNb*uMinFade/(uMax-uMin); uMaxFadeNb = * uNb*uMaxFade/(uMax-uMin); vMinFadeNb = vNb*vMinFade/(vMax-vMin); * vMaxFadeNb = vNb*vMaxFade/(vMax-vMin); */ uMinFadeNb = uMinFade / du; uMaxFadeNb = uMaxFade / du; vMinFadeNb = vMinFade / dv; vMaxFadeNb = vMaxFade / dv; // Application.debug("vMin, vMax, dv="+vMin+", "+vMax+", "+dv); for (int ui = 0; ui < uNb; ui++) { for (int vi = 0; vi < vNb; vi++) { drawQuad(ui, vi); } } manager.endGeometry(); } // private Coords n1, n2, n3, n4, v1, v2, v3, v4; public void drawSphere(int size, Coords center, double radius) { int longitude = 8; int size1 = size + 3; while (longitude * 6 <= size1 * size1) {// find the correct longitude // size // (size=3 <-> longitude=12 and // size=9 <-> longitude=48) longitude *= 2; } drawSphere(center, radius, longitude, 0, longitude, Double.POSITIVE_INFINITY); } protected static void cosSin(int vi, int latitude, double[] ret) { double v = ((double) vi / latitude) * Math.PI / 2; ret[0] = Math.cos(v); ret[1] = Math.sin(v); } protected static final void sphericalCoords(int ui, int longitude, double longitudeStart, double[] cosSinV, Coords n) { double u = ((double) ui / longitude) * 2 * Math.PI + longitudeStart; n.setX(Math.cos(u) * cosSinV[0]); n.setY(Math.sin(u) * cosSinV[0]); n.setZ(cosSinV[1]); } /** * draws a parallelogram * * @param center * @param v1 * @param v2 * @param l1 * @param l2 */ public void parallelogram(Coords center, Coords v1, Coords v2, double l1, double l2) { manager.startGeometry(Manager.Type.TRIANGLES); manager.setDummyTexture(); tmpCoords.setCrossProduct(v1, v2); manager.normalToScale(tmpCoords); tmpCoords.setAdd(center, tmpCoords.setMul(v1, l1)); tmpCoords2.setAdd(tmpCoords, tmpCoords2.setMul(v2, l2)); tmpCoords3.setAdd(center, tmpCoords3.setMul(v2, l2)); manager.vertexToScale(center); manager.vertexToScale(tmpCoords); manager.vertexToScale(tmpCoords2); manager.vertexToScale(center); manager.vertexToScale(tmpCoords2); manager.vertexToScale(tmpCoords3); manager.endGeometry(); } /** * draws an ellipse * * @param center * @param v1 * @param v2 * @param a * @param b * @param start * @param extent */ public void ellipsePart(Coords center, Coords v1, Coords v2, double a, double b, double start, double extent) { ellipsePart(center, v1, v2, a, b, start, extent, true); } protected Coords m = new Coords(4); protected Coords tmpCoords = new Coords(4); protected Coords tmpCoords2 = new Coords(4); protected Coords tmpCoords3 = new Coords(4); protected Coords center1 = new Coords(4); protected Coords center2 = new Coords(4); private Coords n = new Coords(4); /** * @param center * @param v1 * @param v2 * @param a * @param b * @param start * @param extent * @param fromEllipseCenter * says if the surface is drawn from center of the ellipse */ public void ellipsePart(Coords center, Coords v1, Coords v2, double a, double b, double start, double extent, boolean fromEllipseCenter) { manager.startGeometry(Manager.Type.TRIANGLE_FAN); int longitude = manager.getLongitudeDefault(); float u, v; float dt = (float) 1 / longitude; float da = (float) (extent * dt); manager.setDummyTexture(); manager.normalToScale(v1.crossProduct(v2)); u = (float) Math.cos(start); v = (float) Math.sin(start); v1.mul(a * u, m); v2.mul(b * v, tmpCoords); m.add(tmpCoords, m); // center of the triangle fan if (fromEllipseCenter) { // center of the ellipse manager.triangleFanApex(center); } else { // mid point of the ellipse start and end u = (float) Math.cos(start + extent); v = (float) Math.sin(start + extent); v1.mul(a * u, tmpCoords2); v2.mul(b * v, tmpCoords); tmpCoords2.add(tmpCoords, tmpCoords2); tmpCoords2.add(m, tmpCoords2); tmpCoords2.mul(0.5, tmpCoords2); center.add(tmpCoords2, tmpCoords2); manager.triangleFanApex(tmpCoords2); } // first point tmpCoords3.setW(1); manager.triangleFanVertex(tmpCoords3.setAdd3(center, m)); for (int i = 1; i <= longitude; i++) { u = (float) Math.cos(start + i * da); v = (float) Math.sin(start + i * da); v1.mul(a * u, m); v2.mul(b * v, tmpCoords); m.add(tmpCoords, m); center.add(m, m); manager.triangleFanVertex(m); } manager.endGeometry(); } /** * @param center * @param vx * @param vy * @param vz * @param radius * @param start * @param extent * @param height * @param fading * @return center of the bottom */ public Coords cone(Coords center, Coords vx, Coords vy, Coords vz, double radius, double start, double extent, double height, float fading) { return cone(center, vx, vy, vz, radius, radius, start, extent, height, fading); } /** * @param center * @param vx * @param vy * @param vz * @param r1 * @param r2 * @param start * @param extent * @param height * @param fading * @return center of the bottom */ public Coords cone(Coords center, Coords vx, Coords vy, Coords vz, double r1, double r2, double start, double extent, double height, float fading) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); int longitude = manager.getLongitudeDefault(); double u, v; float dt = (float) 1 / longitude; float da = (float) (extent * dt); // if (height > 0){ // ensure correct back/front face culling // da *= -1; // } if (fading == 1) { // no fading manager.setDummyTexture(); } center2.set(vz); center2.mulInside3(height); center2.addInside(center); double r1h = r1 * -height; double r2h = r2 * -height; double rr = r1 * r2; for (int i = 0; i <= longitude; i++) { u = Math.cos(start + i * da); v = Math.sin(start + i * da); m.setAdd(tmpCoords2.setMul(vx, u * r1h), tmpCoords3.setMul(vy, v * r2h)); n.setMul(vx, r2 * u); tmpCoords.setMul(vy, r1 * v); n.addInside(tmpCoords); tmpCoords.setMul(vz, rr); n.addInside(tmpCoords); n.normalize(); // center of the triangle fan if (fading < 1) { manager.texture(0, fading); } manager.normalToScale(n); manager.vertexToScale(center); // point on circle if (fading < 1) { manager.texture(0, 1); } manager.normalToScale(n); manager.vertexToScale(tmpCoords2.setAdd(center2, m)); } manager.endGeometry(); return center2; } public void cone(Coords center, Coords vx, Coords vy, Coords vz, double radius, double start, double extent, double min, double max, boolean minFading, boolean maxFading) { cone(center, vx, vy, vz, radius, radius, start, extent, min, max, minFading, maxFading); } public void cone(Coords center, Coords vx, Coords vy, Coords vz, double r1, double r2, double start, double extent, double min, double max, boolean minFading, boolean maxFading) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); int longitude = manager.getLongitudeDefault(); float u, v; float dt = (float) 1 / longitude; float da = (float) (extent * dt); center1.set(vz); center1.mulInside3(min); center1.addInside(center); center2.set(vz); center2.mulInside3(max); center2.addInside(center); double rmin = r1 * min; double rmax = r1 * max; double ratio = r2 / r1; // ensure radius are positive and normals go outside int sgn = 1; if (max > 0) { sgn = -1; } else { rmin *= -1; rmax *= -1; } double rr = r1 * r2 * sgn; boolean fading = minFading || maxFading; if (!fading) { manager.setDummyTexture(); } for (int i = 0; i <= longitude; i++) { u = (float) Math.cos(start + i * da); v = (float) Math.sin(start + i * da); m.setAdd(tmpCoords2.setMul(vx, u), tmpCoords3.setMul(vy, v * ratio)); n.setMul(vx, r2 * u); tmpCoords.setMul(vy, r1 * v); n.addInside(tmpCoords); tmpCoords.setMul(vz, rr); n.addInside(tmpCoords); n.normalize(); // point on top circle if (fading) { if (maxFading) { manager.texture(0, 1); } else { manager.texture(0, 0); } } manager.normalToScale(n); manager.vertexToScale( tmpCoords2.setAdd(center2, tmpCoords3.setMul(m, rmax))); // point on bottom circle if (fading) { if (minFading) { manager.texture(0, 1); } else { manager.texture(0, 0); } } manager.normalToScale(n); manager.vertexToScale( tmpCoords2.setAdd(center1, tmpCoords3.setMul(m, rmin))); } manager.endGeometry(); } public Coords cylinder(Coords center, Coords vx, Coords vy, Coords vz, double radius, double start, double extent, double min, double max, boolean minFading, boolean maxFading, int longitude) { return cylinder(center, vx, vy, vz, radius, radius, start, extent, min, max, minFading, maxFading, longitude); } public Coords cylinder(Coords center, Coords vx, Coords vy, Coords vz, double r1, double r2, double start, double extent, double min, double max, boolean minFading, boolean maxFading, int longitude) { manager.startGeometry(Manager.Type.TRIANGLE_STRIP); float c, s; float dt = (float) 1 / longitude; float da = (float) (extent * dt); center1.set(vz); center1.mulInside3(min); center1.addInside(center); center2.set(vz); center2.mulInside3(max); center2.addInside(center); boolean fading = minFading || maxFading; if (!fading) { manager.setDummyTexture(); } for (int i = 0; i <= longitude; i++) { c = (float) Math.cos(start + i * da); s = (float) Math.sin(start + i * da); n.setAdd(tmpCoords.setMul(vx, r2 * c), tmpCoords2.setMul(vy, r1 * s)); n.normalize(); // point on top circle if (fading) { if (maxFading) { manager.texture(0, 1); } else { manager.texture(0, 0); } } tmpCoords3.setAdd(tmpCoords.setMul(vx, r1 * c), tmpCoords2.setMul(vy, r2 * s)); manager.normalToScale(n); manager.vertexToScale(tmpCoords.setAdd(center2, tmpCoords3)); // point on bottom circle if (fading) { if (minFading) { manager.texture(0, 1); } else { manager.texture(0, 0); } } manager.normalToScale(n); manager.vertexToScale(tmpCoords.setAdd(center1, tmpCoords3)); } manager.endGeometry(); return center2; } /** * draws the inside of the hyperobola part * * @param center * center * @param v1 * 1st eigenvector * @param v2 * 2nd eigenvector * @param a * 1st eigenvalue * @param b * 2nd eigenvalue * @param tMin * t min * @param tMax * t max */ public void hyperbolaPart(Coords center, Coords v1, Coords v2, double a, double b, double tMin, double tMax) { manager.startGeometry(Manager.Type.TRIANGLE_FAN); manager.texture(0, 0); manager.normalToScale(v1.crossProduct(v2)); int longitude = manager.getLongitudeDefault(); Coords m1; float dt = (float) (tMax - tMin) / longitude; float u, v; // first point on the branch u = (float) Math.cosh(tMin); v = (float) Math.sinh(tMin); m1 = v1.mul(a * u).add(v2.mul(b * v)); // center of the fan is midpoint of branch ends u = (float) Math.cosh(tMax); v = (float) Math.sinh(tMax); manager.triangleFanApex( center.add( (m1.add(v1.mul(a * u).add(v2.mul(b * v)))).mul(0.5))); // first point manager.triangleFanVertex(center.add(m1)); for (int i = 1; i <= longitude; i++) { u = (float) Math.cosh(tMin + i * dt); v = (float) Math.sinh(tMin + i * dt); m1 = v1.mul(a * u).add(v2.mul(b * v)); manager.triangleFanVertex(center.add(m1)); } manager.endGeometry(); } /** * fill a parabola * * @param center * center * @param v1 * 1st eigenvector * @param v2 * 2nd eigenvector * @param p * eigenvalue * @param tMin * t min * @param tMax * t max */ public void parabola(Coords center, Coords v1, Coords v2, double p, double tMin, double tMax) { manager.startGeometry(Manager.Type.TRIANGLE_FAN); manager.texture(0, 0); manager.normalToScale(v1.crossProduct(v2)); int longitude = manager.getLongitudeDefault(); Coords m1; float dt = (float) (tMax - tMin) / longitude; float u, v; double t; // first point t = tMin; u = (float) (p * t * t / 2); v = (float) (p * t); m1 = v1.mul(u).add(v2.mul(v)); // center of the fan is midpoint of branch ends t = tMax; u = (float) (p * t * t / 2); v = (float) (p * t); manager.triangleFanApex( center.add((m1.add(v1.mul(u).add(v2.mul(v)))).mul(0.5))); // first point manager.triangleFanVertex(center.add(m1)); for (int i = 1; i <= longitude; i++) { t = tMin + i * dt; u = (float) (p * t * t / 2); v = (float) (p * t); m1 = v1.mul(u).add(v2.mul(v)); manager.triangleFanVertex(center.add(m1)); } manager.endGeometry(); } private void drawQuad(int ui, int vi) { drawTNV(ui, vi); drawTNV(ui + 1, vi); drawTNV(ui + 1, vi + 1); drawTNV(ui, vi); drawTNV(ui + 1, vi + 1); drawTNV(ui, vi + 1); } private void drawTNV(int ui, int vi) { float uT = getTextureCoord(ui, uNb, uMinFadeNb, uMaxFadeNb); float vT = getTextureCoord(vi, vNb, vMinFadeNb, vMaxFadeNb); manager.texture(uT, vT); float u = uMin + ui * du; float v = vMin + vi * dv; functional2Var.evaluatePoint(u, v, tmpCoords2); drawNV(functional2Var.evaluateNormal(u, v), tmpCoords2); } private Coords coords1 = new Coords(4); /** * draws normal and point at center + normal * radius * * @param normal * @param center * @param radius */ protected void drawNCr(Coords normal, Coords center, double radius) { normal.mul(radius, coords1); center.add(coords1, coords1); // drawNV(normal, center.add(normal.mul(radius))); drawNV(normal, coords1); } private Coords normal2 = new Coords(4); /** * draws normal and point at center - normal * radius * * @param normal * @param center * @param radius */ protected void drawNCrm(Coords normal, Coords center, double radius) { normal2.setX(normal.getX()); normal2.setY(normal.getY()); normal2.setZ(-normal.getZ()); drawNCr(normal2, center, radius); } public void drawNV(Coords normal, Coords point) { manager.normalToScale(normal); manager.vertexToScale(point); } private static float getTextureCoord(int i, int n, float fadeMin, float fadeMax) { float t; if (fadeMin != 0) { if (i <= n / 2) { t = i / fadeMin; return TEXTURE_FADE_OUT * (1 - t) + TEXTURE_FADE_IN * t; } } if (fadeMax != 0) { if (i >= n / 2) { t = (n - i) / fadeMax; return TEXTURE_FADE_OUT * (1 - t) + TEXTURE_FADE_IN * t; } } return TEXTURE_FADE_IN; } }