/* * 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. */ /* * AlgoIntersectLines.java * * Created on 11. March 2015, 19:14 */ package org.geogebra.common.kernel.algos; import java.util.ArrayList; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; 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.kernel.kernelND.GeoPointND; /** * Algorithm for intersection of two PolyLines * * @author thilina * */ public class AlgoIntersectPolyLines extends AlgoElement { protected GeoPoly polyA, polyB; protected final boolean polyAClosed, polyBClosed; protected ArrayList<Coords> intersectingCoords; // intersections protected OutputHandler<GeoElement> outputPoints; // output // two dummy GeoSegments to be used at intersecting points calculation protected GeoSegment[] dummySegment = new GeoSegment[2]; // one dummy GeoPOint to be used at intersecting point calculation protected GeoPoint[] dummyPoint = new GeoPoint[5]; public boolean isPolyAClosed() { return polyAClosed; } public boolean isPolyBClosed() { return polyBClosed; } /** * Common Constructor * * @param construction * @param labels * @param polyLine1 * @param polyLine2 */ public AlgoIntersectPolyLines(Construction construction, String[] labels, GeoPoly polyA, GeoPoly polyB, boolean polyAClosed, boolean polyBClosed) { super(construction); this.polyA = polyA; this.polyAClosed = polyAClosed; this.polyB = polyB; this.polyBClosed = polyBClosed; this.outputPoints = this.createOutputPoints(); this.intersectingCoords = new ArrayList<Coords>(); this.dummySegment[0] = new GeoSegment(getConstruction()); this.dummySegment[1] = new GeoSegment(getConstruction()); for (int i = 0; i < this.dummyPoint.length; i++) { this.dummyPoint[i] = new GeoPoint(getConstruction()); } this.dummySegment[0].setStartPoint(this.dummyPoint[0]); this.dummySegment[0].setEndPoint(this.dummyPoint[1]); this.dummySegment[1].setStartPoint(this.dummyPoint[2]); this.dummySegment[1].setEndPoint(this.dummyPoint[3]); compute(); setInputOutput(); setLabels(labels); update(); } @Override protected void setInputOutput() { input = new GeoElement[2]; input[0] = (GeoElement) this.polyA; input[1] = (GeoElement) this.polyB; setDependencies(); } @Override public void compute() { // clears the point map this.intersectingCoords.clear(); // calculates intersection points intersectionCoords(this.polyA, this.polyB, this.intersectingCoords); // update and/or create points this.outputPoints.adjustOutputSize(this.intersectingCoords.size() > 0 ? this.intersectingCoords.size() : 1); // affect new computed points int index = 0; for (; index < this.intersectingCoords.size(); index++) { Coords coords = this.intersectingCoords.get(index); GeoPointND point = (GeoPointND) this.outputPoints.getElement(index); point.setCoords(coords, false); point.updateCoords(); } // other points are undefined for (; index < this.outputPoints.size(); index++) { this.outputPoints.getElement(index).setUndefined(); } } @Override public GetCommand getClassName() { return Commands.Intersect; } @Override public int getRelatedModeID() { return EuclidianConstants.MODE_INTERSECT; } /** * * @return handler for output points */ protected OutputHandler<GeoElement> createOutputPoints() { return new OutputHandler<GeoElement>(new elementFactory<GeoElement>() { @Override public GeoPoint newElement() { GeoPoint p = new GeoPoint(cons); p.setCoords(0, 0, 1); p.setParentAlgorithm(AlgoIntersectPolyLines.this); return p; } }); } /** * if only one label (e.g. "A") for more than one output, new labels will be * A_1, A_2, ... * * @param labels */ protected void setLabels(String[] labels) { // if only one label (e.g. "A") for more than one output, new labels // will be A_1, A_2, ... if (labels != null && labels.length == 1 && // outputPoints.size() > 1 && labels[0] != null && !labels[0].equals("")) { this.outputPoints.setIndexLabels(labels[0]); } else { this.outputPoints.setLabels(labels); this.outputPoints.setIndexLabels(this.outputPoints.getElement(0) .getLabel(StringTemplate.defaultTemplate)); } } /** * calculates the intersecting points of two polyLines * * @param polyLineA * input polyLine 1 * @param polyLineB * input polyLine 2 * @param newCoords * TreeMap to add calculated intersecting Coords */ protected void intersectionCoords(GeoPoly polyA, GeoPoly polyB, ArrayList<Coords> newCoords) { GeoPointND[] pointsA = polyA.getPoints(); GeoPointND[] pointsB = polyB.getPoints(); int noOfSegmentsA = isPolyAClosed() ? pointsA.length : pointsA.length - 1; int noOfSegmentsB = isPolyBClosed() ? pointsB.length : pointsB.length - 1; for (int i = 0; i < noOfSegmentsA; i++) { this.dummyPoint[0].setCoords(polyA.getPoint(i)); if (i == (pointsA.length - 1)) { this.dummyPoint[1].setCoords(polyA.getPoint(0)); } else { this.dummyPoint[1].setCoords(polyA.getPoint(i + 1)); } GeoVec3D.lineThroughPoints(this.dummyPoint[0], this.dummyPoint[1], this.dummySegment[0]); for (int k = 0; k < noOfSegmentsB; k++) { this.dummyPoint[2].setCoords(polyB.getPoint(k)); if (k == (pointsB.length - 1)) { this.dummyPoint[3].setCoords(polyB.getPoint(0)); } else { this.dummyPoint[3].setCoords(polyB.getPoint(k + 1)); } GeoVec3D.lineThroughPoints(this.dummyPoint[2], this.dummyPoint[3], this.dummySegment[1]); GeoVec3D.cross(this.dummySegment[0], this.dummySegment[1], this.dummyPoint[4]); // checks whether the cross product(this.dummyPoint[4]) actually // an intersection point if (this.dummyPoint[4].isDefined()) { if (!(Kernel .isZero(this.dummySegment[0] .distance(this.dummyPoint[4])) && Kernel.isZero(this.dummySegment[1] .distance(this.dummyPoint[4])))) { this.dummyPoint[4].setUndefined(); } } // adds dummyPoint Coords into intersectingCoords ArrayList if (this.dummyPoint[4].isDefined()) { newCoords.add(this.dummyPoint[4].getCoords()); } } } } }