package org.geogebra.common.kernel.algos;
import org.geogebra.common.euclidian.EuclidianConstants;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoConic;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoSegment;
import org.geogebra.common.kernel.geos.GeoVec3D;
import org.geogebra.common.kernel.kernelND.GeoConicNDConstants;
import org.geogebra.common.kernel.kernelND.GeoConicPartND;
import org.geogebra.common.util.debug.Log;
/**
* @author thilina
*
*/
public class AlgoIntersectSegmentConicRegion extends AlgoIntersect {
protected OutputHandler<GeoSegment> outputSegments;
protected GeoPoint[] intersectPoints;
protected GeoPoint[] closureIntersect;
/**
* when the Conic is a conic part. (Upto today(13/06/2015) only circle parts
* are available. so this won't work with other conic parts)
*/
protected GeoPoint midPoint;
protected GeoPoint[] endPoints;
protected GeoSegment[] closureSegments;
protected GeoSegment segment;
protected GeoConic conic;
private int numberOfLineParts;
private int numberOfOutputLines;
protected boolean hasLabels;
/**
* constructor with label assignment for output
*
* @param cons
* construction
* @param labels
* label array
* @param segment
* input segment
* @param conic
* input conic
*/
public AlgoIntersectSegmentConicRegion(Construction cons, String[] labels,
GeoSegment segment, GeoConic conic) {
this(cons, segment, conic);
setLabels(labels);
hasLabels = true;
update();
}
/**
* Common constructor for AlgoIntersectSegmentConicRegion.
*
* @param cons
* construction
* @param segment
* input segment
* @param conic
* input conic
*/
public AlgoIntersectSegmentConicRegion(Construction cons,
GeoSegment segment, GeoConic conic) {
super(cons);
this.segment = segment;
this.conic = conic;
initElements();
setInputOutput();
setDependencies();
compute();
}
/**
* sets the labels of the output segments
*
* @param labels
* String[]
*/
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);
}
}
@Override
public void compute() {
// calculate intersect point between input segment and input Conic curve
AlgoIntersectSegmentConicRegion.intersectSegmentConic(getSegment(),
getConic(), intersectPoints);
// if conic is a conic part
if (getConic().getType() == GeoConicNDConstants.CONIC_CIRCLE
&& getConic().isLimitedPath()) {
// derive midpoint, endpoint, closure segments of the input conic
AlgoIntersectSegmentConicRegion.calcConicClosure(getConic(),
midPoint, endPoints, closureSegments);
// calculate intersection points between input segment and closure
// segments
AlgoIntersectSegmentConicRegion
.intersectSegmentConicClosureSegments(getSegment(),
getConic(), closureSegments, closureIntersect,
intersectPoints);
}
numberOfLineParts = 1;
calcNoOfLineParts();
GeoPoint P;
GeoPoint Q;
numberOfOutputLines = 0;
switch (numberOfLineParts) {
case 1: {
outputSegments.adjustOutputSize(1, false);
P = getSegment().getStartPoint();
Q = getSegment().getEndPoint();
if (getConic().isInRegion((P.inhomX + Q.inhomX) / 2.0d,
(P.inhomY + Q.inhomY) / 2.0d)) {
outputSegments.getElement(0).set(P, Q, getSegment());
numberOfOutputLines++;
Log.debug("case 1");
break;
}
outputSegments.getElement(0).setUndefined();
Log.debug("case 1");
break;
}
case 2: {
outputSegments.adjustOutputSize(1, false);
P = getSegment().getStartPoint();
Q = getSegment().getEndPoint();
// select intersection point
GeoPoint pnt;
if (intersectPoints[0].isDefined()) {
pnt = intersectPoints[0];
} else if (intersectPoints[1].isDefined()) {
pnt = intersectPoints[1];
} else if (closureIntersect[0].isDefined()) {
pnt = closureIntersect[0];
} else {
pnt = closureIntersect[1];
}
// set output intersect segments
if (!Kernel.isZero(P.distance(pnt))) {
if (getConic().isInRegion((P.inhomX + pnt.inhomX) / 2.0d,
(P.inhomY + pnt.inhomY) / 2.0d)) {
outputSegments.getElement(0).set(P, pnt, getSegment());
numberOfOutputLines++;
Log.debug("case 2");
break;
}
}
if (!Kernel.isZero(pnt.distance(Q))) {
if (getConic().isInRegion((pnt.inhomX + Q.inhomX) / 2.0d,
(pnt.inhomY + Q.inhomY) / 2.0d)) {
outputSegments.getElement(0).set(pnt, Q, getSegment());
numberOfOutputLines++;
Log.debug("case 2");
break;
}
}
outputSegments.getElement(0).setUndefined();
Log.debug("case 2");
break;
}
case 3: {
double t1, t2;
int order[] = { 0, 1, 2, 3 };
boolean[] isPartValid = { false, false, false };
// select the intersecting points
GeoPoint pnt[] = new GeoPoint[4];
pnt[0] = getSegment().getStartPoint();
int count = 1;
if (intersectPoints[0].isDefined()) {
pnt[count] = intersectPoints[0];
count++;
}
if (intersectPoints[1].isDefined()) {
pnt[count] = intersectPoints[1];
count++;
}
if (closureIntersect[0].isDefined()) {
pnt[count] = closureIntersect[0];
count++;
}
if (closureIntersect[1].isDefined()) {
pnt[count] = closureIntersect[1];
count++;
}
pnt[3] = getSegment().getEndPoint();
// sorting intersection points
t1 = getSegment().getPossibleParameter(pnt[1].getCoords());
t2 = getSegment().getPossibleParameter(pnt[2].getCoords());
if (t1 > t2) {
double temp = t1;
t1 = t2;
t2 = temp;
int intTemp = order[1];
order[1] = order[2];
order[2] = intTemp;
}
// counting no of output segments and assining them as output
count = 0;
for (int i = 0; i < 3; i++) {
if (!Kernel.isZero(pnt[order[i]].distance(pnt[order[i + 1]]))) {
if (getConic().isInRegion(
(pnt[order[i]].inhomX + pnt[order[i + 1]].inhomX)
/ 2.0d,
(pnt[order[i]].inhomY + pnt[order[i + 1]].inhomY)
/ 2.0d)) {
isPartValid[i] = true;
count++;
}
}
}
outputSegments.adjustOutputSize(count, false);
numberOfOutputLines = count;
count = 0;
for (int i = 0; i < 3; i++) {
if (isPartValid[i]) {
outputSegments.getElement(count).set(pnt[order[i]],
pnt[order[i + 1]], getSegment());
count++;
}
}
Log.debug("case 3");
break;
}
case 4: {
double t1, t2, t3;
int order[] = { 0, 1, 2, 3, 4 };
boolean[] isPartValid = { false, false, false, false };
// select the intersecting points
GeoPoint[] pnt = new GeoPoint[5];
int count = 1;
pnt[0] = getSegment().getStartPoint();
if (intersectPoints[0].isDefined()) {
pnt[count] = intersectPoints[0];
count++;
}
if (intersectPoints[1].isDefined()) {
pnt[count] = intersectPoints[1];
count++;
}
if (closureIntersect[0].isDefined()) {
pnt[count] = closureIntersect[0];
count++;
}
if (closureIntersect[1].isDefined()) {
pnt[count] = closureIntersect[1];
count++;
}
pnt[4] = getSegment().getEndPoint();
// sorting intersection points order on input segment starting from
// inputsegment.getStartPoint()
double temp;
t1 = getSegment().getPossibleParameter(pnt[1].getCoords());
t2 = getSegment().getPossibleParameter(pnt[2].getCoords());
t3 = getSegment().getPossibleParameter(pnt[3].getCoords());
int intTemp;
if (t1 > t2) {
temp = t1;
t1 = t2;
t2 = temp;
intTemp = order[1];
order[1] = order[2];
order[2] = intTemp;
}
if (t1 > t3) {
temp = t1;
t1 = t3;
t3 = temp;
intTemp = order[1];
order[1] = order[3];
order[3] = intTemp;
}
if (t2 > t3) {
temp = t2;
t2 = t3;
t3 = temp;
intTemp = order[2];
order[2] = order[3];
order[3] = intTemp;
}
// counting no of output segments and assining them as output
count = 0;
for (int i = 0; i < 4; i++) {
if (!Kernel.isZero(pnt[order[i]].distance(pnt[order[i + 1]]))) {
if (getConic().isInRegion(
(pnt[order[i]].inhomX + pnt[order[i + 1]].inhomX)
/ 2.0d,
(pnt[order[i]].inhomY + pnt[order[i + 1]].inhomY)
/ 2.0d)) {
isPartValid[i] = true;
count++;
}
}
}
outputSegments.adjustOutputSize(count, false);
numberOfOutputLines = count;
count = 0;
for (int i = 0; i < 4; i++) {
if (isPartValid[i]) {
outputSegments.getElement(count).set(pnt[order[i]],
pnt[order[i + 1]], getSegment());
count++;
}
}
Log.debug("case 4");
break;
}
case 5: {
double t1, t2, t3, t4, temp;
int[] order = { 0, 1, 2, 3, 4 };
int intTemp, count = 0;
boolean[] isPartValid = { false, false, false, false, false };
// get intersect point on segment
GeoPoint[] pnt = { getSegment().getStartPoint(), intersectPoints[0],
intersectPoints[1], closureIntersect[0],
closureIntersect[1], getSegment().getEndPoint() };
// sorting intersection points order on input segment starting from
// inputsegment.getStartPoint()
t1 = getSegment().getPossibleParameter(pnt[1].getCoords());
t2 = getSegment().getPossibleParameter(pnt[2].getCoords());
t3 = getSegment().getPossibleParameter(pnt[3].getCoords());
t4 = getSegment().getPossibleParameter(pnt[4].getCoords());
if (t1 > t2) {
temp = t1;
t1 = t2;
t2 = temp;
intTemp = order[1];
order[1] = order[2];
order[2] = intTemp;
}
if (t1 > t3) {
temp = t1;
t1 = t3;
t3 = temp;
intTemp = order[1];
order[1] = order[3];
order[3] = intTemp;
}
if (t1 > t4) {
temp = t1;
t1 = t4;
t4 = temp;
intTemp = order[1];
order[1] = order[4];
order[4] = intTemp;
}
if (t2 > t3) {
temp = t2;
t2 = t3;
t3 = temp;
intTemp = order[2];
order[2] = order[3];
order[3] = intTemp;
}
if (t2 > t4) {
temp = t2;
t2 = t4;
t4 = temp;
intTemp = order[2];
order[2] = order[4];
order[4] = intTemp;
}
if (t3 > t4) {
temp = t3;
t3 = t4;
t4 = temp;
intTemp = order[3];
order[3] = order[4];
order[4] = intTemp;
}
// counting no of output segments and assining them as output
count = 0;
for (int i = 0; i < 4; i++) {
if (!Kernel.isZero(pnt[order[i]].distance(pnt[order[i + 1]]))) {
if (getConic().isInRegion(
(pnt[order[i]].inhomX + pnt[order[i + 1]].inhomX)
/ 2.0d,
(pnt[order[i]].inhomY + pnt[order[i + 1]].inhomY)
/ 2.0d)) {
isPartValid[i] = true;
count++;
}
}
}
outputSegments.adjustOutputSize(count, false);
numberOfOutputLines = count;
count = 0;
for (int i = 0; i < 4; i++) {
if (isPartValid[i]) {
outputSegments.getElement(count).set(pnt[order[i]],
pnt[order[i + 1]], getSegment());
count++;
}
}
Log.debug("case 5");
break;
}
default: {
outputSegments.adjustOutputSize(1, false);
outputSegments.getElement(0).setUndefined();
Log.debug("case default");
}
}
if (hasLabels) {
outputSegments.updateLabels();
}
}
/**
* initialize Elements. CreateOutput is called inside
*/
protected void initElements() {
numberOfLineParts = 1;
numberOfOutputLines = 0;
hasLabels = false;
intersectPoints = new GeoPoint[2];
endPoints = new GeoPoint[2];
closureSegments = new GeoSegment[2];
closureIntersect = new GeoPoint[2];
for (int i = 0; i < intersectPoints.length; i++) {
intersectPoints[i] = new GeoPoint(getConstruction());
endPoints[i] = new GeoPoint(getConstruction());
closureSegments[i] = new GeoSegment(getConstruction());
closureIntersect[i] = new GeoPoint(getConstruction());
}
midPoint = new GeoPoint(getConstruction());
outputSegments = createOutputSegments();
}
/**
* calculates the numberOfLineParts and initialize the output points
*/
private void calcNoOfLineParts() {
// decide number of line parts using calculated intersection points
for (int i = 0; i < 2; i++) {
if (intersectPoints[i].isDefined()) {
numberOfLineParts += 1;
}
if (closureIntersect[i].isDefined()) {
numberOfLineParts += 1;
}
}
}
/**
* /** calculates and assigns the intersection point between given segment
* and conic curve
*
* @param segment
* GeoSegment
* @param conic
* GeoConic
* @param intersectPoints
* GeoPoint[2]
*/
public static void intersectSegmentConic(GeoSegment segment, GeoConic conic,
GeoPoint[] intersectPoints) {
/*
* calculate and assign intersection point between segment(as line) and
* conic(full conic)
*/
AlgoIntersectLineConic.intersectLineConic(segment, conic,
intersectPoints, Kernel.MIN_PRECISION);
for (int i = 0; i < 2; i++) {
if (intersectPoints[i].isDefined()) {
// checks the validitiy and the incidence on both conic and
// segment of intersection point
if (!(segment.isOnPath(intersectPoints[i], Kernel.MIN_PRECISION)
&& conic.isOnPath(intersectPoints[i],
Kernel.MIN_PRECISION))) {
intersectPoints[i].setUndefined();
}
}
}
}
/**
* calculate and set closure of the conic part. here midpoint,endPoints, and
* closure segments will be set
*
* @param conic
* GeoConic
* @param midPoint
* GeoPoint
* @param endPoints
* GeoPoint[2]
* @param closureSegments
* GeoSegment[2]
*/
public static void calcConicClosure(GeoConic conic, GeoPoint midPoint,
GeoPoint[] endPoints, GeoSegment[] closureSegments) {
if (conic.isLimitedPath()) {
double startParameter = ((GeoConicPartND) conic)
.getParameterStart();
double endParameter = ((GeoConicPartND) conic).getParameterEnd();
midPoint.setCoords(conic.getMidpoint(), true);
double[] halfAxes = conic.getHalfAxes();
// set end points
double x = midPoint.getX() + halfAxes[0] * Math.cos(startParameter);
double y = midPoint.getY() + halfAxes[0] * Math.sin(startParameter);
endPoints[0].setCoords(x, y, 1);
// Log.debug("closureSegment1-endPoints" + x + " , " + y);
x = midPoint.getX() + halfAxes[0] * Math.cos(endParameter);
y = midPoint.getY() + halfAxes[0] * Math.sin(endParameter);
endPoints[1].setCoords(x, y, 1);
// Log.debug("closureSegment1-endPoints" + x + " , " + y);
// set closure segments
switch (((GeoConicPartND) conic).getConicPartType()) {
case GeoConicNDConstants.CONIC_PART_ARC:
GeoVec3D.lineThroughPoints(endPoints[0], endPoints[1],
closureSegments[0]);
closureSegments[0].setPoints(endPoints[0], endPoints[1]);
closureSegments[1].setCoords(Double.NaN, Double.NaN,
Double.NaN);
break;
case GeoConicNDConstants.CONIC_PART_SECTOR:
GeoVec3D.lineThroughPoints(midPoint, endPoints[0],
closureSegments[0]);
closureSegments[0].setPoints(midPoint, endPoints[0]);
GeoVec3D.lineThroughPoints(midPoint, endPoints[1],
closureSegments[1]);
closureSegments[1].setPoints(midPoint, endPoints[1]);
break;
default:
for (int i = 0; i < 2; i++) {
closureSegments[i].setCoords(Double.NaN, Double.NaN,
Double.NaN);
}
}
} else { // if full conic no endpoints or closure segments
for (int i = 0; i < 2; i++) {
endPoints[i].setUndefined();
closureSegments[i].setCoords(Double.NaN, Double.NaN,
Double.NaN);
}
}
}
/**
* calculate and assign intersection point between closure segments and the
* segment
*
* @param segment
* GeoSegment
* @param conic
* GeoConinc
* @param closureSegments
* GeoSegment[2]
* @param closureIntersect
* GeoPoint[2]
* @param intersectPoints
* calculated intersecting point between conic curve and input
* segment GeoPoint[2]
*/
public static void intersectSegmentConicClosureSegments(GeoSegment segment,
GeoConic conic, GeoSegment[] closureSegments,
GeoPoint[] closureIntersect, GeoPoint[] intersectPoints) {
switch (((GeoConicPartND) conic).getConicPartType()) {
case GeoConicNDConstants.CONIC_PART_ARC:
if (!Double.isNaN(closureSegments[0].getX())) {
// calc intersection points
GeoVec3D.cross(closureSegments[0], segment,
closureIntersect[0]);
// checks whether the cross product(intersection point) is
// actually an intersection point
if (closureIntersect[0].isDefined()) {
if (!(Kernel
.isZero(closureSegments[0]
.distance(closureIntersect[0]))
&& Kernel.isZero(
segment.distance(closureIntersect[0])))) {
closureIntersect[0].setUndefined();
}
}
if (closureIntersect[0].isDefined()) {
for (int i = 0; i < 2; i++) {
if (Kernel.isZero(closureIntersect[0]
.distanceSqr(intersectPoints[i]))) {
closureIntersect[0].setUndefined();
}
}
}
} else {
closureIntersect[0].setUndefined();
closureIntersect[1].setUndefined();
}
break;
case GeoConicNDConstants.CONIC_PART_SECTOR:
if (!Double.isNaN(closureSegments[0].getX())
&& !Double.isNaN(closureSegments[1].getX())) {
for (int i = 0; i < closureIntersect.length; i++) {
// calc intersection points
GeoVec3D.cross(closureSegments[i], segment,
closureIntersect[i]);
// checks whether the cross product(intersection point) is
// actually an intersection point
if (closureIntersect[i].isDefined()) {
if (!(Kernel
.isZero(closureSegments[i]
.distance(closureIntersect[i]))
&& Kernel.isZero(segment
.distance(closureIntersect[i])))) {
closureIntersect[i].setUndefined();
}
}
if (closureIntersect[i].isDefined()) {
for (int j = 0; j < 2; j++) {
if (Kernel.isZero(closureIntersect[i]
.distanceSqr(intersectPoints[j]))) {
closureIntersect[i].setUndefined();
}
}
}
}
} else {
closureIntersect[0].setUndefined();
closureIntersect[1].setUndefined();
}
break;
default:
closureIntersect[0].setUndefined();
closureIntersect[1].setUndefined();
}
}
/**
* Returns output intersection segments
*
* @return GeoSegment[]
*/
public GeoSegment[] getIntersectionSegments() {
GeoSegment[] seg = new GeoSegment[numberOfOutputLines];
for (int i = 0; i < numberOfOutputLines; i++) {
seg[i] = outputSegments.getElement(i);
}
return seg;
}
/**
* create the necessary output handlers
*
* @return OutputHandler<GeoSegment>
*/
protected OutputHandler<GeoSegment> createOutputSegments() {
return new OutputHandler<GeoSegment>(new elementFactory<GeoSegment>() {
@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(AlgoIntersectSegmentConicRegion.this);
setSegmentVisualProperties(a);
return a;
}
});
}
/**
* set visual style for new segments
*
* @param segment
* GeoElement 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);
}
}
/**
* set visual style for intersecting points passed to this method
*
* @param pnt
* GeoPoint pnt
*/
protected void setPointVisualProperties(GeoPoint pnt) {
pnt.setAuxiliaryObject(true);
pnt.setViewFlags(getConic().getViewSet());
}
/**
* Returns number of line Parts on the input segment created by intersecting
* points
*
* @return int
*/
public int getNumOfLineParts() {
return numberOfLineParts;
}
/**
* Returns number of intersection path segments
*
* @return int
*/
public int getOutputSize() {
return numberOfOutputLines;
}
@Override
public Commands getClassName() {
return Commands.IntersectPath;
}
@Override
public int getRelatedModeID() {
return EuclidianConstants.MODE_INTERSECTION_CURVE;
}
@Override
public GeoPoint[] getIntersectionPoints() {
return intersectPoints;
}
@Override
protected GeoPoint[] getLastDefinedIntersectionPoints() {
// TODO
return null;
}
@Override
protected void setInputOutput() {
input = new GeoElement[2];
input[0] = getSegment();
input[1] = getConic();
}
/**
* getter of input conic
*
* @return GeoConic
*/
public GeoConic getConic() {
return this.conic;
}
/**
* getter of input segment
*
* @return GeoSegment
*/
public GeoSegment getSegment() {
return this.segment;
}
}