1 /*
2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.geometry;
17
18 import com.irurueta.algebra.Matrix;
19 import com.irurueta.algebra.WrongSizeException;
20
21 import java.io.Serializable;
22
23 /**
24 * This class defines additional parameters that can be defined on affine
25 * 3D transformations.
26 */
27 public class AffineParameters3D implements Serializable {
28
29 /**
30 * Default scale value having no effect on transformations.
31 */
32 public static final double DEFAULT_SCALE = 1.0;
33
34 /**
35 * Default skewness value having no effect on transformations.
36 */
37 public static final double DEFAULT_SKEWNESS = 0.0;
38
39 /**
40 * Number of inhomogeneous coordinates in 3D space.
41 */
42 public static final int INHOM_COORDS = 3;
43
44 /**
45 * Default threshold to determine whether a matrix is a valid 3x3 upper
46 * triangular one.
47 */
48 private static final double DEFAULT_VALID_THRESHOLD = 1e-8;
49
50 /**
51 * Scale on x coordinates.
52 */
53 private double scaleX;
54
55 /**
56 * Scale on y coordinates.
57 */
58 private double scaleY;
59
60 /**
61 * Scale on z coordinates.
62 */
63 private double scaleZ;
64
65 /**
66 * Skewness factor between axes x-y. If 0.0, x and y axes remain orthogonal,
67 * otherwise they get slanted.
68 */
69 private double skewnessXY;
70
71 /**
72 * Skewness factor between axes x-z. If 0.0, x and z axes remain orthogonal,
73 * otherwise they get slanted.
74 */
75 private double skewnessXZ;
76
77 /**
78 * Skewness factor between axes y-z. If 0.0, y and z axes remain orthogonal,
79 * otherwise they get slanted.
80 */
81 private double skewnessYZ;
82
83 /**
84 * Constructor.
85 * Sets default scale and skewness.
86 */
87 public AffineParameters3D() {
88 scaleX = scaleY = scaleZ = DEFAULT_SCALE;
89 skewnessXY = skewnessXZ = skewnessYZ = DEFAULT_SKEWNESS;
90 }
91
92 /**
93 * Constructor with scale.
94 * Sets default skewness and provided scale.
95 *
96 * @param scale Scale to be set (in x, y and z axes). A value between 0.0
97 * and 1.0 will reduce objects size, a value larger than 1.0 will enlarge
98 * objects size, and negative values will reverse objects.
99 */
100 public AffineParameters3D(final double scale) {
101 scaleX = scaleY = scaleZ = scale;
102 skewnessXY = skewnessXZ = skewnessYZ = DEFAULT_SKEWNESS;
103 }
104
105 /**
106 * Constructor with scale and skewness.
107 *
108 * @param scale Scale to be set (in x, y and z axes). A value between 0.0
109 * and 1.0 will reduce objects size, a value larger than 1.0 will enlarge
110 * objects size, and negative values will reverse objects.
111 * @param skewness Skewness to be set for all axes.
112 */
113 public AffineParameters3D(final double scale, final double skewness) {
114 scaleX = scaleY = scaleZ = scale;
115 skewnessXY = skewnessXZ = skewnessYZ = skewness;
116 }
117
118 /**
119 * Constructor with scale and skewness on each axis.
120 *
121 * @param scaleX Scale to be set on x-axis. A value between 0.0 and 1.0
122 * will reduce objects size, a value larger than 1.0 will enlarge objects
123 * size, and negative values will reverse objects.
124 * @param scaleY Scale to be set on y-axis. A value between 0.0 and 1.0
125 * will reduce objects size, a value larger than 1.0 will enlarge objects.
126 * @param scaleZ Scale to be set on z-axis. A value between 0.0 and 1.0
127 * will reduce objects size, a value larger than 1.0 will enlarge objects.
128 * @param skewnessXY Skewness to be set for axes x-y.
129 * @param skewnessXZ Skewness to be set for axes x-z.
130 * @param skewnessYZ Skewness to be set for axes y-z.
131 */
132 public AffineParameters3D(final double scaleX, final double scaleY, final double scaleZ,
133 final double skewnessXY, final double skewnessXZ, final double skewnessYZ) {
134 this.scaleX = scaleX;
135 this.scaleY = scaleY;
136 this.scaleZ = scaleZ;
137 this.skewnessXY = skewnessXY;
138 this.skewnessXZ = skewnessXZ;
139 this.skewnessYZ = skewnessYZ;
140 }
141
142 /**
143 * Constructor with matrix and threshold.
144 *
145 * @param m Upper triangular matrix to extract affine parameters from.
146 * @param threshold Threshold to determine whether provided matrix is a
147 * valid upper triangular one.
148 * @throws IllegalArgumentException Raised if provided matrix is not 3x3 or
149 * if it is not upper triangular, or if threshold is negative.
150 */
151 public AffineParameters3D(final Matrix m, final double threshold) {
152 fromMatrix(m, threshold);
153 }
154
155 /**
156 * Constructor with matrix.
157 *
158 * @param m Upper triangular matrix to extract affine parameters from.
159 * @throws IllegalArgumentException Raised if provided matrix is not 3x3 or
160 * if it is not upper triangular, or if threshold is negative.
161 */
162 public AffineParameters3D(final Matrix m) {
163 fromMatrix(m);
164 }
165
166 /**
167 * Returns scale for x-axis.
168 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
169 * 1.0 will enlarge objects size, and negative values will reverse objects.
170 *
171 * @return Scale for x axis.
172 */
173 public double getScaleX() {
174 return scaleX;
175 }
176
177 /**
178 * Sets scale for x-axis.
179 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
180 * 1.0 will enlarge objects size, and negative values will reverse objects.
181 *
182 * @param scaleX scale to be set on x-axis.
183 */
184 public void setScaleX(final double scaleX) {
185 this.scaleX = scaleX;
186 }
187
188 /**
189 * Returns scale for y-axis.
190 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
191 * 1.0 will enlarge objects size, and negative values will reverse objects.
192 *
193 * @return Scale for y axis.
194 */
195 public double getScaleY() {
196 return scaleY;
197 }
198
199 /**
200 * Sets scale for y-axis.
201 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
202 * 1.0 will enlarge objects size, and negative values will reverse objects.
203 *
204 * @param scaleY scale to be set on y-axis.
205 */
206 public void setScaleY(final double scaleY) {
207 this.scaleY = scaleY;
208 }
209
210 /**
211 * Returns scale for z axis.
212 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
213 * 1.0 will enlarge objects size, and negative values will reverse objects.
214 *
215 * @return Scale for z axis.
216 */
217 public double getScaleZ() {
218 return scaleZ;
219 }
220
221 /**
222 * Sets scale for z axis.
223 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
224 * 1.0 will enlarge objects size, and negative values will reverse objects.
225 *
226 * @param scaleZ scale to be set on z axis.
227 */
228 public void setScaleZ(final double scaleZ) {
229 this.scaleZ = scaleZ;
230 }
231
232 /**
233 * Sets overall scale (for x, y and z axes).
234 * A value between 0.0 and 1.0 will reduce objects size, a value larger than
235 * 1.0 will enlarge objects size, and negative values will reverse objects.
236 *
237 * @param scale Scale to be set for x, y and z axes.
238 */
239 public void setScale(final double scale) {
240 scaleX = scaleY = scaleZ = scale;
241 }
242
243 /**
244 * Returns skewness value for x-y axes.
245 * A value of 0.0 indicates that horizontal and vertical axis are
246 * orthogonal, otherwise they are slanted to each other.
247 *
248 * @return Skewness value for x-y axes.
249 */
250 public double getSkewnessXY() {
251 return skewnessXY;
252 }
253
254 /**
255 * Sets skewness value for x-y axes.
256 * A value of 0.0 indicates that horizontal and vertical axis are
257 * orthogonal, otherwise they are slanted to each other.
258 *
259 * @param skewnessXY Skewness to be set for x-y axes.
260 */
261 public void setSkewnessXY(final double skewnessXY) {
262 this.skewnessXY = skewnessXY;
263 }
264
265 /**
266 * Returns skewness value for x-z axes.
267 * A value of 0.0 indicates that horizontal and vertical axis are
268 * orthogonal, otherwise they are slanted to each other.
269 *
270 * @return Skewness value for x-z axes.
271 */
272 public double getSkewnessXZ() {
273 return skewnessXZ;
274 }
275
276 /**
277 * Sets skewness value for x-z axes.
278 * A value of 0.0 indicates that horizontal and vertical axis are
279 * orthogonal, otherwise they are slanted to each other.
280 *
281 * @param skewnessXZ Skewness to be set for x-z axes.
282 */
283 public void setSkewnessXZ(final double skewnessXZ) {
284 this.skewnessXZ = skewnessXZ;
285 }
286
287 /**
288 * Returns skewness value for y-z axes.
289 * A value of 0.0 indicates that horizontal and vertical axis are
290 * orthogonal, otherwise they are slanted to each other.
291 *
292 * @return Skewness value for y-z axes.
293 */
294 public double getSkewnessYZ() {
295 return skewnessYZ;
296 }
297
298 /**
299 * Sets skewness value for y-z axes.
300 * A value of 0.0 indicates that horizontal and vertical axis are
301 * orthogonal, otherwise they are slanted to each other.
302 *
303 * @param skewnessYZ Skewness to be set for y-z axes.
304 */
305 public void setSkewnessYZ(final double skewnessYZ) {
306 this.skewnessYZ = skewnessYZ;
307 }
308
309 /**
310 * Converts this affine parameters instance into matrix representation
311 *
312 * @return A matrix representation of this instance.
313 */
314 public Matrix asMatrix() {
315 Matrix m = null;
316 try {
317 m = new Matrix(INHOM_COORDS, INHOM_COORDS);
318 asMatrix(m);
319 } catch (final WrongSizeException ignore) {
320 // never happens
321 }
322 return m;
323 }
324
325 /**
326 * Converts this affine parameters instance into matrix representation and
327 * stores the result into provided matrix.
328 *
329 * @param m Matrix where representation of this instance will be stored.
330 * @throws IllegalArgumentException Raised if provided matrix is not 3x3.
331 */
332 public void asMatrix(final Matrix m) {
333 if (m.getRows() != INHOM_COORDS || m.getColumns() != INHOM_COORDS) {
334 throw new IllegalArgumentException();
335 }
336
337 m.setElementAt(0, 0, scaleX);
338 m.setElementAt(1, 0, 0.0);
339 m.setElementAt(2, 0, 0.0);
340
341 m.setElementAt(0, 1, skewnessXY);
342 m.setElementAt(1, 1, scaleY);
343 m.setElementAt(2, 1, 0.0);
344
345 m.setElementAt(0, 2, skewnessXZ);
346 m.setElementAt(1, 2, skewnessYZ);
347 m.setElementAt(2, 2, scaleZ);
348 }
349
350 /**
351 * Sets parameters of this instance from provided matrix.
352 *
353 * @param m Matrix to set parameters from. Provided matrix must be 3x3 and
354 * upper triangular.
355 * @throws IllegalArgumentException Raised if provided matrix is not 3x3 or
356 * if it is not upper triangular.
357 */
358 public final void fromMatrix(final Matrix m) {
359 fromMatrix(m, DEFAULT_VALID_THRESHOLD);
360 }
361
362 /**
363 * Sets parameters of this instance from provided matrix.
364 *
365 * @param m Matrix to set parameters from. Provided matrix must be 3x3 and
366 * upper triangular up to provided threshold.
367 * @param threshold Threshold to determine whether provided matrix is upper
368 * triangular. Matrix will be considered upper triangular if its lower
369 * triangular elements are smaller or equal than provided threshold.
370 * @throws IllegalArgumentException Raised if provided matrix is not 3x3 or
371 * if it is not upper triangular or if threshold is negative.
372 */
373 public final void fromMatrix(final Matrix m, final double threshold) {
374 if (!isValidMatrix(m, threshold)) {
375 throw new IllegalArgumentException();
376 }
377
378 scaleX = m.getElementAt(0, 0);
379 skewnessXY = m.getElementAt(0, 1);
380 scaleY = m.getElementAt(1, 1);
381 skewnessXZ = m.getElementAt(0, 2);
382 skewnessYZ = m.getElementAt(1, 2);
383 scaleZ = m.getElementAt(2, 2);
384 }
385
386 /**
387 * Returns boolean indicating whether provided matrix is a valid matrix
388 * to set affine parameters from.
389 * Valid matrices need to be 3x3 and upper triangular.
390 *
391 * @param m A matrix to determine whether it is valid to set affine
392 * parameters from.
393 * @return True if matrix is valid, false otherwise.
394 */
395 public static boolean isValidMatrix(final Matrix m) {
396 return isValidMatrix(m, DEFAULT_VALID_THRESHOLD);
397 }
398
399 /**
400 * Returns boolean indicating whether provided matrix is a valid matrix to
401 * set affine parameters form.
402 * Valid matrices need to be 3x3 and upper triangular up to provided
403 * threshold. In Layman terms, a valid matrix lower triangular elements need
404 * to be smaller or equal than provided threshold.
405 *
406 * @param m A matrix to determine whether it is valid to set affine
407 * parameters from.
408 * @param threshold A threshold to determine whether provided matrix is
409 * upper triangular. Matrix will be considered upper triangular if its lower
410 * triangular elements are smaller or equal than provided threshold (without
411 * taking into account the sign of the elements).
412 * @return True if matrix is valid, false otherwise.
413 * @throws IllegalArgumentException Raised if provided threshold is negative.
414 */
415 @SuppressWarnings("DuplicatedCode")
416 public static boolean isValidMatrix(final Matrix m, final double threshold) {
417 if (threshold < 0.0) {
418 throw new IllegalArgumentException();
419 }
420
421 if (m.getRows() != INHOM_COORDS || m.getColumns() != INHOM_COORDS) {
422 return false;
423 }
424
425 // check is upper triangular
426 final var rows = m.getRows();
427 final var cols = m.getColumns();
428
429 for (var v = 0; v < cols; v++) {
430 for (var u = 0; u < rows; u++) {
431 if (u > v && Math.abs(m.getElementAt(u, v)) > threshold) {
432 return false;
433 }
434 }
435 }
436
437 return true;
438 }
439 }