package org.geogebra.common.geogebra3D.euclidian3D; import java.util.ArrayList; import java.util.LinkedList; import java.util.TreeMap; import java.util.TreeSet; import org.geogebra.common.awt.GBufferedImage; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GFont; import org.geogebra.common.awt.GGraphics2D; import org.geogebra.common.awt.GPoint; import org.geogebra.common.awt.GPointWithZ; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.euclidian.DrawableND; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.euclidian.EuclidianController; import org.geogebra.common.euclidian.EuclidianCursor; import org.geogebra.common.euclidian.EuclidianStatic; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.euclidian.EuclidianViewCompanion; import org.geogebra.common.euclidian.Hits; import org.geogebra.common.euclidian.Previewable; import org.geogebra.common.euclidian.event.PointerEventType; import org.geogebra.common.euclidian3D.EuclidianView3DInterface; import org.geogebra.common.euclidian3D.Mouse3DEvent; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawAngle3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawAxis3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawClippingCube3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawConic3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawConicPart3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawConicSection3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawConify3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawCurve3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawExtrusion3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawImplicitCurve3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawImplicitSurface3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawLine3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawList3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawLocus3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPlane3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPlaneConstant3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPoint3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPointDecorations; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPolyLine3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPolygon3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawPolyhedron3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawQuadric3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawQuadric3DLimited; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawQuadric3DPart; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawRay3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawSegment3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawSurface3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawSurface3DElements; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawText3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawVector3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3DLists; import org.geogebra.common.geogebra3D.euclidian3D.draw.Drawable3DListsForView; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Manager.ScalerXYZ; import org.geogebra.common.geogebra3D.euclidian3D.openGL.PlotterCursor; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.kernel3D.Kernel3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoClippingCube3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoConicSection; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPlane3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPlane3DConstant; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPoint3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPolyhedron; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3D; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3DLimited; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoQuadric3DPart; import org.geogebra.common.geogebra3D.kernel3D.geos.GeoSurfaceCartesian3D; import org.geogebra.common.geogebra3D.kernel3D.implicit3D.GeoImplicitSurface; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.EVProperty; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Matrix.CoordMatrix; import org.geogebra.common.kernel.Matrix.CoordMatrix4x4; import org.geogebra.common.kernel.Matrix.CoordMatrixUtil; import org.geogebra.common.kernel.Matrix.CoordSys; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.Matrix.Coords3; import org.geogebra.common.kernel.algos.AlgoElement; import org.geogebra.common.kernel.geos.GProperty; import org.geogebra.common.kernel.geos.GeoAngle; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.kernel.geos.GeoFunctionNVar; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoLocusNDInterface; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.kernel.geos.GeoPolygon; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.kernel.implicit.GeoImplicit; import org.geogebra.common.kernel.kernelND.CurveEvaluable; import org.geogebra.common.kernel.kernelND.GeoAxisND; import org.geogebra.common.kernel.kernelND.GeoConicND; import org.geogebra.common.kernel.kernelND.GeoConicPartND; import org.geogebra.common.kernel.kernelND.GeoDirectionND; import org.geogebra.common.kernel.kernelND.GeoLineND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.kernel.kernelND.GeoQuadricNDConstants; import org.geogebra.common.kernel.kernelND.GeoRayND; import org.geogebra.common.kernel.kernelND.GeoSegmentND; import org.geogebra.common.kernel.kernelND.GeoVectorND; import org.geogebra.common.kernel.kernelND.SurfaceEvaluable; import org.geogebra.common.main.App; import org.geogebra.common.main.App.ExportType; import org.geogebra.common.main.Feature; import org.geogebra.common.main.settings.AbstractSettings; import org.geogebra.common.main.settings.EuclidianSettings; import org.geogebra.common.main.settings.EuclidianSettings3D; import org.geogebra.common.plugin.EuclidianStyleConstants; import org.geogebra.common.util.NumberFormatAdapter; import org.geogebra.common.util.debug.Log; /** * Class for 3D view * * @author mathieu * */ @SuppressWarnings("javadoc") public abstract class EuclidianView3D extends EuclidianView implements EuclidianView3DInterface, ScalerXYZ { // since V3.0 this factor is 1, before it was 0.5 final public static double DEFAULT_GRID_DIST_FACTOR = 1; /** default scene x-coord of origin */ public static final double XZERO_SCENE_STANDARD = 0; /** default scene y-coord of origin */ public static final double YZERO_SCENE_STANDARD = 0; /** default scene z-coord of origin */ public static final double ZZERO_SCENE_STANDARD = -1.5; public final static double ANGLE_ROT_OZ = -60; public final static double ANGLE_ROT_XOY = 20; /** * number of drawables linked to this view (xOy plane, Ox, Oy, Oz axis) */ static final public int DRAWABLES_NB = 4; /** * no point under the cursor */ public static final int PREVIEW_POINT_NONE = 0; /** * free point under the cursor */ public static final int PREVIEW_POINT_FREE = 1; /** * path point under the cursor */ public static final int PREVIEW_POINT_PATH = 2; /** * region point under the cursor */ public static final int PREVIEW_POINT_REGION = 3; /** * dependent point under the cursor */ public static final int PREVIEW_POINT_DEPENDENT = 4; /** * already existing point under the cursor */ public static final int PREVIEW_POINT_ALREADY = 5; /** * region as path (e.g. quadric as line) point under the cursor */ public static final int PREVIEW_POINT_REGION_AS_PATH = 6; final static public int PROJECTION_ORTHOGRAPHIC = 0; final static public int PROJECTION_PERSPECTIVE = 1; final static public int PROJECTION_GLASSES = 2; // DrawList3D(); final static public int PROJECTION_OBLIQUE = 3; // DrawList3D(); final static public int PROJECTION_EQUIRECTANGULAR = 4; public static final int CURSOR_DEFAULT = 0; private static final int PROJECTION_PERSPECTIVE_EYE_DISTANCE_DEFAULT = 2500; // maximum angle between two line segments private static final double MAX_ANGLE_SPEED_SURFACE = 20; // degrees private static final double MAX_BEND_SPEED_SURFACE = Math .tan(MAX_ANGLE_SPEED_SURFACE * Kernel.PI_180); public static double automaticGridDistanceFactor = DEFAULT_GRID_DIST_FACTOR; protected Renderer renderer; // viewing values protected double zZero; protected double zZeroOld = 0; protected double aOld, bOld; // picking and hits protected Hits3D hits = new Hits3D(); // objects picked from openGL protected DrawClippingCube3D clippingCubeDrawable; protected GeoPoint3D cursorOnXOYPlane; protected CoordMatrix rotationAndScaleMatrix; // EuclidianViewInterface protected Coords pickPoint = new Coords(0, 0, 0, 1); protected CoordMatrix4x4 tmpMatrix4x4_3 = CoordMatrix4x4.Identity(); protected Coords tmpCoords1 = new Coords(4), tmpCoords2 = new Coords(4); protected GColor bgColor, bgApplyedColor; // cursor // private Kernel kernel; private Kernel3D kernel3D; // list of 3D objects private boolean waitForUpdate = true; // says if it waits for update... // public boolean waitForPick = false; //says if it waits for update... private Drawable3DListsForView drawable3DLists;// = new DrawList3D(); /** * list for drawables that will be added on next frame */ private LinkedList<Drawable3D> drawable3DListToBeAdded;// = new /** * list for drawables that will be removed on next frame */ private LinkedList<Drawable3D> drawable3DListToBeRemoved;// = new /** * list for Geos to that will be added on next frame */ private TreeSet<GeoElement> geosToBeAdded; // Map (geo, drawable) for GeoElements and Drawables private TreeMap<GeoElement, Drawable3D> drawable3DMap = new TreeMap<GeoElement, Drawable3D>(); // matrix for changing coordinate system private CoordMatrix4x4 mWithoutScale = CoordMatrix4x4.Identity(); private CoordMatrix4x4 mWithScale = CoordMatrix4x4.Identity(); private CoordMatrix4x4 mInvWithUnscale = CoordMatrix4x4.Identity(); private CoordMatrix4x4 mInvTranspose = CoordMatrix4x4.Identity(); private CoordMatrix4x4 undoRotationMatrix = CoordMatrix4x4.Identity(); private double a = ANGLE_ROT_OZ; private double b = ANGLE_ROT_XOY;// angles (in degrees) // animation private double aNew, bNew; /** * direction of view */ private Coords viewDirection = Coords.VZ.copyVector(); private Coords eyePosition = new Coords(4); // axis and xOy plane private GeoPlane3DConstant xOyPlane; private GeoAxisND[] axis; private GeoClippingCube3D clippingCube; private DrawPlane3D xOyPlaneDrawable; private DrawAxis3D[] axisDrawable; // point decorations private DrawPointDecorations pointDecorations; // preview private Previewable previewDrawable; private GeoPoint3D cursor3D; private int cursor3DType = PREVIEW_POINT_NONE; private EuclidianCursor cursor = EuclidianCursor.DEFAULT; /** starting and ending scales */ private double xScaleEnd, yScaleEnd, zScaleStart, zScaleEnd; /** velocity of animated scaling */ private double animatedScaleTimeFactor; /** starting time for animated scale */ private double animatedScaleTimeStart; /** x start of animated scale */ private double animatedScaleStartX; /** y start of animated scale */ private double animatedScaleStartY; /** z start of animated scale */ private double animatedScaleStartZ; /** x end of animated scale */ private double animatedScaleEndX; /** y end of animated scale */ private double animatedScaleEndY; /** z end of animated scale */ private double animatedScaleEndZ; /** speed for animated rotation */ private double animatedRotSpeed; /** starting time for animated rotation */ private double animatedRotTimeStart; private CoordMatrix4x4 scaleMatrix = CoordMatrix4x4.Identity(); private CoordMatrix4x4 undoScaleMatrix = CoordMatrix4x4.Identity(); private CoordMatrix4x4 translationMatrixWithScale = CoordMatrix4x4 .Identity(); private CoordMatrix4x4 translationMatrixWithoutScale = CoordMatrix4x4 .Identity(); private CoordMatrix4x4 undoTranslationMatrix = CoordMatrix4x4.Identity(); private CoordMatrix rotationMatrix; private Coords viewDirectionPersp = new Coords(4); private Coords tmpCoordsLength3 = new Coords(3); private int intersectionThickness; private GeoPointND intersectionPoint; private CoordMatrix4x4 tmpMatrix4x4 = CoordMatrix4x4.Identity(); private boolean defaultCursorWillBeHitCursor = false; private double[] parameters = new double[2]; private boolean viewChangedByZoom = true; private boolean viewChangedByTranslate = true; private boolean viewChangedByRotate = true; private int pointStyle; private int projection = PROJECTION_ORTHOGRAPHIC; private double[] projectionPerspectiveEyeDistance = { PROJECTION_PERSPECTIVE_EYE_DISTANCE_DEFAULT, PROJECTION_PERSPECTIVE_EYE_DISTANCE_DEFAULT }; private boolean isGlassesGrayScaled = true; private boolean isGlassesShutDownGreen = false; private double[] eyeX = { -100, 100 }, eyeY = { 0, 0 }; private double projectionObliqueAngle = 30; private double projectionObliqueFactor = 0.5; private Coords boundsMin, boundsMax; private double fontScale = 1; /** * common constructor * * @param ec * controller on this */ public EuclidianView3D(EuclidianController3D ec, EuclidianSettings settings) { super(ec, EVNO_3D, settings); // don't remove, it's important we pick up when this class is created by // mistake Log.error( "******************************************************************************"); Log.error( "******************* 3D View being initialized ********************************"); Log.error( "******************************************************************************"); // Log.printStacktrace(""); this.kernel3D = (Kernel3D) ec.getKernel(); euclidianController.setView(this); startPos = new Coords(4); startPos.setW(1); start(); } final private static void changeCoords(CoordMatrix mat, Coords vInOut) { Coords v1 = vInOut.getCoordsLast1(); vInOut.set(mat.mul(v1)); } /** * 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 void intervalUnion(double[] minmax, double v1, double v2) { // Log.debug(v1+","+v2); if (Double.isNaN(v2)) { return; } double vMin = v1; double vMax = v2; if (v1 > v2) { vMax = v1; vMin = v2; } if (vMin < minmax[0] && !Double.isInfinite(vMin)) { minmax[0] = vMin; } if (vMax > minmax[1] && !Double.isInfinite(vMax)) { minmax[1] = vMax; } } @Override protected void initAxesValues() { axesNumberFormat = new NumberFormatAdapter[3]; showAxesNumbers = new boolean[] { true, true, true }; axesLabels = new String[] { null, null, null }; axesLabelsStyle = new int[] { GFont.PLAIN, GFont.PLAIN, GFont.PLAIN }; axesUnitLabels = new String[] { null, null, null }; axesTickStyles = new int[] { EuclidianStyleConstants.AXES_TICK_STYLE_MAJOR, EuclidianStyleConstants.AXES_TICK_STYLE_MAJOR, EuclidianStyleConstants.AXES_TICK_STYLE_MAJOR }; automaticAxesNumberingDistances = new boolean[] { true, true, true }; axesNumberingDistances = new double[] { 2, 2, 2 }; axesDistanceObjects = new GeoNumberValue[] { null, null, null }; drawBorderAxes = new boolean[] { false, false, false }; axisCross = new double[] { 0, 0, 0 }; positiveAxes = new boolean[] { false, false, false }; piAxisUnit = new boolean[] { false, false, false }; gridDistances = new double[] { 2, 2, Math.PI / 6 }; axesTickInterval = new double[] { 1, 1, 1 }; } public int getAxisTickStyle(int i) { return axesTickStyles[i]; } /** * create the panel */ abstract protected void createPanel(); abstract protected Renderer createRenderer(); protected void start() { drawable3DLists = new Drawable3DListsForView(this); drawable3DListToBeAdded = new LinkedList<Drawable3D>(); drawable3DListToBeRemoved = new LinkedList<Drawable3D>(); geosToBeAdded = new TreeSet<GeoElement>(); Log.debug("create gl renderer"); renderer = createRenderer(); if (renderer == null) { return; } renderer.setDrawable3DLists(drawable3DLists); createExportToPrinter3D(); createPanel(); attachView(); initAxisAndPlane(); kernel3D.getConstruction().setIgnoringNewTypes(true); // previewables cursor3D = new GeoPoint3D(kernel3D.getConstruction()); cursor3D.setCoords(0, 0, 0, 1); cursor3D.setIsPickable(false); // cursor3D.setLabelOffset(5, -5); // cursor3D.setEuclidianVisible(false); cursor3D.setMoveNormalDirection(Coords.VZ); cursorOnXOYPlane = new GeoPoint3D(kernel3D.getConstruction()); cursorOnXOYPlane.setCoords(0, 0, 0, 1); cursorOnXOYPlane.setIsPickable(false); cursorOnXOYPlane.setMoveNormalDirection(Coords.VZ); cursorOnXOYPlane.setRegion(xOyPlane); cursorOnXOYPlane.setMoveMode(GeoPointND.MOVE_MODE_XY); kernel3D.getConstruction().setIgnoringNewTypes(false); // point decorations initPointDecorations(); // tells the renderer if use clipping cube updateUseClippingCube(); } protected void createExportToPrinter3D() { // not implemented here } /** * init the axis and xOy plane */ final public void initAxisAndPlane() { // axis axis = new GeoAxisND[3]; axisDrawable = new DrawAxis3D[3]; axis[0] = kernel3D.getXAxis3D(); axis[1] = kernel3D.getYAxis3D(); axis[2] = kernel3D.getZAxis3D(); for (int i = 0; i < 3; i++) { axis[i].setLabelVisible(true); axisDrawable[i] = (DrawAxis3D) createDrawable((GeoElement) axis[i]); } Construction cons = kernel3D.getConstruction(); // clipping cube clippingCube = (GeoClippingCube3D) cons.getClippingCube(); clippingCube.setEuclidianVisible(true); clippingCube.setObjColor(GColor.GRAY); clippingCube.setLineThickness(1); clippingCube.setIsPickable(false); clippingCubeDrawable = (DrawClippingCube3D) createDrawable( clippingCube); // plane xOyPlane = (GeoPlane3DConstant) cons.getXOYPlane(); xOyPlane.setEuclidianVisible(true); xOyPlane.setGridVisible(true); xOyPlane.setPlateVisible(true); // xOyPlane.setFading(0); xOyPlaneDrawable = (DrawPlane3D) createDrawable(xOyPlane); // companion getCompanion().initAxisAndPlane(); } // POINT_CAPTURING_STICKY_POINTS locks onto these points // not implemented yet in 3D @Override public ArrayList<GeoPointND> getStickyPointList() { return new ArrayList<GeoPointND>(); } /** * return the 3D kernel * * @return the 3D kernel */ @Override public Kernel3D getKernel() { return kernel3D; } /** * @return gl renderer */ public Renderer getRenderer() { return renderer; } /** * adds a GeoElement3D to this view */ @Override public void add(GeoElement geo) { if (geo.isVisibleInView3D()) { setWaitForUpdate(); geosToBeAdded.add(geo); } } @Override protected boolean createAndAddDrawable(GeoElement geo) { geosToBeAdded.add(geo); return true; } @Override protected void repaintForPreviewFromInputBar() { setWaitForUpdate(); repaintView(); } /** * add the geo now * * @param geo */ private void addNow(GeoElement geo) { // check if geo has been already added if (getDrawableND(geo) != null) { return; } // create the drawable Drawable3D d = null; d = createDrawable(geo); if (d != null) { drawable3DLists.add(d); } } /** * add the drawable to the lists of drawables * * @param d */ public void addToDrawable3DLists(Drawable3D d) { /* * if (d.getGeoElement().getLabel().equals("a")){ * Application.debug("d="+d); } */ drawable3DListToBeAdded.add(d); } @Override public Drawable3D newDrawable(GeoElement geo) { Drawable3D d = null; if (geo.hasDrawable3D()) { switch (geo.getGeoClassType()) { default: Log.debug("missing case " + geo.getGeoClassType()); break; // 2D also shown in 3D case LIST: d = new DrawList3D(this, (GeoList) geo); break; // 3D stuff case POINT: case POINT3D: d = new DrawPoint3D(this, (GeoPointND) geo); break; case VECTOR: case VECTOR3D: d = new DrawVector3D(this, (GeoVectorND) geo); break; case SEGMENT: case SEGMENT3D: d = new DrawSegment3D(this, (GeoSegmentND) geo); break; case PLANE3D: if (geo instanceof GeoPlane3DConstant) { d = new DrawPlaneConstant3D(this, (GeoPlane3D) geo, axisDrawable[AXIS_X], axisDrawable[AXIS_Y]); } else { d = new DrawPlane3D(this, (GeoPlane3D) geo); } break; case POLYGON: case POLYGON3D: d = new DrawPolygon3D(this, (GeoPolygon) geo); break; case PENSTROKE: case POLYLINE: case POLYLINE3D: d = new DrawPolyLine3D(this, geo); break; case LINE: case LINE3D: d = new DrawLine3D(this, (GeoLineND) geo); break; case RAY: case RAY3D: d = new DrawRay3D(this, (GeoRayND) geo); break; case CONIC: case CONIC3D: d = new DrawConic3D(this, (GeoConicND) geo); break; case CONICPART: d = new DrawConicPart3D(this, (GeoConicPartND) geo); break; case CONICSECTION: d = new DrawConicSection3D(this, (GeoConicSection) geo); break; case AXIS: case AXIS3D: d = new DrawAxis3D(this, (GeoAxisND) geo); break; case FUNCTION: if (((GeoFunction) geo).isBooleanFunction()) { d = newDrawSurface3D((SurfaceEvaluable) geo); } else { d = new DrawCurve3D(this, (CurveEvaluable) geo); } break; case CURVE_CARTESIAN: case CURVE_CARTESIAN3D: d = new DrawCurve3D(this, (CurveEvaluable) geo); break; case LOCUS: d = new DrawLocus3D(this, ((GeoLocusNDInterface) geo).getLocus(), geo, CoordSys.XOY); break; case IMPLICIT_POLY: d = new DrawImplicitCurve3D(this, (GeoImplicit) geo); break; case ANGLE: case ANGLE3D: if (geo.isIndependent()) { // TODO: slider } else { d = new DrawAngle3D(this, (GeoAngle) geo); } break; case QUADRIC: d = new DrawQuadric3D(this, (GeoQuadric3D) geo); break; case QUADRIC_PART: d = new DrawQuadric3DPart(this, (GeoQuadric3DPart) geo); break; case QUADRIC_LIMITED: if (!((GeoQuadric3DLimited) geo).getSide().isLabelSet()) { // create drawable when side is not explicitely created // (e.g. in sequence, or with transformation) d = new DrawQuadric3DLimited(this, (GeoQuadric3DLimited) geo); } break; case POLYHEDRON: d = new DrawPolyhedron3D(this, (GeoPolyhedron) geo); break; case FUNCTION_NVAR: GeoFunctionNVar geoFun = (GeoFunctionNVar) geo; switch (geoFun.getVarNumber()) { default: // do nothing break; case 2: d = newDrawSurface3D(geoFun); break; /* * case 3: d = new DrawImplicitFunction3Var(this, geoFun); * break; */ } break; case SURFACECARTESIAN3D: d = newDrawSurface3D((GeoSurfaceCartesian3D) geo); break; case TEXT: d = new DrawText3D(this, (GeoText) geo); break; case CLIPPINGCUBE3D: d = new DrawClippingCube3D(this, (GeoClippingCube3D) geo); break; case IMPLICIT_SURFACE_3D: d = new DrawImplicitSurface3D(this, (GeoImplicitSurface) geo); } } return d; } final private DrawSurface3D newDrawSurface3D(SurfaceEvaluable surface) { if (renderer.useShaders()) { return new DrawSurface3DElements(this, surface); } return new DrawSurface3D(this, surface); } @Override protected Drawable3D createDrawable(GeoElement geo) { Drawable3D d = newDrawable(geo); if (d != null) { drawable3DMap.put(geo, d); } return d; } /** * converts the vector to scene coords * * @param vInOut * vector */ final public void toSceneCoords3D(Coords vInOut) { changeCoords(mInvWithUnscale, vInOut); } /** * converts the vector to screen coords * * @param vInOut * vector */ final public void toScreenCoords3D(Coords vInOut) { changeCoords(mWithScale, vInOut); } /** * return the matrix : screen coords -> scene coords. * * @return the matrix : screen coords -> scene coords. */ final public CoordMatrix4x4 getToSceneMatrix() { return mInvWithUnscale; } final public CoordMatrix4x4 getToSceneMatrixTranspose() { return mInvTranspose; } /** * return the matrix : scene coords -> screen coords. * * @return the matrix : scene coords -> screen coords. */ final public CoordMatrix4x4 getToScreenMatrix() { return mWithScale; } final public CoordMatrix4x4 getToScreenMatrixForGL() { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return mWithoutScale; } return mWithScale; } /** * return the matrix undoing the rotation : scene coords -> screen coords. * * @return the matrix undoing the rotation : scene coords -> screen coords. */ final public CoordMatrix4x4 getUndoRotationMatrix() { return undoRotationMatrix; } /** * * @return true if y axis is vertical (and not z axis) */ public boolean getYAxisVertical() { return getSettings().getYAxisVertical(); } @Override public void setYAxisVertical(boolean flag) { getSettings().setYAxisVertical(flag); } public boolean getUseLight() { return getSettings().getUseLight(); } private void updateRotationMatrix() { CoordMatrix m1, m2; if (getYAxisVertical()) { // y axis taken for up-down direction m1 = CoordMatrix.rotation3DMatrix(CoordMatrix.X_AXIS, (this.b) * EuclidianController3D.ANGLE_TO_DEGREES); m2 = CoordMatrix.rotation3DMatrix(CoordMatrix.Y_AXIS, (-this.a - 90) * EuclidianController3D.ANGLE_TO_DEGREES); } else { // z axis taken for up-down direction m1 = CoordMatrix.rotation3DMatrix(CoordMatrix.X_AXIS, (this.b - 90) * EuclidianController3D.ANGLE_TO_DEGREES); m2 = CoordMatrix.rotation3DMatrix(CoordMatrix.Z_AXIS, (-this.a - 90) * EuclidianController3D.ANGLE_TO_DEGREES); } rotationMatrix = m1.mul(m2); } // TODO specific scaling for each direction // private double scale = 50; private void updateScaleMatrix() { scaleMatrix.set(1, 1, getXscale()); scaleMatrix.set(2, 2, getYscale()); scaleMatrix.set(3, 3, getZscale()); } protected void updateTranslationMatrix() { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { translationMatrixWithScale.set(1, 4, getXZero() * getXscale()); translationMatrixWithScale.set(2, 4, getYZero() * getYscale()); translationMatrixWithScale.set(3, 4, getZZero() * getZscale()); } translationMatrixWithoutScale.set(1, 4, getXZero()); translationMatrixWithoutScale.set(2, 4, getYZero()); translationMatrixWithoutScale.set(3, 4, getZZero()); } protected void updateRotationAndScaleMatrices() { // rotations updateRotationMatrix(); undoRotationMatrix.set(rotationMatrix.inverse()); // scaling updateScaleMatrix(); undoScaleMatrix.set(1, 1, 1 / getXscale()); undoScaleMatrix.set(2, 2, 1 / getYscale()); undoScaleMatrix.set(3, 3, 1 / getZscale()); rotationAndScaleMatrix = rotationMatrix.mul(scaleMatrix); } /** * @return current rotation matrix */ public CoordMatrix getRotationMatrix() { return rotationMatrix; } protected void setGlobalMatrices() { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { mWithoutScale.setMul(rotationMatrix, translationMatrixWithScale); } mWithScale.setMul(rotationAndScaleMatrix, translationMatrixWithoutScale); mInvWithUnscale.setMul(undoTranslationMatrix, tmpMatrix4x4.setMul(undoScaleMatrix, undoRotationMatrix)); mInvTranspose.setTranspose(mInvWithUnscale); updateEye(); } @Override public void updateMatrix() { // rotations and scaling updateRotationAndScaleMatrices(); // translation updateTranslationMatrix(); updateUndoTranslationMatrix(); // set global matrix and inverse, and eye position setGlobalMatrices(); } protected void updateUndoTranslationMatrix() { undoTranslationMatrix.set(1, 4, -getXZero()); undoTranslationMatrix.set(2, 4, -getYZero()); undoTranslationMatrix.set(3, 4, -getZZero()); } private void updateEye() { // update view direction if (projection == PROJECTION_OBLIQUE) { viewDirection = renderer.getObliqueOrthoDirection().copyVector(); } else { viewDirection = Coords.VZm.copyVector(); } toSceneCoords3D(viewDirection); viewDirection.normalize(); // update eye position if (projection == PROJECTION_ORTHOGRAPHIC || projection == PROJECTION_OBLIQUE) { eyePosition = viewDirection; } else { eyePosition = renderer.getPerspEye().copyVector(); toSceneCoords3D(eyePosition); } } // //////////////////////////////////// // update /** * * @return ortho direction of the eye */ public Coords getViewDirection() { if (projection == PROJECTION_ORTHOGRAPHIC || projection == PROJECTION_OBLIQUE) { return viewDirection; } return viewDirectionPersp; } /** * * @return direction for hitting */ final public Coords getHittingDirection() { return getCompanion().getHittingDirection(); } /** * @return eye position */ @Override public Coords getEyePosition() { return eyePosition; } // //////////////////////////////////// // picking public void shiftRotAboutZ(double da) { setRotXYinDegrees(aOld + da, bOld); updateRotationAndScaleMatrices(); setGlobalMatrices(); setViewChangedByRotate(); setWaitForUpdate(); } @Override public void setRotXYinDegrees(double a, double b) { // Log.debug("setRotXY: "+a+","+b); if (Double.isNaN(a) || Double.isNaN(b)) { Log.error("NaN values for setRotXYinDegrees"); return; } this.a = a; this.b = b; if (this.b > EuclidianController3D.ANGLE_MAX) { this.b = EuclidianController3D.ANGLE_MAX; } else if (this.b < -EuclidianController3D.ANGLE_MAX) { this.b = -EuclidianController3D.ANGLE_MAX; } this.getSettings().setRotXYinDegreesFromView(a, b); } @Override public EuclidianSettings3D getSettings() { return (EuclidianSettings3D) super.getSettings(); } /** * Sets coord system from mouse move */ @Override final public void translateCoordSystemInPixels(int dx, int dy, int dz, int mode) { setXZero(xZeroOld + dx / getSettings().getXscale()); setYZero(yZeroOld - dy / getSettings().getYscale()); setZZero(zZeroOld + dz / getSettings().getZscale()); getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); updateMatrix(); setViewChangedByTranslate(); setWaitForUpdate(); } @Override final public void pageUpDownTranslateCoordSystem(int height) { translateCoordSystemInPixels(0, 0, height / 100, EuclidianController.MOVE_VIEW); } private int mouseMoveDX, mouseMoveDY, mouseMoveMode; /** * Sets coord system from mouse move */ @Override final public void setCoordSystemFromMouseMove(int dx, int dy, int mode) { mouseMoveDX = dx; mouseMoveDY = dy; mouseMoveMode = mode; animationType = AnimationType.MOUSE_MOVE; } final private void processSetCoordSystemFromMouseMove() { switch (mouseMoveMode) { default: // do nothing break; case EuclidianController.MOVE_ROTATE_VIEW: setRotXYinDegrees(aOld - mouseMoveDX, bOld + mouseMoveDY); updateMatrix(); setViewChangedByRotate(); setWaitForUpdate(); break; case EuclidianController.MOVE_VIEW: Coords v = new Coords(mouseMoveDX, -mouseMoveDY, 0, 0); toSceneCoords3D(v); if (cursorOnXOYPlane.getRealMoveMode() == GeoPointND.MOVE_MODE_XY) { v.projectPlaneThruVIfPossible(CoordMatrix4x4.IDENTITY, getViewDirection(), tmpCoords1); setXZero(xZeroOld + tmpCoords1.getX()); setYZero(yZeroOld + tmpCoords1.getY()); } else { v.projectPlaneInPlaneCoords(CoordMatrix4x4.IDENTITY, tmpCoords1); setZZero(zZeroOld + tmpCoords1.getZ()); } getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); updateMatrix(); setViewChangedByTranslate(); setWaitForUpdate(); break; } } private double axisScaleFactor, axisScaleOld; private int axisScaleMode; final public void setCoordSystemFromAxisScale(double factor, double scaleOld, int mode) { axisScaleFactor = factor; axisScaleOld = scaleOld; axisScaleMode = mode; animationType = AnimationType.AXIS_SCALE; } final private void processSetCoordSystemFromAxisScale() { switch (axisScaleMode) { default: // do nothing break; case EuclidianController.MOVE_X_AXIS: setXZero(xZeroOld / axisScaleFactor); getSettings().setXscaleValue(axisScaleFactor * axisScaleOld); break; case EuclidianController.MOVE_Y_AXIS: setYZero(yZeroOld / axisScaleFactor); getSettings().setYscaleValue(axisScaleFactor * axisScaleOld); break; case EuclidianController.MOVE_Z_AXIS: setZZero(zZeroOld / axisScaleFactor); getSettings().setZscaleValue(axisScaleFactor * axisScaleOld); break; } getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); updateMatrix(); setViewChangedByTranslate(); setViewChangedByZoom(); setWaitForUpdate(); } /* * TODO interaction - note : methods are called by * EuclidianRenderer3D.viewOrtho() to re-center the scene */ @Override public double getXZero() { return xZero; } /** * set the x-coord of the origin * * @param val */ public void setXZero(double val) { xZero = val; } @Override public double getYZero() { return yZero; } /** * set the y-coord of the origin * * @param val */ public void setYZero(double val) { yZero = val; } /** * @return the z-coord of the origin */ @Override public double getZZero() { return zZero; } /** * set the z-coord of the origin * * @param val */ public void setZZero(double val) { zZero = val; } @Override public void setZeroFromXML(double x, double y, double z) { if (app.fileVersionBefore(new int[] { 4, 9, 14, 0 })) { // new matrix multiplication (since 4.9.14) updateRotationMatrix(); updateScaleMatrix(); setXZero(x); setYZero(y); setZZero(z); getSettings().updateOriginFromView(x, y, z); updateTranslationMatrix(); CoordMatrix mRS = rotationMatrix.mul(scaleMatrix); CoordMatrix matrix = ((mRS.inverse()) .mul(translationMatrixWithoutScale).mul(mRS)); Coords origin = matrix.getOrigin(); setXZero(origin.getX()); setYZero(origin.getY()); setZZero(origin.getZ()); updateMatrix(); return; } setXZero(x); setYZero(y); setZZero(z); } public double getXRot() { return a; } public double getZRot() { return b; } @Override public double getXmin() { return clippingCubeDrawable.getMinMax()[0][0]; } @Override public double getXmax() { return clippingCubeDrawable.getMinMax()[0][1]; } @Override public double getYmin() { return clippingCubeDrawable.getMinMax()[1][0]; } @Override public double getYmax() { return clippingCubeDrawable.getMinMax()[1][1]; } @Override public double getZmin() { return clippingCubeDrawable.getMinMax()[2][0]; } // //////////////////////////////////////////// // EuclidianViewInterface @Override public double getZmax() { return clippingCubeDrawable.getMinMax()[2][1]; } /** * * @return coords of the center point */ public Coords getCenter() { return clippingCubeDrawable.getCenter(); } /** * @return max value from center to one FRUSTUM edge */ public double getFrustumRadius() { return clippingCubeDrawable.getFrustumRadius(); } /** * @return min value from center to one FRUSTUM face */ public double getFrustumInteriorRadius() { return clippingCubeDrawable.getFrustumInteriorRadius(); } @Override public double getXscale() { return getSettings().getXscale(); } @Override public double getYscale() { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return getSettings().getYscale(); } return getXscale(); } /** * @return the z-scale */ @Override public double getZscale() { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return getSettings().getZscale(); } return getXscale(); } @Override public double getScale(int i) { switch (i) { case 0: default: return getXscale(); case 1: return getYscale(); case 2: return getZscale(); } } @Override protected void setAxesIntervals(double scale, int axis) { super.setAxesIntervals(scale, axis); axisDrawable[axis].setLabelWaitForUpdate(); setWaitForUpdate(); } /** * @return the all-axis scale */ public double getScale() { return getSettings().getXscale(); } public double getMaxScale() { return getSettings().getMaxScale(); } public double getScaledDistance(Coords p1, Coords p2) { tmpCoordsLength3.setSub(p1, p2); scaleXYZ(tmpCoordsLength3); tmpCoordsLength3.calcNorm(); return tmpCoordsLength3.getNorm(); } /** * set the all-axis scale */ final private void setScale(double xscale, double yscale, double zscale) { getSettings().setXscaleValue(xscale); getSettings().setYscaleValue(yscale); getSettings().setZscaleValue(zscale); setViewChangedByZoom(); } /** remembers the origins values (xzero, ...) */ @Override public void rememberOrigins() { super.rememberOrigins(); aOld = a; bOld = b; zZeroOld = getZZero(); zScaleStart = getZscale(); } public void updateAnimation() { if (isAnimated()) { animate(); setWaitForUpdate(); } } /** * update the drawables for 3D view */ public void update() { updateAnimation(); if (waitForUpdate || !drawable3DListToBeRemoved.isEmpty() || !drawable3DListToBeAdded.isEmpty()) { // drawList3D.updateAll(); // I've placed remove() before add(), otherwise when the two lists // contains the same element, the element will NOT be added. ---Tam, // 2011/7/15 drawable3DLists.remove(drawable3DListToBeRemoved); drawable3DListToBeRemoved.clear(); // add drawables (for preview) drawable3DLists.add(drawable3DListToBeAdded); drawable3DListToBeAdded.clear(); // add geos for (GeoElement geo : geosToBeAdded) { addNow(geo); } geosToBeAdded.clear(); viewChangedOwnDrawables(); // setWaitForUpdateOwnDrawables(); waitForUpdate = false; } // update decorations pointDecorations.update(); getCompanion().update(); } @Override public void setWaitForUpdate() { waitForUpdate = true; } // //////////////////////////////////////////////// // ANIMATION // //////////////////////////////////////////////// /** * (x,y) 2D screen coords -> 3D physical coords * * @param x * @param y * @return 3D physical coords of the picking point */ public Coords getPickPoint(GPoint mouse) { setPickPointFromMouse(mouse); if (projection == PROJECTION_PERSPECTIVE || projection == PROJECTION_GLASSES) { viewDirectionPersp = pickPoint.sub(renderer.getPerspEye()); toSceneCoords3D(viewDirectionPersp); viewDirectionPersp.normalize(); } return pickPoint.copyVector(); } final public Coords getHittingOrigin(GPoint mouse) { return getCompanion().getHittingOrigin(mouse); } /** * @param mouse * mouse position * @param result * mouse position with (0,0) on window center */ public void setCenteredPosition(GPoint mouse, GPoint result) { result.x = mouse.getX() + renderer.getLeft(); result.y = -mouse.getY() + renderer.getTop(); } protected void setPickPointFromMouse(GPoint mouse) { getCompanion().setPickPointFromMouse(mouse, pickPoint); } /** * @param p * 3D point in scene coords * @return (x, y) point aligned with p */ public Coords projectOnScreen(Coords p) { Coords p1 = getToScreenMatrix().mul(p);// .getInhomCoords(); if (projection == PROJECTION_PERSPECTIVE || projection == PROJECTION_GLASSES) { Coords eye = renderer.getPerspEye(); Coords v = p1.sub(eye); return new Coords(eye.getX() - eye.getZ() * v.getX() / v.getZ(), eye.getY() - eye.getZ() * v.getY() / v.getZ()); } return new Coords(p1.getX(), p1.getY()); } /** * p scene coords, (dx,dy) 2D mouse move -> 3D physical coords * * @param p * @param dx * @param dy * @return 3D physical coords */ public Coords getPickFromScenePoint(Coords p, int dx, int dy) { Coords point = getToScreenMatrix().mul(p); pickPoint.setX(point.get(1) + dx); pickPoint.setY(point.get(2) - dy); if (projection == PROJECTION_PERSPECTIVE || projection == PROJECTION_GLASSES) { viewDirectionPersp = pickPoint.sub(renderer.getPerspEye()); toSceneCoords3D(viewDirectionPersp); viewDirectionPersp.normalize(); } return pickPoint.copyVector(); } /** * attach the view to the kernel */ @Override public void attachView() { kernel3D.notifyAddAll(this); kernel3D.attach(this); } @Override public void clearView() { // clear lists drawable3DLists.clear(); geosToBeAdded.clear(); drawable3DListToBeAdded.clear(); drawable3DMap.clear(); setRotContinueAnimation(0, 0); initView(false); } @Override protected void initView(boolean repaint) { super.initView(repaint); setBackground(GColor.WHITE); updateMatrix(); } /** * remove a GeoElement3D from this view */ @Override public void remove(GeoElement geo) { if (geo.hasDrawable3D()) { Drawable3D d = drawable3DMap.get(geo); remove(d); } drawable3DMap.remove(geo); geosToBeAdded.remove(geo); repaintView(); } /** * remove the drawable d * * @param d */ public void remove(Drawable3D d) { drawable3DListToBeRemoved.add(d); } @Override public void rename(GeoElement geo) { // TODO auto-generated } /** * says we want a new repaint after current repaint */ public void waitForNewRepaint() { // nothing done here, see EuclidianView3DW } @Override public void reset() { resetAllDrawables(); setViewChanged(); viewChangedOwnDrawables(); setWaitForUpdate(); } @Override public void update(GeoElement geo) { // String s = geo.toString(); if (s.startsWith("F")) // Application.debug(s); if (geo.hasDrawable3D()) { Drawable3D d = drawable3DMap.get(geo); // ((GeoElement3DInterface) geo).getDrawable3D(); // Application.debug(d); if (d != null) { update(d); // update(((GeoElement3DInterface) geo).getDrawable3D()); } } } @Override public void updateVisualStyle(GeoElement geo, GProperty prop) { // Application.debug(geo); if (geo.hasDrawable3D()) { Drawable3D d = drawable3DMap.get(geo); if (d != null) { d.setWaitForUpdateVisualStyle(prop); } } if (styleBar != null) { styleBar.updateVisualStyle(geo); } } @Override public void updateAllDrawables() { for (Drawable3D d : drawable3DMap.values()) { update(d); } setWaitForUpdateOwnDrawables(); } /** * says this drawable to be updated * * @param d */ public void update(Drawable3D d) { d.setWaitForUpdate(); } @Override public DrawableND getDrawableND(GeoElement geo) { if (geo.hasDrawable3D()) { return drawable3DMap.get(geo); } return null; } @Override final public GeoElement getLabelHit(GPoint p, PointerEventType type) { return getCompanion().getLabelHit(p, type); } @Override public Previewable getPreviewDrawable() { return previewDrawable; } @Override public boolean getShowMouseCoords() { // TODO Auto-generated method stub return false; } @Override public void setShowMouseCoords(boolean b) { // TODO Auto-generated method stub } @Override public boolean getShowXaxis() { return axis[AXIS_X].isEuclidianVisible(); } /* * Point pOld = null; * * * public void setHits(Point p) { * * * * if (p.equals(pOld)){ //Application.printStacktrace(""); return; } * * * pOld = p; * * //sets the flag and mouse location for openGL picking * renderer.setMouseLoc(p.x,p.y,Renderer.PICKING_MODE_LABELS); * * //calc immediately the hits renderer.display(); * * * } */ @Override public boolean getShowYaxis() { return axis[AXIS_Y].isEuclidianVisible(); } @Override public boolean setShowAxis(int axis, boolean flag, boolean update) { boolean old = this.axis[axis].isEuclidianVisible(); this.axis[axis].setEuclidianVisible(flag); return flag != old; } @Override public boolean setShowAxes(boolean flag, boolean update) { boolean changedX = setShowAxis(AXIS_X, flag, false); boolean changedY = setShowAxis(AXIS_Y, flag, false); return setShowAxis(AXIS_Z, flag, true) || changedX || changedY; } @Override public void setShowPlate(boolean flag) { getxOyPlane().setPlateVisible(flag); } /** * sets the visibility of xOy plane grid * * @param flag */ @Override public boolean setShowGrid(boolean flag) { boolean changed = getxOyPlane().setGridVisible(flag); xOyPlaneDrawable.setWaitForUpdate(); return changed; } @Override public int getViewHeight() { return getHeight(); } @Override public int getViewWidth() { return getWidth(); } @Override public boolean hitAnimationButton(int x, int y) { return false; } @Override public void resetMode() { // TODO Auto-generated method stub } /** tells if the view is under animation */ public boolean isAnimated() { return animationType != AnimationType.OFF; } /** * tells if the view is under rot animation * * @return true if there is a rotation animation */ public boolean isRotAnimated() { return isRotAnimatedContinue() || animationType == AnimationType.ROTATION; } /** * @return true if there is a continue rotation animation */ public boolean isRotAnimatedContinue() { return animationType == AnimationType.CONTINUE_ROTATION; } /** * * @param p * point * @return true if the point is between min-max coords values */ public boolean isInside(Coords p) { double val = p.getX(); if (val < getXmin() || val > getXmax()) { return false; } val = p.getY(); if (val < getYmin() || val > getYmax()) { return false; } val = p.getZ(); if (val < getZmin() || val > getZmax()) { return false; } return true; } /** * @return true if use of clipping cube */ public boolean useClippingCube() { return getSettings().useClippingCube(); } // /////////////////////////////////////// // previewables @Override public void setUseClippingCube(boolean flag) { getSettings().setUseClippingCube(flag); updateUseClippingCube(); } private void updateUseClippingCube() { renderer.setEnableClipPlanes(useClippingCube()); setViewChanged(); setWaitForUpdate(); } /** * @return true if clipping cube is shown */ public boolean showClippingCube() { return getSettings().showClippingCube(); } /** * sets if the clipping cube is shown * * @param flag * flag */ @Override public void setShowClippingCube(boolean flag) { getSettings().setShowClippingCube(flag); setWaitForUpdate(); } /** * toggle show/hide clipping */ public void toggleShowAndUseClippingCube() { boolean flag = showClippingCube() || useClippingCube(); setShowClippingCube(!flag); setUseClippingCube(!flag); } /** * @return the reduction of the clipping box */ public int getClippingReduction() { return clippingCube.getReduction(); } /** * sets the reduction of the clipping box * * @param value * reduction */ @Override public void setClippingReduction(int value) { clippingCube.setReduction(value); setViewChanged(); setWaitForUpdate(); } @Override public void setAnimatedCoordSystem(double x0, double y0, int steps, boolean storeUndo) { setAnimatedCoordSystem(XZERO_SCENE_STANDARD, YZERO_SCENE_STANDARD, ZZERO_SCENE_STANDARD, SCALE_STANDARD, steps); } private void setAnimatedCoordSystem(double x, double y, double z, double newScale, int steps) { rememberOrigins(); animatedScaleStartX = getXZero(); animatedScaleStartY = getYZero(); animatedScaleStartZ = getZZero(); animatedScaleEndX = x; animatedScaleEndY = y; animatedScaleEndZ = z; animatedScaleTimeStart = app.getMillisecondTime(); xScaleEnd = newScale; yScaleEnd = newScale; zScaleEnd = newScale; animationType = AnimationType.ANIMATED_SCALE; animatedScaleTimeFactor = 0.0003 * steps; } @Override public void setAnimatedCoordSystem(double ox, double oy, double f, double newScale, int steps, boolean storeUndo) { rememberOrigins(); animatedScaleStartX = getXZero(); animatedScaleStartY = getYZero(); animatedScaleStartZ = getZZero(); Coords v; if (getCursor3DType() == PREVIEW_POINT_NONE) { // use cursor only if on // point/path/region or // xOy plane v = new Coords(-animatedScaleStartX, -animatedScaleStartY, -animatedScaleStartZ, 1); // takes center of the scene for // fixed point } else { v = cursor3D.getInhomCoords(); // Log.debug("\n"+v); if (!v.isDefined()) { v = new Coords(-animatedScaleStartX, -animatedScaleStartY, -animatedScaleStartZ, 1); // takes center of the scene // for fixed point } } // Application.debug(v); double factor = getXscale() / newScale; animatedScaleEndX = -v.getX() + (animatedScaleStartX + v.getX()) * factor; animatedScaleEndY = -v.getY() + (animatedScaleStartY + v.getY()) * factor; animatedScaleEndZ = -v.getZ() + (animatedScaleStartZ + v.getZ()) * factor; // Application.debug("mouse = ("+ox+","+oy+")"+"\nscale end = // ("+animatedScaleEndX+","+animatedScaleEndY+")"+"\nZero = // ("+animatedScaleStartX+","+animatedScaleStartY+")"); animatedScaleTimeStart = app.getMillisecondTime(); xScaleEnd = xScaleStart / factor; yScaleEnd = yScaleStart / factor; zScaleEnd = zScaleStart / factor; animationType = AnimationType.ANIMATED_SCALE; animatedScaleTimeFactor = 0.005; // it will take about 1/2s to achieve // it // this.storeUndo = storeUndo; } /** * sets a continued animation for rotation if delay is too long, no * animation if speed is too small, no animation * * @param delay * delay since last drag * @param rotSpeed * speed of rotation */ public void setRotContinueAnimation(double delay, double rotSpeed) { // Log.debug("delay=" + delay + ", rotSpeed=" + rotSpeed); if (Double.isNaN(rotSpeed)) { Log.error("NaN values for setRotContinueAnimation"); stopAnimation(); return; } double rotSpeed2 = rotSpeed; // if last drag occured more than 200ms ago, then no animation if (delay > 200) { return; } // if speed is too small, no animation if (Math.abs(rotSpeed2) < 0.01) { stopAnimation(); return; } // if speed is too large, use max speed if (rotSpeed2 > 0.1) { rotSpeed2 = 0.1; } else if (rotSpeed2 < -0.1) { rotSpeed2 = -0.1; } this.getSettings().setRotSpeed(0); animationType = AnimationType.CONTINUE_ROTATION; animatedRotSpeed = -rotSpeed2; animatedRotTimeStart = app.getMillisecondTime() - delay; bOld = b; aOld = a; } @Override public void setRotAnimation(Coords vn, boolean checkSameValues, boolean animated) { CoordMatrixUtil.sphericalCoords(vn, tmpCoordsLength3); setRotAnimation(tmpCoordsLength3.get(2) * 180 / Math.PI, tmpCoordsLength3.get(3) * 180 / Math.PI, checkSameValues, animated); } @Override public void setRotAnimation(double rotOz, boolean checkSameValues, boolean animated) { setRotAnimation(rotOz * 180 / Math.PI, this.b, checkSameValues, animated); } /** * start a rotation animation to be in the vector direction * * @param vn */ public void setRotAnimation(Coords vn) { setRotAnimation(vn, true, true); } @Override public void setClosestRotAnimation(Coords v, boolean animated) { if (v.dotproduct(getViewDirection()) > 0) { setRotAnimation(v.mul(-1), true, animated); } else { setRotAnimation(v, true, animated); } } /** * rotate to default */ @Override public void setDefaultRotAnimation() { getCompanion().setDefaultRotAnimation(); } /** * start a rotation animation to go to the new values * * @param aN * @param bN * @param checkSameValues * if true, check new values are same than old, in this case * revert the view */ public void setRotAnimation(double aN, double bN, boolean checkSameValues) { setRotAnimation(aN, bN, checkSameValues, true); } public void setRotAnimation(double aN, double bN, boolean checkSameValues, boolean animated) { if (Double.isNaN(aN) || Double.isNaN(bN)) { Log.error("NaN values for setRotAnimation"); return; } // app.storeUndoInfo(); animationType = animated ? AnimationType.ROTATION : AnimationType.ROTATION_NO_ANIMATION; aOld = this.a % 360; bOld = this.b % 360; aNew = aN; bNew = bN; // if (aNew,bNew)=(0degrees,90degrees), then change it to // (90degrees,90degrees) to have correct // xOy orientation if (Kernel.isEqual(aNew, 0, Kernel.STANDARD_PRECISION) && Kernel .isEqual(Math.abs(bNew), 90, Kernel.STANDARD_PRECISION)) { aNew = -90; } // looking for the smallest path if (aOld - aNew > 180) { aOld -= 360; } else if (aOld - aNew < -180) { aOld += 360; } if (checkSameValues) { if (Kernel.isEqual(aOld, aNew, Kernel.STANDARD_PRECISION)) { if (Kernel.isEqual(bOld, bNew, Kernel.STANDARD_PRECISION)) { if (!Kernel.isEqual(Math.abs(bNew), 90, Kernel.STANDARD_PRECISION)) { aNew += 180; } bNew *= -1; // Application.debug("ici"); } } } if (bOld > 180) { bOld -= 360; } animatedRotTimeStart = app.getMillisecondTime(); } /** * stops the animations */ public void stopAnimation() { animationType = AnimationType.OFF; } private enum AnimationType { OFF, ANIMATED_SCALE, SCALE, CONTINUE_ROTATION, ROTATION, ROTATION_NO_ANIMATION, SCREEN_TRANSLATE_AND_SCALE, MOUSE_MOVE, AXIS_SCALE } private AnimationType animationType = AnimationType.OFF; /** * animate the view for changing scale, orientation, etc. */ private void animate() { switch (animationType) { default: // do nothing break; case SCALE: setScale(animatedScaleEndX, animatedScaleEndY, animatedScaleEndZ); updateMatrix(); stopAnimation(); break; case ANIMATED_SCALE: double t; if (animatedScaleTimeFactor == 0) { t = 1; stopAnimation(); } else { t = (app.getMillisecondTime() - animatedScaleTimeStart) * animatedScaleTimeFactor; t += 0.2; // starting at 1/4 if (t >= 1) { t = 1; stopAnimation(); } } // Application.debug("t="+t+"\nscale="+(startScale*(1-t)+endScale*t)); setScale(xScaleStart * (1 - t) + xScaleEnd * t, yScaleStart * (1 - t) + yScaleEnd * t, zScaleStart * (1 - t) + zScaleEnd * t); setXZero(animatedScaleStartX * (1 - t) + animatedScaleEndX * t); setYZero(animatedScaleStartY * (1 - t) + animatedScaleEndY * t); setZZero(animatedScaleStartZ * (1 - t) + animatedScaleEndZ * t); getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); updateMatrix(); setViewChangedByZoom(); setViewChangedByTranslate(); // euclidianController3D.setFlagMouseMoved(); break; case CONTINUE_ROTATION: double da = (app.getMillisecondTime() - animatedRotTimeStart) * animatedRotSpeed; shiftRotAboutZ(da); break; case ROTATION: t = (app.getMillisecondTime() - animatedRotTimeStart) * 0.001; t *= t; // t+=0.2; //starting at 1/4 if (t >= 1) { t = 1; stopAnimation(); } setRotXYinDegrees(aOld * (1 - t) + aNew * t, bOld * (1 - t) + bNew * t); updateMatrix(); setViewChangedByRotate(); break; case ROTATION_NO_ANIMATION: stopAnimation(); setRotXYinDegrees(aNew, bNew); updateMatrix(); setViewChangedByRotate(); break; case SCREEN_TRANSLATE_AND_SCALE: setXZero(xZeroOld + screenTranslateAndScaleDX); setYZero(yZeroOld + screenTranslateAndScaleDY); setZZero(zZeroOld + screenTranslateAndScaleDZ); getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); setScale(xScaleEnd, yScaleEnd, zScaleEnd); updateMatrix(); setViewChangedByZoom(); setViewChangedByTranslate(); setWaitForUpdate(); stopAnimation(); break; case MOUSE_MOVE: processSetCoordSystemFromMouseMove(); stopAnimation(); break; case AXIS_SCALE: processSetCoordSystemFromAxisScale(); stopAnimation(); break; } } @Override public void setHits(GPoint p, PointerEventType type) { renderer.setHits(p, getCapturingThreshold(type)); if (type == PointerEventType.TOUCH && hitsEmptyOrOnlyContainsXOYPlane()) { renderer.setHits(p, getCapturingThresholdForTouch(type)); } hasMouse = true; updateCursor3D(); } private boolean hitsEmptyOrOnlyContainsXOYPlane() { if (hits.size() == 0) { return true; } if (hits.size() == 1) { return hits.get(0) == getxOyPlane(); } return false; } public int getCapturingThreshold(PointerEventType type) { return getCompanion().getCapturingThreshold(type); } public int getCapturingThresholdForTouch(PointerEventType type) { return app.getCapturingThreshold(type) * 3; } public DrawAxis3D getAxisDrawable(int i) { return axisDrawable[i]; } public DrawPlane3D getPlaneDrawable() { return xOyPlaneDrawable; } public Hits3D getHits3D() { return hits; } @Override public Hits getHits() { return hits.cloneHits(); } /** * init the hits for this view * * @param hits */ public void setHits(Hits3D hits) { this.hits = hits; } @Override public void updateCursor(GeoPointND point) { hits.init(); hits.add((GeoElement) point); updateCursor3D(); } @Override public void setSelectionRectangle(GRectangle selectionRectangle) { // TODO Auto-generated method stub } @Override public void setShowAxesRatio(boolean b) { // TODO Auto-generated method stub } // /////////////////////////////////////////////////// // // POINT DECORATION // // /////////////////////////////////////////////////// @Override public void zoom(double px, double py, double zoomFactor, int steps, boolean storeUndo) { animatedScaleEndX = getXscale() * zoomFactor; animatedScaleEndY = getYscale() * zoomFactor; animatedScaleEndZ = getZscale() * zoomFactor; animationType = AnimationType.SCALE; } /** * return the point used for 3D cursor * * @return the point used for 3D cursor */ public GeoPoint3D getCursor3D() { return cursor3D; } // /////////////////////////////////////////////////// // // CURSOR // // /////////////////////////////////////////////////// /** * @return the type of the cursor */ public int getCursor3DType() { return cursor3DType; } /** * sets the type of the cursor * * @param v */ public void setCursor3DType(int v) { cursor3DType = v; // Log.debug(""+v); } public void setIntersectionThickness(GeoElement a, GeoElement b) { int t1 = a.getLineThickness(); int t2 = b.getLineThickness(); if (t2 > t1) { intersectionThickness = t2; } else { intersectionThickness = t1; } intersectionThickness += 6; } public int getIntersectionThickness() { return intersectionThickness; } public GeoPointND getIntersectionPoint() { return intersectionPoint; } public void setIntersectionPoint(GeoPointND point) { intersectionPoint = point; } /** * @return the list of 3D drawables */ public Drawable3DLists getDrawList3D() { return drawable3DLists; } @Override public Previewable createPreviewLine(ArrayList<GeoPointND> selectedPoints) { return new DrawLine3D(this, selectedPoints); } @Override public Previewable createPreviewSegment( ArrayList<GeoPointND> selectedPoints) { return new DrawSegment3D(this, selectedPoints); } @Override public Previewable createPreviewRay(ArrayList<GeoPointND> selectedPoints) { return new DrawRay3D(this, selectedPoints); } @Override public Previewable createPreviewVector( ArrayList<GeoPointND> selectedPoints) { return new DrawVector3D(this, selectedPoints); } @Override public Previewable createPreviewPolygon( ArrayList<GeoPointND> selectedPoints) { return new DrawPolygon3D(this, selectedPoints); } public Previewable createPreviewPyramidOrPrism( ArrayList<GeoPointND> selectedPoints, ArrayList<GeoPolygon> selectedPolygons, int mode) { return new DrawPolyhedron3D(this, selectedPoints, selectedPolygons, mode); } @Override public Previewable createPreviewConic(int mode, ArrayList<GeoPointND> selectedPoints) { return null; } /** * @param selectedPoints * @return a preview sphere (center-point) */ public Previewable createPreviewSphere( ArrayList<GeoPointND> selectedPoints) { return new DrawQuadric3D(this, selectedPoints, GeoQuadricNDConstants.QUADRIC_SPHERE); } /** * @return a preview right prism/cylinder (basis and height) */ public Previewable createPreviewExtrusion( ArrayList<GeoPolygon> selectedPolygons, ArrayList<GeoConicND> selectedConics) { return new DrawExtrusion3D(this, selectedPolygons, selectedConics); } /** * @return a preview pyramid/cone (basis and height) */ public Previewable createPreviewConify( ArrayList<GeoPolygon> selectedPolygons, ArrayList<GeoConicND> selectedConics) { return new DrawConify3D(this, selectedPolygons, selectedConics); } @Override public void updatePreviewable() { if (getCursor3DType() != PREVIEW_POINT_NONE) { previewDrawable.updatePreview(); } } @Override public void updatePreviewableForProcessMode() { if (previewDrawable != null) { updatePreviewable(); } } /** * update the 3D cursor with current hits * * @param hits1 */ public void updateCursor3D(Hits hits1) { if (hasMouse()) { getEuclidianController().updateNewPoint(true, hits1, true, true, getMode() != EuclidianConstants.MODE_MOVE, // TODO // doSingleHighlighting // = false ? false, false); updateCursorOnXOYPlane(); updateMatrixForCursor3D(); } } private void updateCursorOnXOYPlane() { cursorOnXOYPlane.setWillingCoords(getCursor3D().getCoords()); cursorOnXOYPlane.setWillingDirection(getHittingDirection()); cursorOnXOYPlane.doRegion(); // cursorOnXOYPlaneVisible = // isInside(cursorOnXOYPlane.getInhomCoords()); // if (cursorOnXOYPlaneVisible) if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { cursorOnXOYPlane.getDrawingMatrix().setDiag(1); scaleXYZ(cursorOnXOYPlane.getDrawingMatrix().getOrigin()); } else { cursorOnXOYPlane.getDrawingMatrix().setDiag(1 / getScale()); } // Application.debug(cursorOnXOYPlane.getCoords()); // Application.debug(cursorOnXOYPlane.getDrawingMatrix()); } public void switchMoveCursor() { if (moveCursorIsVisible()) { cursorOnXOYPlane.switchMoveMode(getMode()); } } final protected boolean moveCursorIsVisible() { return getCompanion().moveCursorIsVisible(); } /** * update the 3D cursor with current hits */ public void updateCursor3D() { // updateCursor3D(getHits().getTopHits()); // we also want to see different pick orders in preview, e.g. line/plane // intersection // For now we follow the practice of EView2D: we reserve only points if // there are any, // and return the clone if there are no points. // TODO: define this behavior better if (getHits().containsGeoPoint()) { updateCursor3D(getHits().getTopHits()); } else { updateCursor3D(getHits()); } } private CoordMatrix4x4 cursorMatrix = new CoordMatrix4x4(); private Coords cursorNormal = new Coords(3); /** * update cursor3D matrix */ public void updateMatrixForCursor3D() { double t; Coords v; if (getEuclidianController() .getMode() == EuclidianConstants.MODE_VIEW_IN_FRONT_OF) { switch (getCursor3DType()) { default: // do nothing break; case PREVIEW_POINT_REGION: // use region drawing directions for the cross cursorNormal.set3(getCursor3D().getMoveNormalDirection()); if (cursorNormal.dotproduct(getViewDirection()) > 0) { cursorNormal.mulInside(-1); } if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleNormalXYZ(cursorNormal); cursorNormal.normalize(); } CoordMatrix4x4.createOrthoToDirection( getCursor3D().getDrawingMatrix().getOrigin(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleXYZ(cursorMatrix.getOrigin()); } else { // use region drawing directions for the arrow t = 1 / getScale(); cursorMatrix.mulAllButOrigin(t); } break; case PREVIEW_POINT_PATH: // use path drawing directions for the arrow if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); scaleXYZ(cursorMatrix.getOrigin()); cursorNormal.set3(((GeoElement) getCursor3D().getPath()) .getMainDirection()); if (cursorNormal.dotproduct(getViewDirection()) > 0) { cursorNormal.mulInside(-1); } scaleXYZ(cursorNormal); cursorNormal.normalize(); CoordMatrix4x4.createOrthoToDirection( getCursor3D().getDrawingMatrix().getOrigin(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); scaleXYZ(cursorMatrix.getOrigin()); } else { t = 1 / getScale(); cursorNormal.set3(((GeoElement) getCursor3D().getPath()) .getMainDirection()); if (cursorNormal.dotproduct(getViewDirection()) > 0) { cursorNormal.mulInside(-1); } cursorNormal.normalize(); CoordMatrix4x4.createOrthoToDirection( getCursor3D().getDrawingMatrix().getOrigin(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); cursorMatrix.mulAllButOrigin(t); } break; } } else if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D) && moveCursorIsVisible()) { if (cursor != EuclidianCursor.MOVE) { cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); scaleXYZ(cursorMatrix.getOrigin()); switch (cursor) { default: // do nothing break; case RESIZE_X: cursorMatrix.setVx(Coords.VY); cursorMatrix.setVy(Coords.VZ); cursorMatrix.setVz(Coords.VX); break; case RESIZE_Y: cursorMatrix.setVx(Coords.VZ); cursorMatrix.setVy(Coords.VX); cursorMatrix.setVz(Coords.VY); break; case RESIZE_Z: cursorMatrix.setVx(Coords.VX); cursorMatrix.setVy(Coords.VY); cursorMatrix.setVz(Coords.VZ); break; } } } else { switch (getCursor3DType()) { default: // do nothing break; case PREVIEW_POINT_FREE: // use default directions for the cross if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { cursorMatrix.setDiagonal3(1); cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); scaleXYZ(cursorMatrix.getOrigin()); } else { cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); t = 1 / getScale(); cursorMatrix.setVx(Coords.VX.mul(t)); cursorMatrix.setVy(Coords.VY.mul(t)); cursorMatrix.setVz(Coords.VZ.mul(t)); } break; case PREVIEW_POINT_REGION: // use region drawing directions for the cross cursorNormal.set3(getCursor3D().getMoveNormalDirection()); if (cursorNormal.dotproduct(getViewDirection()) > 0) { cursorNormal.mulInside(-1); } if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleNormalXYZ(cursorNormal); cursorNormal.normalize(); CoordMatrix4x4.createOrthoToDirection( getCursor3D().getDrawingMatrix().getOrigin(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); scaleXYZ(cursorMatrix.getOrigin()); } else { // use region drawing directions for the arrow CoordMatrix4x4.createOrthoToDirection( getCursor3D().getDrawingMatrix().getOrigin(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); t = 1 / getScale(); cursorMatrix.mulAllButOrigin(t); } break; case PREVIEW_POINT_PATH: case PREVIEW_POINT_REGION_AS_PATH: // use path drawing directions for the cross if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); scaleXYZ(cursorMatrix.getOrigin()); GeoElement path = getCursorPath(); cursorNormal.set3(path.getMainDirection()); scaleXYZ(cursorNormal); cursorNormal.normalize(); CoordMatrix4x4.completeOrtho(cursorNormal, tmpCoords1, tmpCoords2, cursorMatrix); t = 10 + path.getLineThickness(); cursorMatrix.getVy().mulInside3(t); cursorMatrix.getVz().mulInside3(t); } else { cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); t = 1 / getScale(); GeoElement path = getCursorPath(); v = path.getMainDirection(); CoordMatrix4x4.completeOrtho(v, tmpCoords1, tmpCoords2, cursorMatrix); cursorMatrix .setVx(cursorMatrix.getVx().normalized().mul(t)); t *= (10 + path.getLineThickness()); cursorMatrix.setVy(cursorMatrix.getVy().mul(t)); cursorMatrix.setVz(cursorMatrix.getVz().mul(t)); } break; case PREVIEW_POINT_DEPENDENT: // use size of intersection cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleXYZ(cursorMatrix.getOrigin()); } t = unscale(getIntersectionThickness()); cursorMatrix.getVx().setMul(Coords.VX, t); cursorMatrix.getVy().setMul(Coords.VY, t); cursorMatrix.getVz().setMul(Coords.VZ, t); break; case PREVIEW_POINT_ALREADY: if (getCursor3D().isPointOnPath()) { cursorNormal.set3(((GeoElement) getCursor3D().getPath()) .getMainDirection()); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleXYZ(cursorNormal); cursorNormal.normalize(); } CoordMatrix4x4.completeOrtho(cursorNormal, tmpCoords1, tmpCoords2, tmpMatrix4x4); cursorMatrix.setVx(tmpMatrix4x4.getVy()); cursorMatrix.setVy(tmpMatrix4x4.getVz()); cursorMatrix.setVz(tmpMatrix4x4.getVx()); cursorMatrix.setOrigin(tmpMatrix4x4.getOrigin()); } else if (getCursor3D().hasRegion()) { cursorNormal.set3(getCursor3D().getMoveNormalDirection()); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleNormalXYZ(cursorNormal); cursorNormal.normalize(); } CoordMatrix4x4.createOrthoToDirection( getCursor3D().getCoordsInD3(), cursorNormal, CoordMatrix4x4.VZ, tmpCoords1, tmpCoords2, cursorMatrix); } else { CoordMatrix4x4.Identity(cursorMatrix); } cursorMatrix.setOrigin( getCursor3D().getDrawingMatrix().getOrigin()); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { scaleXYZ(cursorMatrix.getOrigin()); } cursorMatrix.getVx().normalize(); // use size of point t = unscale( Math.max(1, getCursor3D().getPointSize() / 6.0 + 0.5)); cursorMatrix.getVx().mulInside3(t); cursorMatrix.getVy().mulInside3(t); cursorMatrix.getVz().mulInside3(t); break; } } // Application.debug("getCursor3DType()="+getCursor3DType()); } public Coords getCursorNormal() { return cursorNormal; } // /////////////////////////////////////////////////// // // EUCLIDIANVIEW DRAWABLES (AXIS AND PLANE) // // /////////////////////////////////////////////////// private GeoElement getCursorPath() { if (getCursor3DType() == PREVIEW_POINT_PATH) { return (GeoElement) getCursor3D().getPath(); } // PREVIEW_POINT_REGION_AS_PATH return (GeoElement) getCursor3D().getRegion(); } @Override public void setPreview(Previewable previewDrawable) { // Log.debug(""+previewDrawable); if (this.previewDrawable == previewDrawable) { return; } if (this.previewDrawable != null) { this.previewDrawable.disposePreview(); } if (previewDrawable instanceof Drawable3D) { if (((Drawable3D) previewDrawable).getGeoElement() != null) { addToDrawable3DLists((Drawable3D) previewDrawable); // drawable3DLists.add((Drawable3D) previewDrawable); } } // Application.debug("drawList3D :\n"+drawList3D); // setCursor3DType(PREVIEW_POINT_NONE); this.previewDrawable = previewDrawable; } private void initPointDecorations() { // Application.debug("hop"); pointDecorations = new DrawPointDecorations(this); } /** * update decorations for localizing point in the space * */ public void updatePointDecorations() { pointDecorations.setWaitForUpdate(); } /** * set point for point decorations for localizing point in the space. If * point==null, no decoration will be drawn * * @param point */ public void setPointDecorations(GeoPointND point) { pointDecorations.setPoint(point); } /** * draws the mouse cursor (for glasses) * * @param renderer1 * renderer */ final public void drawMouseCursor(Renderer renderer1) { getCompanion().drawMouseCursor(renderer1); } /** * draw mouse cursor for location v * * @param renderer1 * renderer * @param v * location */ public void drawMouseCursor(Renderer renderer1, Coords v) { CoordMatrix4x4.Identity(tmpMatrix4x4_3); tmpMatrix4x4_3.setOrigin(v); renderer1.setMatrix(tmpMatrix4x4_3); renderer1.drawMouseCursor(); } protected void drawFreeCursor(Renderer renderer1) { getCompanion().drawFreeCursor(renderer1); } protected void drawTranslateViewCursor(Renderer renderer1) { getCompanion().drawTranslateViewCursor(renderer1, cursor, cursorOnXOYPlane, cursorMatrix); } /** * draws the cursor * * @param renderer1 * renderer */ public void drawCursor(Renderer renderer1) { // Log.debug("\nhasMouse=" // + hasMouse // + "\n!getEuclidianController().mouseIsOverLabel() " // + !getEuclidianController().mouseIsOverLabel() // + // "\ngetEuclidianController().cursor3DVisibleForCurrentMode(getCursor3DType())" // + ((EuclidianController3D) getEuclidianController()) // .cursor3DVisibleForCurrentMode(getCursor3DType()) // + "\ncursor=" + cursor + "\ngetCursor3DType()=" // + getCursor3DType()); if (hasMouse()) { // mouse cursor if (moveCursorIsVisible()) { drawTranslateViewCursor(renderer1); } else if (!getEuclidianController().mouseIsOverLabel() && ((EuclidianController3D) getEuclidianController()) .cursor3DVisibleForCurrentMode(getCursor3DType())) { renderer1.setMatrix(cursorMatrix); switch (cursor) { case DEFAULT: switch (getCursor3DType()) { case PREVIEW_POINT_FREE: drawFreeCursor(renderer1); break; case PREVIEW_POINT_ALREADY: // showing arrows directions drawPointAlready(getCursor3D()); break; default: case PREVIEW_POINT_NONE: // do nothing break; } break; case HIT: switch (getCursor3DType()) { default: // do nothing break; case PREVIEW_POINT_FREE: if (getCompanion().drawCrossForFreePoint()) { renderer1.drawCursor(PlotterCursor.TYPE_CROSS2D); } break; case PREVIEW_POINT_REGION: if (getEuclidianController() .getMode() == EuclidianConstants.MODE_VIEW_IN_FRONT_OF) { renderer1.drawViewInFrontOf(); } else { renderer1.drawCursor(PlotterCursor.TYPE_CROSS2D); } break; case PREVIEW_POINT_PATH: case PREVIEW_POINT_REGION_AS_PATH: if (getEuclidianController() .getMode() == EuclidianConstants.MODE_VIEW_IN_FRONT_OF) { renderer1.drawViewInFrontOf(); } else { renderer1.drawCursor(PlotterCursor.TYPE_CYLINDER); } break; case PREVIEW_POINT_DEPENDENT: renderer1.drawCursor(PlotterCursor.TYPE_DIAMOND); break; case PREVIEW_POINT_ALREADY: drawPointAlready(getCursor3D()); break; } break; } } } } /** * @param point * moved point * @return true if it has to draw 2D/1D arrows to move this point */ protected boolean drawCrossForPoint(GeoPoint3D point) { return true; } protected void drawPointAlready(GeoPoint3D point) { getCompanion().drawPointAlready(point); } public void drawPointAlready(int mode) { // Application.debug(mode); int pointMoveMode = mode; if (pointMoveMode == GeoPointND.MOVE_MODE_TOOL_DEFAULT) { pointMoveMode = ((EuclidianController3D) euclidianController) .getPointMoveMode(); } switch (pointMoveMode) { case GeoPointND.MOVE_MODE_XY: renderer.drawCursor(PlotterCursor.TYPE_ALREADY_XY); break; case GeoPointND.MOVE_MODE_Z: renderer.drawCursor(PlotterCursor.TYPE_ALREADY_Z); break; default: // draw nothing break; } } /** * says all drawables owned by the view that the view has changed */ /* * public void viewChangedOwnDrawables(){ * * //xOyPlaneDrawable.viewChanged(); xOyPlaneDrawable.setWaitForUpdate(); * * for(int i=0;i<3;i++) axisDrawable[i].viewChanged(); * * * } */ public void setMoveCursor() { // 3D cursor cursor = EuclidianCursor.MOVE; // Application.printStacktrace(""); // Application.debug("ici"); } public EuclidianCursor getCursor() { return cursor; } final protected boolean cursorIsTranslateViewCursor() { return cursor == EuclidianCursor.MOVE || cursor == EuclidianCursor.RESIZE_X || cursor == EuclidianCursor.RESIZE_Y || cursor == EuclidianCursor.RESIZE_Z; } public EuclidianCursor updateCursorIfNotTranslateViewCursor() { if (!cursorIsTranslateViewCursor()) { EuclidianCursor ret = cursor; Hits hits1 = getHits(); if (hits1 != null && hits1.size() >= 1) { setCursorForTranslateView(hits1); } else { cursor = EuclidianCursor.MOVE; } return ret; } return null; } public void setCursorForTranslateView(Hits hits) { EuclidianCursor old = cursor; if (hits.hasXAxis()) { cursor = EuclidianCursor.RESIZE_X; } else if (hits.hasYAxis()) { cursor = EuclidianCursor.RESIZE_Y; } else if (hits.hasZAxis()) { cursor = EuclidianCursor.RESIZE_Z; } else { cursor = EuclidianCursor.MOVE; } if (cursor != EuclidianCursor.MOVE && cursor != old) { // update may has failed since cursor was not correct type updateCursor3D(); } } public void setCursorForTranslateViewNoHit() { cursor = EuclidianCursor.MOVE; setCursor(EuclidianCursor.DEFAULT); } @Override public void setCursor(EuclidianCursor cursor1) { switch (cursor1) { case HIT: setHitCursor(); return; case DRAG: setDragCursor(); return; case MOVE: setMoveCursor(); return; case DEFAULT: setDefaultCursor(); return; case RESIZE_X: cursor = EuclidianCursor.RESIZE_X; return; case RESIZE_Y: cursor = EuclidianCursor.RESIZE_Y; return; case RESIZE_Z: cursor = EuclidianCursor.RESIZE_Z; return; case RESIZE_NESW: cursor = EuclidianCursor.RESIZE_NESW; return; case RESIZE_NWSE: cursor = EuclidianCursor.RESIZE_NWSE; return; case RESIZE_EW: cursor = EuclidianCursor.RESIZE_EW; return; case RESIZE_NS: cursor = EuclidianCursor.RESIZE_NS; return; case TRANSPARENT: setTransparentCursor(); return; default: setDefaultCursor(); break; } } protected abstract void setTransparentCursor(); /** * next call to setDefaultCursor() will call setHitCursor() instead */ public void setDefaultCursorWillBeHitCursor() { defaultCursorWillBeHitCursor = true; } public void setDragCursor() { // 2D cursor is invisible // setCursor(app.getTransparentCursor()); // 3D cursor cursor = EuclidianCursor.DRAG; // Application.printStacktrace("setDragCursor"); } /** * @return true if shift key is down */ abstract protected boolean getShiftDown(); public void setDefaultCursor() { // App.printStacktrace("setDefaultCursor:"+defaultCursorWillBeHitCursor); if (getShiftDown()) { return; } if (defaultCursorWillBeHitCursor) { defaultCursorWillBeHitCursor = false; setHitCursor(); return; } // 2D cursor if (getProjection() == PROJECTION_GLASSES) { setCursor(EuclidianCursor.TRANSPARENT); // use own 3D cursor // (for depth) // setDefault2DCursor(); } else { setDefault2DCursor(); } // 3D cursor cursor = EuclidianCursor.DEFAULT; } /** * set 2D cursor to default */ abstract protected void setDefault2DCursor(); public void setHitCursor() { if (getShiftDown()) { return; } // App.printStacktrace("setHitCursor"); cursor = EuclidianCursor.HIT; } /** * returns settings in XML format, read by xml handlers * * @return the XML description of 3D view settings * @see org.geogebra.common.io.MyXMLHandler * @see org.geogebra.common.geogebra3D.io.MyXMLHandler3D */ @Override public void getXML(StringBuilder sb, boolean asPreference) { // Application.debug("getXML: "+a+","+b); // if (true) return ""; sb.append("<euclidianView3D>\n"); // coord system sb.append("\t<coordSystem"); sb.append(" xZero=\""); sb.append(getXZero()); sb.append("\" yZero=\""); sb.append(getYZero()); sb.append("\" zZero=\""); sb.append(getZZero()); sb.append("\""); sb.append(" scale=\""); sb.append(getXscale()); sb.append("\""); if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D) && !getSettings().hasSameScales()) { sb.append(" yscale=\""); sb.append(getYscale()); sb.append("\""); sb.append(" zscale=\""); sb.append(getZscale()); sb.append("\""); } sb.append(" xAngle=\""); sb.append(b); sb.append("\" zAngle=\""); sb.append(a); sb.append("\"/>\n"); // ev settings sb.append("\t<evSettings axes=\""); sb.append(getShowAxis(0) || getShowAxis(1) || getShowAxis(2)); sb.append("\" grid=\""); sb.append(getShowGrid()); sb.append("\" gridIsBold=\""); // sb.append(gridIsBold); // Michael Borcherds 2008-04-11 sb.append("\" pointCapturing=\""); // make sure POINT_CAPTURING_STICKY_POINTS isn't written to XML sb.append( getPointCapturingMode() > EuclidianStyleConstants.POINT_CAPTURING_XML_MAX ? EuclidianStyleConstants.POINT_CAPTURING_DEFAULT : getPointCapturingMode()); sb.append("\" rightAngleStyle=\""); sb.append(getApplication().rightAngleStyle); // if (asPreference) { // sb.append("\" allowShowMouseCoords=\""); // sb.append(getAllowShowMouseCoords()); // // sb.append("\" allowToolTips=\""); // sb.append(getAllowToolTips()); // // sb.append("\" deleteToolSize=\""); // sb.append(getEuclidianController().getDeleteToolSize()); // } // sb.append("\" checkboxSize=\""); // sb.append(app.getCheckboxSize()); // Michael Borcherds // 2008-05-12 sb.append("\" gridType=\""); sb.append(getGridType()); // cartesian/isometric/polar // if (lockedAxesRatio != null) { // sb.append("\" lockedAxesRatio=\""); // sb.append(lockedAxesRatio); // } sb.append("\"/>\n"); // end ev settings // axis settings for (int i = 0; i < 3; i++) { this.getSettings().addAxisXML(i, sb); } // xOy plane settings sb.append("\t<plate show=\""); sb.append(getxOyPlane().isPlateVisible()); sb.append("\"/>\n"); // // sb.append("\t<grid show=\""); // sb.append(getxOyPlane().isGridVisible()); // sb.append("\"/>\n"); // background color sb.append("\t<bgColor r=\""); sb.append(bgColor.getRed()); sb.append("\" g=\""); sb.append(bgColor.getGreen()); sb.append("\" b=\""); sb.append(bgColor.getBlue()); sb.append("\"/>\n"); // y axis is up if (getYAxisVertical()) { sb.append("\t<yAxisVertical val=\"true\"/>\n"); } // use light if (!getUseLight()) { sb.append("\t<light val=\"false\"/>\n"); } // clipping cube sb.append("\t<clipping use=\""); sb.append(useClippingCube()); sb.append("\" show=\""); sb.append(showClippingCube()); sb.append("\" size=\""); sb.append(getClippingReduction()); sb.append("\"/>\n"); // projection sb.append("\t<projection type=\""); sb.append(getProjection()); getXMLForStereo(sb); if (!Kernel.isEqual(projectionObliqueAngle, EuclidianSettings3D.PROJECTION_OBLIQUE_ANGLE_DEFAULT)) { sb.append("\" obliqueAngle=\""); sb.append(projectionObliqueAngle); } if (!Kernel.isEqual(projectionObliqueFactor, EuclidianSettings3D.PROJECTION_OBLIQUE_FACTOR_DEFAULT)) { sb.append("\" obliqueFactor=\""); sb.append(projectionObliqueFactor); } sb.append("\"/>\n"); // axes label style int style = getSettings().getAxisFontStyle(); if (style == GFont.BOLD || style == GFont.ITALIC || style == GFont.BOLD + GFont.ITALIC) { sb.append("\t<labelStyle axes=\""); sb.append(style); sb.append("\"/>\n"); } // end sb.append("</euclidianView3D>\n"); } final protected void getXMLForStereo(StringBuilder sb) { int eyeDistance = (int) projectionPerspectiveEyeDistance[0]; int sep = (int) getEyeSep(); getCompanion().getXMLForStereo(sb, eyeDistance, sep); } /** * toggle the visibility of axes */ public void toggleAxis() { getSettings().setShowAxes(!axesAreAllVisible()); } /** * says if all axes are visible * * @return true if all axes are visible */ public boolean axesAreAllVisible() { boolean flag = true; for (int i = 0; i < 3; i++) { flag = (flag && axis[i].isEuclidianVisible()); } return flag; } // //////////////////////////////////////////////////// // AXES // //////////////////////////////////////////////////// /** * @return true if show xOy plane */ public boolean getShowPlane() { return xOyPlane.isPlateVisible(); } @Override public void setShowPlane(boolean flag) { getxOyPlane().setEuclidianVisible(flag); } /** * toggle the visibility of xOy grid */ public void toggleGrid() { getSettings().showGrid(!getShowGrid()); } public GeoPlane3DConstant getxOyPlane() { return xOyPlane; } /** * says if this geo is owned by the view (xOy plane, ...) * * @param geo * @return if this geo is owned by the view (xOy plane, ...) */ public boolean owns(GeoElement geo) { boolean ret = (geo == xOyPlane); for (int i = 0; (!ret) && (i < 3); i++) { ret = (geo == axis[i]); } return ret; } /** * draw transparent parts of view's drawables (xOy plane) * * @param renderer */ public void drawTransp(Renderer renderer1) { if (xOyPlane.isPlateVisible()) { xOyPlaneDrawable.drawTransp(renderer1); } getCompanion().drawTransp(renderer1); } /** * draw hiding parts of view's drawables (xOy plane) * * @param renderer */ public void drawHiding(Renderer renderer1) { xOyPlaneDrawable.drawHiding(renderer1); getCompanion().drawHiding(renderer1); } /** * draw not hidden parts of view's drawables (axis) * * @param renderer1 */ public void draw(Renderer renderer1) { for (int i = 0; i < 3; i++) { axisDrawable[i].drawOutline(renderer1); } if (showClippingCube()) { clippingCubeDrawable.drawOutline(renderer1); } getCompanion().draw(renderer1); } // /////////////////////////// // OPTIONS // ////////////////////////// /** * draw hidden parts of view's drawables (axis) * * @param renderer1 */ public void drawHidden(Renderer renderer1) { for (int i = 0; i < 3; i++) { axisDrawable[i].drawHidden(renderer1); } xOyPlaneDrawable.drawHidden(renderer1); if (showClippingCube()) { clippingCubeDrawable.drawHidden(renderer1); } if (getCompanion().decorationVisible()) { pointDecorations.drawHidden(renderer1); } getCompanion().drawHidden(renderer1); } public DrawPointDecorations getPointDecorations() { return pointDecorations; } /** * draw ticks on axis * * @param renderer1 */ public void drawLabel(Renderer renderer1) { for (int i = 0; i < 3; i++) { axisDrawable[i].drawLabel(renderer1); } } /** * tell all drawables owned by the view to be udpated */ private void setWaitForUpdateOwnDrawables() { xOyPlaneDrawable.setWaitForUpdate(); for (int i = 0; i < 3; i++) { axisDrawable[i].setWaitForUpdate(); } clippingCubeDrawable.setWaitForUpdate(); } /** * says all labels owned by the view that the view has changed */ public void resetOwnDrawables() { xOyPlaneDrawable.setWaitForReset(); for (int i = 0; i < 3; i++) { axisDrawable[i].setWaitForReset(); } pointDecorations.setWaitForReset(); clippingCubeDrawable.setWaitForReset(); getCompanion().resetOwnDrawables(); } /** * says all labels to be recomputed */ public void resetAllDrawables() { resetOwnDrawables(); drawable3DLists.resetAllDrawables(); } /** * reset all drawables visual styles */ public void resetAllVisualStyles() { // own drawables xOyPlaneDrawable.setWaitForUpdateVisualStyle(null); for (int i = 0; i < 3; i++) { axisDrawable[i].setWaitForUpdateVisualStyle(null); } pointDecorations.setWaitForUpdateVisualStyle(null); // other drawables drawable3DLists.resetAllVisualStyles(); getCompanion().resetAllVisualStyles(); } /** * @param i * index * @return i-th vertex of the clipping cube */ public Coords getClippingVertex(int i) { return clippingCubeDrawable.getVertex(i); } /** * update minmax to fit minimum interval that is outside clipping, i.e. the * clipping box is between the two orthogonal planes to the (o,v) line, * through min and max parameters * * @param minmax * initial and returned min/max values * @param o * line origin * @param v * line direction */ public void getMinIntervalOutsideClipping(double[] minmax, Coords o, Coords v) { Coords p1, p2; // check 4 x opposite corners p1 = clippingCubeDrawable.getVertex(0); p2 = clippingCubeDrawable.getVertex(7); intervalUnionOutside(minmax, o, v, p1, p2); p1 = clippingCubeDrawable.getVertex(1); p2 = clippingCubeDrawable.getVertex(6); intervalUnionOutside(minmax, o, v, p1, p2); p1 = clippingCubeDrawable.getVertex(3); p2 = clippingCubeDrawable.getVertex(4); intervalUnionOutside(minmax, o, v, p1, p2); p1 = clippingCubeDrawable.getVertex(2); p2 = clippingCubeDrawable.getVertex(5); intervalUnionOutside(minmax, o, v, p1, p2); } // ////////////////////////////////////// // ALGEBRA VIEW // ////////////////////////////////////// private void intervalUnionOutside(double[] minmax, Coords o, Coords v, Coords p1, Coords p2) { p1.projectLine(o, v, tmpCoords1, parameters); double t1 = parameters[0]; p2.projectLine(o, v, tmpCoords1, parameters); double t2 = parameters[0]; intervalUnion(minmax, t1, t2); } public void updateBounds() { ((Kernel3D) kernel).setEuclidianView3DBounds(evNo, getXmin(), getXmax(), getYmin(), getYmax(), getZmin(), getZmax(), getXscale(), getYscale(), getZscale()); calcPrintingScale(); } @Override public void updateBounds(boolean updateDrawables, boolean updateSettings) { for (int i = 0; i < axesDistanceObjects.length; i++) { if (axesDistanceObjects[i] != null && axesDistanceObjects[i].getDouble() > 0) { axesNumberingDistances[i] = axesDistanceObjects[i].getDouble(); } } updateBounds(); } private void viewChangedOwnDrawables() { // update, but not in case where view changed by rotation if (viewChangedByTranslate() || viewChangedByZoom()) { // update clipping cube double[][] minMax = clippingCubeDrawable.updateMinMax(); // e.g. Corner[] algos are updated by clippingCubeDrawable clippingCubeDrawable.setWaitForUpdate(); // xOy plane wait for update xOyPlaneDrawable.setWaitForUpdate(); // update decorations and wait for update for (int i = 0; i < 3; i++) { axisDrawable[i].setDrawMinMaxImmediatly(minMax); axisDrawable[i].updateDecorations(); setAxesIntervals(getScale(i), i); axisDrawable[i].setWaitForUpdate(); } } if (viewChangedByRotate()) { // we need to update renderer clip planes, since they are in screen // coordinates clippingCubeDrawable.updateRendererClipPlanes(); // we need to update axis numbers locations for (int i = 0; i < 3; i++) { axisDrawable[i].updateDecorations(); axisDrawable[i].setLabelWaitForUpdate(); } // update e.g. Corner[] kernel.notifyEuclidianViewCE(EVProperty.ROTATION); } } // /////////////////////////////////////////////// // UPDATE VIEW : ZOOM, TRANSLATE, ROTATE // /////////////////////////////////////////////// /** * update all drawables now */ public void updateOwnDrawablesNow() { for (int i = 0; i < 3; i++) { axisDrawable[i].update(); } // update xOyPlane xOyPlaneDrawable.update(); clippingCubeDrawable.update(); // update intersection curves in controller ((EuclidianController3D) getEuclidianController()) .updateOwnDrawablesNow(); } public void updateDrawables(Drawable3DListsForView drawables3D) { drawables3D.updateAll(); } public void updateOtherDrawables() { updateDrawables(drawable3DLists); } /** * @param i * index * @return i-th label */ public String getAxisLabel(int i) { return axesLabels[i]; } public GFont getAxisLabelFont(int i) { return getFontLine().deriveFont(axesLabelsStyle[i]); } public String getAxisUnitLabel(int i) { return axesUnitLabels[i]; } public boolean getPiAxisUnit(int i) { return piAxisUnit[i]; } @Override public void setAxesLabels(String[] axesLabels) { this.axesLabels = axesLabels; for (int i = 0; i < 3; i++) { if (axesLabels[i] != null && axesLabels[i].length() == 0) { axesLabels[i] = null; } } } @Override public void setAxisLabel(int axis, String axisLabel) { super.setAxisLabel(axis, axisLabel); axisDrawable[axis].setLabelWaitForUpdate(); setWaitForUpdate(); } @Override public void setAxesUnitLabels(String[] axesUnitLabels) { super.setAxesUnitLabels(axesUnitLabels); setAxesIntervals(getZscale(), 2); for (int i = 0; i < 3; i++) { axisDrawable[i].setLabelWaitForUpdate(); } setWaitForUpdate(); } /** * @param i * index * @return if i-th axis shows numbers */ public boolean getShowAxisNumbers(int i) { return showAxesNumbers[i]; } @Override public Previewable createPreviewParallelLine( ArrayList<GeoPointND> selectedPoints, ArrayList<GeoLineND> selectedLines) { // TODO Auto-generated method stub return null; } @Override public Previewable createPreviewPerpendicularLine( ArrayList<GeoPointND> selectedPoints, ArrayList<GeoLineND> selectedLines) { // TODO Auto-generated method stub return null; } @Override public Previewable createPreviewPerpendicularBisector( ArrayList<GeoPointND> selectedPoints) { // TODO Auto-generated method stub return null; } @Override public Previewable createPreviewAngleBisector( ArrayList<GeoPointND> selectedPoints) { // TODO Auto-generated method stub return null; } @Override public Previewable createPreviewPolyLine( ArrayList<GeoPointND> selectedPoints) { return new DrawPolyLine3D(this, selectedPoints); } public boolean getPositiveAxis(int i) { return positiveAxes[i]; } @Override public void setPositiveAxis(int axis, boolean isPositiveAxis) { super.setPositiveAxis(axis, isPositiveAxis); axisDrawable[axis].setLabelWaitForUpdate(); setWaitForUpdate(); } @Override public boolean getShowGrid() { return xOyPlane.isGridVisible(); } @Override public boolean showGrid(boolean selected) { return setShowGrid(selected); } @Override public void setAutomaticGridDistance(boolean flag) { super.setAutomaticGridDistance(flag); setAxesIntervals(getZscale(), 2); } @Override public int getMode() { return getEuclidianController().getMode(); } // /////////////////////////////////////////////// // PROJECTION (ORTHO/PERSPECTIVE/...) // /////////////////////////////////////////////// protected void setViewChangedByZoom() { viewChangedByZoom = true; } protected void setViewChangedByTranslate() { viewChangedByTranslate = true; } protected void setViewChangedByRotate() { viewChangedByRotate = true; } @Override public void setViewChanged() { setViewChangedByZoom(); setViewChangedByTranslate(); setViewChangedByRotate(); } public boolean viewChangedByZoom() { return viewChangedByZoom; } public boolean viewChangedByTranslate() { return viewChangedByTranslate; } public boolean viewChangedByRotate() { return viewChangedByRotate; } public boolean viewChanged() { return viewChangedByZoom || viewChangedByTranslate || viewChangedByRotate; } public void resetViewChanged() { viewChangedByZoom = false; viewChangedByTranslate = false; viewChangedByRotate = false; } final public int getPointStyle() { return pointStyle; } @Override public String getFromPlaneString() { return "space"; } @Override public String getTranslatedFromPlaneString() { return app.getLocalization().getMenu("space"); } @Override public Previewable createPreviewAngle( ArrayList<GeoPointND> selectedPoints) { // TODO Auto-generated method stub return null; } @Override public boolean isDefault2D() { return false; } @Override public boolean isEuclidianView3D() { return true; } @Override public ArrayList<GeoPointND> getFreeInputPoints(AlgoElement algoParent) { return algoParent.getFreeInputPoints(); } private void setProjectionValues(int projection) { if (this.projection != projection) { this.projection = projection; updateEye(); setViewChanged(); setWaitForUpdate(); // resetAllDrawables(); resetAllVisualStyles(); renderer.setWaitForUpdateClearColor(); } } public int getProjection() { return projection; } @Override public void setProjection(int projection) { switch (projection) { default: case PROJECTION_ORTHOGRAPHIC: setProjectionOrthographic(); break; case PROJECTION_PERSPECTIVE: setProjectionPerspective(); break; case PROJECTION_GLASSES: setProjectionGlasses(); break; case PROJECTION_OBLIQUE: setProjectionOblique(); break; } } public void setProjectionOrthographic() { renderer.setWaitForDisableStencilLines(); renderer.updateOrthoValues(); setProjectionValues(PROJECTION_ORTHOGRAPHIC); setDefault2DCursor(); } public void setProjectionPerspective() { renderer.setWaitForDisableStencilLines(); updateProjectionPerspectiveEyeDistance(); setProjectionValues(PROJECTION_PERSPECTIVE); setDefault2DCursor(); // setTransparentCursor(); } /** * set the near distance regarding eye distance to the screen for * perspective (in pixels) * * @param distance */ public void setProjectionPerspectiveEyeDistance(double distanceLeft, double distanceRight) { projectionPerspectiveEyeDistance[0] = distanceLeft; projectionPerspectiveEyeDistance[1] = distanceRight; if (projection != PROJECTION_PERSPECTIVE && projection != PROJECTION_GLASSES && projection != PROJECTION_EQUIRECTANGULAR) { projection = PROJECTION_PERSPECTIVE; } updateProjectionPerspectiveEyeDistance(); if (projection == PROJECTION_GLASSES || projection == PROJECTION_EQUIRECTANGULAR) { // also update // eyes // separation renderer.updateGlassesValues(); } } final private void updateProjectionPerspectiveEyeDistance() { renderer.setNear(projectionPerspectiveEyeDistance[0], projectionPerspectiveEyeDistance[1]); } /** * * @return eye distance to the screen for perspective */ public double getProjectionPerspectiveEyeDistance() { return projectionPerspectiveEyeDistance[0]; } public void setProjectionGlasses() { updateProjectionPerspectiveEyeDistance(); renderer.updateGlassesValues(); if (getCompanion().isPolarized()) { renderer.setWaitForSetStencilLines(); } else { renderer.setWaitForDisableStencilLines(); } setProjectionValues(PROJECTION_GLASSES); setCursor(EuclidianCursor.TRANSPARENT); } public void setProjectionEquirectangular() { // updateProjectionPerspectiveEyeDistance(); double d = renderer.getVisibleDepth() / 2 + 100; renderer.setNear(d, d); eyeX[1] = 10; eyeX[0] = -eyeX[1]; renderer.updateGlassesValues(); if (getCompanion().isPolarized()) { renderer.setWaitForSetStencilLines(); } else { renderer.setWaitForDisableStencilLines(); } setProjectionValues(PROJECTION_EQUIRECTANGULAR); setCursor(EuclidianCursor.TRANSPARENT); // set view origin setXZero(0); setYZero(0); setZZero(0); // no horizontal angle b = 0; // update updateMatrix(); setViewChangedByTranslate(); setWaitForUpdate(); } public void setEquirectangularAngle(double angle) { // change angle a = angle; // update updateMatrix(); setViewChangedByRotate(); setWaitForUpdate(); } public boolean isGlassesGrayScaled() { return isGlassesGrayScaled; } public void setGlassesGrayScaled(boolean flag) { if (isGlassesGrayScaled == flag) { return; } isGlassesGrayScaled = flag; resetAllDrawables(); } public double getScreenZOffset() { return getCompanion().getScreenZOffset(); } public boolean isGrayScaled() { return projection == PROJECTION_GLASSES && !getCompanion().isPolarized() && !getCompanion().isStereoBuffered() && isGlassesGrayScaled(); } public boolean isGlassesShutDownGreen() { return isGlassesShutDownGreen; } public void setGlassesShutDownGreen(boolean flag) { if (isGlassesShutDownGreen == flag) { return; } isGlassesShutDownGreen = flag; renderer.setWaitForUpdateClearColor(); } public boolean isShutDownGreen() { return projection == PROJECTION_GLASSES && isGlassesShutDownGreen(); } public void setEyes(double leftX, double leftY, double rightX, double rightY) { eyeX[0] = leftX; eyeY[0] = leftY; eyeX[1] = rightX; eyeY[1] = rightY; renderer.updateGlassesValues(); } public double getEyeSep() { return eyeX[1] - eyeX[0]; } public double getEyeX(int i) { return eyeX[i]; } public double getEyeY(int i) { return eyeY[i]; } public boolean isUnitAxesRatio() { // TODO Auto-generated method stub return false; } @Override public int getViewID() { return App.VIEW_EUCLIDIAN3D - evNo - 1; } // //////////////////////////////////////////////////// // // //////////////////////////////////////////////////// public void setProjectionOblique() { renderer.updateProjectionObliqueValues(); renderer.setWaitForDisableStencilLines(); setProjectionValues(PROJECTION_OBLIQUE); setDefault2DCursor(); } public double getProjectionObliqueAngle() { return projectionObliqueAngle; } public void setProjectionObliqueAngle(double angle) { projectionObliqueAngle = angle; renderer.updateProjectionObliqueValues(); } public double getProjectionObliqueFactor() { return projectionObliqueFactor; } public void setProjectionObliqueFactor(double factor) { projectionObliqueFactor = factor; renderer.updateProjectionObliqueValues(); } @Override public boolean getShowAxis(int axisNo) { return this.axis[axisNo].isEuclidianVisible(); } /* * @Override public geogebra.common.awt.GColor getBackgroundCommon() { * return new geogebra.awt.GColorD(getBackground()); * * } */ // //////////////////////////////////// // PICKING @Override public void replaceBoundObject(GeoNumeric num, GeoNumeric geoNumeric) { // TODO fix this for 3D dynamic bounds } public GColor getBackground() { return bgColor; } // //////////////////////////////////// // SOME LINKS WITH 2D VIEW @Override final public void setBackground(GColor color) { getCompanion().setBackground(color); } public GColor getApplyedBackground() { return bgApplyedColor; } // //////////////////////////////////////// // ABSTRACTEUCLIDIANVIEW // //////////////////////////////////////// // //////////////////////////////////////// // EUCLIDIANVIEWND // //////////////////////////////////////// @Override final public GColor getBackgroundCommon() { return getBackground(); } @Override public int getFontSize() { return app.getFontSize(); } @Override public int getEuclidianViewNo() { return getViewID(); } @Override public void setEuclidianViewNo(int evNo) { this.evNo = evNo; } @Override protected void initCursor() { // no normal cursor in 3D } @Override public void setShowAxis(boolean show) { setShowAxis(0, show, false); setShowAxis(1, show, false); setShowAxis(2, show, true); } @Override public GGraphics2D getGraphicsForPen() { return null; } @Override protected void drawResetIcon(GGraphics2D g) { Log.debug("unimplemented"); } public double[] getIntervalClippedLarge(double[] minmax, Coords o, Coords v) { return clippingCubeDrawable.getIntervalClippedLarge(minmax, o, v); } public double[] getIntervalClipped(double[] minmax, Coords o, Coords v) { return clippingCubeDrawable.getIntervalClipped(minmax, o, v); } @Override public boolean isOnView(double[] coords) { // check first x, y if (!super.isOnView(coords)) { return false; } // check z if (coords.length < 3) { // 2D points : z = 0 return (0 >= getZmin()) && (0 <= getZmax()); } // 3D point return (coords[2] >= getZmin()) && (coords[2] <= getZmax()); } @Override public double[] getOnScreenDiff(double[] p1, double[] p2) { double[] ret = new double[p1.length]; ret[0] = (p2[0] - p1[0]) * getXscale(); ret[1] = (p2[1] - p1[1]) * getYscale(); if (ret.length > 2) { ret[2] = (p2[2] - p1[2]) * getZscale(); } return ret; } @Override public boolean isSegmentOffView(double[] p1, double[] p2) { if (super.isSegmentOffView(p1, p2)) { return true; } double tolerance = EuclidianStatic.CLIP_DISTANCE / getZscale(); // check z double z1, z2; if (p1.length < 3) { // 2D points : z = 0 z1 = 0; z2 = 0; } else { z1 = p1[2]; z2 = p2[2]; } if (Kernel.isGreater(getZmin(), z1, tolerance) && Kernel.isGreater(getZmin(), z2, tolerance)) { return true; } if (Kernel.isGreater(z1, getZmax(), tolerance) && Kernel.isGreater(z2, getZmax(), tolerance)) { return true; } // close to screen return false; } @Override protected boolean drawPlayButtonInThisView() { // TODO Auto-generated method stub return false; } /** * * @return distance between two number ticks on the x axis */ public double getNumbersDistance() { return getAxisNumberingDistance(AXIS_X); } /** * * @param axis * axis * @return distance between two number ticks on the axis */ public double getAxisNumberingDistance(int i) { return axesNumberingDistances[i]; } @Override public double getGridDistances(int i) { if (i == AXIS_Z) { // no grid along z axis return getAxisNumberingDistance(AXIS_Z); } return super.getGridDistances(i); } @Override public EuclidianController getEuclidianController() { return euclidianController; } /** * @return mouse pick width for openGL picking */ final public int getMousePickWidth() { return getCompanion().getMousePickWidth(); } @Override public GeoDirectionND getDirection() { return kernel.getSpace(); } @Override public int getDimension() { return 3; } @Override public final boolean isGridOrAxesShown() { for (int i = 0; i < 3; i++) { if (getShowAxis(i)) { return true; } } return getShowGrid(); } @Override public void settingsChanged(AbstractSettings settings) { companion.settingsChanged(settings); EuclidianSettings3D evs = (EuclidianSettings3D) settings; evs.updateOrigin(this); evs.updateRotXY(this); updateUseClippingCube(); setClippingReduction(evs.getClippingReduction()); setShowPlate(evs.getShowPlate()); setProjectionPerspectiveEyeDistance( evs.getProjectionPerspectiveEyeDistance(), evs.getProjectionPerspectiveEyeDistance()); eyeX[0] = -evs.getEyeSep() / 2.0; eyeX[1] = -eyeX[0]; eyeY[0] = 0; eyeY[1] = 0; projectionObliqueAngle = evs.getProjectionObliqueAngle(); projectionObliqueFactor = evs.getProjectionObliqueFactor(); setProjection(evs.getProjection()); updateMatrix(); setViewChanged(); setWaitForUpdate(); if (evs.getRotSpeed() > 0) { this.setRotContinueAnimation(0, evs.getRotSpeed()); } if (styleBar != null) { styleBar.updateGUI(); } } @Override public final void setViewShowAllObjects(boolean storeUndo, boolean keepRatio) { setViewShowAllObjects(storeUndo, keepRatio, 15); } @Override public final void setViewShowAllObjects(boolean storeUndo, boolean keepRatio, int steps) { if (boundsMin == null) { boundsMin = new Coords(3); boundsMax = new Coords(3); } boundsMin.setPositiveInfinity(); boundsMax.setNegativeInfinity(); drawable3DLists.enlargeBounds(boundsMin, boundsMax); // Log.debug("\nmin=\n"+boundsMin+"\nmax=\n"+boundsMax); // no object if (Double.isInfinite(boundsMin.getX())) { return; } zoomRW(boundsMin, boundsMax, steps); } @Override public void zoomRW(Coords boundsMin2, Coords boundsMax2) { zoomRW(boundsMin2, boundsMax2, 15); } public void zoomRW(Coords boundsMin2, Coords boundsMax2, int steps) { double dx0 = getXmax() - getXmin(); double dy0 = getYmax() - getYmin(); double dz0 = getZmax() - getZmin(); double dx = boundsMax2.getX() - boundsMin2.getX(); double dy = boundsMax2.getY() - boundsMin2.getY(); double dz = boundsMax2.getZ() - boundsMin2.getZ(); double scale = Double.POSITIVE_INFINITY; if (!Kernel.isZero(dx)) { scale = dx0 / dx; } if (!Kernel.isZero(dy)) { double v = dy0 / dy; if (scale > v) { scale = v; } } if (!Kernel.isZero(dz)) { double v = dz0 / dz; if (scale > v) { scale = v; } } if (Double.isInfinite(scale)) { return; } if (Double.isNaN(scale)) { return; } if (Kernel.isZero(scale)) { return; } scale *= getScale(); // let the view a bit greater than the scene scale *= 0.94; double x = -(boundsMin2.getX() + boundsMax2.getX()) / 2; double y = -(boundsMin2.getY() + boundsMax2.getY()) / 2; double z = -(boundsMin2.getZ() + boundsMax2.getZ()) / 2; setAnimatedCoordSystem(x, y, z, scale, steps); } /** * dispose current preview */ public void disposePreview() { if (this.previewDrawable != null) { this.previewDrawable.disposePreview(); } } @Override protected void updateDrawableFontSize() { drawable3DLists.resetAllLabels(); for (int i = 0; i < 3; i++) { axisDrawable[i].setWaitForUpdate(); axisDrawable[i].setLabelWaitForReset(); } repaintView(); } @Override public void centerView(GeoPointND point) { Coords p = point.getInhomCoordsInD3(); setXZero(-p.getX()); setYZero(-p.getY()); setZZero(-p.getZ()); getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); // update the view updateTranslationMatrix(); updateUndoTranslationMatrix(); setGlobalMatrices(); setViewChangedByTranslate(); setWaitForUpdate(); } public double getMaxBendSpeedSurface() { return MAX_BEND_SPEED_SURFACE; } @Override public int getExportWidth() { return getWidth(); } @Override public int getExportHeight() { return getHeight(); } public double getFontScale() { return fontScale; } /** * set font scale * * @param scale * scale */ public void setFontScale(double scale) { if (!Kernel.isEqual(scale, fontScale)) { fontScale = scale; updateDrawableFontSize(); } } /** * set zNear nearest value * * @param zNear */ final public void setZNearest(double zNear) { getCompanion().setZNearest(zNear); } /** * * @return true if consumes space key hitted */ public boolean handleSpaceKey() { return getCompanion().handleSpaceKey(); } @Override public void closeDropdowns() { // no combo box in 3D for now } /** * update background color and apply color to background * * @param updatedColor * color to update background * @param applyedColor * color actually applyed * */ public void setBackground(GColor updatedColor, GColor applyedColor) { this.bgColor = updatedColor; this.bgApplyedColor = applyedColor; if (renderer != null) { renderer.setWaitForUpdateClearColor(); } } @Override final public void paintBackground(GGraphics2D g2) { // not used in 3D } private double screenTranslateAndScaleDX; private double screenTranslateAndScaleDY; private double screenTranslateAndScaleDZ; public void screenTranslateAndScale(double dx, double dy, double scaleFactor) { // dx and dy are translation in screen coords // dx moves along "visible left-right" axis on xOy plane // dy moves along "visible front-back" axis on xOy plane // or z-axis if this one "visibly" more than sqrt(2)*front-back axis tmpCoords1.set(Coords.VX); toSceneCoords3D(tmpCoords1); screenTranslateAndScaleDX = tmpCoords1.getX() * dx; screenTranslateAndScaleDY = tmpCoords1.getY() * dx; tmpCoords1.set(Coords.VY); toSceneCoords3D(tmpCoords1); double z = tmpCoords1.getZ() * getScale(); if (z > 0.85) { screenTranslateAndScaleDZ = tmpCoords1.getZ() * (-dy); } else if (z < 0.45) { screenTranslateAndScaleDX += tmpCoords1.getX() * (-dy); screenTranslateAndScaleDY += tmpCoords1.getY() * (-dy); screenTranslateAndScaleDZ = 0; } else { screenTranslateAndScaleDZ = 0; } xScaleEnd = xScaleStart * scaleFactor; yScaleEnd = yScaleStart * scaleFactor; zScaleEnd = zScaleStart * scaleFactor; animationType = AnimationType.SCREEN_TRANSLATE_AND_SCALE; } @Override public void endBatchUpdate() { this.batchUpdate = false; } @Override public final void drawActionObjects(GGraphics2D g) { // TODO Auto-generated method stub } public void stopScreenTranslateAndScale() { if (animationType == AnimationType.SCREEN_TRANSLATE_AND_SCALE) { animationType = AnimationType.OFF; } } @Override public void scaleXYZ(Coords coords) { coords.mulInside(getXscale(), getYscale(), getZscale()); } public void scaleXYZ(Coords3 coords) { coords.mulInside(getXscale(), getYscale(), getZscale()); } /** * scale coords as normal vector * * @param coords * normal vector */ public void scaleNormalXYZ(Coords coords) { EuclidianSettings3D settings = getSettings(); if (settings.hasSameScales()) { return; } coords.mulInside(settings.getYZscale(), settings.getZXscale(), settings.getXYscale()); } public void scaleAndNormalizeNormalXYZ(Coords3 coords) { EuclidianSettings3D settings = getSettings(); if (settings.hasSameScales()) { return; } coords.mulInside(settings.getYZscale(), settings.getZXscale(), settings.getXYscale()); coords.normalizeIfPossible(); } @Override public boolean scaleAndNormalizeNormalXYZ(Coords coords, Coords ret) { EuclidianSettings3D settings = getSettings(); if (settings.hasSameScales()) { return false; } ret.setMul(coords, settings.getYZscale(), settings.getZXscale(), settings.getXYscale()); ret.normalize(); return true; } /** * TODO remove this (not needed after Feature.DIFFERENT_AXIS_RATIO_3D) * * @param value * @return value/scale */ public double unscale(double value) { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return value; } return value / getScale(); } /** * TODO remove this (not needed after Feature.DIFFERENT_AXIS_RATIO_3D) * * @param value * @return value/scale */ public float unscale(float value) { if (app.has(Feature.DIFFERENT_AXIS_RATIO_3D)) { return value; } return (float) (value / getScale()); } @Override public GBufferedImage getExportImage(double scale, boolean transparency, ExportType exportType) { return getRenderer().getExportImage(scale); } @Override final public boolean getKeepCenter() { // no need in 3D return false; } @Override final public void setKeepCenter(boolean center) { // no need in 3D } @Override protected void setXYMinMaxForSetCoordSystem() { // no need in 3D } /** * set the coord system regarding 3D mouse move * * @param translation * translation vector */ public void setCoordSystemFromMouse3DMove(Coords translation) { setXZero(xZeroOld + translation.getX()); setYZero(yZeroOld + translation.getY()); setZZero(zZeroOld + translation.getZ()); // update the view updateTranslationMatrix(); updateUndoTranslationMatrix(); setGlobalMatrices(); setViewChangedByTranslate(); setWaitForUpdate(); } private Coords startPos; private CoordMatrix4x4 startTranslation = CoordMatrix4x4.Identity(); // private CoordMatrix4x4 startTranslationScreen = // CoordMatrix4x4.Identity(); /** * set mouse start pos * * @param screenStartPos * mouse start pos (screen) */ public void setStartPos(Coords screenStartPos) { startPos.set(screenStartPos); toSceneCoords3D(startPos); startTranslation.setOrigin(screenStartPos.add(startPos)); } /** * set the coord system regarding 3D mouse move * * @param startPos1 * start 3D position (screen) * @param newPos * current 3D position (screen) * @param rotX * relative mouse rotate around x (screen) * @param rotZ * relative mouse rotate around z (view) */ public void setCoordSystemFromMouse3DMove(Coords startPos1, Coords newPos, double rotX, double rotZ) { // translation Coords v = new Coords(4); v.set(newPos.sub(startPos1)); toSceneCoords3D(v); // rotation setRotXYinDegrees(aOld + rotX, bOld + rotZ); updateRotationAndScaleMatrices(); // center rotation on pick point ( + v for translation) CoordMatrix m1 = rotationAndScaleMatrix.inverse().mul(startTranslation) .mul(rotationAndScaleMatrix); Coords t1 = m1.getOrigin(); setXZero(t1.getX() - startPos.getX() + v.getX()); setYZero(t1.getY() - startPos.getY() + v.getY()); setZZero(t1.getZ() - startPos.getZ() + v.getZ()); getSettings().updateOriginFromView(getXZero(), getYZero(), getZZero()); // update the view updateTranslationMatrix(); updateUndoTranslationMatrix(); setGlobalMatrices(); setViewChangedByTranslate(); setViewChangedByRotate(); setWaitForUpdate(); } public Mouse3DEvent createMouse3DEvent(GPointWithZ mouse3DLoc) { return new Mouse3DEvent(mouse3DLoc); } private EuclidianView3DCompanion companion3D; @Override protected EuclidianViewCompanion newEuclidianViewCompanion() { companion3D = new EuclidianView3DCompanion(this); return companion3D; } @Override public EuclidianView3DCompanion getCompanion() { return companion3D; } public DrawClippingCube3D getClippingCubeDrawable() { return clippingCubeDrawable; } }