This is an automated email from the ASF dual-hosted git repository. erans pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-geometry.git
The following commit(s) were added to refs/heads/master by this push: new eba3a8e GEOMETRY-42: Adding Vector2D.signedArea(Vector2D) method; removing Vector2D.cross() method since the cross product is not defined in two dimensions new 1f334d1 Merge branch 'GEOMETRY-42__matt' eba3a8e is described below commit eba3a8e286cf960d93b6b896592eb95120b3d6a2 Author: Matt Juntunen <matt.juntu...@hotmail.com> AuthorDate: Wed Feb 6 00:25:35 2019 -0500 GEOMETRY-42: Adding Vector2D.signedArea(Vector2D) method; removing Vector2D.cross() method since the cross product is not defined in two dimensions --- .../commons/geometry/euclidean/twod/Vector2D.java | 40 ++++++++----------- .../geometry/euclidean/twod/Vector2DTest.java | 45 +++++++++++++++++----- .../euclidean/twod/hull/AklToussaintHeuristic.java | 15 +++++++- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java index 08b4f1f..b5c1356 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java @@ -281,32 +281,24 @@ public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> { return dir.getComponent(this, true, Vector2D::normalize); } - /** - * Compute the cross-product of the instance and the given vector. - * <p> - * The cross product can be used to determine the location of a point - * with regard to the line formed by (p1, p2) and is calculated as: - * \[ - * P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1) - * \] - * with \(p3 = (x_3, y_3)\) being this instance. - * <p> - * If the result is 0, the points are collinear, i.e. lie on a single straight line L; - * if it is positive, this point lies to the left, otherwise to the right of the line - * formed by (p1, p2). - * - * @param p1 first point of the line - * @param p2 second point of the line - * @return the cross-product + /** Compute the signed area of the parallelogram with sides formed by this instance + * and the given vector. * - * @see <a href="http://mathworld.wolfram.com/CrossProduct.html">Cross product (Mathworld)</a> + * <p>The parallelogram in question can be visualized by taking the current instance as the + * first side and placing {@code v} at the end of it to create the second. The other sides + * are formed by lines parallel to these two vectors. If {@code v} points to the <em>left</em> of + * the current instance (ie, the parallelogram is wound counter-clockwise), then the + * returned area is positive. If {@code v} points to the <em>right</em> of the current instance, + * (ie, the parallelogram is wound clockwise), then the returned area is negative. If + * the vectors are collinear (ie, they lie on the same line), then 0 is returned. The area of + * the triangle formed by the two vectors is exactly half of the returned value. + * @param v vector representing the second side of the constructed parallelogram + * @return the signed area of the parallelogram formed by this instance and the given vector */ - public double cross(final Vector2D p1, final Vector2D p2) { - final double x1 = p2.x - p1.x; - final double y1 = y - p1.y; - final double x2 = x - p1.x; - final double y2 = p2.y - p1.y; - return LinearCombination.value(x1, y1, -x2, y2); + public double signedArea(final Vector2D v) { + return LinearCombination.value( + x, v.y, + -y, v.x); } /** Apply the given transform to this vector, returning the result as a diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java index 5a4c268..aac0f5d 100644 --- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java +++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java @@ -513,19 +513,46 @@ public class Vector2DTest { } @Test - public void testCrossProduct() { + public void testSignedArea() { // arrange - Vector2D p1 = Vector2D.of(1, 1); - Vector2D p2 = Vector2D.of(2, 2); + double eps = 1e-10; - Vector2D p3 = Vector2D.of(3, 3); - Vector2D p4 = Vector2D.of(1, 2); - Vector2D p5 = Vector2D.of(2, 1); + Vector2D a = Vector2D.PLUS_X; + Vector2D b = Vector2D.PLUS_Y; + Vector2D c = Vector2D.of(1, 1).withNorm(2.0); + Vector2D d = Vector2D.of(-1, 1).withNorm(3.0); // act/assert - Assert.assertEquals(0.0, p3.cross(p1, p2), EPS); - Assert.assertEquals(1.0, p4.cross(p1, p2), EPS); - Assert.assertEquals(-1.0, p5.cross(p1, p2), EPS); + Assert.assertEquals(1.0, a.signedArea(b), eps); + Assert.assertEquals(-1.0, b.signedArea(a), eps); + + double xAxisAndCArea = 2 * Math.cos(0.25 * Geometry.PI); + Assert.assertEquals(xAxisAndCArea, a.signedArea(c), eps); + Assert.assertEquals(-xAxisAndCArea, c.signedArea(a), eps); + + double xAxisAndDArea = 3 * Math.cos(0.25 * Geometry.PI); + Assert.assertEquals(xAxisAndDArea, a.signedArea(d), eps); + Assert.assertEquals(-xAxisAndDArea, d.signedArea(a), eps); + + Assert.assertEquals(6.0, c.signedArea(d), eps); + Assert.assertEquals(-6.0, d.signedArea(c), eps); + } + + @Test + public void testSignedArea_collinear() { + // arrange + Vector2D a = Vector2D.PLUS_X; + Vector2D b = Vector2D.PLUS_Y; + Vector2D c = Vector2D.of(-3, 8); + + // act/assert + Assert.assertEquals(0.0, a.signedArea(a), EPS); + Assert.assertEquals(0.0, b.signedArea(b), EPS); + Assert.assertEquals(0.0, c.signedArea(c), EPS); + + Assert.assertEquals(0.0, a.signedArea(a.multiply(100.0)), EPS); + Assert.assertEquals(0.0, b.signedArea(b.negate()), EPS); + Assert.assertEquals(0.0, c.signedArea(c.multiply(-0.03)), EPS); } @Test diff --git a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/AklToussaintHeuristic.java b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/AklToussaintHeuristic.java index af3c034..27e42b6 100644 --- a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/AklToussaintHeuristic.java +++ b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/AklToussaintHeuristic.java @@ -129,7 +129,7 @@ public final class AklToussaintHeuristic { } // get the location of the point relative to the first two vertices - final double last = v0.cross(v1, v2); + final double last = signedAreaPoints(v1, v2, v0); final int size = quadrilateralPoints.size(); // loop through the rest of the vertices for (int i = 1; i < size; i++) { @@ -143,11 +143,22 @@ public final class AklToussaintHeuristic { // do side of line test: multiply the last location with this location // if they are the same sign then the operation will yield a positive result // -x * -y = +xy, x * y = +xy, -x * y = -xy, x * -y = -xy - if (last * v0.cross(v1, v2) < 0) { + if (last * signedAreaPoints(v1, v2, v0) < 0) { return false; } } return true; } + /** Compute the signed area of the parallelogram formed by vectors between the given points. The first + * vector points from {@code p0} to {@code p1} and the second from {@code p0} to {@code p3}. + * @param p0 first point + * @param p1 second point + * @param p2 third point + * @return signed area of parallelogram formed by vectors between the given points + */ + private static double signedAreaPoints(final Vector2D p0, final Vector2D p1, final Vector2D p2) { + return p0.vectorTo(p1).signedArea(p0.vectorTo(p2)); + } + }