package org.geogebra.common.kernel.implicit; import java.util.ArrayList; import org.apache.commons.math3.analysis.polynomials.PolynomialFunction; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.EquationSolverInterface; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.algos.AlgoIntersect; import org.geogebra.common.kernel.algos.AlgoSimpleRootsPolynomial; import org.geogebra.common.kernel.algos.GetCommand; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoPoint; import org.geogebra.common.kernel.geos.GeoPoly; import org.geogebra.common.kernel.geos.GeoSegment; import org.geogebra.common.kernel.geos.GeoVec3D; import org.geogebra.common.util.debug.Log; /** * @author thilina * */ public class AlgoIntersectImplicitpolyPolyLine extends AlgoIntersect { /** * input implicit polynomial */ protected GeoImplicit implicitPolynomial; /** * inpupt polyline/polygon */ protected GeoPoly poly; /** intersection points */ protected OutputHandler<GeoPoint> outputPoints; /** whether labels are used */ protected boolean hasLabels; /** whether the input is a closed polyline (=polygon) */ protected boolean polyclosed; /** * Number of intersections */ protected int numOfOutputPoints; /** * Number of polyline vertices */ protected int polyPointCount; /** * Number of polyline edges */ protected int segCountOfPoly; /** intersection coordinates; */ protected ArrayList<Coords> intersectCoords; private GeoPoint[] tempSegEndPoints; private GeoSegment tempSeg; private PolynomialFunction tx; private PolynomialFunction ty; private EquationSolverInterface eqnSolver; /** * constructor with labels for intersection between implicitPoly and * PolyLine * * @param cons * construction * @param labels * output labels * @param implicitPolynomial * input implicit polynomial * @param poly * input polyline/polygon * @param polyClosed * states whether the input geoPOyl is a polyline or polygon */ public AlgoIntersectImplicitpolyPolyLine(Construction cons, String[] labels, GeoImplicit implicitPolynomial, GeoPoly poly, boolean polyClosed) { this(cons, implicitPolynomial, poly, polyClosed); if (!cons.isSuppressLabelsActive()) { setLabels(labels); hasLabels = true; } update(); } /** * common constructor for intersection between implicitPoly and PolyLine * * @param cons * construction * @param implicitPolynomial * input implicit polynomial * @param poly * input polyline/polygon * @param polyClosed * states whether the input geoPOyl is a polyline or polygon */ public AlgoIntersectImplicitpolyPolyLine(Construction cons, GeoImplicit implicitPolynomial, GeoPoly poly, boolean polyClosed) { super(cons); this.implicitPolynomial = implicitPolynomial; this.poly = poly; this.polyclosed = polyClosed; initElements(); setInputOutput(); setDependencies(); compute(); } @Override public GeoPoint[] getIntersectionPoints() { GeoPoint[] iPoint = new GeoPoint[2]; return outputPoints.getOutput(iPoint); } @Override protected GeoPoint[] getLastDefinedIntersectionPoints() { // TODO Auto-generated method stub return null; } @Override protected void setInputOutput() { input = new GeoElement[2]; input[0] = getImplicitPolynomial().toGeoElement(); input[1] = (GeoElement) getPoly(); } @Override public void compute() { numOfOutputPoints = 0; intersectCoords = new ArrayList<Coords>(); // calculate intersectpaths between poly and conic for (int index = 0; index < segCountOfPoly; index++) { tempSegEndPoints[0] = getPoly().getPoint(index); tempSegEndPoints[1] = getPoly() .getPoint((index + 1) % polyPointCount); GeoVec3D.lineThroughPoints(tempSegEndPoints[0], tempSegEndPoints[1], tempSeg); tempSeg.setPoints(tempSegEndPoints[0], tempSegEndPoints[1]); tempSeg.calcLength(); computePolyLineIntersection(tempSeg, intersectCoords); } numOfOutputPoints = intersectCoords.size(); if (numOfOutputPoints > 0) { outputPoints.adjustOutputSize(numOfOutputPoints, false); for (int i = 0; i < numOfOutputPoints; i++) { outputPoints.getElement(i).setCoords(intersectCoords.get(i), true); } } else { outputPoints.adjustOutputSize(1, false); outputPoints.getElement(0).setUndefined(); } if (hasLabels) { outputPoints.updateLabels(); } } @Override public GetCommand getClassName() { return Commands.Intersect; } @Override public int getRelatedModeID() { return EuclidianConstants.MODE_INTERSECT; } /** * calculates intersection points between given segment and implicitpoly. * assign calculated points to given intersectCoords arrayList */ private void computePolyLineIntersection(GeoSegment tempSeg2, ArrayList<Coords> intersectCoords2) { double startP[] = new double[2]; tempSeg2.getInhomPointOnLine(startP); tx = new PolynomialFunction( new double[] { startP[0], tempSeg2.getY() }); // x=p1+t*r1 ty = new PolynomialFunction( new double[] { startP[1], -tempSeg2.getX() }); // y=p2+t*r2 double maxT = tempSeg2.getMaxParameter(); double minT = tempSeg2.getMinParameter(); PolynomialFunction sum = null; // Insert x and y (univariat)polynomials via the Horner-scheme double[][] coeff = getImplicitPolynomial().getCoeff(); if (coeff != null) { sum = lineIntersect(coeff, tx, ty); } if (sum == null) { Log.debug("problem in AlgoIntersectImplicitpolyPolyLine"); return; } setRootsPolynomialWithinRange(intersectCoords2, sum, minT, maxT); } /** * @param coeff * implicit poly coefficients * @param tx * function of t to plug into x * @param ty * function of t to plug into y * @return function of t representing f(tx(t),ty(t)) */ public static PolynomialFunction lineIntersect(double[][] coeff, PolynomialFunction tx, PolynomialFunction ty) { PolynomialFunction sum = null; PolynomialFunction zs = null; for (int i = coeff.length - 1; i >= 0; i--) { zs = new PolynomialFunction( new double[] { coeff[i][coeff[i].length - 1] }); for (int j = coeff[i].length - 2; j >= 0; j--) { zs = zs.multiply(ty).add( new PolynomialFunction(new double[] { coeff[i][j] }));// y*zs+coeff[i][j]; } if (sum == null) { sum = zs; } else { sum = sum.multiply(tx).add(zs);// sum*x+zs; } } return sum; } private void setRootsPolynomialWithinRange( ArrayList<Coords> intersectCoords2, PolynomialFunction rootsPoly, double min, double max) { double roots[] = rootsPoly.getCoefficients(); int nrRealRoots = 0; if (roots.length > 1) { nrRealRoots = AlgoSimpleRootsPolynomial.getRoots(roots, eqnSolver); } for (int i = 0; i < nrRealRoots; ++i) { if (Kernel.isGreater(roots[i], max, Kernel.STANDARD_PRECISION) || Kernel.isGreater(min, roots[i], Kernel.STANDARD_PRECISION)) { roots[i] = Double.NaN; } } // makePoints(roots, nrRealRoots); // protected double getYValue(double t) { // return ty.value(t); } // @Override // protected double getXValue(double t) { // return tx.value(t); } int count = 0; for (int i = 0; i < nrRealRoots; i++) { if (Double.isNaN(roots[i])) { continue; } Coords pair = new Coords(tx.value(roots[i]), ty.value(roots[i]), 1); for (int k = 1; k < count + 1; k++) { if (count > 0 && distancePairSq(pair, intersectCoords2.get(intersectCoords2.size() - k)) < Kernel.STANDARD_PRECISION) { pair = null; break; } } if (pair != null) { intersectCoords2.add(pair); count++; } } } private static double distancePairSq(Coords p1, Coords p2) { return (p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) + (p1.getY() - p2.getY()) * (p1.getY() - p2.getY()); } /** * initializes auxiliary elements of the algo */ private void initElements() { this.outputPoints = createOutputPoints(); this.hasLabels = false; numOfOutputPoints = 0; polyPointCount = (getPoly().getPoints()).length; segCountOfPoly = isPolyclosed() ? polyPointCount : polyPointCount - 1; tempSegEndPoints = new GeoPoint[2]; for (int i = 0; i < tempSegEndPoints.length; i++) { tempSegEndPoints[i] = new GeoPoint(getConstruction()); } tempSeg = new GeoSegment(getConstruction()); eqnSolver = getConstruction().getKernel().getEquationSolver(); } /** * sets labels of output points * * @param labels * output label */ public void setLabels(String[] labels) { if (labels != null && labels.length == 1 && outputPoints.size() > 1 && labels[0] != null && !labels[0].equals("")) { outputPoints.setIndexLabels(labels[0]); } else { outputPoints.setLabels(labels); } update(); } /** * @return handler for output points */ protected OutputHandler<GeoPoint> createOutputPoints() { return new OutputHandler<GeoPoint>(new elementFactory<GeoPoint>() { @Override public GeoPoint newElement() { GeoPoint p = new GeoPoint(cons); p.setCoords(0, 0, 1); p.setParentAlgorithm(AlgoIntersectImplicitpolyPolyLine.this); return p; } }); } /** * getter of input geoImplicitPolinomial * * @return input implicit polynomial */ public GeoImplicit getImplicitPolynomial() { return implicitPolynomial; } /** * getter of input geoPoly (polyline/polygon) * * @return input geoPoly */ public GeoPoly getPoly() { return poly; } /** * getter of poly type * * @return true->polygon, false->polyline */ public boolean isPolyclosed() { return polyclosed; } }