1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.threed.line;
18
19 import org.apache.commons.geometry.core.GeometryTestUtils;
20 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
21 import org.apache.commons.geometry.euclidean.oned.Interval;
22 import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
23 import org.apache.commons.geometry.euclidean.threed.Vector3D;
24 import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
25 import org.apache.commons.numbers.core.Precision;
26 import org.junit.jupiter.api.Assertions;
27 import org.junit.jupiter.api.Test;
28
29 class Ray3DTest {
30
31 private static final double TEST_EPS = 1e-10;
32
33 private static final Precision.DoubleEquivalence TEST_PRECISION =
34 Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
35
36 @Test
37 void testFromPointAndDirection() {
38
39 final Vector3D pt = Vector3D.of(1, 1, 2);
40
41
42 final Ray3D ray = Lines3D.rayFromPointAndDirection(pt, Vector3D.Unit.PLUS_Z, TEST_PRECISION);
43
44
45 Assertions.assertTrue(ray.isInfinite());
46 Assertions.assertFalse(ray.isFinite());
47
48 EuclideanTestUtils.assertCoordinatesEqual(pt, ray.getStartPoint(), TEST_EPS);
49 Assertions.assertNull(ray.getEndPoint());
50
51 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, ray.getDirection(), TEST_EPS);
52
53 Assertions.assertEquals(2, ray.getSubspaceStart(), TEST_EPS);
54 GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
55
56 GeometryTestUtils.assertPositiveInfinity(ray.getSize());
57
58 Assertions.assertNull(ray.getCentroid());
59 Assertions.assertNull(ray.getBounds());
60 }
61
62 @Test
63 void testFromPointAndDirection_invalidArgs() {
64
65 final Vector3D pt = Vector3D.of(0, 2, 4);
66 final Vector3D dir = Vector3D.of(1e-11, 0, 0);
67
68
69 GeometryTestUtils.assertThrowsWithMessage(() -> {
70 Lines3D.rayFromPointAndDirection(pt, dir, TEST_PRECISION);
71 }, IllegalArgumentException.class, "Line direction cannot be zero");
72 }
73
74 @Test
75 void testFromPoint() {
76
77 final Vector3D pt = Vector3D.of(-2, -1, 2);
78
79 final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(1, 0, 2), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
80
81
82 final Ray3D ray = Lines3D.rayFromPoint(line, pt);
83
84
85 Assertions.assertTrue(ray.isInfinite());
86 Assertions.assertFalse(ray.isFinite());
87
88 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(1, -1, 2), ray.getStartPoint(), TEST_EPS);
89 Assertions.assertNull(ray.getEndPoint());
90
91 Assertions.assertEquals(-1, ray.getSubspaceStart(), TEST_EPS);
92 GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
93
94 GeometryTestUtils.assertPositiveInfinity(ray.getSize());
95
96 Assertions.assertNull(ray.getCentroid());
97 Assertions.assertNull(ray.getBounds());
98 }
99
100 @Test
101 void testFromPoint_invalidArgs() {
102
103 final Line3D line = Lines3D.fromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
104
105
106 GeometryTestUtils.assertThrowsWithMessage(() -> {
107 Lines3D.rayFromPoint(line, Vector3D.NaN);
108 }, IllegalArgumentException.class, "Invalid ray start location: NaN");
109
110 GeometryTestUtils.assertThrowsWithMessage(() -> {
111 Lines3D.rayFromPoint(line, Vector3D.NEGATIVE_INFINITY);
112 }, IllegalArgumentException.class, "Invalid ray start location: NaN");
113
114 GeometryTestUtils.assertThrowsWithMessage(() -> {
115 Lines3D.rayFromPoint(line, Vector3D.POSITIVE_INFINITY);
116 }, IllegalArgumentException.class, "Invalid ray start location: NaN");
117 }
118
119 @Test
120 void testFromLocation() {
121
122 final Line3D line = Lines3D.fromPointAndDirection(Vector3D.of(-1, 0, 0), Vector3D.Unit.PLUS_Z, TEST_PRECISION);
123
124
125 final Ray3D ray = Lines3D.rayFromLocation(line, -1);
126
127
128 Assertions.assertTrue(ray.isInfinite());
129 Assertions.assertFalse(ray.isFinite());
130
131 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(-1, 0, -1), ray.getStartPoint(), TEST_EPS);
132 Assertions.assertNull(ray.getEndPoint());
133
134 Assertions.assertEquals(-1, ray.getSubspaceStart(), TEST_EPS);
135 GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
136
137 GeometryTestUtils.assertPositiveInfinity(ray.getSize());
138
139 Assertions.assertNull(ray.getCentroid());
140 Assertions.assertNull(ray.getBounds());
141 }
142
143 @Test
144 void testTransform() {
145
146 final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
147 .toMatrix()
148 .translate(Vector3D.Unit.PLUS_Y);
149
150 final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_X, TEST_PRECISION);
151
152
153 final Ray3D result = ray.transform(t);
154
155
156 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, -1), result.getStartPoint(), TEST_EPS);
157 Assertions.assertNull(result.getEndPoint());
158
159 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.MINUS_Z, result.getDirection(), TEST_EPS);
160 }
161
162 @Test
163 void testTransform_reflection() {
164
165 final AffineTransformMatrix3D t = QuaternionRotation.fromAxisAngle(Vector3D.Unit.PLUS_Y, 0.5 * Math.PI)
166 .toMatrix()
167 .translate(Vector3D.Unit.PLUS_Y)
168 .scale(1, 1, -2);
169
170 final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 0, 0), Vector3D.Unit.PLUS_X, TEST_PRECISION);
171
172
173 final Ray3D result = ray.transform(t);
174
175
176 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.of(0, 1, 2), result.getStartPoint(), TEST_EPS);
177 Assertions.assertNull(result.getEndPoint());
178
179 EuclideanTestUtils.assertCoordinatesEqual(Vector3D.Unit.PLUS_Z, result.getDirection(), TEST_EPS);
180 }
181
182 @Test
183 void testContains() {
184
185 final Vector3D p0 = Vector3D.of(1, 1, 1);
186
187 final Vector3D delta = Vector3D.of(1e-12, 1e-12, 1e-12);
188
189 final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(1, 1, 1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
190
191
192 Assertions.assertFalse(ray.contains(Vector3D.of(2, 2, 2)));
193 Assertions.assertFalse(ray.contains(Vector3D.of(0.9, 1, 1)));
194 Assertions.assertFalse(ray.contains(Vector3D.of(-1, 1, 1)));
195
196 Assertions.assertTrue(ray.contains(p0));
197 Assertions.assertTrue(ray.contains(p0.subtract(delta)));
198
199 Assertions.assertTrue(ray.contains(Vector3D.of(1000, 1, 1)));
200 }
201
202 @Test
203 void testGetInterval() {
204
205 final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.of(2, -1, 3), Vector3D.Unit.PLUS_Y, TEST_PRECISION);
206
207
208 final Interval interval = ray.getInterval();
209
210
211 Assertions.assertEquals(-1, interval.getMin(), TEST_EPS);
212 GeometryTestUtils.assertPositiveInfinity(interval.getMax());
213
214 Assertions.assertSame(ray.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
215 }
216
217 @Test
218 void testToString() {
219
220 final Ray3D ray = Lines3D.rayFromPointAndDirection(Vector3D.ZERO, Vector3D.Unit.PLUS_X, TEST_PRECISION);
221
222
223 final String str = ray.toString();
224
225
226 GeometryTestUtils.assertContains("Ray3D[startPoint= (0", str);
227 GeometryTestUtils.assertContains(", direction= (1", str);
228 }
229 }