/*
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 30. August 2001, 21:37
*/
package org.geogebra.common.kernel.algos;
import java.util.ArrayList;
import java.util.TreeMap;
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.CoordMatrixUtil;
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.GeoPolygon;
import org.geogebra.common.kernel.geos.GeoRay;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
/**
* Algo for intersection of a line with the interior of a polygon
*
* @author Mathieu
*/
public class AlgoIntersectPathLinePolygon extends AlgoElement {
protected GeoLineND g; // input
protected GeoPolygon p; // input
protected OutputHandler<GeoElement> outputSegments; // output
protected TreeMap<Double, Coords> newCoords;
/**
* common constructor
*
* @param c
* @param geo
* line
* @param p
* polygon
*/
public AlgoIntersectPathLinePolygon(Construction c, GeoElement geo,
GeoElement p) {
super(c);
outputSegments = createOutputSegments();
setFirstInput(geo);
setSecondInput(p);
newCoords = new TreeMap<Double, Coords>(
Kernel.doubleComparator(Kernel.STANDARD_PRECISION));
setInputOutput(); // for AlgoElement
}
/**
* common constructor
*
* @param c
* @param labels
* @param geo
* line
* @param p
* polygon
*/
public AlgoIntersectPathLinePolygon(Construction c, String[] labels,
GeoElement geo, GeoElement p) {
this(c, geo, p);
if (!c.isSuppressLabelsActive()) {
setLabels(labels);
hasLabels = true;
}
update();
}
private boolean hasLabels = false;
public AlgoIntersectPathLinePolygon(Construction c) {
super(c);
}
/**
* @param geo
* first input
*/
protected void setFirstInput(GeoElement geo) {
this.g = (GeoLineND) geo;
}
/**
*
* @return first input
*/
protected GeoElement getFirstInput() {
return (GeoElement) g;
}
/**
* @param geo
* first input
*/
protected void setSecondInput(GeoElement geo) {
this.p = (GeoPolygon) geo;
}
/**
*
* @return first input
*/
protected GeoElement getSecondInput() {
return p;
}
protected OutputHandler<GeoElement> createOutputSegments() {
return new OutputHandler<GeoElement>(new elementFactory<GeoElement>() {
@Override
public GeoSegment newElement() {
GeoSegment a = new GeoSegment(cons);
GeoPoint aS = new GeoPoint(cons);
aS.setCoords(0, 0, 1);
GeoPoint aE = new GeoPoint(cons);
aE.setCoords(0, 0, 1);
a.setPoints(aS, aE);
a.setParentAlgorithm(AlgoIntersectPathLinePolygon.this);
setSegmentVisualProperties(a);
return a;
}
});
}
/**
* set visual style for new segments
*
* @param segment
* segment
*/
public void setSegmentVisualProperties(GeoElement segment) {
if (outputSegments.size() > 0) {
GeoElement seg0 = outputSegments.getElement(0);
segment.setAllVisualProperties(seg0, false);
segment.setViewFlags(seg0.getViewSet());
segment.setVisibleInView3D(seg0);
segment.setVisibleInViewForPlane(seg0);
}
}
@Override
public Commands getClassName() {
return Commands.IntersectPath;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECTION_CURVE;
}
// for AlgoElement
@Override
protected void setInputOutput() {
input = new GeoElement[2];
input[0] = getFirstInput();
input[1] = getSecondInput();
setDependencies(); // done by AlgoElement
}
protected Coords o1, d1;
protected void setIntersectionLine() {
o1 = g.getPointInD(3, 0).getInhomCoordsInSameDimension();
d1 = g.getPointInD(3, 1).getInhomCoordsInSameDimension().sub(o1);
}
/**
* check the first parameter
*
* @param t1
* parameter
* @return true if ok
*/
protected boolean checkParameter(double t1) {
return g.respectLimitedPath(t1);
}
private Coords project1, project2;
private double[] lineCoords, tmp;
/**
* calc all intersection points between line and polygon p
*
* @param p
* polygon
* @param newCoords
* intersection points
*/
protected void intersectionsCoords(GeoPolygon p) {
for (int i = 0; i < p.getSegments().length; i++) {
GeoSegmentND seg = p.getSegments()[i];
// check if the segment is defined (e.g. for regular polygons)
if (seg.isDefined()) {
Coords o2 = seg.getPointInD(3, 0)
.getInhomCoordsInSameDimension();
Coords d2 = seg.getPointInD(3, 1)
.getInhomCoordsInSameDimension().sub(o2);
if (project1 == null) {
project1 = new Coords(4);
project2 = new Coords(4);
lineCoords = new double[2];
tmp = new double[4];
}
CoordMatrixUtil.nearestPointsFromTwoLines(o1, d1, o2, d2,
project1.val, project2.val, lineCoords, tmp);
// check if projection is intersection point
if (!Double.isNaN(lineCoords[0]) && project1
.equalsForKernel(project2, Kernel.STANDARD_PRECISION)) {
double t1 = lineCoords[0]; // parameter on line
double t2 = lineCoords[1]; // parameter on segment
if (checkParameter(t1) && onSegment(t2)) {
addCoords(t1, project1, seg);
}
}
}
}
}
final private static boolean onSegment(double t) {
// t=0 and t=1 can be ignored: vertices will be added by
// addPolygonPoints()
return Kernel.isGreater(t, 0) && Kernel.isGreater(1, t);
}
/**
* check if midpoint (a,b) is in the polygon
*
* @param p
* polygon
* @param a
* point
* @param b
* point
* @return check
*/
protected boolean checkMidpoint(GeoPolygon p, Coords a, Coords b) {
return p.isInRegion((a.getX() + b.getX()) / 2,
(a.getY() + b.getY()) / 2);
}
/**
* add start/end points to new coords collection
*/
protected void addStartEndPoints() {
if (g instanceof GeoSegment) {
newCoords.put(0.0, g.getStartPoint().getInhomCoordsInD2());
newCoords.put(1.0, g.getEndPoint().getInhomCoordsInD2());
} else if (g instanceof GeoRay) {
newCoords.put(0d, g.getStartPoint().getInhomCoordsInD2());
}
}
private Coords project = Coords.createInhomCoorsInD3();
private double[] parameters = new double[2];
/**
* add polygon points that are on the line
*/
protected void addPolygonPoints() {
for (int i = 0; i < p.getPoints().length; i++) {
GeoPointND geoPoint = p.getPointsND()[i];
// check if the point is defined (e.g. for regular polygons)
if (geoPoint.isDefined()) {
Coords point = geoPoint.getInhomCoordsInD3();
point.projectLine(o1, d1, project, parameters);
// Log.debug("\npoint=\n"+point+"\nproject=\n"+project[0]);
// check if projection is intersection point
if (project.equalsForKernel(point, Kernel.STANDARD_PRECISION)) {
double t1 = parameters[0];
if (checkParameter(t1)) {
addCoords(t1, project, geoPoint);
}
}
}
}
}
/**
* add coords
*
* @param parameter
* @param coords
* @param newCoords
*/
protected void addCoords(double parameter, Coords coords,
GeoElementND parent) {
newCoords.put(parameter, new Coords(coords.getX(), coords.getY()));
}
/**
* set all new intersection points coords
*/
protected void setNewCoords() {
newCoords.clear();
// line origin and direction
setIntersectionLine();
// add start/end points for segments/rays
addStartEndPoints();
// add polygon points
addPolygonPoints();
// fill a new points map
intersectionsCoords(p);
}
@Override
public void compute() {
// set the point map
setNewCoords();
// set segments
if (newCoords.size() < 2) { // no segment
outputSegments.adjustOutputSize(1);
outputSegments.getElement(0).setUndefined();
} else {
// check which bi-points are segments, and save indices
ArrayList<Coords[]> segmentList = new ArrayList<Coords[]>();
Coords[] points = new Coords[newCoords.size()];
newCoords.values().toArray(points);
Coords b = points[0];
Coords startSegment = null;
Coords endSegment = null;
for (int i = 1; i < newCoords.size(); i++) {
Coords a = b;
b = points[i];
if (checkMidpoint(p, a, b)) {
if (startSegment == null)
{
startSegment = a; // new start segment
}
endSegment = b; // extend segment to b
} else {
if (startSegment != null) {// add last correct segment
segmentList
.add(new Coords[] { startSegment, endSegment });
startSegment = null;
}
}
}
if (startSegment != null) {
segmentList.add(new Coords[] { startSegment, endSegment });
}
// adjust segments output
if (segmentList.size() == 0) {
outputSegments.adjustOutputSize(1);
outputSegments.getElement(0).setUndefined();
} else {
outputSegments.adjustOutputSize(segmentList.size());
if (hasLabels) {
outputSegments.updateLabels();
}
int indexSegment = 0;
for (Coords[] seg : segmentList) {
GeoSegmentND segment = (GeoSegmentND) outputSegments
.getElement(indexSegment);
// Log.debug("\na=\n"+seg[0]+"\nb=\n"+seg[1]);
setSegment(segment, seg[0], seg[1]);
// ((GeoElement) segment).update(); // TODO optimize it
indexSegment++;
}
}
}
}
/**
* set segment start and end points
*
* @param seg
* segment
* @param start
* point
* @param end
* point
*/
final protected static void setSegment(GeoSegmentND seg, Coords start,
Coords end) {
seg.setTwoPointsInhomCoords(start, end);
}
@Override
public String toString(StringTemplate tpl) {
return getLoc().getPlain("IntersectionOfAandB",
getFirstInput().getLabel(tpl), getSecondInput().getLabel(tpl));
}
protected void setLabels(String[] labels) {
if (labels != null && labels.length == 1 && outputSegments.size() > 1
&& labels[0] != null && !labels[0].equals("")) {
outputSegments.setIndexLabels(labels[0]);
} else {
outputSegments.setLabels(labels);
}
}
}