/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.common.kernel; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.kernelND.GeoPointND; /** * Path mover for most common paths */ public class PathMoverGeneric implements PathMover { private static final int BOUNDS_FIXED = 1; private static final int BOUNDS_INFINITE = 2; private static final int BOUNDS_FIXED_INFINITE = 3; private static final int BOUNDS_INFINITE_FIXED = 4; /** minimal number of steps for this particular instance */ public int MIN_STEPS_INSTANCE = MIN_STEPS; /** path */ protected Path path; /** start parameter */ protected double start_param; /** start param + param extent */ protected double start_paramUP; /** start param - param extent */ protected double start_paramDOWN; /** current parameter */ protected double curr_param; /** last parameter */ protected double last_param; /** difference between max and min params */ protected double param_extent; /** minimal parameter */ protected double min_param; /** maximal parameter */ protected double max_param; /** maximal step width */ protected double max_step_width; /** step width */ protected double step_width; /** finite border in case of semi-infinite path */ protected double offset; /** mode (BOUNDS_INFINITE, BOUNDS_FIXED, ..) */ protected int mode; /** positive orientation */ protected boolean posOrientation; /** true if we just arrived at max */ protected boolean maxBorderSet; /** true if we just arrived at min */ protected boolean minBorderSet; /** true if we arrived at max in last step */ protected boolean lastMaxBorderSet; /** true if we just arrived at min in last step */ protected boolean lastMinBorderSet; /** * Creates new path mover for given path * * @param path * path */ public PathMoverGeneric(Path path) { this.path = path; } /** * Creates new path mover */ public PathMoverGeneric() { } @Override public void init(GeoPointND p, int min_steps) { MIN_STEPS_INSTANCE = min_steps; PathParameter pp = p.getPathParameter(); init(pp.t); } private void init(double param) { start_param = param; min_param = path.getMinParameter(); max_param = path.getMaxParameter(); if (min_param > max_param) { // case should not happen, by the way double par = max_param; max_param = min_param; min_param = par; } // make sure start_param is between min and max if (start_param < min_param || start_param > max_param) { param_extent = max_param - min_param; start_param = (start_param - min_param) % param_extent; if (start_param < min_param) { start_param += param_extent; } } if (min_param == Double.NEGATIVE_INFINITY) { if (max_param == Double.POSITIVE_INFINITY) { // (-infinite, +infinite) mode = BOUNDS_INFINITE; // infFunction(-1, 1) min_param = -1 + OPEN_BORDER_OFFSET; max_param = 1 - OPEN_BORDER_OFFSET; // transform start parameter to be in (-1, 1) start_param = PathNormalizer.inverseInfFunction(start_param); } else { // (-infinite, max_param] mode = BOUNDS_INFINITE_FIXED; start_param = 0; // max_param + infFunction(-1 ... 0) offset = max_param; min_param = -1 + OPEN_BORDER_OFFSET; max_param = 0; } } else { if (max_param == Double.POSITIVE_INFINITY) { // [min_param, +infinite) mode = BOUNDS_FIXED_INFINITE; start_param = 0; // min_param + infFunction(0 ... 1) offset = min_param; min_param = 0; max_param = 1 - OPEN_BORDER_OFFSET; } else { // [min_param, max_param] mode = BOUNDS_FIXED; } } param_extent = max_param - min_param; start_paramUP = start_param + param_extent; start_paramDOWN = start_param - param_extent; max_step_width = param_extent / MIN_STEPS_INSTANCE; if (max_step_width < MIN_STEP_WIDTH || Double.isNaN(max_step_width) || Double.isInfinite(max_step_width)) { // case should not happen, by the way max_step_width = MIN_STEP_WIDTH; } posOrientation = true; resetStartParameter(); // System.out.println("init Path mover"); // System.out.println(" min_param : " + min_param); // System.out.println(" max_param : " + max_param); // System.out.println(" start_param : " + start_param); // System.out.println(" max_step_width : " + max_step_width); } @Override public void resetStartParameter() { curr_param = start_param; last_param = start_param; maxBorderSet = lastMaxBorderSet = minBorderSet = lastMinBorderSet = false; step_width = max_step_width; } @Override public void getCurrentPosition(GeoPointND p) { calcPoint(p); } @Override public boolean getNext(GeoPointND p) { // check if we are in our interval boolean lineTo = true; last_param = curr_param; lastMaxBorderSet = maxBorderSet; lastMinBorderSet = minBorderSet; // in the last step we got outside a border and stopped there // now continue at the other border if (maxBorderSet) { curr_param = min_param; lineTo = path.isClosedPath(); maxBorderSet = false; } else if (minBorderSet) { curr_param = max_param; lineTo = path.isClosedPath(); minBorderSet = false; } // STANDARD CASE else { double new_param = curr_param + step_width; // new_param too big if (new_param >= max_param) { // slow down by making smaller steps while (new_param >= max_param && smallerStep()) { new_param = curr_param + step_width; } // max border reached if (new_param >= max_param) { new_param = max_param; maxBorderSet = true; } } // new_param too small else if (new_param <= min_param) { // slow down by making smaller steps while (new_param <= min_param && smallerStep()) { new_param = curr_param + step_width; } // min border reached if (new_param <= min_param) { new_param = min_param; minBorderSet = true; } } // set parameter curr_param = new_param; } // calculate point for current parameter calcPoint(p); return lineTo; } /** * Updates path parameter of point p from curr_param * * @param p * point */ protected void calcPoint(GeoPointND p) { double param; switch (mode) { case BOUNDS_FIXED: param = curr_param; break; case BOUNDS_INFINITE: param = PathNormalizer.infFunction(curr_param); break; case BOUNDS_FIXED_INFINITE: case BOUNDS_INFINITE_FIXED: param = offset + PathNormalizer.infFunction(curr_param); break; default: param = Double.NaN; } PathParameter pp = p.getPathParameter(); pp.t = param; path.pathChanged(p); p.updateCoords(); } @Override public boolean hasNext() { // check if we pass the start parameter // from last_param to the next parameter curr_param boolean hasNext; double next_param = curr_param + step_width; if (posOrientation) { hasNext = !(curr_param < start_param && next_param >= start_param || curr_param < start_paramUP && next_param >= start_paramUP); } else { hasNext = !(curr_param > start_param && next_param <= start_param || curr_param > start_paramDOWN && next_param <= start_paramDOWN); } return hasNext; } @Override public double getCurrentParameter() { return curr_param; } @Override public void changeOrientation() { posOrientation = !posOrientation; step_width = -step_width; } @Override public boolean hasPositiveOrientation() { return posOrientation; } @Override final public boolean smallerStep() { return changeStep(step_width * STEP_DECREASE_FACTOR); } @Override final public boolean biggerStep() { return changeStep(step_width * STEP_INCREASE_FACTOR); } @Override final public boolean setStep(double step) { return changeStep(step); } @Override final public double getStep() { return step_width; } private boolean changeStep(double new_step) { double abs_new_step = Math.abs(new_step); if (new_step < MIN_STEP_WIDTH && new_step > NEG_MIN_STEP_WIDTH) { if (new_step > 0.0d) { if (step_width == MIN_STEP_WIDTH) { return false; } step_width = MIN_STEP_WIDTH; return true; } else if (new_step < -0.0d) { if (step_width == NEG_MIN_STEP_WIDTH) { return false; } step_width = NEG_MIN_STEP_WIDTH; return true; } if (step_width == NEG_MIN_STEP_WIDTH || step_width == MIN_STEP_WIDTH) { return false; } if (step_width >= 0.0d) { step_width = MIN_STEP_WIDTH; } else { step_width = NEG_MIN_STEP_WIDTH; } return true; } else if (abs_new_step > max_step_width) { if (new_step >= 0.0d) { if (MyDouble.exactEqual(step_width, max_step_width)) { return false; } step_width = max_step_width; return true; } if (step_width == -max_step_width) { return false; } step_width = -max_step_width; return true; } else { step_width = new_step; return true; } } @Override public void stepBack() { curr_param = last_param; maxBorderSet = lastMaxBorderSet; minBorderSet = lastMinBorderSet; } }