/* 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.algos; import java.util.ArrayList; import java.util.Iterator; import java.util.TreeSet; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Path; import org.geogebra.common.kernel.PathMover; import org.geogebra.common.kernel.SegmentType; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoLocus; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.implicit.GeoImplicit; import org.geogebra.common.util.debug.Log; /** * locus line for Q dependent on P */ public class AlgoLocusList extends AlgoElement { public static final int MIN_STEPS_REALLY = 16; ArrayList<AlgoElement> arrLocus; private GeoPoint movingPoint, locusPoint; // input private GeoLocus locus; // output // for efficient dependency handling private GeoElement[] efficientInput, standardInput; private Path path; // path of P private boolean foundDefined; private TreeSet<GeoElement> Qin; private boolean shouldUpdateScreenBorders = false; public AlgoLocusList(Construction cons, GeoPoint Q, GeoPoint P, boolean registerCE) { // just ignoring try_steps here because it would // probably not be OK to split MIN_STEPS any more super(cons, registerCE); this.movingPoint = P; this.locusPoint = Q; path = P.getPath(); locus = new GeoLocus(cons); // locus.setFillable(false); setInputOutput(); // for AlgoElement compute(); // we may have created a starting point for the path now // make sure that the movingPoint in the main construction // uses the correct path parameter for it path.pointChanged(P); } public AlgoLocusList(Construction cons, String label, GeoPoint Q, GeoPoint P) { super(cons); this.movingPoint = P; this.locusPoint = Q; path = P.getPath(); locus = new GeoLocus(cons); // locus.setFillable(false); updateScreenBorders(); setInputOutput(); // for AlgoElement cons.registerEuclidianViewCE(this); compute(); // we may have created a starting point for the path now // make sure that the movingPoint in the main construction // uses the correct path parameter for it path.pointChanged(P); locus.setLabel(label); } private void fillLocusArray(GeoPoint Q, GeoPoint P) { if (arrLocus == null) { arrLocus = new ArrayList<AlgoElement>(); } // AlgoLocusList should be called only when the path is a GeoList GeoElement actel, pathp; AlgoElement actal; Path oldel; // however... try { int try_steps = PathMover.MIN_STEPS / ((GeoList) path).size() + 1; if (try_steps < MIN_STEPS_REALLY) { try_steps = MIN_STEPS_REALLY; } int arrLocusSize = arrLocus.size(); for (int i = arrLocusSize - 1; i >= ((GeoList) path).size(); i--) { arrLocus.remove(i); } arrLocusSize = arrLocus.size(); for (int i = 0; i < ((GeoList) path).size(); i++) { actel = ((GeoList) path).get(i); if (actel instanceof Path) { if (i < arrLocusSize) { if (arrLocus.get(i) instanceof AlgoLocusList) { oldel = ((AlgoLocusList) arrLocus.get(i)) .getMovingPoint().getPath(); } else if (arrLocus.get(i) instanceof AlgoLocus) { oldel = ((AlgoLocus) arrLocus.get(i)) .getMovingPoint().getPath(); } else { oldel = null; } if (oldel == actel) { if (shouldUpdateScreenBorders) { if (arrLocus.get(i) instanceof AlgoLocus) { ((AlgoLocus) arrLocus.get(i)) .updateScreenBorders(); } else if (arrLocus .get(i) instanceof AlgoLocusList) { ((AlgoLocusList) arrLocus.get(i)) .updateScreenBorders(); } } arrLocus.get(i).compute(); continue; } } P.setPath((Path) actel); // new AlgoLocus(List) does not need updateScreenBorders and // compute if (actel instanceof GeoList) { if (((GeoList) actel).shouldUseAlgoLocusList(true)) { actal = new AlgoLocusList(cons, Q, P, false); pathp = ((AlgoLocusList) actal).getLocus(); } else { actal = new AlgoLocus(cons, Q, P, try_steps, false); pathp = ((AlgoLocus) actal).getLocus(); } } else { actal = new AlgoLocus(cons, Q, P, try_steps, false); pathp = ((AlgoLocus) actal).getLocus(); } cons.removeFromAlgorithmList(actal); // cons.removeFromConstructionList(actal); // cons.unregisterEuclidianViewCE(actal); cons.removeFromConstructionList(pathp); P.setPath(path); if (i < arrLocusSize) { arrLocus.set(i, actal); } else { arrLocus.add(actal); } } else { if (i < arrLocusSize) { arrLocus.set(i, null); } else { arrLocus.add(null); } } } } catch (Exception ex) { Log.error(ex.getMessage()); } } @Override public Commands getClassName() { return Commands.Locus; } @Override public int getRelatedModeID() { return EuclidianConstants.MODE_LOCUS; } public ArrayList<?> getMoveableInputPoints() { // TODO ? return null; } /** * Returns the dependent point * * @return dependent point Q */ public GeoPoint getQ() { return locusPoint; } /** * A way more descriptive name for the getter. * * @return dependent point Q */ public GeoPoint getLocusPoint() { return locusPoint; } /** * @return moving point P. */ public GeoPoint getMovingPoint() { return movingPoint; } // for AlgoElement @Override protected void setInputOutput() { // it is inefficient to have Q and P as input // let's take all independent parents of Q // and the path as input TreeSet<GeoElement> inSet = new TreeSet<GeoElement>(); inSet.add(path.toGeoElement()); // we need all independent parents of Q PLUS // all parents of Q that are points on a path Qin = locusPoint.getAllPredecessors(); Iterator<GeoElement> it = Qin.iterator(); while (it.hasNext()) { GeoElement geo = it.next(); if (geo.isIndependent() || geo.isPointOnPath()) { inSet.add(geo); } } // remove P from input set! inSet.remove(movingPoint); efficientInput = new GeoElement[inSet.size()]; it = inSet.iterator(); int i = 0; while (it.hasNext()) { efficientInput[i] = it.next(); i++; } // the standardInput array should be used for // the dependency graph standardInput = new GeoElement[2]; standardInput[0] = locusPoint; standardInput[1] = movingPoint; setOutputLength(1); setOutput(0, locus); // handle dependencies setEfficientDependencies(standardInput, efficientInput); } /** * Returns locus * * @return locus */ public GeoLocus getLocus() { return locus; } // compute locus line @Override public final void compute() { if (!movingPoint.isDefined() || !isPathIterable(path.toGeoElement())) { locus.setUndefined(); return; } // it is necessary to call this here to make arrLocus up-to-date fillLocusArray(locusPoint, movingPoint); locus.clearPoints(); foundDefined = false; AlgoElement actLocus; GeoLocus actGeo; for (int i = 0; i < arrLocus.size(); i++) { actLocus = arrLocus.get(i); if (actLocus instanceof AlgoLocusList) { actGeo = ((AlgoLocusList) actLocus).getLocus(); } else if (actLocus instanceof AlgoLocus) { actGeo = (GeoLocus) ((AlgoLocus) actLocus).getLocus(); } else { continue; } for (int j = 0; j < actGeo.getPointLength(); j++) { insertPoint(actGeo.getPoints().get(j).x, actGeo.getPoints() .get(j).y, j != 0 && actGeo.getPoints().get(j).getLineTo()); } if (actGeo.getPointLength() > 0) { foundDefined = true; } } // set defined/undefined locus.setDefined(foundDefined); shouldUpdateScreenBorders = false; } private static boolean isPathIterable(GeoElement geoElement) { if (geoElement.isGeoImplicitPoly()) { return ((GeoImplicit) geoElement).isOnScreen(); } return geoElement.isDefined(); } private void insertPoint(double x, double y, boolean lineTo) { // Application.debug("insertPoint: " + x + ", " + y + ", lineto: " + // lineTo); locus.insertPoint(x, y, lineTo ? SegmentType.LINE_TO : SegmentType.MOVE_TO); } @Override public boolean euclidianViewUpdate() { updateScreenBorders(); update(); return false; } /** * This should register the wish that screen borders should be updated in * the subloci in time */ void updateScreenBorders() { shouldUpdateScreenBorders = true; } }