| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.geometry.euclidean.twod; |
| |
| import java.util.List; |
| |
| import org.apache.commons.geometry.core.Geometry; |
| import org.apache.commons.geometry.core.GeometryTestUtils; |
| import org.apache.commons.geometry.core.RegionLocation; |
| import org.apache.commons.geometry.core.exception.GeometryValueException; |
| import org.apache.commons.geometry.core.partitioning.HyperplaneLocation; |
| import org.apache.commons.geometry.core.partitioning.Split; |
| 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.Interval; |
| import org.apache.commons.geometry.euclidean.oned.Vector1D; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class SegmentTest { |
| |
| private static final double TEST_EPS = 1e-10; |
| |
| private static final DoublePrecisionContext TEST_PRECISION = |
| new EpsilonDoublePrecisionContext(TEST_EPS); |
| |
| @Test |
| public void testFromPoints() { |
| // arrange |
| Vector2D p0 = Vector2D.of(1, 3); |
| Vector2D p1 = Vector2D.of(1, 2); |
| Vector2D p2 = Vector2D.of(-3, 4); |
| Vector2D p3 = Vector2D.of(-5, -6); |
| |
| // act/assert |
| |
| checkFiniteSegment(Segment.fromPoints(p0, p1, TEST_PRECISION), p0, p1); |
| checkFiniteSegment(Segment.fromPoints(p1, p0, TEST_PRECISION), p1, p0); |
| |
| checkFiniteSegment(Segment.fromPoints(p0, p2, TEST_PRECISION), p0, p2); |
| checkFiniteSegment(Segment.fromPoints(p2, p0, TEST_PRECISION), p2, p0); |
| |
| checkFiniteSegment(Segment.fromPoints(p0, p3, TEST_PRECISION), p0, p3); |
| checkFiniteSegment(Segment.fromPoints(p3, p0, TEST_PRECISION), p3, p0); |
| } |
| |
| @Test |
| public void testFromPoints_invalidArgs() { |
| // arrange |
| Vector2D p0 = Vector2D.of(-1, 2); |
| |
| // act/assert |
| GeometryTestUtils.assertThrows(() -> { |
| Segment.fromPoints(p0, p0, TEST_PRECISION); |
| }, GeometryValueException.class); |
| |
| GeometryTestUtils.assertThrows(() -> { |
| Segment.fromPoints(p0, Vector2D.POSITIVE_INFINITY, TEST_PRECISION); |
| }, GeometryValueException.class); |
| |
| GeometryTestUtils.assertThrows(() -> { |
| Segment.fromPoints(p0, Vector2D.NEGATIVE_INFINITY, TEST_PRECISION); |
| }, GeometryValueException.class); |
| |
| GeometryTestUtils.assertThrows(() -> { |
| Segment.fromPoints(p0, Vector2D.NaN, TEST_PRECISION); |
| }, GeometryValueException.class); |
| } |
| |
| @Test |
| public void testFromPointAndDirection() { |
| // act |
| Segment seg = Segment.fromPointAndDirection(Vector2D.of(1, 3), Vector2D.Unit.PLUS_Y, TEST_PRECISION); |
| |
| // assert |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 3), seg.getStartPoint(), TEST_EPS); |
| Assert.assertNull(seg.getEndPoint()); |
| |
| Line line = seg.getLine(); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 0), line.getOrigin(), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_Y, line.getDirection(), TEST_EPS); |
| } |
| |
| @Test |
| public void testFromInterval_intervalArg_finite() { |
| // arrange |
| DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2); |
| Interval interval = Interval.of(-1, 2, intervalPrecision); |
| |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, interval); |
| |
| // assert |
| double side = 1.0 / Math.sqrt(2); |
| checkFiniteSegment(segment, Vector2D.of(-side, -side), Vector2D.of(2 * side, 2 * side)); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_intervalArg_full() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, Interval.full()); |
| |
| // assert |
| GeometryTestUtils.assertNegativeInfinity(segment.getSubspaceStart()); |
| GeometryTestUtils.assertPositiveInfinity(segment.getSubspaceEnd()); |
| |
| Assert.assertNull(segment.getStartPoint()); |
| Assert.assertNull(segment.getEndPoint()); |
| |
| Assert.assertSame(Interval.full(), segment.getInterval()); |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_intervalArg_positiveHalfSpace() { |
| // arrange |
| DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2); |
| Interval interval = Interval.min(-1, intervalPrecision); |
| |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, interval); |
| |
| // assert |
| Assert.assertEquals(-1.0, segment.getSubspaceStart(), TEST_EPS); |
| GeometryTestUtils.assertPositiveInfinity(segment.getSubspaceEnd()); |
| |
| double side = 1.0 / Math.sqrt(2); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-side, -side), segment.getStartPoint(), TEST_EPS); |
| Assert.assertNull(segment.getEndPoint()); |
| |
| Assert.assertSame(interval, segment.getInterval()); |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_intervalArg_negativeHalfSpace() { |
| // arrange |
| DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2); |
| Interval interval = Interval.max(2, intervalPrecision); |
| |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, interval); |
| |
| // assert |
| GeometryTestUtils.assertNegativeInfinity(segment.getSubspaceStart()); |
| Assert.assertEquals(2, segment.getSubspaceEnd(), TEST_EPS); |
| |
| double side = 1.0 / Math.sqrt(2); |
| |
| Assert.assertNull(segment.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * side, 2 * side), segment.getEndPoint(), TEST_EPS); |
| |
| Assert.assertSame(interval, segment.getInterval()); |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_doubleArgs_finite() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, -1, 2); |
| |
| // assert |
| double side = 1.0 / Math.sqrt(2); |
| checkFiniteSegment(segment, Vector2D.of(-side, -side), Vector2D.of(2 * side, 2 * side)); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_doubleArgs_full() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); |
| |
| // assert |
| GeometryTestUtils.assertNegativeInfinity(segment.getSubspaceStart()); |
| GeometryTestUtils.assertPositiveInfinity(segment.getSubspaceEnd()); |
| |
| Assert.assertNull(segment.getStartPoint()); |
| Assert.assertNull(segment.getEndPoint()); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_doubleArgs_positiveHalfSpace() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, -1, Double.POSITIVE_INFINITY); |
| |
| // assert |
| Assert.assertEquals(-1.0, segment.getSubspaceStart(), TEST_EPS); |
| GeometryTestUtils.assertPositiveInfinity(segment.getSubspaceEnd()); |
| |
| double side = 1.0 / Math.sqrt(2); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-side, -side), segment.getStartPoint(), TEST_EPS); |
| Assert.assertNull(segment.getEndPoint()); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_doubleArgs_negativeHalfSpace() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, 2, Double.NEGATIVE_INFINITY); |
| |
| // assert |
| GeometryTestUtils.assertNegativeInfinity(segment.getSubspaceStart()); |
| Assert.assertEquals(2, segment.getSubspaceEnd(), TEST_EPS); |
| |
| double side = 1.0 / Math.sqrt(2); |
| |
| Assert.assertNull(segment.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * side, 2 * side), segment.getEndPoint(), TEST_EPS); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testFromInterval_vectorArgs() { |
| // arrange |
| Line line = Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| Segment segment = Segment.fromInterval(line, Vector1D.of(-1), Vector1D.of(2)); |
| |
| // assert |
| double side = 1.0 / Math.sqrt(2); |
| checkFiniteSegment(segment, Vector2D.of(-side, -side), Vector2D.of(2 * side, 2 * side)); |
| |
| Assert.assertSame(TEST_PRECISION, segment.getPrecision()); |
| } |
| |
| @Test |
| public void testIsFull() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertTrue(Segment.fromInterval(line, Interval.full()).isFull()); |
| Assert.assertTrue(Segment.fromInterval(line, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isFull()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.min(0, TEST_PRECISION)).isFull()); |
| Assert.assertFalse(Segment.fromInterval(line, Interval.max(0, TEST_PRECISION)).isFull()); |
| |
| Assert.assertFalse(Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION).isFull()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.point(1, TEST_PRECISION)).isEmpty()); |
| } |
| |
| @Test |
| public void testIsInfinite() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertTrue(Segment.fromInterval(line, Interval.full()).isInfinite()); |
| Assert.assertTrue(Segment.fromInterval(line, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isInfinite()); |
| |
| Assert.assertTrue(Segment.fromInterval(line, Interval.min(0, TEST_PRECISION)).isInfinite()); |
| Assert.assertTrue(Segment.fromInterval(line, Interval.max(0, TEST_PRECISION)).isInfinite()); |
| |
| Assert.assertFalse(Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION).isInfinite()); |
| Assert.assertFalse(Segment.fromInterval(line, Interval.point(1, TEST_PRECISION)).isInfinite()); |
| } |
| |
| @Test |
| public void testIsFinite() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertTrue(Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION).isFinite()); |
| Assert.assertTrue(Segment.fromInterval(line, Interval.point(1, TEST_PRECISION)).isFinite()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.full()).isFinite()); |
| Assert.assertFalse(Segment.fromInterval(line, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isFinite()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.min(0, TEST_PRECISION)).isFinite()); |
| Assert.assertFalse(Segment.fromInterval(line, Interval.max(0, TEST_PRECISION)).isFinite()); |
| } |
| |
| @Test |
| public void testIsEmpty_alwaysReturnsFalse() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertFalse(Segment.fromInterval(line, Interval.full()).isEmpty()); |
| Assert.assertFalse(Segment.fromInterval(line, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isEmpty()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.min(0, TEST_PRECISION)).isEmpty()); |
| Assert.assertFalse(Segment.fromInterval(line, Interval.max(0, TEST_PRECISION)).isEmpty()); |
| |
| Assert.assertFalse(Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION).isEmpty()); |
| |
| Assert.assertFalse(Segment.fromInterval(line, Interval.point(1, TEST_PRECISION)).isEmpty()); |
| } |
| |
| @Test |
| public void testGetSize() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION); |
| |
| // act/assert |
| GeometryTestUtils.assertPositiveInfinity(Segment.fromInterval(line, Interval.full()).getSize()); |
| GeometryTestUtils.assertPositiveInfinity(Segment.fromInterval(line, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).getSize()); |
| |
| GeometryTestUtils.assertPositiveInfinity(Segment.fromInterval(line, Interval.min(0, TEST_PRECISION)).getSize()); |
| GeometryTestUtils.assertPositiveInfinity(Segment.fromInterval(line, Interval.max(0, TEST_PRECISION)).getSize()); |
| |
| Assert.assertEquals(Math.sqrt(2), Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION).getSize(), TEST_EPS); |
| Assert.assertEquals(9.0, Segment.fromPoints(Vector2D.of(1, 1), Vector2D.of(1, 10), TEST_PRECISION).getSize(), TEST_EPS); |
| |
| Assert.assertEquals(0.0, Segment.fromInterval(line, Interval.point(1, TEST_PRECISION)).getSize(), TEST_EPS); |
| Assert.assertEquals(1.0, Segment.fromInterval(line, Interval.of(1, 2, TEST_PRECISION)).getSize(), TEST_EPS); |
| } |
| |
| @Test |
| public void testClassify() { |
| // arrange |
| DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2); |
| Segment segment = Segment.fromPoints(Vector2D.ZERO, Vector2D.of(1, 2), precision); |
| |
| // act/assert |
| checkClassify(segment, RegionLocation.OUTSIDE, |
| Vector2D.of(0.25, 1), Vector2D.of(0.75, 1), |
| Vector2D.of(-1, -2), Vector2D.of(-0.1, 0), |
| Vector2D.of(1.1, 2), Vector2D.of(2, 4)); |
| |
| checkClassify(segment, RegionLocation.BOUNDARY, |
| Vector2D.ZERO, Vector2D.of(0.005, 0), |
| Vector2D.of(1, 2), Vector2D.of(1, 1.995)); |
| |
| checkClassify(segment, RegionLocation.INSIDE, |
| Vector2D.of(0.25, 0.5), Vector2D.of(0.495, 1), |
| Vector2D.of(0.75, 1.5)); |
| } |
| |
| @Test |
| public void testClosest() { |
| // arrange |
| Segment segment = Segment.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act/assert |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, segment.closest(Vector2D.of(-1, -1)), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, segment.closest(Vector2D.of(-2, 2)), TEST_EPS); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 0.5), segment.closest(Vector2D.of(0.5, 0.5)), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 0.5), segment.closest(Vector2D.of(0, 1)), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 0.5), segment.closest(Vector2D.of(1, 0)), TEST_EPS); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), segment.closest(Vector2D.of(2, 2)), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), segment.closest(Vector2D.of(5, 10)), TEST_EPS); |
| } |
| |
| @Test |
| public void testToConvex() { |
| // arrange |
| Segment segment = Segment.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION); |
| |
| // act |
| List<Segment> segments = segment.toConvex(); |
| |
| // assert |
| Assert.assertEquals(1, segments.size()); |
| Assert.assertSame(segment, segments.get(0)); |
| } |
| |
| @Test |
| public void testTransform_finite() { |
| // arrange |
| Segment segment = Segment.fromPoints(Vector2D.of(0, 1), Vector2D.of(2, 3), TEST_PRECISION); |
| |
| Transform2D translation = AffineTransformMatrix2D.createTranslation(-1, 1); |
| Transform2D rotation = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI); |
| Transform2D scale = AffineTransformMatrix2D.createScale(2, 3); |
| Transform2D reflect = FunctionTransform2D.from((pt) -> Vector2D.of(pt.getX(), -pt.getY())); |
| |
| // act/assert |
| checkFiniteSegment(segment.transform(translation), Vector2D.of(-1, 2), Vector2D.of(1, 4)); |
| checkFiniteSegment(segment.transform(rotation), Vector2D.of(-1, 0), Vector2D.of(-3, 2)); |
| checkFiniteSegment(segment.transform(scale), Vector2D.of(0, 3), Vector2D.of(4, 9)); |
| checkFiniteSegment(segment.transform(reflect), Vector2D.of(0, -1), Vector2D.of(2, -3)); |
| } |
| |
| @Test |
| public void testTransform_singlePoint() { |
| // arrange |
| Segment segment = Segment.fromInterval(Line.fromPoints(Vector2D.of(0, 1), Vector2D.of(1, 1), TEST_PRECISION), |
| Interval.point(0, TEST_PRECISION)); |
| |
| Transform2D translation = AffineTransformMatrix2D.createTranslation(-1, 1); |
| Transform2D rotation = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI); |
| Transform2D scale = AffineTransformMatrix2D.createScale(2, 3); |
| Transform2D reflect = FunctionTransform2D.from((pt) -> Vector2D.of(pt.getX(), -pt.getY())); |
| |
| // act/assert |
| checkFiniteSegment(segment.transform(translation), Vector2D.of(-1, 2), Vector2D.of(-1, 2)); |
| checkFiniteSegment(segment.transform(rotation), Vector2D.of(-1, 0), Vector2D.of(-1, 0)); |
| checkFiniteSegment(segment.transform(scale), Vector2D.of(0, 3), Vector2D.of(0, 3)); |
| checkFiniteSegment(segment.transform(reflect), Vector2D.of(0, -1), Vector2D.of(0, -1)); |
| } |
| |
| @Test |
| public void testTransform_full() { |
| // arrange |
| Segment segment = Segment.fromInterval(Line.fromPoints(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION), |
| Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); |
| |
| Transform2D translation = AffineTransformMatrix2D.createTranslation(-1, 1); |
| Transform2D rotation = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI); |
| Transform2D scale = AffineTransformMatrix2D.createScale(2, 3); |
| Transform2D reflect = FunctionTransform2D.from((pt) -> Vector2D.of(pt.getX(), -pt.getY())); |
| |
| // act/assert |
| Segment translated = segment.transform(translation); |
| Assert.assertTrue(translated.isFull()); |
| Assert.assertTrue(translated.contains(Vector2D.of(-1, 1))); |
| Assert.assertTrue(translated.contains(Vector2D.of(1, 2))); |
| Assert.assertNull(translated.getStartPoint()); |
| Assert.assertNull(translated.getEndPoint()); |
| |
| Segment rotated = segment.transform(rotation); |
| Assert.assertTrue(rotated.isFull()); |
| Assert.assertTrue(rotated.contains(Vector2D.ZERO)); |
| Assert.assertTrue(rotated.contains(Vector2D.of(-1, 2))); |
| Assert.assertNull(rotated.getStartPoint()); |
| Assert.assertNull(rotated.getEndPoint()); |
| |
| Segment scaled = segment.transform(scale); |
| Assert.assertTrue(scaled.isFull()); |
| Assert.assertTrue(scaled.contains(Vector2D.ZERO)); |
| Assert.assertTrue(scaled.contains(Vector2D.of(4, 3))); |
| Assert.assertNull(scaled.getStartPoint()); |
| Assert.assertNull(scaled.getEndPoint()); |
| |
| Segment reflected = segment.transform(reflect); |
| Assert.assertTrue(reflected.isFull()); |
| Assert.assertTrue(reflected.contains(Vector2D.ZERO)); |
| Assert.assertTrue(reflected.contains(Vector2D.of(2, -1))); |
| Assert.assertNull(reflected.getStartPoint()); |
| Assert.assertNull(reflected.getEndPoint()); |
| } |
| |
| @Test |
| public void testTransform_positiveHalfspace() { |
| // arrange |
| Segment segment = Segment.fromInterval(Line.fromPoints(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION), |
| 0.0, Double.POSITIVE_INFINITY); |
| |
| Transform2D translation = AffineTransformMatrix2D.createTranslation(-1, 1); |
| Transform2D rotation = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI); |
| Transform2D scale = AffineTransformMatrix2D.createScale(2, 3); |
| Transform2D reflect = FunctionTransform2D.from((pt) -> Vector2D.of(pt.getX(), -pt.getY())); |
| |
| // act/assert |
| Segment translated = segment.transform(translation); |
| Assert.assertTrue(translated.isInfinite()); |
| Assert.assertTrue(translated.contains(Vector2D.of(-1, 1))); |
| Assert.assertTrue(translated.contains(Vector2D.of(1, 2))); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), translated.getStartPoint(), TEST_EPS); |
| Assert.assertNull(translated.getEndPoint()); |
| |
| Segment rotated = segment.transform(rotation); |
| Assert.assertTrue(rotated.isInfinite()); |
| Assert.assertTrue(rotated.contains(Vector2D.ZERO)); |
| Assert.assertTrue(rotated.contains(Vector2D.of(-1, 2))); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, rotated.getStartPoint(), TEST_EPS); |
| Assert.assertNull(rotated.getEndPoint()); |
| |
| Segment scaled = segment.transform(scale); |
| Assert.assertTrue(scaled.isInfinite()); |
| Assert.assertTrue(scaled.contains(Vector2D.ZERO)); |
| Assert.assertTrue(scaled.contains(Vector2D.of(4, 3))); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, scaled.getStartPoint(), TEST_EPS); |
| Assert.assertNull(scaled.getEndPoint()); |
| |
| Segment reflected = segment.transform(reflect); |
| Assert.assertTrue(reflected.isInfinite()); |
| Assert.assertTrue(reflected.contains(Vector2D.ZERO)); |
| Assert.assertTrue(reflected.contains(Vector2D.of(2, -1))); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, reflected.getStartPoint(), TEST_EPS); |
| Assert.assertNull(reflected.getEndPoint()); |
| } |
| |
| @Test |
| public void testTransform_negativeHalfspace() { |
| // arrange |
| Segment segment = Segment.fromInterval(Line.fromPoints(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION), |
| Double.NEGATIVE_INFINITY, 0.0); |
| |
| Transform2D translation = AffineTransformMatrix2D.createTranslation(-1, 1); |
| Transform2D rotation = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI); |
| Transform2D scale = AffineTransformMatrix2D.createScale(2, 3); |
| Transform2D reflect = FunctionTransform2D.from((pt) -> Vector2D.of(pt.getX(), -pt.getY())); |
| |
| // act/assert |
| Segment translated = segment.transform(translation); |
| Assert.assertTrue(translated.isInfinite()); |
| Assert.assertTrue(translated.contains(Vector2D.of(-1, 1))); |
| Assert.assertTrue(translated.contains(Vector2D.of(-3, 0))); |
| Assert.assertNull(translated.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), translated.getEndPoint(), TEST_EPS); |
| |
| Segment rotated = segment.transform(rotation); |
| Assert.assertTrue(rotated.isInfinite()); |
| Assert.assertTrue(rotated.contains(Vector2D.ZERO)); |
| Assert.assertTrue(rotated.contains(Vector2D.of(1, -2))); |
| Assert.assertNull(rotated.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, rotated.getEndPoint(), TEST_EPS); |
| |
| Segment scaled = segment.transform(scale); |
| Assert.assertTrue(scaled.isInfinite()); |
| Assert.assertTrue(scaled.contains(Vector2D.ZERO)); |
| Assert.assertTrue(scaled.contains(Vector2D.of(-4, -3))); |
| Assert.assertNull(scaled.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, scaled.getEndPoint(), TEST_EPS); |
| |
| Segment reflected = segment.transform(reflect); |
| Assert.assertTrue(reflected.isInfinite()); |
| Assert.assertTrue(reflected.contains(Vector2D.ZERO)); |
| Assert.assertTrue(reflected.contains(Vector2D.of(-2, 1))); |
| Assert.assertNull(reflected.getStartPoint()); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, reflected.getEndPoint(), TEST_EPS); |
| } |
| |
| @Test |
| public void testIntersection_line() { |
| // arrange |
| Segment aSeg = Segment.fromPoints(Vector2D.of(1, 0), Vector2D.of(2, 0), TEST_PRECISION); |
| Segment bSeg = Segment.fromPoints(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION); |
| |
| Line xAxis = Line.fromPointAndAngle(Vector2D.ZERO, Geometry.ZERO_PI, TEST_PRECISION); |
| Line yAxis = Line.fromPointAndAngle(Vector2D.ZERO, Geometry.HALF_PI, TEST_PRECISION); |
| Line angledLine = Line.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 0), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertNull(aSeg.intersection(xAxis)); |
| Assert.assertNull(aSeg.intersection(yAxis)); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bSeg.intersection(xAxis), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bSeg.intersection(yAxis), TEST_EPS); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), bSeg.intersection(angledLine), TEST_EPS); |
| } |
| |
| @Test |
| public void testIntersection_lineSegment() { |
| // arrange |
| Segment a = Segment.fromPoints(Vector2D.of(1, 0), Vector2D.of(2, 0), TEST_PRECISION); |
| Segment b = Segment.fromPoints(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION); |
| Segment c = Segment.fromPoints(Vector2D.of(-1, 0), Vector2D.ZERO, TEST_PRECISION); |
| Segment d = Segment.fromPoints(Vector2D.of(0, 3), Vector2D.of(3, 0), TEST_PRECISION); |
| |
| // act/assert |
| Assert.assertNull(a.intersection(a)); |
| Assert.assertNull(a.intersection(c)); |
| Assert.assertNull(a.intersection(b)); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, b.intersection(c), TEST_EPS); |
| |
| Assert.assertNull(b.intersection(d)); |
| Assert.assertNull(d.intersection(b)); |
| } |
| |
| @Test |
| public void testReverse_full() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| Segment segment = line.span(); |
| |
| // act |
| Segment result = segment.reverse(); |
| |
| // assert |
| checkInfiniteSegment(result, segment.getLine().reverse(), null, null); |
| } |
| |
| @Test |
| public void testReverse_positiveHalfSpace() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| Segment segment = line.segment(1, Double.POSITIVE_INFINITY); |
| |
| // act |
| Segment result = segment.reverse(); |
| |
| // assert |
| checkInfiniteSegment(result, segment.getLine().reverse(), null, Vector2D.of(1, 0)); |
| } |
| |
| @Test |
| public void testReverse_negativeHalfSpace() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| Segment segment = line.segment(Double.NEGATIVE_INFINITY, 1); |
| |
| // act |
| Segment result = segment.reverse(); |
| |
| // assert |
| checkInfiniteSegment(result, segment.getLine().reverse(), Vector2D.of(1, 0), null); |
| } |
| |
| @Test |
| public void testReverse_finiteSegment() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| Segment segment = line.segment(3, 4); |
| |
| // act |
| Segment result = segment.reverse(); |
| |
| // assert |
| checkFiniteSegment(result, Vector2D.of(4, 0), Vector2D.of(3, 0)); |
| } |
| |
| @Test |
| public void testSplit_finite() { |
| // arrange |
| Vector2D start = Vector2D.of(1, 1); |
| Vector2D end = Vector2D.of(3, 2); |
| Vector2D middle = start.lerp(end, 0.5); |
| |
| Segment seg = Segment.fromPoints(start, end, TEST_PRECISION); |
| |
| // act/assert |
| Split<Segment> both = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION)); |
| checkFiniteSegment(both.getMinus(), middle, end); |
| checkFiniteSegment(both.getPlus(), start, middle); |
| |
| Split<Segment> bothReversed = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION)); |
| checkFiniteSegment(bothReversed.getMinus(), start, middle); |
| checkFiniteSegment(bothReversed.getPlus(), middle, end); |
| |
| Split<Segment> minusOnlyOrthogonal = seg.split(Line.fromPointAndDirection(start, Vector2D.of(1, -2), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyOrthogonal.getMinus()); |
| Assert.assertNull(minusOnlyOrthogonal.getPlus()); |
| |
| Split<Segment> minusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyParallel.getMinus()); |
| Assert.assertNull(minusOnlyParallel.getPlus()); |
| |
| Split<Segment> plusOnlyOrthogonal = seg.split(Line.fromPointAndDirection(end, Vector2D.of(1, -2), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyOrthogonal.getMinus()); |
| Assert.assertSame(seg, plusOnlyOrthogonal.getPlus()); |
| |
| Split<Segment> plusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyParallel.getMinus()); |
| Assert.assertSame(seg, plusOnlyParallel.getPlus()); |
| |
| Split<Segment> hyper = seg.split(Line.fromPointAndDirection(start, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertNull(hyper.getMinus()); |
| Assert.assertNull(hyper.getPlus()); |
| } |
| |
| @Test |
| public void testSplit_full() { |
| // arrange |
| Vector2D p1 = Vector2D.of(1, 1); |
| Vector2D p2 = Vector2D.of(3, 2); |
| Vector2D middle = p1.lerp(p2, 0.5); |
| |
| Line line = Line.fromPoints(p1, p2, TEST_PRECISION); |
| |
| Segment seg = Segment.fromInterval(line, Interval.full()); |
| |
| // act/assert |
| Split<Segment> both = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION)); |
| checkInfiniteSegment(both.getMinus(), line, middle, null); |
| checkInfiniteSegment(both.getPlus(), line, null, middle); |
| |
| Split<Segment> bothReversed = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION)); |
| checkInfiniteSegment(bothReversed.getMinus(), line, null, middle); |
| checkInfiniteSegment(bothReversed.getPlus(), line, middle, null); |
| |
| Split<Segment> minusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyParallel.getMinus()); |
| Assert.assertNull(minusOnlyParallel.getPlus()); |
| |
| Split<Segment> plusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyParallel.getMinus()); |
| Assert.assertSame(seg, plusOnlyParallel.getPlus()); |
| |
| Split<Segment> hyper = seg.split(Line.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertNull(hyper.getMinus()); |
| Assert.assertNull(hyper.getPlus()); |
| } |
| |
| @Test |
| public void testSplit_positiveHalfSpace() { |
| // arrange |
| Vector2D p1 = Vector2D.of(1, 1); |
| Vector2D p2 = Vector2D.of(3, 2); |
| Vector2D middle = p1.lerp(p2, 0.5); |
| |
| Line line = Line.fromPoints(p1, p2, TEST_PRECISION); |
| |
| Segment seg = Segment.fromInterval(line, Interval.min(line.toSubspace(p1).getX(), TEST_PRECISION)); |
| |
| // act/assert |
| Split<Segment> both = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION)); |
| checkInfiniteSegment(both.getMinus(), line, middle, null); |
| checkFiniteSegment(both.getPlus(), p1, middle); |
| |
| Split<Segment> bothReversed = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION)); |
| checkFiniteSegment(bothReversed.getMinus(), p1, middle); |
| checkInfiniteSegment(bothReversed.getPlus(), line, middle, null); |
| |
| Split<Segment> minusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyParallel.getMinus()); |
| Assert.assertNull(minusOnlyParallel.getPlus()); |
| |
| Split<Segment> minusOnlyOrthogonal = seg.split(Line.fromPointAndDirection(p1, Vector2D.of(1, -2), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyOrthogonal.getMinus()); |
| Assert.assertNull(minusOnlyOrthogonal.getPlus()); |
| |
| Split<Segment> plusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyParallel.getMinus()); |
| Assert.assertSame(seg, plusOnlyParallel.getPlus()); |
| |
| Split<Segment> hyper = seg.split(Line.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertNull(hyper.getMinus()); |
| Assert.assertNull(hyper.getPlus()); |
| } |
| |
| @Test |
| public void testSplit_negativeHalfSpace() { |
| // arrange |
| Vector2D p1 = Vector2D.of(1, 1); |
| Vector2D p2 = Vector2D.of(3, 2); |
| Vector2D middle = p1.lerp(p2, 0.5); |
| |
| Line line = Line.fromPoints(p1, p2, TEST_PRECISION); |
| |
| Segment seg = Segment.fromInterval(line, Interval.max(line.toSubspace(p2).getX(), TEST_PRECISION)); |
| |
| // act/assert |
| Split<Segment> both = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION)); |
| checkFiniteSegment(both.getMinus(), middle, p2); |
| checkInfiniteSegment(both.getPlus(), line, null, middle); |
| |
| Split<Segment> bothReversed = seg.split(Line.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION)); |
| checkInfiniteSegment(bothReversed.getMinus(), line, null, middle); |
| checkFiniteSegment(bothReversed.getPlus(), middle, p2); |
| |
| Split<Segment> minusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertSame(seg, minusOnlyParallel.getMinus()); |
| Assert.assertNull(minusOnlyParallel.getPlus()); |
| |
| Split<Segment> plusOnlyParallel = seg.split(Line.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyParallel.getMinus()); |
| Assert.assertSame(seg, plusOnlyParallel.getPlus()); |
| |
| Split<Segment> plusOnlyOrthogonal = seg.split(Line.fromPointAndDirection(p2, Vector2D.of(1, -2), TEST_PRECISION)); |
| Assert.assertNull(plusOnlyOrthogonal.getMinus()); |
| Assert.assertSame(seg, plusOnlyOrthogonal.getPlus()); |
| |
| Split<Segment> hyper = seg.split(Line.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION)); |
| Assert.assertNull(hyper.getMinus()); |
| Assert.assertNull(hyper.getPlus()); |
| } |
| |
| @Test |
| public void testToString() { |
| // arrange |
| Line line = Line.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| |
| Segment full = Segment.fromInterval(line, Interval.full()); |
| Segment startOnly = Segment.fromInterval(line, 0, Double.POSITIVE_INFINITY); |
| Segment endOnly = Segment.fromInterval(line, Double.NEGATIVE_INFINITY, 0); |
| Segment finite = Segment.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION); |
| |
| // act/assert |
| String fullStr = full.toString(); |
| Assert.assertTrue(fullStr.contains("lineOrigin=") && fullStr.contains("lineDirection=")); |
| |
| String startOnlyStr = startOnly.toString(); |
| Assert.assertTrue(startOnlyStr.contains("start=") && startOnlyStr.contains("direction=")); |
| |
| String endOnlyStr = endOnly.toString(); |
| Assert.assertTrue(endOnlyStr.contains("direction=") && endOnlyStr.contains("end=")); |
| |
| String finiteStr = finite.toString(); |
| Assert.assertTrue(finiteStr.contains("start=") && finiteStr.contains("end=")); |
| } |
| |
| @Test |
| public void testEmpty() { |
| // act |
| Polyline path = Polyline.empty(); |
| |
| // assert |
| Assert.assertTrue(path.isEmpty()); |
| Assert.assertFalse(path.isClosed()); |
| Assert.assertTrue(path.isFinite()); |
| Assert.assertFalse(path.isInfinite()); |
| |
| Assert.assertEquals(0, path.getSegments().size()); |
| } |
| |
| private static void checkClassify(Segment segment, RegionLocation loc, Vector2D ... points) { |
| for (Vector2D pt : points) { |
| String msg = "Unexpected location for point " + pt; |
| |
| Assert.assertEquals(msg, loc, segment.classify(pt)); |
| } |
| } |
| |
| private static void checkFiniteSegment(Segment segment, Vector2D start, Vector2D end) { |
| checkFiniteSegment(segment, start, end, TEST_PRECISION); |
| } |
| |
| private static void checkFiniteSegment(Segment segment, Vector2D start, Vector2D end, DoublePrecisionContext precision) { |
| Assert.assertFalse(segment.isInfinite()); |
| |
| EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS); |
| EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS); |
| |
| Line line = segment.getLine(); |
| Assert.assertEquals(HyperplaneLocation.ON, line.classify(segment.getStartPoint())); |
| Assert.assertEquals(HyperplaneLocation.ON, line.classify(segment.getEndPoint())); |
| |
| Assert.assertEquals(line.toSubspace(segment.getStartPoint()).getX(), segment.getSubspaceStart(), TEST_EPS); |
| Assert.assertEquals(line.toSubspace(segment.getEndPoint()).getX(), segment.getSubspaceEnd(), TEST_EPS); |
| |
| Assert.assertSame(precision, segment.getPrecision()); |
| Assert.assertSame(precision, line.getPrecision()); |
| } |
| |
| private static void checkInfiniteSegment(Segment segment, Line line, Vector2D start, Vector2D end) { |
| checkInfiniteSegment(segment, line, start, end, TEST_PRECISION); |
| } |
| |
| private static void checkInfiniteSegment(Segment segment, Line line, Vector2D start, Vector2D end, |
| DoublePrecisionContext precision) { |
| |
| Assert.assertTrue(segment.isInfinite()); |
| |
| Assert.assertEquals(line, segment.getLine()); |
| |
| if (start == null) { |
| Assert.assertNull(segment.getStartPoint()); |
| } |
| else { |
| EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS); |
| Assert.assertEquals(line.toSubspace(segment.getStartPoint()).getX(), segment.getSubspaceStart(), TEST_EPS); |
| } |
| |
| if (end == null) { |
| Assert.assertNull(segment.getEndPoint()); |
| } |
| else { |
| EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS); |
| Assert.assertEquals(line.toSubspace(segment.getEndPoint()).getX(), segment.getSubspaceEnd(), TEST_EPS); |
| } |
| |
| Assert.assertSame(precision, segment.getPrecision()); |
| Assert.assertSame(precision, line.getPrecision()); |
| } |
| } |