Created
April 19, 2021 14:20
-
-
Save micycle1/e6548e4e2cb66a52371499baac634683 to your computer and use it in GitHub Desktop.
MinimumEnclosingTriangle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.locationtech.jts.geom.Coordinate; | |
import java.util.ArrayList; | |
import java.util.List; | |
/** | |
* Implementation of linear minimum area enclosing triangle algorithm. | |
* Computational and Applied Mathematics, 35(2), 423–438. | |
* doi:10.1007/s40314-014-0198-8 | |
* | |
* https://github.com/opencv/opencv/blob/master/modules/imgproc/src/min_enclosing_triangle.cpp | |
* https://github.com/ovidiuparvu/minimal-area-triangle/tree/master/modules/triangle/src | |
* | |
* @author Algorithm by Ovidiu Parvu | |
* @author Java port by Michael Carleton | |
* | |
*/ | |
public class MinimumEnclosingTriangle { | |
// Intersection of line and polygon | |
private static final int INTERSECTS_BELOW = 1; | |
private static final int INTERSECTS_ABOVE = 2; | |
private static final int INTERSECTS_CRITICAL = 3; | |
// Error messages | |
private static final String ERR_SIDE_B_GAMMA = "The position of side B could not be determined, because gamma(b) could not be computed."; | |
private static final String ERR_VERTEX_C_ON_SIDE_B = "The position of the vertex C on side B could not be determined, because the considered lines do not intersect."; | |
// Possible values for validation flag | |
private static final int VALIDATION_SIDE_A_TANGENT = 0; | |
private static final int VALIDATION_SIDE_B_TANGENT = 1; | |
private static final int VALIDATION_SIDES_FLUSH = 2; | |
// Threshold value for comparisons | |
private static final double EPSILON = 1E-5; | |
/* | |
* Broken out into class level for Java as C++ uses pass-by-reference. | |
*/ | |
Coordinate vertexA, vertexB, vertexC; | |
Coordinate sideAStartVertex, sideAEndVertex; | |
Coordinate sideBStartVertex, sideBEndVertex; | |
Coordinate sideCStartVertex, sideCEndVertex; | |
public List<Coordinate> triangle; | |
public double area = Double.MAX_VALUE; | |
public int validationFlag; | |
public MinimumEnclosingTriangle() { | |
triangle = new ArrayList<Coordinate>(); | |
vertexA = new Coordinate(); | |
vertexB = new Coordinate(); | |
vertexC = new Coordinate(); | |
sideAStartVertex = new Coordinate(); | |
sideBStartVertex = new Coordinate(); | |
sideCStartVertex = new Coordinate(); | |
sideAEndVertex = new Coordinate(); | |
sideBEndVertex = new Coordinate(); | |
sideCEndVertex = new Coordinate(); | |
} | |
/** | |
* Find the minimum area enclosing triangle for the given polygon | |
* | |
* @param polygon The polygon representing the convex hull of the points (not | |
* closed) | |
*/ | |
public void findMinimumAreaEnclosingTriangle(final List<Coordinate> polygon) { | |
// Algorithm specific variables | |
List<Coordinate> copy = new ArrayList<Coordinate>(); | |
polygon.forEach(c -> copy.add(c.copy())); | |
MutableInt a, b, c; | |
final int nrOfPoints; | |
// Variables initialisation | |
nrOfPoints = polygon.size(); | |
a = new MutableInt(1); | |
b = new MutableInt(2); | |
c = new MutableInt(0); | |
// Main algorithm steps | |
while (c.value < nrOfPoints) { | |
advanceBToRightChain(polygon, nrOfPoints, b, c); | |
moveAIfLowAndBIfHigh(polygon, nrOfPoints, a, b, c); | |
searchForBTangency(polygon, nrOfPoints, a, b, c); | |
updateSidesCA(polygon, nrOfPoints, a, c); | |
if (isNotBTangency(polygon, nrOfPoints, a.value, b.value, c.value)) { | |
updateSidesBA(polygon, nrOfPoints, a.value, b.value, c.value); | |
} else { | |
updateSideB(polygon, nrOfPoints, a.value, b.value, c.value); | |
} | |
if (isLocalMinimalTriangle(polygon, nrOfPoints, a.value, b.value)) { | |
updateMinimumAreaEnclosingTriangle(); | |
} | |
c.inc(); | |
} | |
} | |
/** | |
* Return the minimum area enclosing (pseudo-)triangle in case the convex // | |
* polygon has at most three points | |
* | |
* | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param triangle Minimum area triangle enclosing the given polygon | |
* | |
* @param area Area of the minimum area enclosing triangle | |
*/ | |
void returnMinimumAreaEnclosingTriangle(final List<Coordinate> polygon) { | |
int nrOfPoints = polygon.size(); | |
for (int i = 0; i < 3; i++) { | |
triangle.add(polygon.get(i % nrOfPoints)); | |
} | |
area = areaOfTriangle(triangle.get(0), triangle.get(1), triangle.get(2)); | |
} | |
/** | |
* Advance b to the right chain | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param b Index b | |
* | |
* @param c Index c int | |
*/ | |
void advanceBToRightChain(final List<Coordinate> polygon, int nrOfPoints, MutableInt b, final MutableInt c) { | |
while (greaterOrEqual(height(successor(b.value, nrOfPoints), polygon, nrOfPoints, c.value), | |
height(b, polygon, nrOfPoints, c))) { | |
advance(b, nrOfPoints); | |
} | |
} | |
/** | |
* Move "a" if it is low and "b" if it is high | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param c Index c | |
*/ | |
void moveAIfLowAndBIfHigh(final List<Coordinate> polygon, int nrOfPoints, MutableInt a, MutableInt b, | |
MutableInt c) { | |
Coordinate gammaOfA = new Coordinate(); | |
while (height(b, polygon, nrOfPoints, c) > height(a, polygon, nrOfPoints, c)) { | |
if ((gamma(a.value, gammaOfA, polygon, nrOfPoints, a.value, c.value)) | |
&& (intersectsBelow(gammaOfA, b.value, polygon, nrOfPoints, c.value))) { | |
advance(b, nrOfPoints); | |
} else { | |
advance(a, nrOfPoints); | |
} | |
} | |
} | |
/** | |
* Search for the tangency of side B | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param c Index c | |
*/ | |
void searchForBTangency(final List<Coordinate> polygon, int nrOfPoints, MutableInt a, MutableInt b, MutableInt c) { | |
Coordinate gammaOfB = new Coordinate(); // TODO mutates by gamma() | |
while (((gamma(b.value, gammaOfB, polygon, nrOfPoints, a.value, c.value)) | |
&& (intersectsBelow(gammaOfB, b.value, polygon, nrOfPoints, c.value))) | |
&& (greaterOrEqual(height(b.value, polygon, nrOfPoints, c.value), | |
height(predecessor(a.value, nrOfPoints), polygon, nrOfPoints, c.value)))) { | |
advance(b, nrOfPoints); | |
} | |
} | |
/** | |
* Check if tangency for side B was not obtained | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param c Index c | |
*/ | |
boolean isNotBTangency(final List<Coordinate> polygon, int nrOfPoints, int a, int b, int c) { | |
Coordinate gammaOfB = new Coordinate(); // TODO mutates by gamma(); | |
if (((gamma(b, gammaOfB, polygon, nrOfPoints, a, c)) && (intersectsAbove(gammaOfB, b, polygon, nrOfPoints, c))) | |
|| (height(b, polygon, nrOfPoints, c) < height(predecessor(a, nrOfPoints), polygon, nrOfPoints, c))) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Update sides A and C | |
* | |
* Side C will have as start and end vertices the polygon points "c" and "c-1" | |
* Side A will have as start and end vertices the polygon points "a" and "a-1" | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param c Index c | |
* | |
* @param sideAStartVertex Start vertex for defining side A | |
* | |
* @param sideAEndVertex End vertex for defining side A | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
void updateSidesCA(final List<Coordinate> polygon, int nrOfPoints, MutableInt a, MutableInt c) { | |
sideCStartVertex = polygon.get(predecessor(c.value, nrOfPoints)); | |
sideCEndVertex = polygon.get(c.value).copy(); | |
sideAStartVertex = polygon.get(predecessor(a.value, nrOfPoints)); | |
sideAEndVertex = polygon.get(a.value).copy(); | |
} | |
/** | |
* Update sides B and possibly A if tangency for side B was not obtained | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param c Index c | |
* | |
* @param validationFlag Flag used for validation | |
* | |
* @param sideAStartVertex Start vertex for defining side A | |
* | |
* @param sideAEndVertex End vertex for defining side A | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
void updateSidesBA(final List<Coordinate> polygon, int nrOfPoints, int a, int b, int c) { | |
// Side B is flush with edge [b, b-1) | |
sideBStartVertex = polygon.get(predecessor(b, nrOfPoints)).copy(); | |
sideBEndVertex = polygon.get(b).copy(); | |
// Find middle point of side B | |
Coordinate sideBMiddlePoint = new Coordinate(); | |
if ((middlePointOfSideB(sideBMiddlePoint, sideAStartVertex, sideAEndVertex, sideBStartVertex, sideBEndVertex, | |
sideCStartVertex, sideCEndVertex)) | |
&& (height(sideBMiddlePoint, polygon, nrOfPoints, c) < height(predecessor(a, nrOfPoints), polygon, | |
nrOfPoints, c))) { | |
sideAStartVertex = polygon.get(predecessor(a, nrOfPoints)).copy(); | |
sideAEndVertex = findVertexCOnSideB(polygon, nrOfPoints, a, c, sideBStartVertex, sideBEndVertex, | |
sideCStartVertex, sideCEndVertex).copy(); | |
validationFlag = VALIDATION_SIDE_A_TANGENT; | |
} else { | |
validationFlag = VALIDATION_SIDES_FLUSH; | |
} | |
} | |
/** | |
* Set side B if tangency for side B was obtained | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param c Index c | |
* | |
* @param validationFlag Flag used for validation | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
*/ | |
void updateSideB(final List<Coordinate> polygon, int nrOfPoints, int a, int b, int c) { | |
if (!gamma(b, sideBStartVertex, polygon, nrOfPoints, a, c)) { | |
System.err.println(ERR_SIDE_B_GAMMA); | |
} | |
sideBEndVertex = polygon.get(b).copy(); | |
validationFlag = VALIDATION_SIDE_B_TANGENT; | |
} | |
/** | |
* Update the triangle vertices after all sides were set and check if a local // | |
* minimal triangle was found or not | |
* | |
* See paper .get(2) for more details | |
* | |
* @param vertexA Vertex A of the enclosing triangle | |
* | |
* @param vertexB Vertex B of the enclosing triangle | |
* | |
* @param vertexC Vertex C of the enclosing triangle | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param validationFlag Flag used for validation | |
* | |
* @param sideAStartVertex Start vertex for defining side A | |
* | |
* @param sideAEndVertex End vertex for defining side A | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
boolean isLocalMinimalTriangle(final List<Coordinate> polygon, int nrOfPoints, int a, int b) { | |
if ((!lineIntersection(sideAStartVertex, sideAEndVertex, sideBStartVertex, sideBEndVertex, vertexC)) | |
|| (!lineIntersection(sideAStartVertex, sideAEndVertex, sideCStartVertex, sideCEndVertex, vertexB)) | |
|| (!lineIntersection(sideBStartVertex, sideBEndVertex, sideCStartVertex, sideCEndVertex, vertexA))) { | |
return false; | |
} | |
return isValidMinimalTriangle(polygon, nrOfPoints, a, b); | |
} | |
/** | |
* Check if the found minimal triangle is valid | |
* | |
* This means that all midpoints of the triangle should touch the polygon | |
* | |
* See paper .get(2) for more details | |
* | |
* @param vertexA Vertex A of the enclosing triangle | |
* | |
* @param vertexB Vertex B of the enclosing triangle | |
* | |
* @param vertexC Vertex C of the enclosing triangle | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param b Index b | |
* | |
* @param validationFlag Flag used for validation | |
* | |
* @param sideAStartVertex Start vertex for defining side A | |
* | |
* @param sideAEndVertex End vertex for defining side A | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
boolean isValidMinimalTriangle(final List<Coordinate> polygon, int nrOfPoints, int a, int b) { | |
Coordinate midpointSideA = middlePoint(vertexB, vertexC); | |
Coordinate midpointSideB = middlePoint(vertexA, vertexC); | |
Coordinate midpointSideC = middlePoint(vertexA, vertexB); | |
boolean sideAValid = (validationFlag == VALIDATION_SIDE_A_TANGENT) | |
? (areEqualPoints(midpointSideA, polygon.get(predecessor(a, nrOfPoints)))) | |
: (isPointOnLineSegment(midpointSideA, sideAStartVertex, sideAEndVertex)); | |
boolean sideBValid = (validationFlag == VALIDATION_SIDE_B_TANGENT) | |
? (areEqualPoints(midpointSideB, polygon.get(b))) | |
: (isPointOnLineSegment(midpointSideB, sideBStartVertex, sideBEndVertex)); | |
boolean sideCValid = (validationFlag == VALIDATION_SIDES_FLUSH) | |
|| isPointOnLineSegment(midpointSideC, sideCStartVertex, sideCEndVertex); | |
return (sideAValid && sideBValid && sideCValid); | |
} | |
/** | |
* Update the current minimum area enclosing triangle if the newly obtained // | |
* one has a smaller area | |
* | |
* | |
* | |
* @param triangle Minimum area triangle enclosing the given polygon | |
* | |
* @param area Area of the minimum area triangle enclosing the given polygon | |
* | |
* @param vertexA Vertex A of the enclosing triangle | |
* | |
* @param vertexB Vertex B of the enclosing triangle | |
* | |
* @param vertexC Vertex C of the enclosing triangle | |
*/ | |
void updateMinimumAreaEnclosingTriangle() { | |
final double triangleArea = areaOfTriangle(vertexA, vertexB, vertexC); | |
if (triangleArea < area) { | |
triangle.clear(); | |
triangle.add(vertexA); | |
triangle.add(vertexB); | |
triangle.add(vertexC); | |
area = triangleArea; | |
} | |
} | |
/** | |
* Return the middle point of side B | |
* | |
* @param middlePointOfSideB Middle point of side B | |
* | |
* @param sideAStartVertex Start vertex for defining side A | |
* | |
* @param sideAEndVertex End vertex for defining side A | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
boolean middlePointOfSideB(final Coordinate middlePointOfSideB, final Coordinate sideAStartVertex, | |
final Coordinate sideAEndVertex, final Coordinate sideBStartVertex, final Coordinate sideBEndVertex, | |
final Coordinate sideCStartVertex, final Coordinate sideCEndVertex) { | |
// Coordinate vertexA = new Coordinate(); // TODO init as empty | |
// Coordinate vertexC = new Coordinate(); | |
if ((!lineIntersection(sideBStartVertex, sideBEndVertex, sideCStartVertex, sideCEndVertex, vertexA)) | |
|| (!lineIntersection(sideBStartVertex, sideBEndVertex, sideAStartVertex, sideAEndVertex, vertexC))) { | |
return false; | |
} | |
final Coordinate c = middlePoint(vertexA, vertexC); | |
middlePointOfSideB.x = c.x; | |
middlePointOfSideB.y = c.y; | |
return true; | |
} | |
/** | |
* Check if the line intersects below | |
* | |
* Check if the line determined by gammaPoint and polygon.get(polygonPointIndex) | |
* intersects the polygon below the point polygon.get(polygonPointIndex) | |
* | |
* @param gammaPoint Gamma(p) | |
* | |
* @param polygonPointIndex Index of the polygon point which is considered when | |
* determining the line | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
boolean intersectsBelow(final Coordinate gammaPoint, int polygonPointIndex, final List<Coordinate> polygon, | |
int nrOfPoints, int c) { | |
double angleOfGammaAndPoint = angleOfLineWrtOxAxis(polygon.get(polygonPointIndex), gammaPoint); | |
return (intersects(angleOfGammaAndPoint, polygonPointIndex, polygon, nrOfPoints, c) == INTERSECTS_BELOW); | |
} | |
/** | |
* Check if the line intersects above | |
* | |
* Check if the line determined by gammaPoint and polygon.get(polygonPointIndex) | |
* intersects the polygon above the point polygon.get(polygonPointIndex) | |
* | |
* @param gammaPoint Gamma(p) | |
* | |
* @param polygonPointIndex Index of the polygon point which is considered when | |
* determining the line | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
boolean intersectsAbove(final Coordinate gammaPoint, int polygonPointIndex, final List<Coordinate> polygon, | |
int nrOfPoints, int c) { | |
double angleOfGammaAndPoint = angleOfLineWrtOxAxis(gammaPoint, polygon.get(polygonPointIndex)); | |
return (intersects(angleOfGammaAndPoint, polygonPointIndex, polygon, nrOfPoints, c) == INTERSECTS_ABOVE); | |
} | |
/** | |
* Check if/where the line determined by gammaPoint and // | |
* polygon.get(polygonPointIndex) intersects the polygon | |
* | |
* | |
* | |
* @param angleGammaAndPoint Angle determined by gammaPoint and | |
* polygon.get(polygonPointIndex) wrt Ox axis | |
* | |
* @param polygonPointIndex Index of the polygon point which is considered when | |
* determining the line | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
int intersects(double angleGammaAndPoint, int polygonPointIndex, final List<Coordinate> polygon, int nrOfPoints, | |
int c) { | |
double anglePointPredecessor = angleOfLineWrtOxAxis(polygon.get(predecessor(polygonPointIndex, nrOfPoints)), | |
polygon.get(polygonPointIndex)); | |
double anglePointSuccessor = angleOfLineWrtOxAxis(polygon.get(successor(polygonPointIndex, nrOfPoints)), | |
polygon.get(polygonPointIndex)); | |
double angleFlushEdge = angleOfLineWrtOxAxis(polygon.get(predecessor(c, nrOfPoints)), polygon.get(c)); | |
if (isFlushAngleBtwPredAndSucc(angleFlushEdge, anglePointPredecessor, anglePointSuccessor)) { | |
if ((isGammaAngleBtw(angleGammaAndPoint, anglePointPredecessor, angleFlushEdge)) | |
|| (almostEqual(angleGammaAndPoint, anglePointPredecessor))) { | |
return intersectsAboveOrBelow(predecessor(polygonPointIndex, nrOfPoints), polygonPointIndex, polygon, | |
nrOfPoints, c); | |
} else if ((isGammaAngleBtw(angleGammaAndPoint, anglePointSuccessor, angleFlushEdge)) | |
|| (almostEqual(angleGammaAndPoint, anglePointSuccessor))) { | |
return intersectsAboveOrBelow(successor(polygonPointIndex, nrOfPoints), polygonPointIndex, polygon, | |
nrOfPoints, c); | |
} | |
} else { | |
if ((isGammaAngleBtw(angleGammaAndPoint, anglePointPredecessor, anglePointSuccessor)) | |
|| ((isGammaAngleEqualTo(angleGammaAndPoint, anglePointPredecessor)) | |
&& (!isGammaAngleEqualTo(angleGammaAndPoint, angleFlushEdge))) | |
|| ((isGammaAngleEqualTo(angleGammaAndPoint, anglePointSuccessor)) | |
&& (!isGammaAngleEqualTo(angleGammaAndPoint, angleFlushEdge)))) { | |
return INTERSECTS_BELOW; | |
} | |
} | |
return INTERSECTS_CRITICAL; | |
} | |
/** | |
* If (gamma(x) x) intersects P between successorOrPredecessorIndex and // | |
* pointIntex is it above/below? | |
* | |
* | |
* | |
* @param succPredIndex Index of the successor or predecessor | |
* | |
* @param pointIndex Index of the point x in the polygon | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
private static int intersectsAboveOrBelow(int succPredIndex, int pointIndex, final List<Coordinate> polygon, | |
int nrOfPoints, | |
int c) { | |
if (height(succPredIndex, polygon, nrOfPoints, c) > height(pointIndex, polygon, nrOfPoints, c)) { | |
return INTERSECTS_ABOVE; | |
} else { | |
return INTERSECTS_BELOW; | |
} | |
} | |
/** | |
* Find gamma for a given point "p" specified by its index | |
* | |
* The function returns true if gamma exists i.e. if lines (a a-1) and (x y) | |
* intersect and false otherwise. In case the two lines intersect in point | |
* intersectionPoint, gamma is computed. | |
* | |
* Considering that line (x y) is a line parallel to (c c-1) and that the | |
* distance between the lines is equal to 2 * height(p), we can have two | |
* possible (x y) lines. | |
* | |
* Therefore, we will compute two intersection points between the lines (x y) | |
* and (a a-1) and take the point which is on the same side of line (c c-1) as | |
* the polygon. | |
* | |
* See paper .get(2) and formula for distance from point to a line for more | |
* details | |
* | |
* @param polygonPointIndex Index of the polygon point | |
* | |
* @param gammaPoint Point gamma(polygon.get(polygonPointIndex)) | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param c Index c | |
*/ | |
boolean gamma(int polygonPointIndex, Coordinate gammaPoint, final List<Coordinate> polygon, int nrOfPoints, int a, | |
int c) { | |
Coordinate intersectionPoint1 = new Coordinate(); // init empty (other methods set vals) | |
Coordinate intersectionPoint2 = new Coordinate(); // init empty | |
// Get intersection points if they exist | |
if (!findGammaIntersectionPoints(polygon, nrOfPoints, c, polygonPointIndex, polygon.get(a), | |
polygon.get(predecessor(a, nrOfPoints)), polygon.get(c), polygon.get(predecessor(c, nrOfPoints)), | |
intersectionPoint1, intersectionPoint2)) { | |
return false; | |
} | |
// Select the point which is on the same side of line C as the polygon | |
if (areOnTheSameSideOfLine(intersectionPoint1, polygon.get(successor(c, nrOfPoints)), polygon.get(c), | |
polygon.get(predecessor(c, nrOfPoints)))) { | |
gammaPoint.x = intersectionPoint1.x; // TODO mutate x & y instead of c++ reference | |
gammaPoint.y = intersectionPoint1.y; // TODO mutate x & y instead of c++ reference | |
} else { | |
gammaPoint.x = intersectionPoint2.x; | |
gammaPoint.y = intersectionPoint2.y; | |
} | |
return true; | |
} | |
/** | |
* Find vertex C which lies on side B at a distance = 2 * height(a-1) from // | |
* side C | |
* | |
* Considering that line (x y) is a line parallel to (c c-1) and that the | |
* distance between the lines is equal to 2 * height(a-1), we can have two | |
* possible (x y) lines. | |
* | |
* Therefore, we will compute two intersection points between the lines (x y) | |
* and (b b-1) and take the point which is on the same side of line (c c-1) as | |
* the polygon. | |
* | |
* See paper .get(2) and formula for distance from point to a line for more | |
* details | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param a Index a | |
* | |
* @param c Index c | |
* | |
* @param sideBStartVertex Start vertex for defining side B | |
* | |
* @param sideBEndVertex End vertex for defining side B | |
* | |
* @param sideCStartVertex Start vertex for defining side C | |
* | |
* @param sideCEndVertex End vertex for defining side C | |
*/ | |
Coordinate findVertexCOnSideB(final List<Coordinate> polygon, int nrOfPoints, int a, int c, | |
final Coordinate sideBStartVertex, final Coordinate sideBEndVertex, final Coordinate sideCStartVertex, | |
final Coordinate sideCEndVertex) { | |
Coordinate intersectionPoint1 = new Coordinate(); | |
Coordinate intersectionPoint2 = new Coordinate(); | |
// Get intersection points if they exist | |
if (!findGammaIntersectionPoints(polygon, nrOfPoints, c, predecessor(a, nrOfPoints), sideBStartVertex, | |
sideBEndVertex, sideCStartVertex, sideCEndVertex, intersectionPoint1, intersectionPoint2)) { | |
System.out.println(ERR_VERTEX_C_ON_SIDE_B); | |
} | |
// Select the point which is on the same side of line C as the polygon | |
if (areOnTheSameSideOfLine(intersectionPoint1, polygon.get(successor(c, nrOfPoints)), polygon.get(c), | |
polygon.get(predecessor(c, nrOfPoints)))) { | |
return intersectionPoint1; | |
} else { | |
return intersectionPoint2; | |
} | |
} | |
/** | |
* Find the intersection points to compute gamma(point) | |
* | |
* | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
* | |
* @param polygonPointIndex Index of the polygon point for which the distance | |
* is known | |
* | |
* @param side1StartVertex Start vertex for side 1 | |
* | |
* @param side1EndVertex End vertex for side 1 | |
* | |
* @param side2StartVertex Start vertex for side 2 | |
* | |
* @param side2EndVertex End vertex for side 2 | |
* | |
* @param intersectionPoint1 First intersection point between one pair of lines | |
* | |
* @param intersectionPoint2 Second intersection point between other pair of | |
* lines | |
*/ | |
boolean findGammaIntersectionPoints(final List<Coordinate> polygon, int nrOfPoints, int c, int polygonPointIndex, | |
final Coordinate side1StartVertex, final Coordinate side1EndVertex, final Coordinate side2StartVertex, | |
final Coordinate side2EndVertex, Coordinate intersectionPoint1, Coordinate intersectionPoint2) { | |
List<Double> side1Params = lineEquationParameters(side1StartVertex, side1EndVertex); | |
List<Double> side2Params = lineEquationParameters(side2StartVertex, side2EndVertex); | |
// Compute side C extra parameter using the formula for distance from a point to | |
// a line | |
double polygonPointHeight = height(polygonPointIndex, polygon, nrOfPoints, c); | |
double distFormulaDenom = Math | |
.sqrt((side2Params.get(0) * side2Params.get(0)) + (side2Params.get(1) * side2Params.get(1))); | |
double sideCExtraParam = 2 * polygonPointHeight * distFormulaDenom; | |
// Get intersection points if they exist or if lines are identical | |
if (areIntersectingLines(side1Params, side2Params, sideCExtraParam, intersectionPoint1, intersectionPoint2)) { | |
return true; | |
} else if (areIdenticalLines(side1Params, side2Params, sideCExtraParam)) { | |
intersectionPoint1.x = side1StartVertex.x; // TODO mutate x,y | |
intersectionPoint1.y = side1StartVertex.y; | |
intersectionPoint2.x = side1EndVertex.x; | |
intersectionPoint2.y = side1EndVertex.y; | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Check if the given lines are identical or not | |
* | |
* The lines are specified as: ax + by + c = 0 OR ax + by + c (+/-) | |
* sideCExtraParam = 0 | |
* | |
* @param side1Params Vector containing the values of a, b and c for side 1 | |
* | |
* @param side2Params Vector containing the values of a, b and c for side 2 | |
* | |
* @param sideCExtraParam Extra parameter for the flush edge C | |
*/ | |
boolean areIdenticalLines(final List<Double> side1Params, final List<Double> side2Params, double sideCExtraParam) { | |
return ((areIdenticalLines(side1Params.get(0), side1Params.get(1), -(side1Params.get(2)), side2Params.get(0), | |
side2Params.get(1), -(side2Params.get(2)) - sideCExtraParam)) | |
|| (areIdenticalLines(side1Params.get(0), side1Params.get(1), -(side1Params.get(2)), side2Params.get(0), | |
side2Params.get(1), -(side2Params.get(2)) + sideCExtraParam))); | |
} | |
/** | |
* Check if the given lines intersect or not. If the lines intersect find // | |
* their intersection points. | |
* | |
* The lines are specified as: ax + by + c = 0 OR ax + by + c (+/-) | |
* sideCExtraParam = 0 | |
* | |
* @param side1Params Vector containing the values of a, b and c for side | |
* 1 | |
* | |
* @param side2Params Vector containing the values of a, b and c for side | |
* 2 | |
* | |
* @param sideCExtraParam Extra parameter for the flush edge C | |
* | |
* @param intersectionPoint1 The first intersection point, if it exists | |
* | |
* @param intersectionPoint2 The second intersection point, if it exists | |
*/ | |
boolean areIntersectingLines(final List<Double> side1Params, final List<Double> side2Params, double sideCExtraParam, | |
Coordinate intersectionPoint1, Coordinate intersectionPoint2) { | |
return ((lineIntersection(side1Params.get(0), side1Params.get(1), -(side1Params.get(2)), side2Params.get(0), | |
side2Params.get(1), -(side2Params.get(2)) - sideCExtraParam, intersectionPoint1)) | |
&& (lineIntersection(side1Params.get(0), side1Params.get(1), -(side1Params.get(2)), side2Params.get(0), | |
side2Params.get(1), -(side2Params.get(2)) + sideCExtraParam, intersectionPoint2))); | |
} | |
/** | |
* Get the line equation parameters "a", "b" and "c" for the line determined // | |
* by points "p" and "q" | |
* | |
* The equation of the line is considered in the general form: ax + by + c = 0 | |
* | |
* @param p One point for defining the equation of the line | |
* | |
* @param q Second point for defining the equation of the line | |
*/ | |
List<Double> lineEquationParameters(final Coordinate p, final Coordinate q) { // TODO CHANGE TO ARRAY | |
List<Double> lineEquationParameters = new ArrayList<Double>(); | |
double[] out = lineEquationDeterminedByPoints(p, q); | |
double a = out[0]; | |
double b = out[1]; | |
double c = out[2]; | |
lineEquationParameters.add(a); | |
lineEquationParameters.add(b); | |
lineEquationParameters.add(c); | |
return lineEquationParameters; | |
} | |
/** | |
* Compute the height of the point | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygonPoint Polygon point | |
* | |
* @param polygon The polygon representing the convex hull of the points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
double height(final Coordinate polygonPoint, final List<Coordinate> polygon, int nrOfPoints, int c) { | |
Coordinate pointC = polygon.get(c); | |
Coordinate pointCPredecessor = polygon.get(predecessor(c, nrOfPoints)); | |
return distanceFromPointToLine(polygonPoint, pointC, pointCPredecessor); | |
} | |
/** | |
* Compute the height of the point specified by the given index | |
* | |
* See paper .get(2) for more details | |
* | |
* @param polygonPointIndex Index of the polygon point | |
* | |
* @param polygon The polygon representing the convex hull of the | |
* points | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
* | |
* @param c Index c | |
*/ | |
double height(MutableInt polygonPointIndex, final List<Coordinate> polygon, int nrOfPoints, MutableInt c) { | |
Coordinate pointC = polygon.get(c.value); | |
Coordinate pointCPredecessor = polygon.get(predecessor(c.value, nrOfPoints)); | |
Coordinate polygonPoint = polygon.get(polygonPointIndex.value); | |
return distanceFromPointToLine(polygonPoint, pointC, pointCPredecessor); | |
} | |
private static double height(int polygonPointIndex, final List<Coordinate> polygon, int nrOfPoints, int c) { | |
Coordinate pointC = polygon.get(c); | |
Coordinate pointCPredecessor = polygon.get(predecessor(c, nrOfPoints)); | |
Coordinate polygonPoint = polygon.get(polygonPointIndex); | |
return distanceFromPointToLine(polygonPoint, pointC, pointCPredecessor); | |
} | |
/** | |
* Advance the given index with one position | |
* | |
* @param index Index of the point | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
*/ | |
private void advance(MutableInt index, int nrOfPoints) { | |
index.setValue(successor(index.value, nrOfPoints)); | |
} | |
/** | |
* Return the successor of the provided point index | |
* | |
* The successor of the last polygon point is the first polygon point (circular | |
* referencing) | |
* | |
* @param index Index of the point | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
*/ | |
private static int successor(int index, int nrOfPoints) { | |
return ((index + 1) % nrOfPoints); | |
} | |
/** | |
* Return the predecessor of the provided point index | |
* | |
* The predecessor of the first polygon point is the last polygon point | |
* (circular referencing) | |
* | |
* @param index Index of the point | |
* | |
* @param nrOfPoints Number of points defining the convex polygon | |
*/ | |
private static int predecessor(int index, int nrOfPoints) { | |
return (index == 0) ? (nrOfPoints - 1) : (index - 1); | |
} | |
/** | |
* Check if the flush edge angle/opposite angle lie between the predecessor // | |
* and successor angle | |
* | |
* Check if the angle of the flush edge or its opposite angle lie between the | |
* angle of the predecessor and successor | |
* | |
* @param angleFlushEdge Angle of the flush edge | |
* | |
* @param anglePred Angle of the predecessor | |
* | |
* @param angleSucc Angle of the successor | |
*/ | |
boolean isFlushAngleBtwPredAndSucc(double angleFlushEdge, double anglePred, double angleSucc) { | |
if (isAngleBetweenNonReflex(angleFlushEdge, anglePred, angleSucc)) { | |
return true; | |
} else if (isOppositeAngleBetweenNonReflex(angleFlushEdge, anglePred, angleSucc)) { | |
angleFlushEdge = oppositeAngle(angleFlushEdge); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Check if the angle of the line (gamma(p) p) or its opposite angle is equal // | |
* to the given angle | |
* | |
* | |
* | |
* @param gammaAngle Angle of the line (gamma(p) p) | |
* | |
* @param angle Angle to compare against | |
*/ | |
boolean isGammaAngleEqualTo(double gammaAngle, double angle) { | |
return (almostEqual(gammaAngle, angle)); | |
} | |
/** | |
* Check if the angle of the line (gamma(p) p) or its opposite angle lie // | |
* between angle1 and angle2 | |
* | |
* | |
* | |
* @param gammaAngle Angle of the line (gamma(p) p) | |
* | |
* @param angle1 One of the boundary angles | |
* | |
* @param angle2 Another boundary angle | |
*/ | |
boolean isGammaAngleBtw(double gammaAngle, double angle1, double angle2) { | |
return (isAngleBetweenNonReflex(gammaAngle, angle1, angle2)); | |
} | |
/** | |
* Get the angle of the line measured from the Ox axis in counterclockwise // | |
* direction | |
* | |
* The line is specified by points "a" and "b". The value of the angle is | |
* expressed in degrees. | |
* | |
* @param a Point a | |
* | |
* @param b Point b | |
*/ | |
private static double angleOfLineWrtOxAxis(final Coordinate a, final Coordinate b) { | |
double y = b.y - a.y; | |
double x = b.x - a.x; | |
double angle = (Math.atan2(y, x) * 180 / Math.PI); | |
return (angle < 0) ? (angle + 360) : angle; | |
} | |
/** | |
* Check if angle1 lies between non reflex angle determined by angles 2 and 3 | |
* | |
* | |
* | |
* @param angle1 The angle which lies between angle2 and angle3 or not | |
* | |
* @param angle2 One of the boundary angles | |
* | |
* @param angle3 The other boundary angle | |
*/ | |
boolean isAngleBetweenNonReflex(double angle1, double angle2, double angle3) { | |
if (Math.abs(angle2 - angle3) > 180) { | |
if (angle2 > angle3) { | |
return (((angle2 < angle1) && (lessOrEqual(angle1, 360))) | |
|| ((lessOrEqual(0, angle1)) && (angle1 < angle3))); | |
} else { | |
return (((angle3 < angle1) && (lessOrEqual(angle1, 360))) | |
|| ((lessOrEqual(0, angle1)) && (angle1 < angle2))); | |
} | |
} else { | |
return isAngleBetween(angle1, angle2, angle3); | |
} | |
} | |
/** | |
* Check if the opposite of angle1, ((angle1 + 180) % 360), lies between non // | |
* reflex angle determined by angles 2 and 3 | |
* | |
* | |
* | |
* @param angle1 The angle which lies between angle2 and angle3 or not | |
* | |
* @param angle2 One of the boundary angles | |
* | |
* @param angle3 The other boundary angle | |
*/ | |
boolean isOppositeAngleBetweenNonReflex(double angle1, double angle2, double angle3) { | |
double angle1Opposite = oppositeAngle(angle1); | |
return (isAngleBetweenNonReflex(angle1Opposite, angle2, angle3)); | |
} | |
/** | |
* Check if angle1 lies between angles 2 and 3 | |
* | |
* | |
* | |
* @param angle1 The angle which lies between angle2 and angle3 or not | |
* | |
* @param angle2 One of the boundary angles | |
* | |
* @param angle3 The other boundary angle | |
*/ | |
private static boolean isAngleBetween(double angle1, double angle2, double angle3) { | |
if ((((int) (angle2 - angle3)) % 180) > 0) { | |
return ((angle3 < angle1) && (angle1 < angle2)); | |
} else { | |
return ((angle2 < angle1) && (angle1 < angle3)); | |
} | |
} | |
/** | |
* Return the angle opposite to the given angle | |
* | |
* if (angle < 180) then return (angle + 180); else return (angle - 180); endif | |
* | |
* @param angle Angle | |
*/ | |
private static double oppositeAngle(double angle) { | |
return (angle > 180) ? (angle - 180) : (angle + 180); | |
} | |
/** | |
* Compute the distance from a point "a" to a line specified by two points "B" | |
* // and "C" | |
* | |
* Formula used: | |
* | |
* |(x_c - x_b) * (y_b - y_a) - (x_b - x_a) * (y_c - y_b)| d = | |
* ------------------------------------------------------- sqrt(((x_c - x_b)^2) | |
* + ((y_c - y_b)^2)) | |
* | |
* Reference: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html | |
* (Last access: 15.09.2013) | |
* | |
* @param a Point from which the distance is measures | |
* | |
* @param linePointB One of the points determining the line | |
* | |
* @param linePointC One of the points determining the line | |
*/ | |
private static double distanceFromPointToLine(final Coordinate a, final Coordinate linePointB, | |
final Coordinate linePointC) { | |
double term1 = linePointC.x - linePointB.x; | |
double term2 = linePointB.y - a.y; | |
double term3 = linePointB.x - a.x; | |
double term4 = linePointC.y - linePointB.y; | |
double nominator = Math.abs((term1 * term2) - (term3 * term4)); | |
double denominator = Math.sqrt((term1 * term1) + (term4 * term4)); | |
return (denominator != 0) ? (nominator / denominator) : 0; | |
} | |
/** | |
* Compute the distance between two points | |
* | |
* Compute the Euclidean distance between two points | |
* | |
* @param a Point a | |
* | |
* @param b Point b | |
*/ | |
double distanceBtwPoints(final Coordinate a, final Coordinate b) { | |
double xDiff = a.x - b.x; | |
double yDiff = a.y - b.y; | |
return Math.sqrt((xDiff * xDiff) + (yDiff * yDiff)); | |
} | |
/** | |
* Compute the area of a triangle defined by three points | |
* | |
* The area is computed using the determinant method. An example is depicted at | |
* http://demonstrations.wolfram.com/TheAreaOfATriangleUsingADeterminant/ (Last | |
* access: 15.09.2013) | |
* | |
* @param a Point a | |
* | |
* @param b Point b | |
* | |
* @param c Point c | |
*/ | |
private static double areaOfTriangle(final Coordinate a, final Coordinate b, final Coordinate c) { | |
double posTerm = (a.x * b.y) + (a.y * c.x) + (b.x * c.y); | |
double negTerm = (b.y * c.x) + (a.x * c.y) + (a.y * b.x); | |
double determinant = posTerm - negTerm; | |
return Math.abs(determinant) / 2d; | |
} | |
/** | |
* Get the point in the middle of the segment determined by points "a" and "b" | |
* | |
* @param a Point a | |
* | |
* @param b Point b | |
*/ | |
private static Coordinate middlePoint(final Coordinate a, final Coordinate b) { | |
double middleX = (a.x + b.x) / 2d; | |
double middleY = (a.y + b.y) / 2d; | |
return new Coordinate(middleX, middleY); | |
} | |
/** | |
* Determine the intersection point of two lines, if this point exists | |
* | |
* Two lines intersect if they are not parallel (Parallel lines intersect at +/- | |
* infinity, but we do not consider this case here). | |
* | |
* The lines are specified in the following form: A1x + B1x = C1 A2x + B2x = C2 | |
* | |
* If det (= A1*B2 - A2*B1) == 0, then lines are parallel else they intersect | |
* | |
* If they intersect, then let us denote the intersection point with P(x, y) | |
* where: x = (C1*B2 - C2*B1) / (det) y = (C2*A1 - C1*A2) / (det) | |
* | |
* @param a1 A1 | |
* | |
* @param b1 B1 | |
* | |
* @param c1 C1 | |
* | |
* @param a2 A2 | |
* | |
* @param b2 B2 | |
* | |
* @param c2 C2 | |
* | |
* @param intersection The intersection point, if this point exists (mutates) | |
*/ | |
private static boolean lineIntersection(double a1, double b1, double c1, double a2, double b2, double c2, | |
Coordinate intersection) { | |
double det = (a1 * b2) - (a2 * b1); | |
if (!(almostEqual(det, 0))) { | |
intersection.x = ((c1 * b2) - (c2 * b1)) / (det); | |
intersection.y = ((c2 * a1) - (c1 * a2)) / (det); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Determine the intersection point of two lines, if this point exists | |
* | |
* Two lines intersect if they are not parallel (Parallel lines intersect at +/- | |
* infinity, but we do not consider this case here). | |
* | |
* The lines are specified by a pair of points each. If they intersect, then the | |
* function returns true, else it returns false. | |
* | |
* Lines can be specified in the following form: A1x + B1x = C1 A2x + B2x = C2 | |
* | |
* If det (= A1*B2 - A2*B1) == 0, then lines are parallel else they intersect | |
* | |
* If they intersect, then let us denote the intersection point with P(x, y) | |
* where: x = (C1*B2 - C2*B1) / (det) y = (C2*A1 - C1*A2) / (det) | |
* | |
* @param a1 First point for determining the first line | |
* | |
* @param b1 Second point for determining the first line | |
* | |
* @param a2 First point for determining the second line | |
* | |
* @param b2 Second point for determining the second line | |
* | |
* @param intersection The intersection point, if this point exists | |
*/ | |
private static boolean lineIntersection(final Coordinate a1, final Coordinate b1, final Coordinate a2, | |
final Coordinate b2, Coordinate intersection) { | |
double A1 = b1.y - a1.y; | |
double B1 = a1.x - b1.x; | |
double C1 = (a1.x * A1) + (a1.y * B1); | |
double A2 = b2.y - a2.y; | |
double B2 = a2.x - b2.x; | |
double C2 = (a2.x * A2) + (a2.y * B2); | |
double det = (A1 * B2) - (A2 * B1); | |
if (!almostEqual(det, 0)) { | |
intersection.x = ((C1 * B2) - (C2 * B1)) / det; | |
intersection.y = ((C2 * A1) - (C1 * A2)) / det; | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Get the values of "a", "b" and "c" of the line equation ax + by + c = 0 // | |
* knowing that point "p" and "q" are on the line | |
* | |
* a = q.y - p.y b = p.x - q.x c = - (p.x * a) - (p.y * b) | |
* | |
* @param p Point p | |
* | |
* @param q Point q | |
* | |
* @param a Parameter "a" from the line equation | |
* | |
* @param b Parameter "b" from the line equation | |
* | |
* @param c Parameter "c" from the line equation | |
*/ | |
double[] lineEquationDeterminedByPoints(final Coordinate p, final Coordinate q) { | |
// CV_Assert(areEqualPoints(p, q) == false); | |
double a = q.y - p.y; | |
double b = p.x - q.x; | |
double c = ((-p.y) * b) - (p.x * a); | |
return new double[] { a, b, c }; | |
} | |
/** | |
* Check if p1 and p2 are on the same side of the line determined by points a // | |
* and b | |
* | |
* | |
* | |
* @param p1 Point p1 | |
* | |
* @param p2 Point p2 | |
* | |
* @param a First point for determining line | |
* | |
* @param b Second point for determining line | |
*/ | |
boolean areOnTheSameSideOfLine(final Coordinate p1, final Coordinate p2, final Coordinate a, final Coordinate b) { | |
double[] out = lineEquationDeterminedByPoints(a, b); | |
double a1 = out[0]; | |
double b1 = out[1]; | |
double c1 = out[2]; | |
double p1OnLine = (a1 * p1.x) + (b1 * p1.y) + c1; | |
double p2OnLine = (a1 * p2.x) + (b1 * p2.y) + c1; | |
return (sign(p1OnLine) == sign(p2OnLine)); | |
} | |
/** | |
* Check if one point lies between two other points | |
* | |
* | |
* | |
* @param point Point lying possibly outside the line segment | |
* | |
* @param lineSegmentStart First point determining the line segment | |
* | |
* @param lineSegmentEnd Second point determining the line segment | |
*/ | |
boolean isPointOnLineSegment(final Coordinate point, final Coordinate lineSegmentStart, | |
final Coordinate lineSegmentEnd) { | |
double d1 = distanceBtwPoints(point, lineSegmentStart); | |
double d2 = distanceBtwPoints(point, lineSegmentEnd); | |
double lineSegmentLength = distanceBtwPoints(lineSegmentStart, lineSegmentEnd); | |
return (almostEqual(d1 + d2, lineSegmentLength)); | |
} | |
/** | |
* Check if two lines are identical | |
* | |
* Lines are be specified in the following form: A1x + B1x = C1 A2x + B2x = C2 | |
* | |
* If (A1/A2) == (B1/B2) == (C1/C2), then the lines are identical else they are | |
* not | |
* | |
* @param a1 A1 | |
* | |
* @param b1 B1 | |
* | |
* @param c1 C1 | |
* | |
* @param a2 A2 | |
* | |
* @param b2 B2 | |
* | |
* @param c2 C2 | |
*/ | |
boolean areIdenticalLines(double a1, double b1, double c1, double a2, double b2, double c2) { | |
double a1B2 = a1 * b2; | |
double a2B1 = a2 * b1; | |
double a1C2 = a1 * c2; | |
double a2C1 = a2 * c1; | |
double b1C2 = b1 * c2; | |
double b2C1 = b2 * c1; | |
return ((almostEqual(a1B2, a2B1)) && (almostEqual(b1C2, b2C1)) && (almostEqual(a1C2, a2C1))); | |
} | |
/** | |
* Check if points point1 and point2 are equal or not | |
* | |
* | |
* | |
* @param point1 One point | |
* | |
* @param point2 The other point | |
*/ | |
boolean areEqualPoints(final Coordinate point1, final Coordinate point2) { | |
return (almostEqual(point1.x, point2.x) && almostEqual(point1.y, point2.y)); | |
} | |
/** | |
* Return the sign of the number | |
* | |
* The sign function returns: -1, if number < 0 +1, if number > 0 0, otherwise | |
*/ | |
private static int sign(double number) { | |
return (number > 0) ? 1 : ((number < 0) ? -1 : 0); | |
} | |
/** | |
* Return the maximum of the provided numbers | |
*/ | |
private static double maximum(double number1, double number2, double number3) { | |
return Math.max(Math.max(number1, number2), number3); | |
} | |
/** | |
* Check if the two numbers are equal (almost) | |
* | |
* The expression for determining if two real numbers are equal is: if (Abs(x - | |
* y) <= EPSILON * Max(1.0f, Abs(x), Abs(y))). | |
* | |
* @param number1 First number | |
* | |
* @param number2 Second number | |
*/ | |
private static boolean almostEqual(double number1, double number2) { | |
return (Math.abs(number1 - number2) <= (EPSILON * maximum(1.0, Math.abs(number1), Math.abs(number2)))); | |
} | |
/** | |
* Check if the first number is greater than or equal to the second number | |
* | |
* | |
* | |
* @param number1 First number | |
* | |
* @param number2 Second number | |
*/ | |
private static boolean greaterOrEqual(double number1, double number2) { | |
return ((number1 > number2) || (almostEqual(number1, number2))); | |
} | |
/** | |
* Check if the first number is less than or equal to the second number | |
* | |
* | |
* | |
* @param number1 First number | |
* | |
* @param number2 Second number | |
*/ | |
private static boolean lessOrEqual(double number1, double number2) { | |
return ((number1 < number2) || (almostEqual(number1, number2))); | |
} | |
private class MutableInt { | |
int value; | |
public MutableInt(int value) { | |
this.value = value; | |
} | |
public int getValue() { | |
return this.value; | |
} | |
public void setValue(int value) { | |
this.value = value; | |
} | |
public void inc() { | |
this.value++; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment