package org.geogebra.common.geogebra3D.euclidian3D.draw; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterBrush; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoClippingCube3D; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.main.Feature; /** * Class for drawing 3D constant planes. * * @author matthieu * */ public class DrawClippingCube3D extends Drawable3DCurves { private double[][] minMax, minMaxLarge; private Coords[] vertices; private Coords center; static private double REDUCTION_LARGE = 0; // (1-1./1)/2 static private double REDUCTION_ENLARGE = 1.5; static private double[] REDUCTION_VALUES = { (1 - 1. / Math.sqrt(3)) / 2, // small (1 - 1. / Math.sqrt(2)) / 2, // medium REDUCTION_LARGE // large }; static private double[] INTERIOR_RADIUS_FACTOR = { 1, Math.sqrt(2), Math.sqrt(3) }; /** * Common constructor * * @param a_view3D * view * @param clippingCube * geo */ public DrawClippingCube3D(EuclidianView3D a_view3D, GeoClippingCube3D clippingCube) { super(a_view3D, clippingCube); center = new Coords(4); center.setW(1); minMax = new double[3][]; minMaxLarge = new double[3][]; for (int i = 0; i < 3; i++) { minMax[i] = new double[2]; minMaxLarge[i] = new double[2]; } vertices = new Coords[8]; for (int i = 0; i < 8; i++) { vertices[i] = new Coords(0, 0, 0, 1); } } /* * public double xmin(){ return minMax[0][0]; } public double ymin(){ return * minMax[1][0]; } public double zmin(){ return minMax[2][0]; } public * double xmax(){ return minMax[0][1]; } public double ymax(){ return * minMax[1][1]; } public double zmax(){ return minMax[2][1]; } */ private double horizontalDiagonal; /** * max value from center to one FRUSTUM edge */ private double frustumRadius; /** * min value from center to one FRUSTUM face */ private double frustumInteriorRadius; /** * * @return big diagonal */ public double getHorizontalDiagonal() { return horizontalDiagonal; } /** * @return max value from center to one FRUSTUM edge */ public double getFrustumRadius() { return frustumRadius; } /** * @return min value from center to one FRUSTUM face */ public double getFrustumInteriorRadius() { return frustumInteriorRadius; } /** * update the x,y,z min/max values * * @return the min/max values */ public double[][] updateMinMax() { EuclidianView3D view = getView3D(); Renderer renderer = view.getRenderer(); double xscale = view.getXscale(); double yscale = view.getYscale(); double zscale = view.getZscale(); Coords origin = getView3D().getToSceneMatrix().getOrigin(); double x0 = origin.getX(), y0 = origin.getY(), z0 = origin.getZ(); double ymin, ymax, zmin, zmax; double halfWidth = renderer.getWidth() / 2.0; double xmin = -halfWidth / xscale + x0; double xmax = halfWidth / xscale + x0; if (getView3D().getYAxisVertical()) { zmin = (renderer.getBottom()) / yscale + y0; zmax = (renderer.getTop()) / yscale + y0; ymin = -halfWidth / zscale + z0; ymax = halfWidth / zscale + z0; } else { ymin = (renderer.getBottom()) / zscale + z0; ymax = (renderer.getTop()) / zscale + z0; zmin = -halfWidth / yscale + y0; zmax = halfWidth / yscale + y0; } int reductionIndex = ((GeoClippingCube3D) getGeoElement()) .getReduction(); double rv = REDUCTION_VALUES[reductionIndex]; double xr = (xmax - xmin) * rv; double yr = (ymax - ymin) * rv; double zr = (zmax - zmin) * rv; minMax[0][0] = xmin + xr; minMax[0][1] = xmax - xr; minMax[2][0] = ymin + yr; // z values : when 0 orientation, z is up on // screen minMax[2][1] = ymax - yr; minMax[1][0] = zmin + zr; minMax[1][1] = zmax - zr; setVertices(); horizontalDiagonal = renderer.getWidth() * (1 - 2 * rv) * Math.sqrt(2); double scaleMax = Math.max(Math.max(xscale, yscale), zscale); double scaleMin = Math.min(Math.min(xscale, yscale), zscale); int w = renderer.getWidth(); int h = renderer.getHeight(); int d = renderer.getVisibleDepth(); frustumRadius = Math.sqrt(w * w + h * h + d * d) / (2 * scaleMin); frustumInteriorRadius = Math.min(w, Math.min(h, d)) / (2 * scaleMax); frustumInteriorRadius *= INTERIOR_RADIUS_FACTOR[reductionIndex]; // double h = minMax[2][1]-minMax[2][0]; frustumRadius = h/2; view.setXYMinMax(minMax); // minMaxLarge to cut lines rv = REDUCTION_ENLARGE * rv + (1 - REDUCTION_ENLARGE) / 2;// (1-(1-rv*2)*REDUCTION_ENLARGE)/2; xr = (xmax - xmin) * rv; yr = (ymax - ymin) * rv; zr = (zmax - zmin) * rv; minMaxLarge[0][0] = xmin + xr; minMaxLarge[0][1] = xmax - xr; minMaxLarge[2][0] = ymin + yr; minMaxLarge[2][1] = ymax - yr; minMaxLarge[1][0] = zmin + zr; minMaxLarge[1][1] = zmax - zr; // update ev 3D depending algos getView3D().updateBounds(); return minMax; } private int nearestCornerX = -1, nearestCornerY = -1, nearestCornerZ = -1; /** * update corner nearest to the eye * * @return true if nearest corner has changed */ public boolean updateNearestCorner() { Coords eye = getView3D().getEyePosition(); int x, y, z; if (getView3D() .getProjection() == EuclidianView3D.PROJECTION_ORTHOGRAPHIC || getView3D() .getProjection() == EuclidianView3D.PROJECTION_OBLIQUE) { x = eye.getX() > 0 ? 0 : 1; y = eye.getY() > 0 ? 0 : 1; z = eye.getZ() > 0 ? 0 : 1; } else { x = eye.getX() > 0 ? 1 : 0; y = eye.getY() > 0 ? 1 : 0; z = eye.getZ() > 0 ? 1 : 0; } boolean changed = false; if (x != nearestCornerX) { nearestCornerX = x; changed = true; } if (y != nearestCornerY) { nearestCornerY = y; changed = true; } if (z != nearestCornerZ) { nearestCornerZ = z; changed = true; } return changed; } private void setVertices() { for (int x = 0; x < 2; x++) { for (int y = 0; y < 2; y++) { for (int z = 0; z < 2; z++) { Coords vertex = vertices[x + 2 * y + 4 * z]; vertex.setX(minMax[0][x]); vertex.setY(minMax[1][y]); vertex.setZ(minMax[2][z]); } } } for (int i = 0; i < 3; i++) { center.set(i + 1, (minMax[i][0] + minMax[i][1]) / 2); } } /** * * @param i * index * @return i-th vertex */ public Coords getVertex(int i) { return vertices[i]; } /** * * @return coords of the center point */ public Coords getCenter() { return center; } /** * * @return x, y, z min-max values */ public double[][] getMinMax() { return minMax; } private void setVertexWithBorder(int x, int y, int z, double border, Coords c) { Coords v = vertices[x + 2 * y + 4 * z]; if (getView3D().getApplication().has(Feature.DIFFERENT_AXIS_RATIO_3D)) { c.setX(v.getX() + border * (1 - 2 * x) / getView3D().getXscale()); c.setY(v.getY() + border * (1 - 2 * y) / getView3D().getYscale()); c.setZ(v.getZ() + border * (1 - 2 * z) / getView3D().getZscale()); } else { c.setX(v.getX() + border * (1 - 2 * x)); c.setY(v.getY() + border * (1 - 2 * y)); c.setZ(v.getZ() + border * (1 - 2 * z)); } } /* * @Override protected boolean isVisible(){ return * getView3D().useClippingCube(); } */ private Coords tmpCoords1 = new Coords(3), tmpCoords2 = new Coords(3); private double border; @Override protected boolean updateForItSelf() { Renderer renderer = getView3D().getRenderer(); // clippingBorder = (float) // (GeoElement.MAX_LINE_WIDTH*PlotterBrush.LINE3D_THICKNESS/getView3D().getScale()); // geometry PlotterBrush brush = renderer.getGeometryManager().getBrush(); brush.start(getReusableGeometryIndex()); // use 1.5 factor for border to avoid self clipping border = 1.5 * brush.setThickness(getGeoElement().getLineThickness(), (float) getView3D().getScale()); brush.setAffineTexture(0.5f, 0.25f); drawSegment(brush, 0, 0, 0, 1, 0, 0); drawSegment(brush, 0, 0, 0, 0, 1, 0); drawSegment(brush, 0, 0, 0, 0, 0, 1); drawSegment(brush, 1, 1, 0, 0, 1, 0); drawSegment(brush, 1, 1, 0, 1, 0, 0); drawSegment(brush, 1, 1, 0, 1, 1, 1); drawSegment(brush, 1, 0, 1, 0, 0, 1); drawSegment(brush, 1, 0, 1, 1, 1, 1); drawSegment(brush, 1, 0, 1, 1, 0, 0); drawSegment(brush, 0, 1, 1, 1, 1, 1); drawSegment(brush, 0, 1, 1, 0, 0, 1); drawSegment(brush, 0, 1, 1, 0, 1, 0); setGeometryIndex(brush.end()); updateRendererClipPlanes(); return true; } private void drawSegment(PlotterBrush brush, int x1, int y1, int z1, int x2, int y2, int z2) { setVertexWithBorder(x1, y1, z1, border, tmpCoords1); setVertexWithBorder(x2, y2, z2, border, tmpCoords2); brush.segment(tmpCoords1, tmpCoords2); } /** * update renderer clips planes */ public void updateRendererClipPlanes() { Renderer renderer = getView3D().getRenderer(); renderer.setClipPlanes(minMax); } @Override protected void updateForView() { // nothing to do } @Override public void drawGeometry(Renderer renderer) { renderer.getGeometryManager().draw(getGeometryIndex()); } @Override public int getPickOrder() { // TODO Auto-generated method stub return 0; } /** * for a line described by (o,v), return the min and max parameters to draw * the line * * @param minmax * initial interval * @param o * origin of the line * @param v * direction of the line * @return interval to draw the line */ public double[] getIntervalClippedLarge(double[] minmax, Coords o, Coords v) { for (int i = 1; i <= 3; i++) { double min = (minMaxLarge[i - 1][0] - o.get(i)) / v.get(i); double max = (minMaxLarge[i - 1][1] - o.get(i)) / v.get(i); updateInterval(minmax, min, max); } return minmax; } /** * for a line described by (o,v), return the min and max parameters to draw * the line * * @param minmax * initial interval * @param o * origin of the line * @param v * direction of the line * @return interval to draw the line */ public double[] getIntervalClipped(double[] minmax, Coords o, Coords v) { for (int i = 1; i <= 3; i++) { updateInterval(minmax, o, v, i, minMax[i - 1][0], minMax[i - 1][1]); } return minmax; } /** * intersect minmax interval with interval for (o,v)_index between boundsMin * and boundsMax * * @param minmax * interval to update * @param o * origin * @param v * direction * @param index * x/y/z * @param boundsMin * min for bounds * @param boundsMax * max for bounds */ public static void updateInterval(double[] minmax, Coords o, Coords v, int index, double boundsMin, double boundsMax) { double min = (boundsMin - o.get(index)) / v.get(index); double max = (boundsMax - o.get(index)) / v.get(index); updateInterval(minmax, min, max); } /** * return the intersection of intervals [minmax] and [v1,v2] * * @param minmax * initial interval * @param v1 * first value * @param v2 * second value * @return intersection interval */ private static double[] updateInterval(double[] minmax, double v1, double v2) { double vMin, vMax; if (v1 > v2) { vMax = v1; vMin = v2; } else { vMax = v2; vMin = v1; } if (vMin > minmax[0]) { minmax[0] = vMin; } if (vMax < minmax[1]) { minmax[1] = vMax; } return minmax; } @Override protected boolean isVisible() { return true; // TODO -- take care of updateRendererClipPlanes(); // return getView3D().showClippingCube(); } }