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 a26b977 GEOMETRY-28: Cleaning up o.a.c.geometry.euclidean.twod.Line
API and adding additional unit tests
new a05c6ab Merge branch 'GEOMETRY-28__matt'
a26b977 is described below
commit a26b977a02ca271c89c6b16f0ef1742bdde36755
Author: Matt Juntunen <[email protected]>
AuthorDate: Sat Feb 16 21:41:22 2019 -0500
GEOMETRY-28: Cleaning up o.a.c.geometry.euclidean.twod.Line API and adding
additional unit tests
---
.../geometry/euclidean/oned/OrientedPoint.java | 2 +-
.../euclidean/threed/OutlineExtractor.java | 6 +-
.../geometry/euclidean/threed/PolyhedronsSet.java | 19 +-
.../geometry/euclidean/threed/SubPlane.java | 4 +-
.../euclidean/twod/AffineTransformMatrix2D.java | 27 +
.../commons/geometry/euclidean/twod/Line.java | 597 ++++++-------
.../geometry/euclidean/twod/NestedLoops.java | 2 +-
.../geometry/euclidean/twod/PolygonsSet.java | 16 +-
.../commons/geometry/euclidean/twod/SubLine.java | 4 +-
.../core/partitioning/CharacterizationTest.java | 4 +-
.../geometry/euclidean/EuclideanTestUtils.java | 2 +-
.../twod/AffineTransformMatrix2DTest.java | 33 +
.../commons/geometry/euclidean/twod/LineTest.java | 993 +++++++++++++++++++--
.../geometry/euclidean/twod/PolygonsSetTest.java | 76 +-
.../geometry/euclidean/twod/SegmentTest.java | 2 +-
.../geometry/euclidean/twod/SubLineTest.java | 12 +-
.../geometry/euclidean/twod/hull/ConvexHull2D.java | 6 +-
.../euclidean/twod/hull/MonotoneChain.java | 2 +-
.../euclidean/twod/hull/MonotoneChainTest.java | 2 +-
19 files changed, 1360 insertions(+), 449 deletions(-)
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
index 4b01e66..23ad001 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
@@ -172,7 +172,7 @@ public final class OrientedPoint implements
Hyperplane<Vector1D>, Serializable {
int result = 1;
result = (prime * result) + Objects.hashCode(location);
result = (prime * result) + Boolean.hashCode(positiveFacing);
- result = (prime * result) + Objects.hash(precision);
+ result = (prime * result) + Objects.hashCode(precision);
return result;
}
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
index c1a4eb2..48c8181 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
@@ -214,7 +214,7 @@ public class OutlineExtractor {
final Vector2D cPoint =
Vector2D.of(current3D.dot(u),
current3D.dot(v));
final org.apache.commons.geometry.euclidean.twod.Line
line =
- new
org.apache.commons.geometry.euclidean.twod.Line(pPoint, cPoint, precision);
+
org.apache.commons.geometry.euclidean.twod.Line.fromPoints(pPoint, cPoint,
precision);
SubHyperplane<Vector2D> edge = line.wholeHyperplane();
if (closed || (previous != 1)) {
@@ -222,7 +222,7 @@ public class OutlineExtractor {
// it defines one bounding point of the edge
final double angle = line.getAngle() + 0.5 *
Math.PI;
final
org.apache.commons.geometry.euclidean.twod.Line l =
- new
org.apache.commons.geometry.euclidean.twod.Line(pPoint, angle, precision);
+
org.apache.commons.geometry.euclidean.twod.Line.fromPointAndAngle(pPoint,
angle, precision);
edge = edge.split(l).getPlus();
}
@@ -231,7 +231,7 @@ public class OutlineExtractor {
// it defines one bounding point of the edge
final double angle = line.getAngle() + 0.5 *
Math.PI;
final
org.apache.commons.geometry.euclidean.twod.Line l =
- new
org.apache.commons.geometry.euclidean.twod.Line(cPoint, angle, precision);
+
org.apache.commons.geometry.euclidean.twod.Line.fromPointAndAngle(cPoint,
angle, precision);
edge = edge.split(l).getMinus();
}
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
index a30180c..d522f7c 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
@@ -628,13 +628,10 @@ public class PolyhedronsSet extends
AbstractRegion<Vector3D, Vector2D> {
cachedOriginal = (Plane) original;
cachedTransform =
-
org.apache.commons.geometry.euclidean.twod.Line.getTransform(tP10.getX() -
tP00.getX(),
-
tP10.getY() - tP00.getY(),
-
tP01.getX() - tP00.getX(),
-
tP01.getY() - tP00.getY(),
-
tP00.getX(),
-
tP00.getY());
-
+
org.apache.commons.geometry.euclidean.twod.Line.getTransform(
+ tP00.vectorTo(tP10),
+ tP00.vectorTo(tP01),
+ tP00);
}
return ((SubLine) sub).applyTransform(cachedTransform);
}
@@ -695,10 +692,10 @@ public class PolyhedronsSet extends
AbstractRegion<Vector3D, Vector2D> {
cachedOriginal = (Plane) original;
cachedTransform =
-
org.apache.commons.geometry.euclidean.twod.Line.getTransform(1, 0, 0, 1,
-
shift.getX(),
-
shift.getY());
-
+
org.apache.commons.geometry.euclidean.twod.Line.getTransform(
+ Vector2D.PLUS_X,
+ Vector2D.PLUS_Y,
+ shift);
}
return ((SubLine) sub).applyTransform(cachedTransform);
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
index da6d154..02f646e 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
@@ -84,9 +84,9 @@ public class SubPlane extends AbstractSubHyperplane<Vector3D,
Vector2D> {
q = tmp;
}
final SubHyperplane<Vector2D> l2DMinus =
- new org.apache.commons.geometry.euclidean.twod.Line(p, q,
precision).wholeHyperplane();
+ org.apache.commons.geometry.euclidean.twod.Line.fromPoints(p, q,
precision).wholeHyperplane();
final SubHyperplane<Vector2D> l2DPlus =
- new org.apache.commons.geometry.euclidean.twod.Line(q, p,
precision).wholeHyperplane();
+ org.apache.commons.geometry.euclidean.twod.Line.fromPoints(q, p,
precision).wholeHyperplane();
final BSPTree<Vector2D> splitTree =
getRemainingRegion().getTree(false).split(l2DMinus);
final BSPTree<Vector2D> plusTree =
getRemainingRegion().isEmpty(splitTree.getPlus()) ?
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2D.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2D.java
index 0f7e416..2192f43 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2D.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2D.java
@@ -414,6 +414,33 @@ public final class AffineTransformMatrix2D implements
AffineTransformMatrix<Vect
);
}
+ /** Get a new transform create from the given column vectors. The returned
transform
+ * does not include any translation component.
+ * @param u first column vector; this corresponds to the first basis vector
+ * in the coordinate frame
+ * @param v second column vector; this corresponds to the second basis
vector
+ * in the coordinate frame
+ * @return a new transform with the given column vectors
+ */
+ public static AffineTransformMatrix2D fromColumnVectors(final Vector2D u,
final Vector2D v) {
+ return fromColumnVectors(u, v, Vector2D.ZERO);
+ }
+
+ /** Get a new transform created from the given column vectors.
+ * @param u first column vector; this corresponds to the first basis vector
+ * in the coordinate frame
+ * @param v second column vector; this corresponds to the second basis
vector
+ * in the coordinate frame
+ * @param t third column vector; this corresponds to the translation of
the transform
+ * @return a new transform with the given column vectors
+ */
+ public static AffineTransformMatrix2D fromColumnVectors(final Vector2D u,
final Vector2D v, final Vector2D t) {
+ return new AffineTransformMatrix2D(
+ u.getX(), v.getX(), t.getX(),
+ u.getY(), v.getY(), t.getY()
+ );
+ }
+
/** Get the transform representing the identity matrix. This transform
does not
* modify point or vector values when applied.
* @return transform representing the identity matrix
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
index 37d4a6c..2fc9446 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
@@ -16,6 +16,10 @@
*/
package org.apache.commons.geometry.euclidean.twod;
+import java.io.Serializable;
+import java.util.Objects;
+
+import org.apache.commons.geometry.core.exception.GeometryValueException;
import org.apache.commons.geometry.core.partitioning.Embedding;
import org.apache.commons.geometry.core.partitioning.Hyperplane;
import org.apache.commons.geometry.core.partitioning.SubHyperplane;
@@ -29,225 +33,168 @@ import
org.apache.commons.numbers.arrays.LinearCombination;
/** This class represents an oriented line in the 2D plane.
- * <p>An oriented line can be defined either by prolongating a line
- * segment between two points past these points, or by one point and
- * an angular direction (in trigonometric orientation).</p>
+ * <p>An oriented line can be defined either by extending a line
+ * segment between two points past these points, by specifying a
+ * point and a direction, or by specifying a point and an angle
+ * relative to the x-axis.</p>
- * <p>Since it is oriented the two half planes at its two sides are
- * unambiguously identified as a left half plane and a right half
+ * <p>Since the line oriented, the two half planes on its sides are
+ * unambiguously identified as the left half plane and the right half
* plane. This can be used to identify the interior and the exterior
- * in a simple way by local properties only when part of a line is
- * used to define part of a polygon boundary.</p>
+ * in a simple way when a line is used to define a portion of a polygon
+ * boundary.</p>
* <p>A line can also be used to completely define a reference frame
* in the plane. It is sufficient to select one specific point in the
* line (the orthogonal projection of the original reference frame on
- * the line) and to use the unit vector in the line direction and the
- * orthogonal vector oriented from left half plane to right half
- * plane. We define two coordinates by the process, the
- * <em>abscissa</em> along the line, and the <em>offset</em> across
- * the line. All points of the plane are uniquely identified by these
- * two coordinates. The line is the set of points at zero offset, the
- * left half plane is the set of points with negative offsets and the
- * right half plane is the set of points with positive offsets.</p>
+ * the line) and to use the unit vector in the line direction (see
+ * {@link #getDirection()} and the orthogonal vector oriented from the
+ * left half plane to the right half plane (see {@link #getOffsetDirection()}.
+ * We define two coordinates by the process, the <em>abscissa</em> along
+ * the line, and the <em>offset</em> across the line. All points of the
+ * plane are uniquely identified by these two coordinates. The line is
+ * the set of points at zero offset, the left half plane is the set of
+ * points with negative offsets and the right half plane is the set of
+ * points with positive offsets.</p>
*/
-public class Line implements Hyperplane<Vector2D>, Embedding<Vector2D,
Vector1D> {
- /** Angle with respect to the abscissa axis. */
- private double angle;
+public final class Line implements Hyperplane<Vector2D>, Embedding<Vector2D,
Vector1D>, Serializable {
- /** Cosine of the line angle. */
- private double cos;
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20190120L;
- /** Sine of the line angle. */
- private double sin;
+ /** The direction of the line as a normalized vector. */
+ private final Vector2D direction;
- /** Offset of the frame origin. */
- private double originOffset;
+ /** The distance between the origin and the line. */
+ private final double originOffset;
/** Precision context used to compare floating point numbers. */
private final DoublePrecisionContext precision;
- /** Reverse line. */
- private Line reverse;
-
- /** Build a line from two points.
- * <p>The line is oriented from p1 to p2</p>
- * @param p1 first point
- * @param p2 second point
- * @param precision precision context used to compare floating point values
+ /** Simple constructor.
+ * @param direction The direction of the line.
+ * @param originOffset The signed distance between the line and the origin.
+ * @param precision Precision context used to compare floating point
numbers.
*/
- public Line(final Vector2D p1, final Vector2D p2, final
DoublePrecisionContext precision) {
- reset(p1, p2);
+ private Line(final Vector2D direction, final double originOffset, final
DoublePrecisionContext precision) {
+ this.direction = direction;
+ this.originOffset = originOffset;
this.precision = precision;
}
- /** Build a line from a point and an angle.
- * @param p point belonging to the line
- * @param angle angle of the line with respect to abscissa axis
- * @param precision precision context used to compare floating point values
+ /** Get the angle of the line in radians with respect to the abscissa (+x)
axis. The
+ * returned angle is in the range {@code [0, 2pi)}.
+ * @return the angle of the line with respect to the abscissa (+x) axis in
the range
+ * {@code [0, 2pi)}
*/
- public Line(final Vector2D p, final double angle, final
DoublePrecisionContext precision) {
- reset(p, angle);
- this.precision = precision;
+ public double getAngle() {
+ final double angle = Math.atan2(direction.getY(), direction.getX());
+ return PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(angle);
}
- /** Build a line from its internal characteristics.
- * @param angle angle of the line with respect to abscissa axis
- * @param cos cosine of the angle
- * @param sin sine of the angle
- * @param originOffset offset of the origin
- * @param precision precision context used to compare floating point values
+ /** Get the direction of the line.
+ * @return the direction of the line
*/
- private Line(final double angle, final double cos, final double sin,
- final double originOffset, final DoublePrecisionContext
precision) {
- this.angle = angle;
- this.cos = cos;
- this.sin = sin;
- this.originOffset = originOffset;
- this.precision = precision;
- this.reverse = null;
+ public Vector2D getDirection() {
+ return direction;
}
- /** Copy constructor.
- * <p>The created instance is completely independent from the
- * original instance, it is a deep copy.</p>
- * @param line line to copy
+ /** Get the offset direction of the line. This vector is perpendicular to
the
+ * line and points in the direction of positive offset values, meaning that
+ * it points from the left side of the line to the right when one is
looking
+ * along the line direction.
+ * @return the offset direction of the line.
*/
- public Line(final Line line) {
- angle =
PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(line.angle);
- cos = line.cos;
- sin = line.sin;
- originOffset = line.originOffset;
- precision = line.precision;
- reverse = null;
+ public Vector2D getOffsetDirection() {
+ return Vector2D.of(direction.getY(), -direction.getX());
}
- /** {@inheritDoc} */
- @Override
- public Line copySelf() {
- return new Line(this);
+ /** Get the line origin point. This is the projection of the 2D origin
+ * onto the line and also serves as the origin for the 1D embedded
subspace.
+ * @return the origin point of the line
+ */
+ public Vector2D getOrigin() {
+ return toSpace(Vector1D.ZERO);
}
- /** Reset the instance as if built from two points.
- * <p>The line is oriented from p1 to p2</p>
- * @param p1 first point
- * @param p2 second point
+ /** Get the signed distance from the origin of the 2D space to the
+ * closest point on the line.
+ * @return the signed distance from the origin to the line
*/
- public void reset(final Vector2D p1, final Vector2D p2) {
- unlinkReverse();
- final double dx = p2.getX() - p1.getX();
- final double dy = p2.getY() - p1.getY();
- final double d = Math.hypot(dx, dy);
- if (d == 0.0) {
- angle = 0.0;
- cos = 1.0;
- sin = 0.0;
- originOffset = p1.getY();
- } else {
- angle = Math.PI + Math.atan2(-dy, -dx);
- cos = dx / d;
- sin = dy / d;
- originOffset = LinearCombination.value(p2.getX(), p1.getY(),
-p1.getX(), p2.getY()) / d;
- }
+ public double getOriginOffset() {
+ return originOffset;
}
- /** Reset the instance as if built from a line and an angle.
- * @param p point belonging to the line
- * @param alpha angle of the line with respect to abscissa axis
- */
- public void reset(final Vector2D p, final double alpha) {
- unlinkReverse();
- this.angle = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(alpha);
- cos = Math.cos(this.angle);
- sin = Math.sin(this.angle);
- originOffset = LinearCombination.value(cos, p.getY(), -sin, p.getX());
+ /** {@inheritDoc} */
+ @Override
+ public DoublePrecisionContext getPrecision() {
+ return precision;
}
- /** Revert the instance.
- */
- public void revertSelf() {
- unlinkReverse();
- if (angle < Math.PI) {
- angle += Math.PI;
- } else {
- angle -= Math.PI;
- }
- cos = -cos;
- sin = -sin;
- originOffset = -originOffset;
+ /** {@inheritDoc} */
+ @Override
+ public Line copySelf() {
+ return this;
}
- /** Unset the link between an instance and its reverse.
- */
- private void unlinkReverse() {
- if (reverse != null) {
- reverse.reverse = null;
- }
- reverse = null;
- }
-
- /** Get the reverse of the instance.
- * <p>Get a line with reversed orientation with respect to the
- * instance.</p>
- * <p>
- * As long as neither the instance nor its reverse are modified
- * (i.e. as long as none of the {@link #reset(Vector2D, Vector2D)},
- * {@link #reset(Vector2D, double)}, {@link #revertSelf()},
- * {@link #setAngle(double)} or {@link #setOriginOffset(double)}
- * methods are called), then the line and its reverse remain linked
- * together so that {@code line.getReverse().getReverse() == line}.
- * When one of the line is modified, the link is deleted as both
- * instance becomes independent.
- * </p>
+ /** Get the reverse of the instance, meaning a line containing the same
+ * points but with the opposite orientation.
* @return a new line, with orientation opposite to the instance
orientation
*/
- public Line getReverse() {
- if (reverse == null) {
- reverse = new Line((angle < Math.PI) ? (angle + Math.PI) : (angle
- Math.PI),
- -cos, -sin, -originOffset, precision);
- reverse.reverse = this;
- }
- return reverse;
+ public Line reverse() {
+ return new Line(direction.negate(), -originOffset, precision);
}
/** {@inheritDoc} */
@Override
public Vector1D toSubSpace(final Vector2D point) {
- return Vector1D.of(LinearCombination.value(cos, point.getX(), sin,
point.getY()));
+ return Vector1D.of(direction.dot(point));
}
/** {@inheritDoc} */
@Override
public Vector2D toSpace(final Vector1D point) {
final double abscissa = point.getX();
- return Vector2D.of(LinearCombination.value(abscissa, cos,
-originOffset, sin),
- LinearCombination.value(abscissa, sin,
originOffset, cos));
+
+ // The 2D coordinate is equal to the projection of the
+ // 2D origin onto the line plus the direction multiplied
+ // by the abscissa. We can combine everything into a single
+ // step below given that the origin location is equal to
+ // (-direction.y * originOffset, direction.x * originOffset).
+ return Vector2D.of(
+ LinearCombination.value(abscissa, direction.getX(),
-originOffset, direction.getY()),
+ LinearCombination.value(abscissa, direction.getY(),
originOffset, direction.getX())
+ );
}
/** Get the intersection point of the instance and another line.
* @param other other line
* @return intersection point of the instance and the other line
- * or null if there are no intersection points
+ * or null if there is no unique intersection point (ie, the lines
+ * are parallel or coincident)
*/
public Vector2D intersection(final Line other) {
- final double d = LinearCombination.value(sin, other.cos, -other.sin,
cos);
- if (precision.eqZero(d)) {
+ final double area = this.direction.signedArea(other.direction);
+ if (precision.eqZero(area)) {
+ // lines are parallel
return null;
}
- return Vector2D.of(LinearCombination.value(cos, other.originOffset,
-other.cos, originOffset) / d,
- LinearCombination.value(sin, other.originOffset,
-other.sin, originOffset) / d);
- }
- /** {@inheritDoc} */
- @Override
- public Vector2D project(Vector2D point) {
- return toSpace(toSubSpace(point));
+ final double x = LinearCombination.value(
+ other.direction.getX(), originOffset,
+ -direction.getX(), other.originOffset) / area;
+
+ final double y = LinearCombination.value(
+ other.direction.getY(), originOffset,
+ -direction.getY(), other.originOffset) / area;
+
+ return Vector2D.of(x, y);
}
/** {@inheritDoc} */
@Override
- public DoublePrecisionContext getPrecision() {
- return precision;
+ public Vector2D project(final Vector2D point) {
+ return toSpace(toSubSpace(point));
}
/** {@inheritDoc} */
@@ -265,45 +212,61 @@ public class Line implements Hyperplane<Vector2D>,
Embedding<Vector2D, Vector1D>
return new PolygonsSet(precision);
}
- /** Get the offset (oriented distance) of a parallel line.
- * <p>This method should be called only for parallel lines otherwise
- * the result is not meaningful.</p>
- * <p>The offset is 0 if both lines are the same, it is
- * positive if the line is on the right side of the instance and
- * negative if it is on the left side, according to its natural
- * orientation.</p>
+ /** {@inheritDoc} */
+ @Override
+ public double getOffset(final Vector2D point) {
+ return originOffset - direction.signedArea(point);
+ }
+
+ /** Get the offset (oriented distance) of a line. Since an infinite
+ * number of distances can be calculated between points on two different
+ * lines, this methods returns the value closest to zero. For intersecting
+ * lines, this will simply be zero. For parallel lines, this will be the
+ * perpendicular distance between the two lines, as a signed value.
+ *
+ * <p>The sign of the returned offset indicates the side of the line that
the
+ * argument lies on. The offset is positive if the line lies on the right
side
+ * of the instance and negative if the line lies on the left side
+ * of the instance.</p>
* @param line line to check
* @return offset of the line
+ * @see #distance(Line)
*/
public double getOffset(final Line line) {
- return originOffset +
- (LinearCombination.value(cos, line.cos, sin, line.sin) > 0 ?
-line.originOffset : line.originOffset);
- }
+ if (isParallel(line)) {
+ // since the lines are parallel, the offset between
+ // them is simply the difference between their origin offsets,
+ // with the second offset negated if the lines point if opposite
+ // directions
+ final double dot = direction.dot(line.direction);
+ return originOffset - (Math.signum(dot) * line.originOffset);
+ }
- /** {@inheritDoc} */
- @Override
- public double getOffset(final Vector2D point) {
- return LinearCombination.value(sin, point.getX(), -cos, point.getY(),
1.0, originOffset);
+ // the lines are not parallel, which means they intersect at some point
+ return 0.0;
}
/** {@inheritDoc} */
@Override
public boolean sameOrientationAs(final Hyperplane<Vector2D> other) {
- final Line otherL = (Line) other;
- return LinearCombination.value(sin, otherL.sin, cos, otherL.cos) >=
0.0;
+ final Line otherLine = (Line) other;
+ return direction.dot(otherLine.direction) >= 0.0;
}
- /** Get one point from the plane.
- * @param abscissa desired abscissa for the point
- * @param offset desired offset for the point
+ /** Get one point from the plane, relative to the coordinate system
+ * of the line. Note that the direction of increasing offsets points
+ * to the <em>right</em> of the line. This means that if one pictures
+ * the line (abscissa) direction as equivalent to the +x-axis, the offset
+ * direction will point along the -y axis.
+ * @param abscissa desired abscissa (distance along the line) for the point
+ * @param offset desired offset (distance perpendicular to the line) for
the point
* @return one point in the plane, with given abscissa and offset
- * relative to the line
+ * relative to the line
*/
- public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
- final double x = abscissa.getX();
- final double dOffset = offset - originOffset;
- return Vector2D.of(LinearCombination.value(x, cos, dOffset, sin),
- LinearCombination.value(x, sin, -dOffset, cos));
+ public Vector2D pointAt(final double abscissa, final double offset) {
+ final double pointOffset = offset - originOffset;
+ return Vector2D.of(LinearCombination.value(abscissa, direction.getX(),
pointOffset, direction.getY()),
+ LinearCombination.value(abscissa,
direction.getY(), -pointOffset, direction.getX()));
}
/** Check if the line contains a point.
@@ -314,6 +277,16 @@ public class Line implements Hyperplane<Vector2D>,
Embedding<Vector2D, Vector1D>
return precision.eqZero(getOffset(p));
}
+ /** Check if this instance completely contains the other line.
+ * This will be true if the two instances represent the same line,
+ * with perhaps different directions.
+ * @param line line to check
+ * @return true if this instance contains all points in the given line
+ */
+ public boolean contains(final Line line) {
+ return isParallel(line) && precision.eqZero(getOffset(line));
+ }
+
/** Compute the distance between the instance and a point.
* <p>This is a shortcut for invoking Math.abs(getOffset(p)),
* and provides consistency with what is in the
@@ -326,164 +299,174 @@ public class Line implements Hyperplane<Vector2D>,
Embedding<Vector2D, Vector1D>
return Math.abs(getOffset(p));
}
- /** Check the instance is parallel to another line.
- * @param line other line to check
- * @return true if the instance is parallel to the other line
- * (they can have either the same or opposite orientations)
+ /** Compute the shortest distance between this instance and
+ * the given line. This value will simply be zero for intersecting
+ * lines.
+ * @param line line to compute the closest distance to
+ * @return the shortest distance between this instance and the
+ * given line
+ * @see #getOffset(Line)
*/
- public boolean isParallelTo(final Line line) {
- return precision.eqZero(LinearCombination.value(sin, line.cos, -cos,
line.sin));
+ public double distance(final Line line) {
+ return Math.abs(getOffset(line));
}
- /** Translate the line to force it passing by a point.
- * @param p point by which the line should pass
+ /** Check if the instance is parallel to another line.
+ * @param line other line to check
+ * @return true if the instance is parallel to the other line
+ * (they can have either the same or opposite orientations)
*/
- public void translateToPoint(final Vector2D p) {
- originOffset = LinearCombination.value(cos, p.getY(), -sin, p.getX());
+ public boolean isParallel(final Line line) {
+ final double area = direction.signedArea(line.direction);
+ return precision.eqZero(area);
}
- /** Get the angle of the line.
- * @return the angle of the line with respect to the abscissa axis
+ /** Transform this instance with the given transform.
+ * @param transform transform to apply to this instance
+ * @return a new transformed line
*/
- public double getAngle() {
- return PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(angle);
- }
+ public Line transform(final Transform<Vector2D, Vector1D> transform) {
+ final Vector2D origin = getOrigin();
- /** Set the angle of the line.
- * @param angle new angle of the line with respect to the abscissa axis
- */
- public void setAngle(final double angle) {
- unlinkReverse();
- this.angle = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(angle);
- cos = Math.cos(this.angle);
- sin = Math.sin(this.angle);
- }
+ final Vector2D p1 = transform.apply(origin);
+ final Vector2D p2 = transform.apply(origin.add(direction));
- /** Get the offset of the origin.
- * @return the offset of the origin
- */
- public double getOriginOffset() {
- return originOffset;
+ return Line.fromPoints(p1, p2, precision);
}
- /** Set the offset of the origin.
- * @param offset offset of the origin
- */
- public void setOriginOffset(final double offset) {
- unlinkReverse();
- originOffset = offset;
- }
-
- /** Get a {@link org.apache.commons.geometry.core.partitioning.Transform
- * Transform} embedding an affine transform.
- * @param cXX transform factor between input abscissa and output abscissa
- * @param cYX transform factor between input abscissa and output ordinate
- * @param cXY transform factor between input ordinate and output abscissa
- * @param cYY transform factor between input ordinate and output ordinate
- * @param cX1 transform addendum for output abscissa
- * @param cY1 transform addendum for output ordinate
- * @return a new transform that can be applied to either {@link
- * Vector2D}, {@link Line Line} or {@link
- * org.apache.commons.geometry.core.partitioning.SubHyperplane
- * SubHyperplane} instances
- * @exception IllegalArgumentException if the transform is non invertible
- */
- public static Transform<Vector2D, Vector1D> getTransform(final double cXX,
- final
double cYX,
- final
double cXY,
- final
double cYY,
- final
double cX1,
- final
double cY1)
- throws IllegalArgumentException {
- return new LineTransform(cXX, cYX, cXY, cYY, cX1, cY1);
- }
-
- /** Class embedding an affine transform.
- * <p>This class is used in order to apply an affine transform to a
- * line. Using a specific object allow to perform some computations
- * on the transform only once even if the same transform is to be
- * applied to a large number of lines (for example to a large
- * polygon)./<p>
- */
- private static class LineTransform implements Transform<Vector2D,
Vector1D> {
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 167;
- /** Transform factor between input abscissa and output abscissa. */
- private final double cXX;
+ int result = 1;
+ result = (prime * result) + Objects.hashCode(direction);
+ result = (prime * result) + Double.hashCode(originOffset);
+ result = (prime * result) + Objects.hashCode(precision);
- /** Transform factor between input abscissa and output ordinate. */
- private final double cYX;
+ return result;
+ }
- /** Transform factor between input ordinate and output abscissa. */
- private final double cXY;
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ else if (!(obj instanceof Line)) {
+ return false;
+ }
- /** Transform factor between input ordinate and output ordinate. */
- private final double cYY;
+ Line other = (Line) obj;
- /** Transform addendum for output abscissa. */
- private final double cX1;
+ return Objects.equals(this.direction, other.direction) &&
+ Double.compare(this.originOffset, other.originOffset) == 0 &&
+ Objects.equals(this.precision, other.precision);
+ }
- /** Transform addendum for output ordinate. */
- private final double cY1;
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName())
+ .append("[origin= ")
+ .append(getOrigin())
+ .append(", direction= ")
+ .append(direction)
+ .append(']');
+
+ return sb.toString();
+ }
- /** cXY * cY1 - cYY * cX1. */
- private final double c1Y;
+ /** Create a line from two points lying on the line. The line points in
the direction
+ * from {@code p1} to {@code p2}.
+ * @param p1 first point
+ * @param p2 second point
+ * @param precision precision context used to compare floating point values
+ * @return new line containing {@code p1} and {@code p2} and pointing in
the direction
+ * from {@code p1} to {@code p2}
+ * @throws GeometryValueException If the vector between {@code p1} and
{@code p2} has zero length,
+ * as evaluated by the given precision context
+ */
+ public static Line fromPoints(final Vector2D p1, final Vector2D p2, final
DoublePrecisionContext precision) {
+ return fromPointAndDirection(p1, p1.vectorTo(p2), precision);
+ }
+
+ /** Create a line from a point and direction.
+ * @param pt point belonging to the line
+ * @param dir the direction of the line
+ * @param precision precision context used to compare floating point values
+ * @return new line containing {@code pt} and pointing in direction {@code
dir}
+ * @throws GeometryValueException If {@code dir} has zero length, as
evaluated by the
+ * given precision context
+ */
+ public static Line fromPointAndDirection(final Vector2D pt, final Vector2D
dir, final DoublePrecisionContext precision) {
+ if (dir.isZero(precision)) {
+ throw new GeometryValueException("Line direction cannot be zero");
+ }
- /** cXX * cY1 - cYX * cX1. */
- private final double c1X;
+ final Vector2D normalizedDir = dir.normalize();
+ final double originOffset = normalizedDir.signedArea(pt);
- /** cXX * cYY - cYX * cXY. */
- private final double c11;
+ return new Line(normalizedDir, originOffset, precision);
+ }
- /** Build an affine line transform from a n {@code AffineTransform}.
- * @param cXX transform factor between input abscissa and output
abscissa
- * @param cYX transform factor between input abscissa and output
ordinate
- * @param cXY transform factor between input ordinate and output
abscissa
- * @param cYY transform factor between input ordinate and output
ordinate
- * @param cX1 transform addendum for output abscissa
- * @param cY1 transform addendum for output ordinate
- * @exception IllegalArgumentException if the transform is non
invertible
- */
- LineTransform(final double cXX, final double cYX, final double cXY,
- final double cYY, final double cX1, final double cY1)
- throws IllegalArgumentException {
+ /** Create a line from a point lying on the line and an angle relative to
the abscissa (x) axis. Note that the
+ * line does not need to intersect the x-axis; the given angle is simply
relative to it.
+ * @param pt point belonging to the line
+ * @param angle angle of the line with respect to abscissa (x) axis, in
radians
+ * @param precision precision context used to compare floating point values
+ * @return new line containing {@code pt} and forming the given angle with
the
+ * abscissa (x) axis.
+ */
+ public static Line fromPointAndAngle(final Vector2D pt, final double
angle, final DoublePrecisionContext precision) {
+ final Vector2D dir = Vector2D.normalize(Math.cos(angle),
Math.sin(angle));
+ return fromPointAndDirection(pt, dir, precision);
+ }
- this.cXX = cXX;
- this.cYX = cYX;
- this.cXY = cXY;
- this.cYY = cYY;
- this.cX1 = cX1;
- this.cY1 = cY1;
+ // TODO: Remove this method and associated class after the Transform
interface has been simplified.
+ // See GEOMETRY-24.
+
+ /** Create a {@link Transform} instance from a set of column vectors. The
returned object can be used
+ * to transform {@link SubLine} instances.
+ * @param u first column vector; this corresponds to the first basis vector
+ * in the coordinate frame
+ * @param v second column vector; this corresponds to the second basis
vector
+ * in the coordinate frame
+ * @param t third column vector; this corresponds to the translation of
the transform
+ * @return a new transform instance
+ */
+ public static Transform<Vector2D, Vector1D> getTransform(final Vector2D u,
final Vector2D v, final Vector2D t) {
+ final AffineTransformMatrix2D matrix =
AffineTransformMatrix2D.fromColumnVectors(u, v, t);
+ return new LineTransform(matrix);
+ }
- c1Y = LinearCombination.value(cXY, cY1, -cYY, cX1);
- c1X = LinearCombination.value(cXX, cY1, -cYX, cX1);
- c11 = LinearCombination.value(cXX, cYY, -cYX, cXY);
+ /** Class wrapping an {@link AffineTransformMatrix2D} with the methods
necessary to fulfill the full
+ * {@link Transform} interface.
+ */
+ private static class LineTransform implements Transform<Vector2D,
Vector1D> {
- if (Math.abs(c11) < 1.0e-20) {
- throw new IllegalArgumentException("Non-invertible affine
transform collapses some lines into single points");
- }
+ /** Transform matrix */
+ private final AffineTransformMatrix2D matrix;
+ /** Simple constructor.
+ * @param matrix transform matrix
+ */
+ LineTransform(final AffineTransformMatrix2D matrix) {
+ this.matrix = matrix;
}
/** {@inheritDoc} */
@Override
public Vector2D apply(final Vector2D point) {
- final double x = point.getX();
- final double y = point.getY();
- return Vector2D.of(LinearCombination.value(cXX, x, cXY, y, cX1, 1),
- LinearCombination.value(cYX, x, cYY, y, cY1,
1));
+ return matrix.apply(point);
}
/** {@inheritDoc} */
@Override
public Line apply(final Hyperplane<Vector2D> hyperplane) {
- final Line line = (Line) hyperplane;
- final double rOffset = LinearCombination.value(c1X, line.cos, c1Y,
line.sin, c11, line.originOffset);
- final double rCos = LinearCombination.value(cXX, line.cos, cXY,
line.sin);
- final double rSin = LinearCombination.value(cYX, line.cos, cYY,
line.sin);
- final double inv = 1.0 / Math.sqrt(rSin * rSin + rCos * rCos);
- return new Line(Math.PI + Math.atan2(-rSin, -rCos),
- inv * rCos, inv * rSin,
- inv * rOffset, line.precision);
+ final Line line = (Line) hyperplane;
+ return line.transform(matrix);
}
/** {@inheritDoc} */
@@ -491,14 +474,12 @@ public class Line implements Hyperplane<Vector2D>,
Embedding<Vector2D, Vector1D>
public SubHyperplane<Vector1D> apply(final SubHyperplane<Vector1D> sub,
final Hyperplane<Vector2D>
original,
final Hyperplane<Vector2D>
transformed) {
- final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
- final Line originalLine = (Line) original;
+ final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
+ final Line originalLine = (Line) original;
final Line transformedLine = (Line) transformed;
final Vector1D newLoc =
transformedLine.toSubSpace(apply(originalLine.toSpace(op.getLocation())));
return OrientedPoint.fromPointAndDirection(newLoc,
op.getDirection(), originalLine.precision).wholeHyperplane();
}
-
}
-
}
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
index 6f15bd9..d6dbbf9 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
@@ -93,7 +93,7 @@ class NestedLoops {
for (int i = 0; i < loop.length; ++i) {
final Vector2D previous = current;
current = loop[i];
- final Line line = new Line(previous, current, precision);
+ final Line line = Line.fromPoints(previous, current,
precision);
final IntervalsSet region =
new IntervalsSet(line.toSubSpace(previous).getX(),
line.toSubSpace(current).getX(),
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
index 2d43b1a..ee76ff9 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
@@ -163,10 +163,10 @@ public class PolygonsSet extends AbstractRegion<Vector2D,
Vector1D> {
final Vector2D maxMin = Vector2D.of(xMax, yMin);
final Vector2D maxMax = Vector2D.of(xMax, yMax);
return new Line[] {
- new Line(minMin, maxMin, precision),
- new Line(maxMin, maxMax, precision),
- new Line(maxMax, minMax, precision),
- new Line(minMax, minMin, precision)
+ Line.fromPoints(minMin, maxMin, precision),
+ Line.fromPoints(maxMin, maxMax, precision),
+ Line.fromPoints(maxMax, minMax, precision),
+ Line.fromPoints(minMax, minMin, precision)
};
}
@@ -212,7 +212,7 @@ public class PolygonsSet extends AbstractRegion<Vector2D,
Vector1D> {
// with the current one
Line line = start.sharedLineWith(end);
if (line == null) {
- line = new Line(start.getLocation(), end.getLocation(),
precision);
+ line = Line.fromPoints(start.getLocation(), end.getLocation(),
precision);
}
// create the edge and store it
@@ -738,12 +738,12 @@ public class PolygonsSet extends AbstractRegion<Vector2D,
Vector1D> {
private int splitEdgeConnections(final List<ConnectableSegment> segments) {
int connected = 0;
for (final ConnectableSegment segment : segments) {
- if (segment.getNext() == null) {
+ if (segment.getNext() == null && segment.getEndNode() != null) {
final Hyperplane<Vector2D> hyperplane =
segment.getNode().getCut().getHyperplane();
final BSPTree<Vector2D> end = segment.getEndNode();
for (final ConnectableSegment candidateNext : segments) {
if (candidateNext.getPrevious() ==
null &&
- candidateNext.getNode().getCut().getHyperplane() ==
hyperplane &&
+
candidateNext.getNode().getCut().getHyperplane().equals(hyperplane) &&
candidateNext.getStartNode() ==
end) {
// connect the two segments
segment.setNext(candidateNext);
@@ -1054,7 +1054,7 @@ public class PolygonsSet extends AbstractRegion<Vector2D,
Vector1D> {
final BSPTree<Vector2D> endN = selectClosest(endV,
splitters);
if (reversed) {
- segments.add(new ConnectableSegment(endV, startV,
line.getReverse(),
+ segments.add(new ConnectableSegment(endV, startV,
line.reverse(),
node, endN, startN));
} else {
segments.add(new ConnectableSegment(startV, endV, line,
diff --git
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
index 21b340b..3e07ffa 100644
---
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
+++
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
@@ -50,7 +50,7 @@ public class SubLine extends AbstractSubHyperplane<Vector2D,
Vector1D> {
* @param precision precision context used to compare floating point values
*/
public SubLine(final Vector2D start, final Vector2D end, final
DoublePrecisionContext precision) {
- super(new Line(start, end, precision), buildIntervalSet(start, end,
precision));
+ super(Line.fromPoints(start, end, precision), buildIntervalSet(start,
end, precision));
}
/** Create a sub-line from a segment.
@@ -138,7 +138,7 @@ public class SubLine extends
AbstractSubHyperplane<Vector2D, Vector1D> {
* @return an interval set
*/
private static IntervalsSet buildIntervalSet(final Vector2D start, final
Vector2D end, final DoublePrecisionContext precision) {
- final Line line = new Line(start, end, precision);
+ final Line line = Line.fromPoints(start, end, precision);
return new IntervalsSet(line.toSubSpace(start).getX(),
line.toSubSpace(end).getX(),
precision);
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
index 5f13fb4..f172467 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
@@ -409,11 +409,11 @@ public class CharacterizationTest {
}
private Line buildLine(Vector2D p1, Vector2D p2) {
- return new Line(p1, p2, TEST_PRECISION);
+ return Line.fromPoints(p1, p2, TEST_PRECISION);
}
private SubLine buildSubLine(Vector2D start, Vector2D end) {
- Line line = new Line(start, end, TEST_PRECISION);
+ Line line = Line.fromPoints(start, end, TEST_PRECISION);
double lower = (line.toSubSpace(start)).getX();
double upper = (line.toSubSpace(end)).getX();
return new SubLine(line, new IntervalsSet(lower, upper,
TEST_PRECISION));
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
index 3fc4280..7d6bf18 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
@@ -317,7 +317,7 @@ public class EuclideanTestUtils {
@Override
public Line parseHyperplane()
throws ParseException {
- return new Line(Vector2D.of(getNumber(), getNumber()),
getNumber(), getPrecision());
+ return Line.fromPointAndAngle(Vector2D.of(getNumber(),
getNumber()), getNumber(), getPrecision());
}
};
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2DTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2DTest.java
index b2612af..9235793 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2DTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/AffineTransformMatrix2DTest.java
@@ -53,6 +53,39 @@ public class AffineTransformMatrix2DTest {
}
@Test
+ public void testFromColumnVectors_twoVector() {
+ // arrange
+ Vector2D u = Vector2D.of(1, 2);
+ Vector2D v = Vector2D.of(3, 4);
+
+ // act
+ AffineTransformMatrix2D transform =
AffineTransformMatrix2D.fromColumnVectors(u, v);
+
+ // assert
+ Assert.assertArrayEquals(new double[] {
+ 1, 3, 0,
+ 2, 4, 0
+ }, transform.toArray(), 0.0);
+ }
+
+ @Test
+ public void testFromColumnVectors_threeVectors() {
+ // arrange
+ Vector2D u = Vector2D.of(1, 2);
+ Vector2D v = Vector2D.of(3, 4);
+ Vector2D t = Vector2D.of(5, 6);
+
+ // act
+ AffineTransformMatrix2D transform =
AffineTransformMatrix2D.fromColumnVectors(u, v, t);
+
+ // assert
+ Assert.assertArrayEquals(new double[] {
+ 1, 3, 5,
+ 2, 4, 6
+ }, transform.toArray(), 0.0);
+ }
+
+ @Test
public void testIdentity() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity();
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
index 5a5db25..0e26c92 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
@@ -16,10 +16,15 @@
*/
package org.apache.commons.geometry.euclidean.twod;
+import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.GeometryValueException;
import org.apache.commons.geometry.core.partitioning.Transform;
import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
import
org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
+import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
import org.apache.commons.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.numbers.angle.PlaneAngleRadians;
import org.junit.Assert;
import org.junit.Test;
@@ -31,106 +36,974 @@ public class LineTest {
new EpsilonDoublePrecisionContext(TEST_EPS);
@Test
- public void testContains() {
- Line l = new Line(Vector2D.of(0, 1), Vector2D.of(1, 2),
TEST_PRECISION);
- Assert.assertTrue(l.contains(Vector2D.of(0, 1)));
- Assert.assertTrue(l.contains(Vector2D.of(1, 2)));
- Assert.assertTrue(l.contains(Vector2D.of(7, 8)));
- Assert.assertTrue(! l.contains(Vector2D.of(8, 7)));
+ public void testFromPoints() {
+ // act/assert
+ checkLine(Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+ checkLine(Line.fromPoints(Vector2D.ZERO, Vector2D.of(100, 0),
TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+ checkLine(Line.fromPoints(Vector2D.of(100, 0), Vector2D.ZERO,
TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.MINUS_X);
+ checkLine(Line.fromPoints(Vector2D.of(-100, 0), Vector2D.of(100, 0),
TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+
+ checkLine(Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 2),
TEST_PRECISION),
+ Vector2D.of(-1, 1), Vector2D.of(1, 1).normalize());
+ checkLine(Line.fromPoints(Vector2D.of(0, 2), Vector2D.of(-2, 0),
TEST_PRECISION),
+ Vector2D.of(-1, 1), Vector2D.of(-1, -1).normalize());
+ }
+
+ @Test
+ public void testFromPoints_pointsTooClose() {
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Line.fromPoints(Vector2D.PLUS_X,
Vector2D.PLUS_X, TEST_PRECISION),
+ GeometryValueException.class, "Line direction cannot be zero");
+ GeometryTestUtils.assertThrows(() -> Line.fromPoints(Vector2D.PLUS_X,
Vector2D.of(1 + 1e-11, 1e-11), TEST_PRECISION),
+ GeometryValueException.class, "Line direction cannot be zero");
+ }
+
+ @Test
+ public void testFromPointAndDirection() {
+ // act/assert
+ checkLine(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+ checkLine(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(100,
0), TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+ checkLine(Line.fromPointAndDirection(Vector2D.of(-100, 0),
Vector2D.of(100, 0), TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+
+ checkLine(Line.fromPointAndDirection(Vector2D.of(-2, 0),
Vector2D.of(1, 1), TEST_PRECISION),
+ Vector2D.of(-1, 1), Vector2D.of(1, 1).normalize());
+ checkLine(Line.fromPointAndDirection(Vector2D.of(0, 2),
Vector2D.of(-1, -1), TEST_PRECISION),
+ Vector2D.of(-1, 1), Vector2D.of(-1, -1).normalize());
+ }
+
+ @Test
+ public void testFromPointAndDirection_directionIsZero() {
+ // act/assert
+ GeometryTestUtils.assertThrows(() ->
Line.fromPointAndDirection(Vector2D.PLUS_X, Vector2D.ZERO, TEST_PRECISION),
+ GeometryValueException.class, "Line direction cannot be zero");
+ GeometryTestUtils.assertThrows(() ->
Line.fromPointAndDirection(Vector2D.PLUS_X, Vector2D.of(1e-11, -1e-12),
TEST_PRECISION),
+ GeometryValueException.class, "Line direction cannot be zero");
+ }
+
+ @Test
+ public void testFromPointAndAngle() {
+ // act/assert
+ checkLine(Line.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION),
+ Vector2D.ZERO, Vector2D.PLUS_X);
+ checkLine(Line.fromPointAndAngle(Vector2D.of(1, 1), Geometry.HALF_PI,
TEST_PRECISION),
+ Vector2D.of(1, 0), Vector2D.PLUS_Y);
+ checkLine(Line.fromPointAndAngle(Vector2D.of(-1, -1), Geometry.PI,
TEST_PRECISION),
+ Vector2D.of(0, -1), Vector2D.MINUS_X);
+ checkLine(Line.fromPointAndAngle(Vector2D.of(1, -1),
Geometry.MINUS_HALF_PI, TEST_PRECISION),
+ Vector2D.of(1, 0), Vector2D.MINUS_Y);
+ checkLine(Line.fromPointAndAngle(Vector2D.of(-1, 1), Geometry.TWO_PI,
TEST_PRECISION),
+ Vector2D.of(0, 1), Vector2D.PLUS_X);
+ }
+
+ @Test
+ public void testGetAngle() {
+ // arrange
+ Vector2D vec = Vector2D.of(1, 2);
+
+ for (double theta = -4 * Geometry.PI; theta < 2 * Geometry.PI; theta
+= 0.1) {
+ Line line = Line.fromPointAndAngle(vec, theta, TEST_PRECISION);
+
+ // act/assert
+
Assert.assertEquals(PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(theta),
+ line.getAngle(), TEST_EPS);
+ }
+ }
+
+ @Test
+ public void testGetAngle_multiplesOfPi() {
+ // arrange
+ Vector2D vec = Vector2D.of(-1, -2);
+
+ // act/assert
+ Assert.assertEquals(0, Line.fromPointAndAngle(vec, Geometry.ZERO_PI,
TEST_PRECISION).getAngle(), TEST_EPS);
+ Assert.assertEquals(Geometry.PI, Line.fromPointAndAngle(vec,
Geometry.PI, TEST_PRECISION).getAngle(), TEST_EPS);
+ Assert.assertEquals(0, Line.fromPointAndAngle(vec, Geometry.TWO_PI,
TEST_PRECISION).getAngle(), TEST_EPS);
+
+ Assert.assertEquals(0, Line.fromPointAndAngle(vec, -2 * Geometry.PI,
TEST_PRECISION).getAngle(), TEST_EPS);
+ Assert.assertEquals(Geometry.PI, Line.fromPointAndAngle(vec, -3 *
Geometry.PI, TEST_PRECISION).getAngle(), TEST_EPS);
+ Assert.assertEquals(0, Line.fromPointAndAngle(vec, -4 *
Geometry.TWO_PI, TEST_PRECISION).getAngle(), TEST_EPS);
+ }
+
+ @Test
+ public void testGetDirection() {
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.PLUS_X,
+ Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0),
TEST_PRECISION).getDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_Y,
+ Line.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1),
TEST_PRECISION).getDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_X,
+ Line.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 2),
TEST_PRECISION).getDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.PLUS_X,
+ Line.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2),
TEST_PRECISION).getDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_Y,
+ Line.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1),
TEST_PRECISION).getDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.PLUS_Y,
+ Line.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1),
TEST_PRECISION).getDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1,
-1).normalize(),
+ Line.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0),
TEST_PRECISION).getDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1,
1).normalize(),
+ Line.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2),
TEST_PRECISION).getDirection(), TEST_EPS);
}
@Test
- public void testAbscissa() {
- Line l = new Line(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
+ public void testGetOffsetDirection() {
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_Y,
+ Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_X,
+ Line.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.PLUS_Y,
+ Line.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 2),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_Y,
+ Line.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.MINUS_X,
+ Line.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.PLUS_X,
+ Line.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1,
-1).normalize(),
+ Line.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1,
1).normalize(),
+ Line.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2),
TEST_PRECISION).getOffsetDirection(), TEST_EPS);
+ }
+
+ @Test
+ public void testGetOrigin() {
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
+ Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0),
TEST_PRECISION).getOrigin(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
+ Line.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1),
TEST_PRECISION).getOrigin(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2),
+ Line.fromPoints(Vector2D.of(2, 2), Vector2D.of(3, 2),
TEST_PRECISION).getOrigin(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -2),
+ Line.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2),
TEST_PRECISION).getOrigin(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 0),
+ Line.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1),
TEST_PRECISION).getOrigin(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0),
+ Line.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1),
TEST_PRECISION).getOrigin(), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1),
+ Line.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0),
TEST_PRECISION).getOrigin(), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1),
+ Line.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2),
TEST_PRECISION).getOrigin(), TEST_EPS);
+ }
+
+ @Test
+ public void testGetOriginOffset() {
+ // arrange
+ double sqrt2 = Math.sqrt(2);
+
+ // act/assert
Assert.assertEquals(0.0,
- (l.toSubSpace(Vector2D.of(-3, 4))).getX(),
- 1.0e-10);
+ Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 1),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
Assert.assertEquals(0.0,
- (l.toSubSpace(Vector2D.of( 3, -4))).getX(),
- 1.0e-10);
- Assert.assertEquals(-5.0,
- (l.toSubSpace(Vector2D.of( 7, -1))).getX(),
- 1.0e-10);
- Assert.assertEquals(5.0,
- (l.toSubSpace(Vector2D.of(-1, -7))).getX(),
- 1.0e-10);
+ Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(-1, -1),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
+
+ Assert.assertEquals(sqrt2,
+ Line.fromPoints(Vector2D.of(-1, 1), Vector2D.of(0, 2),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
+ Assert.assertEquals(-sqrt2,
+ Line.fromPoints(Vector2D.of(0, -2), Vector2D.of(1, -1),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
+
+ Assert.assertEquals(-sqrt2,
+ Line.fromPoints(Vector2D.of(0, 2), Vector2D.of(-1, 1),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
+ Assert.assertEquals(sqrt2,
+ Line.fromPoints(Vector2D.of(1, -1), Vector2D.of(0, -2),
TEST_PRECISION).getOriginOffset(), TEST_EPS);
+ }
+
+ @Test
+ public void testGetPrecision() {
+ // act/assert
+ Assert.assertSame(TEST_PRECISION, Line.fromPoints(Vector2D.ZERO,
Vector2D.PLUS_X, TEST_PRECISION).getPrecision());
+ Assert.assertSame(TEST_PRECISION,
Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION).getPrecision());
+ Assert.assertSame(TEST_PRECISION,
Line.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION).getPrecision());
+ }
+
+ @Test
+ public void testCopySelf() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertSame(line, line.copySelf());
+ }
+
+ @Test
+ public void testReverse() {
+ // arrange
+ Vector2D pt = Vector2D.of(0, 1);
+ Vector2D dir = Vector2D.PLUS_X;
+ Line line = Line.fromPointAndDirection(pt, dir, TEST_PRECISION);
+
+ // act
+ Line reversed = line.reverse();
+ Line doubleReversed = reversed.reverse();
+
+ // assert
+ checkLine(reversed, pt, dir.negate());
+ Assert.assertEquals(-1, reversed.getOriginOffset(), TEST_EPS);
+
+ checkLine(doubleReversed, pt, dir);
+ Assert.assertEquals(1, doubleReversed.getOriginOffset(), TEST_EPS);
+ }
+
+ @Test
+ public void testToSubSpace() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(0.0, line.toSubSpace(Vector2D.of(-3, 4)).getX(),
TEST_EPS);
+ Assert.assertEquals(0.0, line.toSubSpace(Vector2D.of( 3, -4)).getX(),
TEST_EPS);
+ Assert.assertEquals(-5.0, line.toSubSpace(Vector2D.of(7, -1)).getX(),
TEST_EPS);
+ Assert.assertEquals(5.0, line.toSubSpace(Vector2D.of(-1, -7)).getX(),
TEST_EPS);
+ }
+
+ @Test
+ public void testToSpace_throughOrigin() {
+ // arrange
+ double invSqrt2 = 1 / Math.sqrt(2);
+ Vector2D dir = Vector2D.of(invSqrt2, invSqrt2);
+
+ Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1),
TEST_PRECISION);
+
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
line.toSpace(Vector1D.of(0)), TEST_EPS);
+
+ for (int i=0; i<100; ++i) {
+ EuclideanTestUtils.assertCoordinatesEqual(dir.multiply(i),
line.toSpace(Vector1D.of(i)), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(dir.multiply(-i),
line.toSpace(Vector1D.of(-i)), TEST_EPS);
+ }
+ }
+
+ @Test
+ public void testToSpace_offsetFromOrigin() {
+ // arrange
+ double angle = Geometry.PI / 6;
+ double cos = Math.cos(angle);
+ double sin = Math.sin(angle);
+ Vector2D pt = Vector2D.of(-5, 0);
+
+ double h = Math.abs(pt.getX()) * cos;
+ double d = h * cos;
+ Vector2D origin = Vector2D.of(
+ pt.getX() + d,
+ h * sin
+ );
+ Vector2D dir = Vector2D.of(cos, sin);
+
+ Line line = Line.fromPointAndAngle(pt, angle, TEST_PRECISION);
+
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(origin,
line.toSpace(Vector1D.of(0)), TEST_EPS);
+
+ for (int i=0; i<100; ++i) {
+
EuclideanTestUtils.assertCoordinatesEqual(origin.add(dir.multiply(i)),
line.toSpace(Vector1D.of(i)), TEST_EPS);
+
EuclideanTestUtils.assertCoordinatesEqual(origin.add(dir.multiply(-i)),
line.toSpace(Vector1D.of(-i)), TEST_EPS);
+ }
+ }
+
+ @Test
+ public void testIntersection() {
+ // arrange
+ Line a = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_Y,
TEST_PRECISION);
+ Line c = Line.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2,
1), TEST_PRECISION);
+ Line d = Line.fromPointAndDirection(Vector2D.of(0, -1), Vector2D.of(2,
-1), TEST_PRECISION);
+
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
a.intersection(b), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
b.intersection(a), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-4, 0),
a.intersection(c), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-4, 0),
c.intersection(a), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0),
a.intersection(d), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0),
d.intersection(a), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2),
b.intersection(c), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2),
c.intersection(b), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -1),
b.intersection(d), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -1),
d.intersection(b), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0.5),
c.intersection(d), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0.5),
d.intersection(c), TEST_EPS);
+ }
+
+ @Test
+ public void testIntersection_parallel() {
+ // arrange
+ Line a = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.of(0, 1),
Vector2D.PLUS_X, TEST_PRECISION);
+
+ Line c = Line.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2,
1), TEST_PRECISION);
+ Line d = Line.fromPointAndDirection(Vector2D.of(0, -1), Vector2D.of(2,
1), TEST_PRECISION);
+
+ // act/assert
+ Assert.assertNull(a.intersection(b));
+ Assert.assertNull(b.intersection(a));
+
+ Assert.assertNull(c.intersection(d));
+ Assert.assertNull(d.intersection(c));
+ }
+
+ @Test
+ public void testIntersection_coincident() {
+ // arrange
+ Line a = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ Line c = Line.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2,
1), TEST_PRECISION);
+ Line d = Line.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2,
1), TEST_PRECISION);
+
+ // act/assert
+ Assert.assertNull(a.intersection(b));
+ Assert.assertNull(b.intersection(a));
+
+ Assert.assertNull(c.intersection(d));
+ Assert.assertNull(d.intersection(c));
+ }
+
+ @Test
+ public void testProject() {
+ // --- arrange
+ Line xAxis = Line.fromPointAndDirection(Vector2D.ZERO,
Vector2D.PLUS_X, TEST_PRECISION);
+ Line yAxis = Line.fromPointAndDirection(Vector2D.ZERO,
Vector2D.PLUS_Y, TEST_PRECISION);
+
+ double diagonalYIntercept = 1;
+ Vector2D diagonalDir = Vector2D.of(1, 2);
+ Line diagonal = Line.fromPointAndDirection(Vector2D.of(0,
diagonalYIntercept), diagonalDir, TEST_PRECISION);
+
+ EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
+ Vector2D pt = Vector2D.of(x, y);
+
+ // --- act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(x, 0),
xAxis.project(pt), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, y),
yAxis.project(pt), TEST_EPS);
+
+ Vector2D diagonalPt = diagonal.project(pt);
+ Assert.assertTrue(diagonal.contains(diagonalPt));
+ Assert.assertEquals(diagonal.distance(pt),
pt.distance(diagonalPt), TEST_EPS);
+
+ // check that y = mx + b is true
+ Assert.assertEquals(diagonalPt.getY(),
+ (diagonalDir.getY() * diagonalPt.getX() /
diagonalDir.getX()) + diagonalYIntercept, TEST_EPS);
+ });
+ }
+
+ @Test
+ public void testWholeHyperplane() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act
+ SubLine result = line.wholeHyperplane();
+
+ // assert
+ Assert.assertSame(line, result.getHyperplane());
+ GeometryTestUtils.assertPositiveInfinity(result.getSize());
+ }
+
+ @Test
+ public void testWholeSpace() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act
+ PolygonsSet result = line.wholeSpace();
+
+ // assert
+ GeometryTestUtils.assertPositiveInfinity(result.getSize());
+ Assert.assertSame(TEST_PRECISION, result.getPrecision());
+ }
+
+ @Test
+ public void testGetOffset_parallelLines() {
+ // arrange
+ double dist = Math.sin(Math.atan2(2, 1));
+
+ Line a = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.of(-3, 0), Vector2D.of(0, 6),
TEST_PRECISION);
+ Line c = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line d = Line.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, -2),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(-dist, a.getOffset(b), TEST_EPS);
+ Assert.assertEquals(dist, b.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(dist, a.getOffset(c), TEST_EPS);
+ Assert.assertEquals(-dist, c.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(3 * dist, a.getOffset(d), TEST_EPS);
+ Assert.assertEquals(3 * dist, d.getOffset(a), TEST_EPS);
+ }
+
+ @Test
+ public void testGetOffset_coincidentLines() {
+ // arrange
+ Line a = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line c = b.reverse();
+
+ // act/assert
+ Assert.assertEquals(0, a.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.getOffset(b), TEST_EPS);
+ Assert.assertEquals(0, b.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.getOffset(c), TEST_EPS);
+ Assert.assertEquals(0, c.getOffset(a), TEST_EPS);
+ }
+
+ @Test
+ public void testGetOffset_nonParallelLines() {
+ // arrange
+ Line a = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_Y,
TEST_PRECISION);
+ Line c = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line d = Line.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(0, a.getOffset(b), TEST_EPS);
+ Assert.assertEquals(0, b.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.getOffset(c), TEST_EPS);
+ Assert.assertEquals(0, c.getOffset(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.getOffset(d), TEST_EPS);
+ Assert.assertEquals(0, d.getOffset(a), TEST_EPS);
+ }
+
+ @Test
+ public void testGetOffset_point() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line reversed = line.reverse();
+
+ // act/assert
+ Assert.assertEquals(0.0, line.getOffset(Vector2D.of(-0.5, 1)),
TEST_EPS);
+ Assert.assertEquals(0.0, line.getOffset(Vector2D.of(-1.5, -1)),
TEST_EPS);
+ Assert.assertEquals(0.0, line.getOffset(Vector2D.of(0.5, 3)),
TEST_EPS);
+
+ double d = Math.sin(Math.atan2(2, 1));
+
+ Assert.assertEquals(d, line.getOffset(Vector2D.ZERO), TEST_EPS);
+ Assert.assertEquals(-d, line.getOffset(Vector2D.of(-1, 2)), TEST_EPS);
+
+ Assert.assertEquals(-d, reversed.getOffset(Vector2D.ZERO), TEST_EPS);
+ Assert.assertEquals(d, reversed.getOffset(Vector2D.of(-1, 2)),
TEST_EPS);
+ }
+
+ @Test
+ public void testGetOffset_point_permute() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Vector2D lineOrigin = line.getOrigin();
+
+ EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
+ Vector2D pt = Vector2D.of(x, y);
+
+ // act
+ double offset = line.getOffset(pt);
+
+ // arrange
+ Vector2D vec = lineOrigin.vectorTo(pt).reject(line.getDirection());
+ double dot = vec.dot(line.getOffsetDirection());
+ double expected = Math.signum(dot) * vec.norm();
+
+ Assert.assertEquals(expected, offset, TEST_EPS);
+ });
+ }
+
+ @Test
+ public void testSameOrientationAs() {
+ // arrange
+ Line a = Line.fromPointAndAngle(Vector2D.ZERO, Geometry.ZERO_PI,
TEST_PRECISION);
+ Line b = Line.fromPointAndAngle(Vector2D.of(4, 5), Geometry.ZERO_PI,
TEST_PRECISION);
+ Line c = Line.fromPointAndAngle(Vector2D.of(-1, -3), 0.4 *
Geometry.PI, TEST_PRECISION);
+ Line d = Line.fromPointAndAngle(Vector2D.of(1, 0), -0.4 * Geometry.PI,
TEST_PRECISION);
+
+ Line e = Line.fromPointAndAngle(Vector2D.of(6, -3), Geometry.PI,
TEST_PRECISION);
+ Line f = Line.fromPointAndAngle(Vector2D.of(8, 5), 0.8 * Geometry.PI,
TEST_PRECISION);
+ Line g = Line.fromPointAndAngle(Vector2D.of(6, -3), -0.8 *
Geometry.PI, TEST_PRECISION);
+
+ // act/assert
+ Assert.assertTrue(a.sameOrientationAs(a));
+ Assert.assertTrue(a.sameOrientationAs(b));
+ Assert.assertTrue(b.sameOrientationAs(a));
+ Assert.assertTrue(a.sameOrientationAs(c));
+ Assert.assertTrue(c.sameOrientationAs(a));
+ Assert.assertTrue(a.sameOrientationAs(d));
+ Assert.assertTrue(d.sameOrientationAs(a));
+
+ Assert.assertFalse(c.sameOrientationAs(d));
+ Assert.assertFalse(d.sameOrientationAs(c));
+
+ Assert.assertTrue(e.sameOrientationAs(f));
+ Assert.assertTrue(f.sameOrientationAs(e));
+ Assert.assertTrue(e.sameOrientationAs(g));
+ Assert.assertTrue(g.sameOrientationAs(e));
+
+ Assert.assertFalse(a.sameOrientationAs(e));
+ Assert.assertFalse(e.sameOrientationAs(a));
+ }
+
+ @Test
+ public void testSameOrientationAs_orthogonal() {
+ // arrange
+ Line a = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.of(4, 5),
Vector2D.PLUS_Y, TEST_PRECISION);
+ Line c = Line.fromPointAndDirection(Vector2D.of(-4, -5),
Vector2D.MINUS_Y, TEST_PRECISION);
+
+ // act/assert
+ Assert.assertTrue(a.sameOrientationAs(b));
+ Assert.assertTrue(b.sameOrientationAs(a));
+ Assert.assertTrue(a.sameOrientationAs(c));
+ Assert.assertTrue(c.sameOrientationAs(a));
+ }
+
+ @Test
+ public void testDistance_parallelLines() {
+ // arrange
+ double dist = Math.sin(Math.atan2(2, 1));
+
+ Line a = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.of(-3, 0), Vector2D.of(0, 6),
TEST_PRECISION);
+ Line c = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line d = Line.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, -2),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(dist, a.distance(b), TEST_EPS);
+ Assert.assertEquals(dist, b.distance(a), TEST_EPS);
+
+ Assert.assertEquals(dist, a.distance(c), TEST_EPS);
+ Assert.assertEquals(dist, c.distance(a), TEST_EPS);
+
+ Assert.assertEquals(3 * dist, a.distance(d), TEST_EPS);
+ Assert.assertEquals(3 * dist, d.distance(a), TEST_EPS);
}
@Test
- public void testOffset() {
- Line l = new Line(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
- Assert.assertEquals(-5.0, l.getOffset(Vector2D.of(5, -3)), 1.0e-10);
- Assert.assertEquals(+5.0, l.getOffset(Vector2D.of(-5, 2)), 1.0e-10);
+ public void testDistance_coincidentLines() {
+ // arrange
+ Line a = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+ Line c = b.reverse();
+
+ // act/assert
+ Assert.assertEquals(0, a.distance(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.distance(b), TEST_EPS);
+ Assert.assertEquals(0, b.distance(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.distance(c), TEST_EPS);
+ Assert.assertEquals(0, c.distance(a), TEST_EPS);
+ }
+
+ @Test
+ public void testDistance_nonParallelLines() {
+ // arrange
+ Line a = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line b = Line.fromPoints(Vector2D.ZERO, Vector2D.PLUS_Y,
TEST_PRECISION);
+ Line c = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line d = Line.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, 4),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(0, a.distance(b), TEST_EPS);
+ Assert.assertEquals(0, b.distance(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.distance(c), TEST_EPS);
+ Assert.assertEquals(0, c.distance(a), TEST_EPS);
+
+ Assert.assertEquals(0, a.distance(d), TEST_EPS);
+ Assert.assertEquals(0, d.distance(a), TEST_EPS);
}
@Test
public void testDistance() {
- Line l = new Line(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
- Assert.assertEquals(+5.0, l.distance(Vector2D.of(5, -3)), 1.0e-10);
- Assert.assertEquals(+5.0, l.distance(Vector2D.of(-5, 2)), 1.0e-10);
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertEquals(0, line.distance(line.getOrigin()), TEST_EPS);
+ Assert.assertEquals(+5.0, line.distance(Vector2D.of(5, -3)), TEST_EPS);
+ Assert.assertEquals(+5.0, line.distance(Vector2D.of(-5, 2)), TEST_EPS);
}
@Test
public void testPointAt() {
- Line l = new Line(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
- for (double a = -2.0; a < 2.0; a += 0.2) {
- Vector1D pA = Vector1D.of(a);
- Vector2D point = l.toSpace(pA);
- Assert.assertEquals(a, (l.toSubSpace(point)).getX(), 1.0e-10);
- Assert.assertEquals(0.0, l.getOffset(point), 1.0e-10);
- for (double o = -2.0; o < 2.0; o += 0.2) {
- point = l.getPointAt(pA, o);
- Assert.assertEquals(a, (l.toSubSpace(point)).getX(), 1.0e-10);
- Assert.assertEquals(o, l.getOffset(point), 1.0e-10);
+ // arrange
+ Vector2D origin = Vector2D.of(-1, 1);
+ double d = Math.sqrt(2);
+ Line line = Line.fromPointAndDirection(origin, Vector2D.of(1, 1),
TEST_PRECISION);
+
+ // act/assert
+ EuclideanTestUtils.assertCoordinatesEqual(origin, line.pointAt(0, 0),
TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
line.pointAt(0, d), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 2),
line.pointAt(0, -d), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0),
line.pointAt(-d, 0), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2),
line.pointAt(d, 0), TEST_EPS);
+
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1),
line.pointAt(d, d), TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1),
line.pointAt(-d, -d), TEST_EPS);
+ }
+
+ @Test
+ public void testPointAt_abscissaOffsetRoundtrip() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2),
TEST_PRECISION);
+
+ for (double abscissa = -2.0; abscissa < 2.0; abscissa += 0.2) {
+ for (double offset = -2.0; offset < 2.0; offset += 0.2) {
+
+ // act
+ Vector2D point = line.pointAt(abscissa, offset);
+
+ // assert
+ Assert.assertEquals(abscissa, line.toSubSpace(point).getX(),
TEST_EPS);
+ Assert.assertEquals(offset, line.getOffset(point), TEST_EPS);
}
}
}
@Test
- public void testOriginOffset() {
- Line l1 = new Line(Vector2D.of(0, 1), Vector2D.of(1, 2),
TEST_PRECISION);
- Assert.assertEquals(Math.sqrt(0.5), l1.getOriginOffset(), 1.0e-10);
- Line l2 = new Line(Vector2D.of(1, 2), Vector2D.of(0, 1),
TEST_PRECISION);
- Assert.assertEquals(-Math.sqrt(0.5), l2.getOriginOffset(), 1.0e-10);
+ public void testContains_line() {
+ // arrange
+ Vector2D pt = Vector2D.of(1, 2);
+ Vector2D dir = Vector2D.of(3, 7);
+ Line a = Line.fromPointAndDirection(pt, dir, TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.of(0, -4), dir,
TEST_PRECISION);
+ Line c = Line.fromPointAndDirection(Vector2D.of(-2, -2), dir.negate(),
TEST_PRECISION);
+ Line d = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ Line e = Line.fromPointAndDirection(pt, dir, TEST_PRECISION);
+ Line f = Line.fromPointAndDirection(pt, dir.negate(), TEST_PRECISION);
+
+ // act/assert
+ Assert.assertTrue(a.contains(a));
+
+ Assert.assertTrue(a.contains(e));
+ Assert.assertTrue(e.contains(a));
+
+ Assert.assertTrue(a.contains(f));
+ Assert.assertTrue(f.contains(a));
+
+ Assert.assertFalse(a.contains(b));
+ Assert.assertFalse(a.contains(c));
+ Assert.assertFalse(a.contains(d));
+ }
+
+ @Test
+ public void testIsParallel_closeToEpsilon() {
+ // arrange
+ double eps = 1e-3;
+ DoublePrecisionContext precision = new
EpsilonDoublePrecisionContext(eps);
+
+ Vector2D p = Vector2D.of(1, 2);
+
+ Line line = Line.fromPointAndAngle(p, Geometry.ZERO_PI, precision);
+
+ // act/assert
+ Vector2D offset1 = Vector2D.of(0, 1e-4);
+ Vector2D offset2 = Vector2D.of(0, 2e-3);
+
+ Assert.assertTrue(line.contains(Line.fromPointAndAngle(p.add(offset1),
Geometry.ZERO_PI, precision)));
+
Assert.assertTrue(line.contains(Line.fromPointAndAngle(p.subtract(offset1),
Geometry.ZERO_PI, precision)));
+
+
Assert.assertFalse(line.contains(Line.fromPointAndAngle(p.add(offset2),
Geometry.ZERO_PI, precision)));
+
Assert.assertFalse(line.contains(Line.fromPointAndAngle(p.subtract(offset2),
Geometry.ZERO_PI, precision)));
+
+ Assert.assertTrue(line.contains(Line.fromPointAndAngle(p, 1e-4,
precision)));
+ Assert.assertFalse(line.contains(Line.fromPointAndAngle(p, 1e-2,
precision)));
+ }
+
+ @Test
+ public void testContains_point() {
+ // arrange
+ Vector2D p1 = Vector2D.of(-1, 0);
+ Vector2D p2 = Vector2D.of(0, 2);
+ Line line = Line.fromPoints(p1, p2, TEST_PRECISION);
+
+ // act/assert
+ Assert.assertTrue(line.contains(p1));
+ Assert.assertTrue(line.contains(p2));
+
+ Assert.assertFalse(line.contains(Vector2D.ZERO));
+ Assert.assertFalse(line.contains(Vector2D.of(100, 79)));
+
+ Vector2D offset1 = Vector2D.of(0.1, 0);
+ Vector2D offset2 = Vector2D.of(0, -0.1);
+ Vector2D v;
+ for (double t=-2; t<=2; t+=0.1) {
+ v = p1.lerp(p2, t);
+
+ Assert.assertTrue(line.contains(v));
+
+ Assert.assertFalse(line.contains(v.add(offset1)));
+ Assert.assertFalse(line.contains(v.add(offset2)));
+ }
+ }
+
+ @Test
+ public void testContains_point_closeToEpsilon() {
+ // arrange
+ double eps = 1e-3;
+ DoublePrecisionContext precision = new
EpsilonDoublePrecisionContext(eps);
+
+ Vector2D p1 = Vector2D.of(-1, 0);
+ Vector2D p2 = Vector2D.of(0, 2);
+ Vector2D mid = p1.lerp(p2, 0.5);
+
+ Line line = Line.fromPoints(p1, p2, precision);
+ Vector2D dir = line.getOffsetDirection();
+
+ // act/assert
+ Assert.assertTrue(line.contains(mid.add(dir.multiply(1e-4))));
+ Assert.assertTrue(line.contains(mid.add(dir.multiply(-1e-4))));
+
+ Assert.assertFalse(line.contains(mid.add(dir.multiply(2e-3))));
+ Assert.assertFalse(line.contains(mid.add(dir.multiply(-2e-3))));
+ }
+
+ @Test
+ public void testDistance_point() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Line reversed = line.reverse();
+
+ // act/assert
+ Assert.assertEquals(0.0, line.distance(Vector2D.of(-0.5, 1)),
TEST_EPS);
+ Assert.assertEquals(0.0, line.distance(Vector2D.of(-1.5, -1)),
TEST_EPS);
+ Assert.assertEquals(0.0, line.distance(Vector2D.of(0.5, 3)), TEST_EPS);
+
+ double d = Math.sin(Math.atan2(2, 1));
+
+ Assert.assertEquals(d, line.distance(Vector2D.ZERO), TEST_EPS);
+ Assert.assertEquals(d, line.distance(Vector2D.of(-1, 2)), TEST_EPS);
+
+ Assert.assertEquals(d, reversed.distance(Vector2D.ZERO), TEST_EPS);
+ Assert.assertEquals(d, reversed.distance(Vector2D.of(-1, 2)),
TEST_EPS);
+ }
+
+ @Test
+ public void testDistance_point_permute() {
+ // arrange
+ Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2),
TEST_PRECISION);
+ Vector2D lineOrigin = line.getOrigin();
+
+ EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
+ Vector2D pt = Vector2D.of(x, y);
+
+ // act
+ double dist = line.distance(pt);
+
+ // arrange
+ Vector2D vec = lineOrigin.vectorTo(pt).reject(line.getDirection());
+ Assert.assertEquals(vec.norm(), dist, TEST_EPS);
+ });
+ }
+
+ @Test
+ public void testIsParallel() {
+ // arrange
+ Vector2D dir = Vector2D.of(3, 7);
+ Line a = Line.fromPointAndDirection(Vector2D.of(1, 2), dir,
TEST_PRECISION);
+ Line b = Line.fromPointAndDirection(Vector2D.of(0, -4), dir,
TEST_PRECISION);
+ Line c = Line.fromPointAndDirection(Vector2D.of(-2, -2), dir.negate(),
TEST_PRECISION);
+ Line d = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act/assert
+ Assert.assertTrue(a.isParallel(a));
+
+ Assert.assertTrue(a.isParallel(b));
+ Assert.assertTrue(b.isParallel(a));
+
+ Assert.assertTrue(a.isParallel(c));
+ Assert.assertTrue(c.isParallel(a));
+
+ Assert.assertFalse(a.isParallel(d));
+ Assert.assertFalse(d.isParallel(a));
}
@Test
- public void testParallel() {
- Line l1 = new Line(Vector2D.of(0, 1), Vector2D.of(1, 2),
TEST_PRECISION);
- Line l2 = new Line(Vector2D.of(2, 2), Vector2D.of(3, 3),
TEST_PRECISION);
- Assert.assertTrue(l1.isParallelTo(l2));
- Line l3 = new Line(Vector2D.of(1, 0), Vector2D.of(0.5, -0.5),
TEST_PRECISION);
- Assert.assertTrue(l1.isParallelTo(l3));
- Line l4 = new Line(Vector2D.of(1, 0), Vector2D.of(0.5, -0.51),
TEST_PRECISION);
- Assert.assertTrue(! l1.isParallelTo(l4));
+ public void testIsParallel_closeToParallel() {
+ // arrange
+ double eps = 1e-3;
+ DoublePrecisionContext precision = new
EpsilonDoublePrecisionContext(eps);
+
+ Vector2D p1 = Vector2D.of(1, 2);
+ Vector2D p2 = Vector2D.of(1, -2);
+
+ Line line = Line.fromPointAndAngle(p1, Geometry.ZERO_PI, precision);
+
+ // act/assert
+ Assert.assertTrue(line.isParallel(Line.fromPointAndAngle(p2, 1e-4,
precision)));
+ Assert.assertFalse(line.isParallel(Line.fromPointAndAngle(p2, 1e-2,
precision)));
}
@Test
public void testTransform() {
+ // arrange
+ AffineTransformMatrix2D scale = AffineTransformMatrix2D.createScale(2,
3);
+ AffineTransformMatrix2D reflect =
AffineTransformMatrix2D.createScale(-1, 1);
+ AffineTransformMatrix2D translate =
AffineTransformMatrix2D.createTranslation(3, 4);
+ AffineTransformMatrix2D rotate =
AffineTransformMatrix2D.createRotation(Geometry.HALF_PI);
+ AffineTransformMatrix2D rotateAroundPt =
AffineTransformMatrix2D.createRotation(Vector2D.of(0, 1), Geometry.HALF_PI);
+
+ Vector2D p1 = Vector2D.of(0, 1);
+ Vector2D p2 = Vector2D.of(1, 0);
+
+ Line horizontal = Line.fromPointAndDirection(p1, Vector2D.PLUS_X,
TEST_PRECISION);
+ Line vertical = Line.fromPointAndDirection(p2, Vector2D.PLUS_Y,
TEST_PRECISION);
+ Line diagonal = Line.fromPointAndDirection(Vector2D.ZERO,
Vector2D.of(1, 1), TEST_PRECISION);
+
+ // act/assert
+ Assert.assertSame(TEST_PRECISION,
horizontal.transform(scale).getPrecision());
+
+ checkLine(horizontal.transform(scale), Vector2D.of(0, 3),
Vector2D.PLUS_X);
+ checkLine(vertical.transform(scale), Vector2D.of(2, 0),
Vector2D.PLUS_Y);
+ checkLine(diagonal.transform(scale), Vector2D.ZERO, Vector2D.of(2,
3).normalize());
- Line l1 = new Line(Vector2D.of(1.0 ,1.0), Vector2D.of(4.0 ,1.0),
TEST_PRECISION);
+ checkLine(horizontal.transform(reflect), p1, Vector2D.MINUS_X);
+ checkLine(vertical.transform(reflect), Vector2D.of(-1, 0),
Vector2D.PLUS_Y);
+ checkLine(diagonal.transform(reflect), Vector2D.ZERO, Vector2D.of(-1,
1).normalize());
+
+ checkLine(horizontal.transform(translate), Vector2D.of(0, 5),
Vector2D.PLUS_X);
+ checkLine(vertical.transform(translate), Vector2D.of(4, 0),
Vector2D.PLUS_Y);
+ checkLine(diagonal.transform(translate), Vector2D.of(-0.5, 0.5),
Vector2D.of(1, 1).normalize());
+
+ checkLine(horizontal.transform(rotate), Vector2D.of(-1, 0),
Vector2D.PLUS_Y);
+ checkLine(vertical.transform(rotate), Vector2D.of(0, 1),
Vector2D.MINUS_X);
+ checkLine(diagonal.transform(rotate), Vector2D.ZERO, Vector2D.of(-1,
1).normalize());
+
+ checkLine(horizontal.transform(rotateAroundPt), Vector2D.ZERO,
Vector2D.PLUS_Y);
+ checkLine(vertical.transform(rotateAroundPt), Vector2D.of(0, 2),
Vector2D.MINUS_X);
+ checkLine(diagonal.transform(rotateAroundPt), Vector2D.of(1, 1),
Vector2D.of(-1, 1).normalize());
+ }
+
+ @Test
+ public void testTransform_collapsedPoints() {
+ // arrange
+ AffineTransformMatrix2D scaleCollapse =
AffineTransformMatrix2D.createScale(0, 1);
+ Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> {
+ line.transform(scaleCollapse);
+ }, GeometryValueException.class, "Line direction cannot be zero");
+ }
+
+ @Test
+ public void testHashCode() {
+ // arrange
+ DoublePrecisionContext precision1 = new
EpsilonDoublePrecisionContext(1e-4);
+ DoublePrecisionContext precision2 = new
EpsilonDoublePrecisionContext(1e-5);
+
+ Vector2D p = Vector2D.of(1, 2);
+ Vector2D v = Vector2D.of(1, 1);
+
+ Line a = Line.fromPointAndDirection(p, v, precision1);
+ Line b = Line.fromPointAndDirection(Vector2D.ZERO, v, precision1);
+ Line c = Line.fromPointAndDirection(p, v.negate(), precision1);
+ Line d = Line.fromPointAndDirection(p, v, precision2);
+ Line e = Line.fromPointAndDirection(p, v, precision1);
+
+ // act/assert
+ int aHash = a.hashCode();
+
+ Assert.assertEquals(aHash, a.hashCode());
+ Assert.assertEquals(aHash, e.hashCode());
+
+ Assert.assertNotEquals(aHash, b.hashCode());
+ Assert.assertNotEquals(aHash, c.hashCode());
+ Assert.assertNotEquals(aHash, d.hashCode());
+ }
+
+ @Test
+ public void testEquals() {
+ // arrange
+ DoublePrecisionContext precision1 = new
EpsilonDoublePrecisionContext(1e-4);
+ DoublePrecisionContext precision2 = new
EpsilonDoublePrecisionContext(1e-5);
+
+ Vector2D p = Vector2D.of(1, 2);
+ Vector2D v = Vector2D.of(1, 1);
+
+ Line a = Line.fromPointAndDirection(p, v, precision1);
+ Line b = Line.fromPointAndDirection(Vector2D.ZERO, v, precision1);
+ Line c = Line.fromPointAndDirection(p, v.negate(), precision1);
+ Line d = Line.fromPointAndDirection(p, v, precision2);
+ Line e = Line.fromPointAndDirection(p, v, precision1);
+
+ // act/assert
+ Assert.assertTrue(a.equals(a));
+ Assert.assertTrue(a.equals(e));
+ Assert.assertTrue(e.equals(a));
+
+ Assert.assertFalse(a.equals(null));
+ Assert.assertFalse(a.equals(new Object()));
+
+ Assert.assertFalse(a.equals(b));
+ Assert.assertFalse(a.equals(c));
+ Assert.assertFalse(a.equals(d));
+ }
+
+ @Test
+ public void testToString() {
+ // arrange
+ Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.PLUS_X,
TEST_PRECISION);
+
+ // act
+ String str = line.toString();
+
+ // assert
+ Assert.assertTrue(str.contains("Line"));
+ Assert.assertTrue(str.contains("origin= (0.0, 0.0)"));
+ Assert.assertTrue(str.contains("direction= (1.0, 0.0)"));
+ }
+
+ @Test
+ public void testLineTransform() {
+
+ Line l1 = Line.fromPoints(Vector2D.of(1.0 ,1.0), Vector2D.of(4.0
,1.0), TEST_PRECISION);
Transform<Vector2D, Vector1D> t1 =
- Line.getTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5);
+ Line.getTransform(Vector2D.of(0.0, 0.5), Vector2D.of(-1.0, 0.0),
Vector2D.of(1.0, 1.5));
Assert.assertEquals(0.5 * Math.PI,
((Line) t1.apply(l1)).getAngle(),
1.0e-10);
- Line l2 = new Line(Vector2D.of(0.0, 0.0), Vector2D.of(1.0, 1.0),
TEST_PRECISION);
+ Line l2 = Line.fromPoints(Vector2D.of(0.0, 0.0), Vector2D.of(1.0,
1.0), TEST_PRECISION);
Transform<Vector2D, Vector1D> t2 =
- Line.getTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5);
+ Line.getTransform(Vector2D.of(0.0, 0.5), Vector2D.of(-1.0, 0.0),
Vector2D.of(1.0, 1.5));
Assert.assertEquals(Math.atan2(1.0, -2.0),
((Line) t2.apply(l2)).getAngle(),
1.0e-10);
}
- @Test
- public void testIntersection() {
- Line l1 = new Line(Vector2D.of( 0, 1), Vector2D.of(1, 2),
TEST_PRECISION);
- Line l2 = new Line(Vector2D.of(-1, 2), Vector2D.of(2, 1),
TEST_PRECISION);
- Vector2D p = l1.intersection(l2);
- Assert.assertEquals(0.5, p.getX(), 1.0e-10);
- Assert.assertEquals(1.5, p.getY(), 1.0e-10);
+ /**
+ * Check that the line has the given defining properties.
+ * @param line
+ * @param origin
+ * @param dir
+ */
+ private void checkLine(Line line, Vector2D origin, Vector2D dir) {
+ EuclideanTestUtils.assertCoordinatesEqual(origin, line.getOrigin(),
TEST_EPS);
+ EuclideanTestUtils.assertCoordinatesEqual(dir, line.getDirection(),
TEST_EPS);
}
-
}
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
index 14f917b..3ea43c5 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
@@ -101,7 +101,7 @@ public class PolygonsSetTest {
@Test
public void testInfiniteLines_single() {
// arrange
- Line line = new Line(Vector2D.of(0, 0), Vector2D.of(1, 1),
TEST_PRECISION);
+ Line line = Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 1),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(line.wholeHyperplane());
@@ -137,8 +137,8 @@ public class PolygonsSetTest {
@Test
public void testInfiniteLines_twoIntersecting() {
// arrange
- Line line1 = new Line(Vector2D.of(0, 0), Vector2D.of(1, 1),
TEST_PRECISION);
- Line line2 = new Line(Vector2D.of(1, -1), Vector2D.of(0, 0),
TEST_PRECISION);
+ Line line1 = Line.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 1),
TEST_PRECISION);
+ Line line2 = Line.fromPoints(Vector2D.of(1, -1), Vector2D.of(0, 0),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(line1.wholeHyperplane());
@@ -175,8 +175,8 @@ public class PolygonsSetTest {
@Test
public void testInfiniteLines_twoParallel_facingIn() {
// arrange
- Line line1 = new Line(Vector2D.of(1, 1), Vector2D.of(0, 1),
TEST_PRECISION);
- Line line2 = new Line(Vector2D.of(0, -1), Vector2D.of(1, -1),
TEST_PRECISION);
+ Line line1 = Line.fromPoints(Vector2D.of(1, 1), Vector2D.of(0, 1),
TEST_PRECISION);
+ Line line2 = Line.fromPoints(Vector2D.of(0, -1), Vector2D.of(1, -1),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(line1.wholeHyperplane());
@@ -221,8 +221,8 @@ public class PolygonsSetTest {
@Test
public void testInfiniteLines_twoParallel_facingOut() {
// arrange
- Line line1 = new Line(Vector2D.of(0, 1), Vector2D.of(1, 1),
TEST_PRECISION);
- Line line2 = new Line(Vector2D.of(1, -1), Vector2D.of(0, -1),
TEST_PRECISION);
+ Line line1 = Line.fromPoints(Vector2D.of(0, 1), Vector2D.of(1, 1),
TEST_PRECISION);
+ Line line2 = Line.fromPoints(Vector2D.of(1, -1), Vector2D.of(0, -1),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(line1.wholeHyperplane());
@@ -267,8 +267,8 @@ public class PolygonsSetTest {
@Test
public void testMixedFiniteAndInfiniteLines_explicitInfiniteBoundaries() {
// arrange
- Line line1 = new Line(Vector2D.of(3, 3), Vector2D.of(0, 3),
TEST_PRECISION);
- Line line2 = new Line(Vector2D.of(0, -3), Vector2D.of(3, -3),
TEST_PRECISION);
+ Line line1 = Line.fromPoints(Vector2D.of(3, 3), Vector2D.of(0, 3),
TEST_PRECISION);
+ Line line2 = Line.fromPoints(Vector2D.of(0, -3), Vector2D.of(3, -3),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(line1.wholeHyperplane());
@@ -318,7 +318,7 @@ public class PolygonsSetTest {
@Test
public void testMixedFiniteAndInfiniteLines_impliedInfiniteBoundaries() {
// arrange
- Line line = new Line(Vector2D.of(3, 0), Vector2D.of(3, 3),
TEST_PRECISION);
+ Line line = Line.fromPoints(Vector2D.of(3, 0), Vector2D.of(3, 3),
TEST_PRECISION);
List<SubHyperplane<Vector2D>> boundaries = new
ArrayList<SubHyperplane<Vector2D>>();
boundaries.add(buildSegment(Vector2D.of(0, 3), Vector2D.of(0, 0)));
@@ -714,7 +714,7 @@ public class PolygonsSetTest {
PolygonsSet set = buildSet(vertices);
// assert
- Line l1 = new Line(Vector2D.of(-1.5, 0.0), Math.PI / 4,
TEST_PRECISION);
+ Line l1 = Line.fromPointAndAngle(Vector2D.of(-1.5, 0.0), Math.PI / 4,
TEST_PRECISION);
SubLine s1 = (SubLine) set.intersection(l1.wholeHyperplane());
List<Interval> i1 = ((IntervalsSet) s1.getRemainingRegion()).asList();
Assert.assertEquals(2, i1.size());
@@ -733,7 +733,7 @@ public class PolygonsSetTest {
Assert.assertEquals(1.5, p11Upper.getX(), TEST_EPS);
Assert.assertEquals(3.0, p11Upper.getY(), TEST_EPS);
- Line l2 = new Line(Vector2D.of(-1.0, 2.0), 0, TEST_PRECISION);
+ Line l2 = Line.fromPointAndAngle(Vector2D.of(-1.0, 2.0), 0,
TEST_PRECISION);
SubLine s2 = (SubLine) set.intersection(l2.wholeHyperplane());
List<Interval> i2 = ((IntervalsSet) s2.getRemainingRegion()).asList();
Assert.assertEquals(1, i2.size());
@@ -1094,13 +1094,13 @@ public class PolygonsSetTest {
double pi6 = Math.PI / 6.0;
double sqrt3 = Math.sqrt(3.0);
SubLine[] hyp = {
- new Line(Vector2D.of( 0.0, 1.0), 5 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of(-sqrt3, 1.0), 7 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of(-sqrt3, 1.0), 9 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of(-sqrt3, 0.0), 11 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of( 0.0, 0.0), 13 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of( 0.0, 1.0), 3 * pi6,
TEST_PRECISION).wholeHyperplane(),
- new Line(Vector2D.of(-5.0 * sqrt3 / 6.0, 0.0), 9 * pi6,
TEST_PRECISION).wholeHyperplane()
+ Line.fromPointAndAngle(Vector2D.of( 0.0, 1.0), 5 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of(-sqrt3, 1.0), 7 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of(-sqrt3, 1.0), 9 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of(-sqrt3, 0.0), 11 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of( 0.0, 0.0), 13 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of( 0.0, 1.0), 3 * pi6,
TEST_PRECISION).wholeHyperplane(),
+ Line.fromPointAndAngle(Vector2D.of(-5.0 * sqrt3 / 6.0, 0.0), 9 *
pi6, TEST_PRECISION).wholeHyperplane()
};
hyp[1] = (SubLine) hyp[1].split(hyp[0].getHyperplane()).getMinus();
hyp[2] = (SubLine) hyp[2].split(hyp[1].getHyperplane()).getMinus();
@@ -1114,7 +1114,7 @@ public class PolygonsSetTest {
}
PolygonsSet set = new PolygonsSet(tree, TEST_PRECISION);
SubLine splitter =
- new Line(Vector2D.of(-2.0 * sqrt3 / 3.0, 0.0), 9 * pi6,
TEST_PRECISION).wholeHyperplane();
+ Line.fromPointAndAngle(Vector2D.of(-2.0 * sqrt3 / 3.0, 0.0), 9 *
pi6, TEST_PRECISION).wholeHyperplane();
// act
PolygonsSet slice =
@@ -1295,13 +1295,13 @@ public class PolygonsSetTest {
public void testBug20041003() {
// arrange
Line[] l = {
- new Line(Vector2D.of(0.0, 0.625000007541172),
+ Line.fromPoints(Vector2D.of(0.0, 0.625000007541172),
Vector2D.of(1.0, 0.625000007541172), TEST_PRECISION),
- new Line(Vector2D.of(-0.19204433621902645, 0.0),
+ Line.fromPoints(Vector2D.of(-0.19204433621902645, 0.0),
Vector2D.of(-0.19204433621902645, 1.0), TEST_PRECISION),
- new Line(Vector2D.of(-0.40303524786887, 0.4248364535319128),
+ Line.fromPoints(Vector2D.of(-0.40303524786887,
0.4248364535319128),
Vector2D.of(-1.12851149797877, -0.2634107480798909),
TEST_PRECISION),
- new Line(Vector2D.of(0.0, 2.0),
+ Line.fromPoints(Vector2D.of(0.0, 2.0),
Vector2D.of(1.0, 2.0), TEST_PRECISION)
};
@@ -1588,10 +1588,10 @@ public class PolygonsSetTest {
// if tolerance is smaller than rectangle width, the rectangle is
computed accurately
DoublePrecisionContext precision = new
EpsilonDoublePrecisionContext(1.0 / 256);
Hyperplane<Vector2D>[] h1 = new Line[] {
- new Line(pA, pB, precision),
- new Line(pB, pC, precision),
- new Line(pC, pD, precision),
- new Line(pD, pA, precision)
+ Line.fromPoints(pA, pB, precision),
+ Line.fromPoints(pB, pC, precision),
+ Line.fromPoints(pC, pD, precision),
+ Line.fromPoints(pD, pA, precision)
};
// act
@@ -1619,10 +1619,10 @@ public class PolygonsSetTest {
DoublePrecisionContext precision = new
EpsilonDoublePrecisionContext(1.0 / 16);
Hyperplane<Vector2D>[] h2 = new Line[] {
- new Line(pA, pB, precision),
- new Line(pB, pC, precision),
- new Line(pC, pD, precision),
- new Line(pD, pA, precision)
+ Line.fromPointAndDirection(pA, Vector2D.MINUS_Y, precision),
+ Line.fromPointAndDirection(pB, Vector2D.PLUS_X, precision),
+ Line.fromPointAndDirection(pC, Vector2D.PLUS_Y, precision),
+ Line.fromPointAndDirection(pD, Vector2D.MINUS_X, precision)
};
// act
@@ -1636,8 +1636,8 @@ public class PolygonsSetTest {
@Test(expected = IllegalArgumentException.class)
public void testInconsistentHyperplanes() {
// act
- new RegionFactory<Vector2D>().buildConvex(new Line(Vector2D.of(0, 0),
Vector2D.of(0, 1), TEST_PRECISION),
- new Line(Vector2D.of(1,
1), Vector2D.of(1, 0), TEST_PRECISION));
+ new
RegionFactory<Vector2D>().buildConvex(Line.fromPoints(Vector2D.of(0, 0),
Vector2D.of(0, 1), TEST_PRECISION),
+
Line.fromPoints(Vector2D.of(1, 1), Vector2D.of(1, 0), TEST_PRECISION));
}
@Test
@@ -1658,7 +1658,7 @@ public class PolygonsSetTest {
// splitting the square in two halves increases the BSP tree
// with 3 more cuts and 3 more leaf nodes
- SubLine cut = new Line(Vector2D.of(0.5, 0.5), 0.0,
square.getPrecision()).wholeHyperplane();
+ SubLine cut = Line.fromPointAndAngle(Vector2D.of(0.5, 0.5), 0.0,
square.getPrecision()).wholeHyperplane();
PolygonsSet splitSquare = new
PolygonsSet(square.getTree(false).split(cut),
square.getPrecision());
Counter splitSquareCount = new Counter();
@@ -1720,7 +1720,7 @@ public class PolygonsSetTest {
}
private SubHyperplane<Vector2D> buildLine(Vector2D start, Vector2D end) {
- return new Line(start, end, TEST_PRECISION).wholeHyperplane();
+ return Line.fromPoints(start, end, TEST_PRECISION).wholeHyperplane();
}
private double intersectionAbscissa(Line l0, Line l1) {
@@ -1730,14 +1730,14 @@ public class PolygonsSetTest {
private SubHyperplane<Vector2D> buildHalfLine(Vector2D start, Vector2D end,
boolean startIsVirtual) {
- Line line = new Line(start, end, TEST_PRECISION);
+ Line line = Line.fromPoints(start, end, TEST_PRECISION);
double lower = startIsVirtual ? Double.NEGATIVE_INFINITY :
(line.toSubSpace(start)).getX();
double upper = startIsVirtual ? (line.toSubSpace(end)).getX() :
Double.POSITIVE_INFINITY;
return new SubLine(line, new IntervalsSet(lower, upper,
TEST_PRECISION));
}
private SubHyperplane<Vector2D> buildSegment(Vector2D start, Vector2D end)
{
- Line line = new Line(start, end, TEST_PRECISION);
+ Line line = Line.fromPoints(start, end, TEST_PRECISION);
double lower = (line.toSubSpace(start)).getX();
double upper = (line.toSubSpace(end)).getX();
return new SubLine(line, new IntervalsSet(lower, upper,
TEST_PRECISION));
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SegmentTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SegmentTest.java
index cd94cfe..d6085b6 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SegmentTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SegmentTest.java
@@ -32,7 +32,7 @@ public class SegmentTest {
public void testDistance() {
Vector2D start = Vector2D.of(2, 2);
Vector2D end = Vector2D.of(-2, -2);
- Segment segment = new Segment(start, end, new Line(start, end,
TEST_PRECISION));
+ Segment segment = new Segment(start, end, Line.fromPoints(start, end,
TEST_PRECISION));
// distance to center of segment
Assert.assertEquals(Math.sqrt(2), segment.distance(Vector2D.of(1,
-1)), TEST_EPS);
diff --git
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SubLineTest.java
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SubLineTest.java
index 6ef6638..2e086bc 100644
---
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SubLineTest.java
+++
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/SubLineTest.java
@@ -37,7 +37,7 @@ public class SubLineTest {
public void testEndPoints() {
Vector2D p1 = Vector2D.of(-1, -7);
Vector2D p2 = Vector2D.of(7, -1);
- Segment segment = new Segment(p1, p2, new Line(p1, p2,
TEST_PRECISION));
+ Segment segment = new Segment(p1, p2, Line.fromPoints(p1, p2,
TEST_PRECISION));
SubLine sub = new SubLine(segment);
List<Segment> segments = sub.getSegments();
Assert.assertEquals(1, segments.size());
@@ -47,7 +47,7 @@ public class SubLineTest {
@Test
public void testNoEndPoints() {
- SubLine wholeLine = new Line(Vector2D.of(-1, 7), Vector2D.of(7, 1),
TEST_PRECISION).wholeHyperplane();
+ SubLine wholeLine = Line.fromPoints(Vector2D.of(-1, 7), Vector2D.of(7,
1), TEST_PRECISION).wholeHyperplane();
List<Segment> segments = wholeLine.getSegments();
Assert.assertEquals(1, segments.size());
Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getX())
&&
@@ -62,7 +62,7 @@ public class SubLineTest {
@Test
public void testNoSegments() {
- SubLine empty = new SubLine(new Line(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
+ SubLine empty = new SubLine(Line.fromPoints(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
new
RegionFactory<Vector1D>().getComplement(new IntervalsSet(TEST_PRECISION)));
List<Segment> segments = empty.getSegments();
Assert.assertEquals(0, segments.size());
@@ -70,7 +70,7 @@ public class SubLineTest {
@Test
public void testSeveralSegments() {
- SubLine twoSubs = new SubLine(new Line(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
+ SubLine twoSubs = new SubLine(Line.fromPoints(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
new RegionFactory<Vector1D>().union(new
IntervalsSet(1, 2, TEST_PRECISION),
new
IntervalsSet(3, 4, TEST_PRECISION)));
List<Segment> segments = twoSubs.getSegments();
@@ -79,7 +79,7 @@ public class SubLineTest {
@Test
public void testHalfInfiniteNeg() {
- SubLine empty = new SubLine(new Line(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
+ SubLine empty = new SubLine(Line.fromPoints(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
new IntervalsSet(Double.NEGATIVE_INFINITY,
0.0, TEST_PRECISION));
List<Segment> segments = empty.getSegments();
Assert.assertEquals(1, segments.size());
@@ -92,7 +92,7 @@ public class SubLineTest {
@Test
public void testHalfInfinitePos() {
- SubLine empty = new SubLine(new Line(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
+ SubLine empty = new SubLine(Line.fromPoints(Vector2D.of(-1, -7),
Vector2D.of(7, -1), TEST_PRECISION),
new IntervalsSet(0.0,
Double.POSITIVE_INFINITY, TEST_PRECISION));
List<Segment> segments = empty.getSegments();
Assert.assertEquals(1, segments.size());
diff --git
a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/ConvexHull2D.java
b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/ConvexHull2D.java
index 2aee11d..7dc7b26 100644
---
a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/ConvexHull2D.java
+++
b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/ConvexHull2D.java
@@ -127,7 +127,7 @@ public class ConvexHull2D implements ConvexHull<Vector2D>,
Serializable {
this.lineSegments = new Segment[1];
final Vector2D p1 = vertices[0];
final Vector2D p2 = vertices[1];
- this.lineSegments[0] = new Segment(p1, p2, new Line(p1, p2,
precision));
+ this.lineSegments[0] = new Segment(p1, p2, Line.fromPoints(p1,
p2, precision));
} else {
this.lineSegments = new Segment[size];
Vector2D firstPoint = null;
@@ -139,12 +139,12 @@ public class ConvexHull2D implements
ConvexHull<Vector2D>, Serializable {
lastPoint = point;
} else {
this.lineSegments[index++] =
- new Segment(lastPoint, point, new
Line(lastPoint, point, precision));
+ new Segment(lastPoint, point,
Line.fromPoints(lastPoint, point, precision));
lastPoint = point;
}
}
this.lineSegments[index] =
- new Segment(lastPoint, firstPoint, new Line(lastPoint,
firstPoint, precision));
+ new Segment(lastPoint, firstPoint,
Line.fromPoints(lastPoint, firstPoint, precision));
}
}
return lineSegments;
diff --git
a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChain.java
b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChain.java
index 80a1686..98d019d 100644
---
a/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChain.java
+++
b/commons-geometry-hull/src/main/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChain.java
@@ -147,7 +147,7 @@ public class MonotoneChain extends
AbstractConvexHullGenerator2D {
final Vector2D p1 = hull.get(size - 2);
final Vector2D p2 = hull.get(size - 1);
- final double offset = new Line(p1, p2, precision).getOffset(point);
+ final double offset = Line.fromPoints(p1, p2,
precision).getOffset(point);
if (precision.eqZero(offset)) {
// the point is collinear to the line (p1, p2)
diff --git
a/commons-geometry-hull/src/test/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChainTest.java
b/commons-geometry-hull/src/test/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChainTest.java
index 2a24af4..2b91053 100644
---
a/commons-geometry-hull/src/test/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChainTest.java
+++
b/commons-geometry-hull/src/test/java/org/apache/commons/geometry/euclidean/twod/hull/MonotoneChainTest.java
@@ -49,7 +49,7 @@ public class MonotoneChainTest extends
ConvexHullGenerator2DAbstractTest {
points.add(Vector2D.of(40, 1));
@SuppressWarnings("unused")
- final ConvexHull2D hull = new MonotoneChain(true, new
EpsilonDoublePrecisionContext(2)).generate(points);
+ final ConvexHull2D hull = new MonotoneChain(true, new
EpsilonDoublePrecisionContext(1)).generate(points);
}
}