View Javadoc
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 }