public AlgoLocus(Construction cons, String label, GeoPoint2 Q, GeoPoint2 P) { super(cons); this.movingPoint = P; this.locusPoint = Q; path = P.getPath(); pathMover = path.createPathMover(); QstartPos = new GeoPoint2(cons); PstartPos = new GeoPoint2(cons); init(); updateScreenBorders(); locus = new GeoLocus(cons); 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); }
// for AlgoElement @Override protected void setInputOutput() { if (param == null) { input = new GeoElement[1]; input[0] = path.toGeoElement(); } else { input = new GeoElement[2]; input[0] = path.toGeoElement(); input[1] = param.toGeoElement(); } setOutputLength(1); setOutput(0, P); setDependencies(); // done by AlgoElement }
@Override public final void compute() { if (param != null) { PathParameter pp = P.getPathParameter(); // Application.debug(param.getDouble()+" "+path.getMinParameter()+" "+path.getMaxParameter()); pp.setT( PathNormalizer.toParentPathParameter( param.getDouble(), path.getMinParameter(), path.getMaxParameter())); // Application.debug(pp.t); } if (input[0].isDefined()) { path.pathChanged(P); P.updateCoords(); } else { P.setUndefined(); } }
public final void compute() { if (input[0].isDefined()) { path.pathChanged(P); P.updateCoords(); } else { P.setUndefined(); } }
// for AlgoElement protected void setInputOutput() { input = new GeoElement[1]; input[0] = (GeoElement) path.toGeoElement(); output = new GeoElement[1]; output[0] = P; setDependencies(); // done by AlgoElement }
// 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((GeoElement) path.toGeoElement()); // we need all independent parents of Q PLUS // all parents of Q that are points on a path 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] = (GeoElement) 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); }
/** * @param point the point to check * @param eps precision * @return true if the point is on the path */ public static boolean isOnPath(Path geo, GeoPointND point, double eps) { return geo.isOnPath(point, eps); }
// compute locus line @Override public final void compute() { if (!movingPoint.isDefined() || macroCons == null || !path.toGeoElement().isDefined()) { locus.setUndefined(); return; } locus.clearPoints(); clearCache(); pointCount = 0; useCache = 0; countUpdates = 0; lastX = Double.MAX_VALUE; lastY = Double.MAX_VALUE; maxTimeExceeded = false; foundDefined = false; boolean prevQcopyDefined = false; int max_runs; // continuous kernel? continuous = kernel.isContinuous(); macroKernel.setContinuous(continuous); // update macro construction with current values of global vars resetMacroConstruction(); macroCons.updateConstruction(); // use current position of movingPoint to start Pcopy pathMover.init(Pcopy); if (continuous) { // continous constructions may need several parameter run throughs // to draw all parts of the locus max_runs = GeoLocus.MAX_PATH_RUNS; } else { max_runs = 1; } // update Pcopy to compute Qcopy pcopyUpdateCascade(); prevQcopyDefined = Qcopy.isDefined() && !Qcopy.isInfinite(); // move Pcopy along the path // do this until Qcopy comes back to its start position // for continuous constructions // this may require several runs of Pcopy along the whole path int runs = 1; int MAX_LOOPS = 2 * PathMover.MAX_POINTS; int whileLoops = 0; do { boolean finishedRun = false; while (!finishedRun && !maxTimeExceeded && pointCount <= PathMover.MAX_POINTS && whileLoops <= MAX_LOOPS) { whileLoops++; // lineTo may be false due to a parameter jump // i.e. param in [0,1] gets bigger than 1 and thus jumps to 0 boolean parameterJump = !pathMover.getNext(Pcopy); boolean stepChanged = false; // update construction pcopyUpdateCascade(); // Qcopy DEFINED if (Qcopy.isDefined() && !Qcopy.isInfinite()) { // STANDARD CASE: no parameter jump if (!parameterJump) { // make steps smaller until distance ok to connect with last point while (Qcopy.isDefined() && !Qcopy.isInfinite() && !distanceOK(Qcopy) && !maxTimeExceeded) { // go back and try smaller step boolean smallerStep = pathMover.smallerStep(); if (!smallerStep) break; stepChanged = true; pathMover.stepBack(); pathMover.getNext(Pcopy); // update construction pcopyUpdateCascade(); } if (Qcopy.isDefined() && !Qcopy.isInfinite()) { // draw point insertPoint(Qcopy.inhomX, Qcopy.inhomY, distanceSmall(Qcopy)); prevQcopyDefined = true; } } // PARAMETER jump: !lineTo else { // draw point insertPoint(Qcopy.inhomX, Qcopy.inhomY, distanceSmall(Qcopy)); prevQcopyDefined = true; } } // Qcopy NOT DEFINED else { // check if we moved from defined to undefined case: // step back and try with smaller step if (prevQcopyDefined && !parameterJump) { pathMover.stepBack(); // set smallest step if (!pathMover.smallerStep()) { prevQcopyDefined = false; } else stepChanged = true; } // add better undefined case support for continuous curves // maybe change orientation of path mover } // if we didn't decrease the step width increase it if (!stepChanged) { pathMover.biggerStep(); } // end of run: the next step would pass the start position if (!pathMover.hasNext()) { if (distanceSmall(QstartPos)) { // draw line back to first point when it's close enough insertPoint(QstartPos.inhomX, QstartPos.inhomY, true); finishedRun = true; } else { // decrease step until another step is possible while (!pathMover.hasNext() && pathMover.smallerStep()) ; // no smaller step possible: run finished if (!pathMover.hasNext()) finishedRun = true; } } } // calculating the steps took too long, so we stopped somewhere if (maxTimeExceeded) { System.err.println("AlgoLocus: max time exceeded"); return; } else { // make sure that Pcopy is back at startPos now // look at Qcopy at startPos Pcopy.set((GeoElement) PstartPos); pcopyUpdateCascade(); if (Qcopy.inhomX != lastX || Qcopy.inhomY != lastY) insertPoint(Qcopy.inhomX, Qcopy.inhomY, distanceSmall(Qcopy)); // Application.debug("run: " + runs); // Application.debug("pointCount: " + pointCount); // Application.debug(" startPos: " + QstartPos); // Application.debug(" Qcopy: " + Qcopy); // we are finished with all runs // if we got back to the start position of Qcopy // AND if the direction of moving along the path // is positive like in the beginning if (pathMover.hasPositiveOrientation()) { kernel.setMinPrecision(); boolean equal = QstartPos.isEqual(Qcopy); kernel.resetPrecision(); if (equal) break; } } pathMover.resetStartParameter(); runs++; } while (runs < max_runs); // set defined/undefined locus.setDefined(foundDefined); // System.out.println(" first point: " + locus.getMyPointList().get(0)); // ArrayList list = locus.getMyPointList(); // for (int i=list.size()-10; i < list.size()-1; i++) { // System.out.println(" point: " + list.get(i)); // } // System.out.println(" last point: " + locus.getMyPointList().get(pointCount-1)); // Application.debug("LOCUS COMPUTE updateCascades: " + countUpdates + ", cache used: " + // useCache); }