package org.geogebra.common.geogebra3D.kernel3D.algos;
import java.util.TreeMap;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoPoint3D;
import org.geogebra.common.geogebra3D.kernel3D.geos.GeoSegment3D;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
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.GeoPolygon;
import org.geogebra.common.kernel.kernelND.GeoLineND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.kernelND.GeoSegmentND;
import org.geogebra.common.kernel.kernelND.HasSegments;
public class AlgoIntersectLinePolygon3D extends AlgoElement3D {
protected HasSegments p;
protected GeoLineND g;
protected OutputHandler<GeoElement> outputPoints; // output
private TreeMap<Double, Coords> newCoords;
public AlgoIntersectLinePolygon3D(Construction c, String[] labels,
GeoLineND g, GeoPolygon p) {
this(c, labels, (GeoElement) g, p);
}
public AlgoIntersectLinePolygon3D(Construction c, String[] labels,
GeoElement g, HasSegments p) {
super(c);
outputPoints = createOutputPoints();
setFirstInput(g);
this.p = p;
newCoords = new TreeMap<Double, Coords>(
Kernel.doubleComparator(Kernel.STANDARD_PRECISION));
compute();
setInputOutput(); // for AlgoElement
setLabels(labels);
update();
}
/**
* set the first input
*
* @param geo
* geo
*/
protected void setFirstInput(GeoElement geo) {
this.g = (GeoLineND) geo;
}
/**
*
* @return first input
*/
protected GeoElement getFirstInput() {
return (GeoElement) g;
}
protected OutputHandler<GeoElement> createOutputPoints() {
return new OutputHandler<GeoElement>(new elementFactory<GeoElement>() {
@Override
public GeoPoint3D newElement() {
GeoPoint3D p1 = new GeoPoint3D(cons);
p1.setCoords(0, 0, 0, 1);
p1.setParentAlgorithm(AlgoIntersectLinePolygon3D.this);
return p1;
}
});
}
protected OutputHandler<GeoElement> createOutputSegments() {
return new OutputHandler<GeoElement>(new elementFactory<GeoElement>() {
@Override
public GeoSegment3D newElement() {
GeoPoint3D aS = new GeoPoint3D(cons);
aS.setCoords(0, 0, 0, 1);
GeoPoint3D aE = new GeoPoint3D(cons);
aE.setCoords(0, 0, 0, 1);
GeoSegment3D a = new GeoSegment3D(cons, aS, aE);
a.setParentAlgorithm(AlgoIntersectLinePolygon3D.this);
return a;
}
});
}
protected Coords o1, d1;
protected void setIntersectionLine() {
o1 = g.getPointInD(3, 0).getInhomCoordsInSameDimension();
d1 = g.getPointInD(3, 1).getInhomCoordsInSameDimension().sub(o1);
}
/**
* calc intersection coords
*
* @param p
* polygon
* @param newCoords
* coords
*/
protected void intersectionsCoords(HasSegments hasSegments,
TreeMap<Double, Coords> newCoords) {
// TODO: move these to intersectLinePolyline3D
GeoPolygon p1 = (GeoPolygon) hasSegments;
// check if the line is contained by the polygon plane
switch (AlgoIntersectCS1D2D.getConfigLinePlane(g, p1)) {
case GENERAL: // intersect line/interior of polygon
intersectionsCoordsGeneral(p1, newCoords);
break;
case CONTAINED: // intesect line/segments
intersectionsCoordsContained(p1, newCoords);
break;
case PARALLEL: // no intersection
break;
}
}
/**
* calc intersection coords when line is contained in polygon's plane
*
* @param p
* polygon
* @param newCoords
* coords
*/
protected void intersectionsCoordsContained(HasSegments p,
TreeMap<Double, Coords> newCoords) {
// line origin and direction
setIntersectionLine();
for (int i = 0; i < p.getSegments().length; i++) {
GeoSegmentND seg = p.getSegments()[i];
Coords o2 = seg.getPointInD(3, 0).getInhomCoordsInSameDimension();
Coords d2 = seg.getPointInD(3, 1).getInhomCoordsInSameDimension()
.sub(o2);
Coords[] project = CoordMatrixUtil.nearestPointsFromTwoLines(o1, d1,
o2, d2);
// check if projection is intersection point
if (project != null && project[0].equalsForKernel(project[1],
Kernel.STANDARD_PRECISION)) {
double t1 = project[2].get(1); // parameter on line
double t2 = project[2].get(2); // parameter on segment
if (checkParameter(t1) && seg.respectLimitedPath(t2)) {
newCoords.put(t1, project[0]);
}
}
}
}
/**
* calc intersection coords when line is not contained in polygon's plane
*
* @param p
* polygon
* @param newCoords
* coords
*/
protected void intersectionsCoordsGeneral(GeoPolygon p,
TreeMap<Double, Coords> newCoords) {
Coords globalCoords = new Coords(4);
Coords inPlaneCoords = new Coords(4);
Coords singlePoint = AlgoIntersectCS1D2D.getIntersectLinePlane(g, p,
globalCoords, inPlaneCoords);
// check if projection is intersection point
if (singlePoint != null) {
newCoords.put(0d, singlePoint);
}
}
/**
* check the first parameter
*
* @param t1
* parameter
* @return true if ok
*/
protected boolean checkParameter(double t1) {
return g.respectLimitedPath(t1);
}
@Override
public void compute() {
// clear the points map
newCoords.clear();
// init index
int index = 0;
if (((GeoElement) p).isDefined() && getFirstInput().isDefined()) {
// fill a new points map
// intersectionsCoords(p, newCoords);
intersectionsCoords(p, newCoords);
// affect new computed points
outputPoints.adjustOutputSize(newCoords.size());
for (Coords coords : newCoords.values()) {
GeoPointND point = (GeoPointND) outputPoints.getElement(index);
point.setCoords(coords, false);
point.updateCoords();
index++;
}
}
// other points are undefined (eventually all if input are undefined)
for (; index < outputPoints.size(); index++) {
outputPoints.getElement(index).setUndefined();
}
}
@Override
public Commands getClassName() {
return Commands.Intersect;
}
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("")) {
outputPoints.setIndexLabels(labels[0]);
} else {
outputPoints.setLabels(labels);
}
}
@Override
protected void setInputOutput() {
input = new GeoElement[2];
input[0] = getFirstInput();
input[1] = (GeoElement) p;
setDependencies(); // done by AlgoElement
}
}