/* 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 java.util.ArrayList; import org.geogebra.common.kernel.geos.GeoLocusND; import org.geogebra.common.kernel.geos.GeoLocusNDInterface; import org.geogebra.common.kernel.kernelND.GeoPointND; /** * Path mover for locus * * @param <T> * for 2D/3D locus */ public class PathMoverLocus<T extends MyPoint> extends PathMoverGeneric { private ArrayList<? extends MyPoint> myPointList; private boolean noLineToSet, lastNoLineToSet; /** * Creates new path mover for given locus * * @param locus * locus */ public PathMoverLocus(GeoLocusND<T> locus) { super(locus); myPointList = locus.getPoints(); } @Override public void init(GeoPointND p, int min_steps) { if (p.getPath() instanceof GeoLocusND) { myPointList = ((GeoLocusNDInterface) p.getPath()).getPoints(); } lastNoLineToSet = noLineToSet = false; super.init(p, min_steps); } @Override public void resetStartParameter() { super.resetStartParameter(); noLineToSet = lastNoLineToSet = false; } @Override protected void calcPoint(GeoPointND p) { // curr_param is between 0 and myPointList.size()-1 now double param = curr_param; PathParameter pp = p.getPathParameter(); pp.t = param; // PATH MOVER CHANGED PARAMETER (see PathMoverGeneric.calcPoint()) // get points left and right of path parameter int leftIndex = (int) Math.max(0, Math.floor(param)); int rightIndex = (int) Math.min(myPointList.size() - 1, Math.ceil(param)); if (myPointList.isEmpty()) { p.setUndefined(); return; } MyPoint leftPoint = myPointList.get(leftIndex); MyPoint rightPoint = myPointList.get(rightIndex); // interpolate between leftPoint and rightPoint double param1 = (param - leftIndex); double param2 = 1.0 - param1; p.set(param1, param2, leftPoint, rightPoint); p.updateCoords(); } @Override public boolean getNext(GeoPointND p) { // check if we are in our interval boolean lineTo = true; last_param = curr_param; lastMaxBorderSet = maxBorderSet; lastMinBorderSet = minBorderSet; lastNoLineToSet = noLineToSet; // 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; } else if (noLineToSet) { curr_param = borderParam(curr_param); lineTo = false; noLineToSet = 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 || noLineTo(new_param)) && smallerStep()) { new_param = curr_param + step_width; } // max border reached if (new_param >= max_param) { new_param = max_param; maxBorderSet = true; } else if (noLineTo(new_param)) { new_param = borderParam(new_param); noLineToSet = true; } } // new_param too small else if (new_param <= min_param) { // slow down by making smaller steps while ((new_param <= min_param || noLineTo(new_param)) && smallerStep()) { new_param = curr_param + step_width; } // min border reached if (new_param <= min_param) { new_param = min_param; minBorderSet = true; } else if (noLineTo(new_param)) { new_param = borderParam(new_param); noLineToSet = true; } } else if (noLineTo(new_param)) { while (noLineTo(new_param) && smallerStep()) { new_param = curr_param + step_width; } if (noLineTo(new_param)) { new_param = borderParam(new_param); noLineToSet = true; } } // set parameter curr_param = new_param; } // calculate point for current parameter calcPoint(p); return lineTo; } @Override public void stepBack() { super.stepBack(); noLineToSet = lastNoLineToSet; } /** * @param new_param * param of next point * @return true if there is not continous line from cur_param to new_param */ protected boolean noLineTo(double new_param) { if (new_param >= max_param || new_param <= min_param) { // not right use case return false; } if (curr_param < new_param) { int leftIndexCurr = (int) Math.max(0, Math.floor(curr_param)); int rightIndexNew = (int) Math.min(myPointList.size() - 1, Math.ceil(new_param)); for (int i = leftIndexCurr + 1; i <= rightIndexNew; i++) { if (!myPointList.get(i).getLineTo()) { return true; } } } else if (curr_param > new_param) { int leftIndexNew = (int) Math.max(0, Math.floor(new_param)); int rightIndexCurr = (int) Math.min(myPointList.size() - 1, Math.ceil(curr_param)); for (int i = leftIndexNew + 1; i <= rightIndexCurr; i++) { if (!myPointList.get(i).getLineTo()) { return true; } } } return false; } /** * @param param * initial parameter * @return parameter of nearest point with line to / move to or min/max * param if not found */ protected double borderParam(double param) { if (curr_param < param) { return Math.min(myPointList.size() - 1, Math.ceil(curr_param)); } else if (curr_param > param) { return Math.max(0, Math.floor(curr_param)); } // from this, suppose param is already an index int paramindex = (int) Math.round(param); paramindex = Math.max(0, paramindex); paramindex = Math.min(paramindex, myPointList.size() - 1); if (posOrientation) { for (int i = paramindex + 2; i <= myPointList.size() - 1; i++) { // lineTo at i == paramindex + 1 cannot happen if (myPointList.get(i).getLineTo()) { return i - 1; } } maxBorderSet = true; return max_param; } for (int i = paramindex - 1; i >= 1; i--) { // lineTo at i == paramindex cannot happen if (myPointList.get(i).getLineTo()) { return i; } } minBorderSet = true; return min_param; } @Override public boolean hasNext() { if (myPointList.isEmpty()) { return false; } // 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) { if (next_param < max_param) { int rightIndexNext = (int) Math.min(myPointList.size() - 1, Math.ceil(next_param)); if (!myPointList.get(rightIndexNext).getLineTo()) { next_param = max_param; for (int i = rightIndexNext + 1; i <= myPointList.size() - 1; i++) { if (myPointList.get(i).getLineTo()) { next_param = i - 1; break; } } } } else if (next_param > max_param) { int rightIndexNext = (int) Math.min(myPointList.size() - 1, Math.ceil(next_param - myPointList.size() + 1)); double next_param_little = next_param - myPointList.size() + 1; if (!myPointList.get(rightIndexNext).getLineTo()) { next_param_little = max_param; for (int i = rightIndexNext + 1; i <= myPointList.size() - 1; i++) { if (myPointList.get(i).getLineTo()) { next_param_little = i - 1; break; } } } next_param = next_param_little + myPointList.size() - 1; } hasNext = !(curr_param < start_param && next_param >= start_param || curr_param < start_paramUP && next_param >= start_paramUP); } else { if (next_param > min_param) { int rightIndexNext = (int) Math.min(myPointList.size() - 1, Math.ceil(next_param)); if (!myPointList.get(rightIndexNext).getLineTo()) { next_param = min_param; for (int i = rightIndexNext - 1; i >= 1; i--) { if (myPointList.get(i).getLineTo()) { next_param = i; break; } } } } else if (next_param < min_param) { int rightIndexNext = (int) Math.min(myPointList.size() - 1, Math.ceil(next_param + myPointList.size() - 1)); double next_param_big = next_param + myPointList.size() - 1; if (!myPointList.get(rightIndexNext).getLineTo()) { next_param_big = min_param; for (int i = rightIndexNext - 1; i >= 1; i--) { if (myPointList.get(i).getLineTo()) { next_param_big = i; break; } } } next_param = next_param_big - myPointList.size() + 1; } hasNext = !(curr_param > start_param && next_param <= start_param || curr_param > start_paramDOWN && next_param <= start_paramDOWN); } return hasNext; } }