package org.geogebra.common.euclidian; import org.geogebra.common.kernel.Kernel; /** * AbstractZoomer is responsible for animated zooming of Euclidian View * */ public abstract class MyZoomer { private enum ZoomerMode { ZOOM, ZOOM_RW, AXES, MOVE } private final EuclidianView view; private static final int MAX_STEPS = 15; // frames /** * Tick of the timer */ public static final int DELAY = 10; private static final int MAX_TIME = 400; // millis private ZoomerMode mode; private double px, py; // zoom point private double factor; private int counter, steps; private double oldScale, newScale, add, dx, dy; private double x0, x1, y0, y1, xminOld, yminOld, ymaxOld, xmaxOld; private long startTime; private boolean storeUndo; /** * Creates new zoomer * * @param view * view */ public MyZoomer(EuclidianView view) { this.view = view; } /** * Init this for axis ratio zooming * * @param ratio * y-zoom * @param doStoreUndo * true to store undo */ public void init(double ratio, boolean doStoreUndo) { // this.ratio = ratio; this.storeUndo = doStoreUndo; // zoomFactor = ratio / scaleRatio; oldScale = view.getYscale(); newScale = view.getXscale() * ratio; // new yscale this.steps = MAX_STEPS; mode = ZoomerMode.AXES; } /** * Init this for normal zoom * * @param ptx * center x-coord * @param pty * center y-coord * @param zoomFactor * zoom factor * @param noOfSteps * number of steps * @param doStoreUndo * true to store undo info */ public void init(double ptx, double pty, double zoomFactor, int noOfSteps, boolean doStoreUndo) { this.px = ptx; this.py = pty; // this.zoomFactor = zoomFactor; this.storeUndo = doStoreUndo; oldScale = view.getXscale(); newScale = view.getXscale() * zoomFactor; this.steps = Math.min(MAX_STEPS, noOfSteps); mode = ZoomerMode.ZOOM; } /** * @param rwx0 * left x * @param rwx1 * right x * @param rwy0 * bottom y * @param rwy1 * top y * @param noOfSteps * number of steps * @param doStoreUndo * true to store undo info */ public synchronized void initRW(double rwx0, double rwx1, double rwy0, double rwy1, int noOfSteps, boolean doStoreUndo) { this.x0 = rwx0; this.x1 = rwx1; this.y0 = rwy0; this.y1 = rwy1; xminOld = view.getXmin(); xmaxOld = view.getXmax(); yminOld = view.getYmin(); ymaxOld = view.getYmax(); // this.zoomFactor = zoomFactor; this.storeUndo = doStoreUndo; this.steps = Math.min(MAX_STEPS, noOfSteps); mode = ZoomerMode.ZOOM_RW; } /** * @param ox * x translation (pixels) * @param oy * y translation (pixels) * @param doStoreUndo * true to store undo info */ public synchronized void init(double ox, double oy, boolean doStoreUndo) { this.px = ox; this.py = oy; this.storeUndo = doStoreUndo; mode = ZoomerMode.MOVE; this.steps = MAX_STEPS; } /** * Perform one zoom step */ protected synchronized void step() { counter++; long time = System.currentTimeMillis() - startTime; if ((counter == steps) || (time > MAX_TIME)) { // end of animation stopAnimation(); } else { switch (mode) { case AXES: factor = 1.0 + ((counter * add) / oldScale); view.setCoordSystem(view.getXZero(), view.getYZero(), view.getXscale(), oldScale * factor); break; case ZOOM: factor = 1.0 + ((counter * add) / oldScale); view.setCoordSystem(px + (dx * factor), py + (dy * factor), oldScale * factor, oldScale * factor * view.getScaleRatio()); break; case ZOOM_RW: double i = counter; double j = steps - counter; view.setRealWorldCoordSystem(((x0 * i) + (xminOld * j)) / steps, ((x1 * i) + (xmaxOld * j)) / steps, ((y0 * i) + (yminOld * j)) / steps, ((y1 * i) + (ymaxOld * j)) / steps); break; case MOVE: factor = 1.0 - (counter * add); view.setCoordSystem(px + (dx * factor), py + (dy * factor), view.getXscale(), view.getYscale()); } } } private synchronized void stopAnimation() { stopTimer(); // setDrawMode(DRAW_MODE_BACKGROUND_IMAGE); switch (mode) { case AXES: view.setCoordSystem(view.getXZero(), view.getYZero(), view.getXscale(), newScale); break; case ZOOM: factor = newScale / oldScale; view.setCoordSystem(px + (dx * factor), py + (dy * factor), newScale, newScale * view.getScaleRatio()); break; case ZOOM_RW: view.setRealWorldCoordSystem(x0, x1, y0, y1); break; case MOVE: view.setCoordSystem(px, py, view.getXscale(), view.getYscale()); break; } if (setStandard) { setStandard = false; view.setAnimatedCoordSystem(standardX, standardY, 0, EuclidianView.SCALE_STANDARD, MAX_STEPS, storeUndo); } if (storeUndo) { view.getApplication().storeUndoInfo(); } } private boolean setStandard = false; private double standardX, standardY; /** * Chain current zoom with resetting standard view * * @param xzero * standard xzero * @param yzero * standard yzero */ public synchronized void setStandardViewAfter(double xzero, double yzero) { setStandard = true; standardX = xzero; standardY = yzero; } /** * Starts the animation */ public synchronized void startAnimation() { if (!hasTimer()) { return; } switch (mode) { case AXES: add = (newScale - oldScale) / steps; break; case ZOOM: add = (newScale - oldScale) / steps; dx = view.getXZero() - px; dy = view.getYZero() - py; break; case MOVE: dx = view.getXZero() - px; dy = view.getYZero() - py; if (Kernel.isZero(dx) && Kernel.isZero(dy)) { return; } // setDrawMode(DRAW_MODE_DIRECT_DRAW); add = 1.0 / MAX_STEPS; break; case ZOOM_RW: break; } counter = 0; startTime = System.currentTimeMillis(); startTimer(); } /** stop timer */ protected abstract void stopTimer(); /** start timer */ protected abstract void startTimer(); /** @return true if timer is defined */ protected abstract boolean hasTimer(); }