View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.geometry.euclidean.twod.shape;
18  
19  import java.util.List;
20  
21  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
22  import org.apache.commons.geometry.euclidean.twod.AffineTransformMatrix2D;
23  import org.apache.commons.geometry.euclidean.twod.LineConvexSubset;
24  import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
25  import org.apache.commons.geometry.euclidean.twod.Vector2D;
26  import org.apache.commons.geometry.euclidean.twod.path.LinePath;
27  import org.apache.commons.geometry.euclidean.twod.rotation.Rotation2D;
28  import org.apache.commons.numbers.core.Precision;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.Test;
31  
32  class ParallelogramTest {
33  
34      private static final double TEST_EPS = 1e-10;
35  
36      private static final Precision.DoubleEquivalence TEST_PRECISION =
37              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
38  
39      @Test
40      void testUnitSquare() {
41          // act
42          final Parallelogram box = Parallelogram.unitSquare(TEST_PRECISION);
43  
44          // assert
45          Assertions.assertEquals(1, box.getSize(), TEST_EPS);
46          Assertions.assertEquals(4, box.getBoundarySize(), TEST_EPS);
47          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, box.getCentroid(), TEST_EPS);
48  
49          final List<Vector2D> vertices = box.getVertices();
50          Assertions.assertEquals(4, vertices.size());
51          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-0.5, -0.5), vertices.get(0), TEST_EPS);
52          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, -0.5), vertices.get(1), TEST_EPS);
53          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 0.5), vertices.get(2), TEST_EPS);
54          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-0.5, 0.5), vertices.get(3), TEST_EPS);
55      }
56  
57      @Test
58      void testFromTransformedUnitSquare() {
59          // arrange
60          final AffineTransformMatrix2D t = AffineTransformMatrix2D.createTranslation(Vector2D.of(1, 0))
61                  .rotate(Math.PI * 0.25)
62                  .scale(Vector2D.of(2, 1));
63  
64          // act
65          final Parallelogram p = Parallelogram.fromTransformedUnitSquare(t, TEST_PRECISION);
66  
67          // assert
68          final double sqrt2 = Math.sqrt(2);
69          final double invSqrt2 = 1 / sqrt2;
70  
71          Assertions.assertEquals(2, p.getSize(), TEST_EPS);
72          Assertions.assertEquals(4 * Math.sqrt(2.5), p.getBoundarySize(), TEST_EPS);
73          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * invSqrt2, invSqrt2), p.getCentroid(), TEST_EPS);
74  
75          final List<Vector2D> vertices = p.getVertices();
76          Assertions.assertEquals(4, vertices.size());
77          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, invSqrt2), vertices.get(0), TEST_EPS);
78          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * invSqrt2, 0), vertices.get(1), TEST_EPS);
79          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * sqrt2, invSqrt2), vertices.get(2), TEST_EPS);
80          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * invSqrt2, sqrt2), vertices.get(3), TEST_EPS);
81      }
82  
83      @Test
84      void testFromTransformedUnitSquare_transformDoesNotPreserveOrientation() {
85          // arrange
86          final AffineTransformMatrix2D t = AffineTransformMatrix2D.createTranslation(Vector2D.of(1, 0))
87                  .rotate(Math.PI * 0.25)
88                  .scale(Vector2D.of(-2, 1));
89  
90          // act
91          final Parallelogram p = Parallelogram.fromTransformedUnitSquare(t, TEST_PRECISION);
92  
93          // assert
94          final double sqrt2 = Math.sqrt(2);
95          final double invSqrt2 = 1 / sqrt2;
96  
97          Assertions.assertEquals(2, p.getSize(), TEST_EPS);
98          Assertions.assertEquals(4 * Math.sqrt(2.5), p.getBoundarySize(), TEST_EPS);
99          EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2 * invSqrt2, invSqrt2), p.getCentroid(), TEST_EPS);
100 
101         final List<Vector2D> vertices = p.getVertices();
102         Assertions.assertEquals(4, vertices.size());
103         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2 * sqrt2, invSqrt2), vertices.get(0), TEST_EPS);
104         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2 * invSqrt2, 0), vertices.get(1), TEST_EPS);
105         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, invSqrt2), vertices.get(2), TEST_EPS);
106         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2 * invSqrt2, sqrt2), vertices.get(3), TEST_EPS);
107     }
108 
109     @Test
110     void testFromTransformedUnitSquare_zeroSizeRegion() {
111         // act/assert
112         Assertions.assertThrows(IllegalArgumentException.class, () ->  Parallelogram.fromTransformedUnitSquare(AffineTransformMatrix2D.createScale(Vector2D.of(1e-16, 1)),
113                 TEST_PRECISION));
114 
115         Assertions.assertThrows(IllegalArgumentException.class, () ->  Parallelogram.fromTransformedUnitSquare(AffineTransformMatrix2D.createScale(Vector2D.of(1, 1e-16)),
116                 TEST_PRECISION));
117     }
118 
119     @Test
120     void testAxisAligned_minFirst() {
121         // act
122         final Parallelogram box = Parallelogram.axisAligned(Vector2D.of(1, 2), Vector2D.of(3, 4), TEST_PRECISION);
123 
124         // assert
125         Assertions.assertEquals(1, box.getBoundaryPaths().size());
126         final LinePath path = box.getBoundaryPaths().get(0);
127 
128         final List<LineConvexSubset> segments = path.getElements();
129         Assertions.assertEquals(4, segments.size());
130 
131         assertSegment(segments.get(0), Vector2D.of(1, 2), Vector2D.of(3, 2));
132         assertSegment(segments.get(1), Vector2D.of(3, 2), Vector2D.of(3, 4));
133         assertSegment(segments.get(2), Vector2D.of(3, 4), Vector2D.of(1, 4));
134         assertSegment(segments.get(3), Vector2D.of(1, 4), Vector2D.of(1, 2));
135     }
136 
137     @Test
138     void testAxisAligned_maxFirst() {
139         // act
140         final Parallelogram box = Parallelogram.axisAligned(Vector2D.ZERO, Vector2D.of(-1, -2), TEST_PRECISION);
141 
142         // assert
143         Assertions.assertEquals(1, box.getBoundaryPaths().size());
144         final LinePath path = box.getBoundaryPaths().get(0);
145 
146         final List<LineConvexSubset> segments = path.getElements();
147         Assertions.assertEquals(4, segments.size());
148 
149         assertSegment(segments.get(0), Vector2D.of(-1, -2), Vector2D.of(0, -2));
150         assertSegment(segments.get(1), Vector2D.of(0, -2), Vector2D.ZERO);
151         assertSegment(segments.get(2), Vector2D.ZERO, Vector2D.of(-1, 0));
152         assertSegment(segments.get(3), Vector2D.of(-1, 0), Vector2D.of(-1, -2));
153     }
154 
155     @Test
156     void testAxisAligned_illegalArgs() {
157         // act/assert
158 
159         Assertions.assertThrows(IllegalArgumentException.class, () ->  Parallelogram.axisAligned(Vector2D.of(1, 1), Vector2D.of(1, 3), TEST_PRECISION));
160         Assertions.assertThrows(IllegalArgumentException.class, () ->  Parallelogram.axisAligned(Vector2D.of(1, 1), Vector2D.of(3, 1), TEST_PRECISION));
161         Assertions.assertThrows(IllegalArgumentException.class, () ->  Parallelogram.axisAligned(Vector2D.of(2, 3), Vector2D.of(2, 3), TEST_PRECISION));
162     }
163 
164     @Test
165     void testBuilder_defaultValues() {
166         // arrange
167         final Parallelogram.Builder builder = Parallelogram.builder(TEST_PRECISION);
168 
169         // act
170         final Parallelogram p = builder.build();
171 
172         // assert
173         Assertions.assertEquals(1, p.getSize(), TEST_EPS);
174         Assertions.assertEquals(4, p.getBoundarySize(), TEST_EPS);
175         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, p.getCentroid(), TEST_EPS);
176 
177         final List<Vector2D> vertices = p.getVertices();
178         Assertions.assertEquals(4, vertices.size());
179         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-0.5, -0.5), vertices.get(0), TEST_EPS);
180         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, -0.5), vertices.get(1), TEST_EPS);
181         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 0.5), vertices.get(2), TEST_EPS);
182         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-0.5, 0.5), vertices.get(3), TEST_EPS);
183     }
184 
185     @Test
186     void testBuilder_rotatedRect_withXDirection() {
187         // arrange
188         final Parallelogram.Builder builder = Parallelogram.builder(TEST_PRECISION);
189 
190         // act
191         final Parallelogram p = builder
192                 .setScale(1, 2)
193                 .setXDirection(Vector2D.Unit.PLUS_Y)
194                 .setPosition(Vector2D.of(1, 2))
195                 .build();
196 
197         // assert
198         Assertions.assertEquals(2, p.getSize(), TEST_EPS);
199         Assertions.assertEquals(6, p.getBoundarySize(), TEST_EPS);
200         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), p.getCentroid(), TEST_EPS);
201 
202         final List<Vector2D> vertices = p.getVertices();
203         Assertions.assertEquals(4, vertices.size());
204         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 1.5), vertices.get(0), TEST_EPS);
205         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 1.5), vertices.get(1), TEST_EPS);
206         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 2.5), vertices.get(2), TEST_EPS);
207         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2.5), vertices.get(3), TEST_EPS);
208     }
209 
210     @Test
211     void testBuilder_rotatedRect_withYDirection() {
212         // arrange
213         final Parallelogram.Builder builder = Parallelogram.builder(TEST_PRECISION);
214 
215         // act
216         final Parallelogram p = builder
217                 .setScale(Vector2D.of(2, 1))
218                 .setYDirection(Vector2D.Unit.MINUS_X)
219                 .setPosition(Vector2D.of(1, 2))
220                 .build();
221 
222         // assert
223         Assertions.assertEquals(2, p.getSize(), TEST_EPS);
224         Assertions.assertEquals(6, p.getBoundarySize(), TEST_EPS);
225         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), p.getCentroid(), TEST_EPS);
226 
227         final List<Vector2D> vertices = p.getVertices();
228         Assertions.assertEquals(4, vertices.size());
229         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 1), vertices.get(0), TEST_EPS);
230         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1.5, 1), vertices.get(1), TEST_EPS);
231         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1.5, 3), vertices.get(2), TEST_EPS);
232         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 3), vertices.get(3), TEST_EPS);
233     }
234 
235     @Test
236     void testBuilder_rotatedRect_withRotation() {
237         // arrange
238         final Parallelogram.Builder builder = Parallelogram.builder(TEST_PRECISION);
239 
240         // act
241         final Parallelogram p = builder
242                 .setScale(2)
243                 .setRotation(Rotation2D.of(0.25 * Math.PI))
244                 .setPosition(Vector2D.of(1, 2))
245                 .build();
246 
247         // assert
248         Assertions.assertEquals(4, p.getSize(), TEST_EPS);
249         Assertions.assertEquals(8, p.getBoundarySize(), TEST_EPS);
250         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), p.getCentroid(), TEST_EPS);
251 
252         final List<Vector2D> vertices = p.getVertices();
253         Assertions.assertEquals(4, vertices.size());
254 
255         final double sqrt2 = Math.sqrt(2);
256         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1 - sqrt2, 2), vertices.get(0), TEST_EPS);
257         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2 - sqrt2), vertices.get(1), TEST_EPS);
258         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1 + sqrt2, 2), vertices.get(2), TEST_EPS);
259         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2 + sqrt2), vertices.get(3), TEST_EPS);
260     }
261 
262     @Test
263     void testToTree() {
264         // act
265         final RegionBSPTree2D tree = Parallelogram.axisAligned(Vector2D.ZERO, Vector2D.of(1, 4), TEST_PRECISION)
266                 .toTree();
267 
268         // assert
269         Assertions.assertFalse(tree.isFull());
270         Assertions.assertFalse(tree.isEmpty());
271 
272         Assertions.assertEquals(4, tree.getSize(), TEST_EPS);
273         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.5, 2), tree.getCentroid(), TEST_EPS);
274     }
275 
276     private static void assertSegment(final LineConvexSubset segment, final Vector2D start, final Vector2D end) {
277         EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS);
278         EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS);
279     }
280 }