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   * 2D transformations.
26   */
27  public class AffineParameters2D 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 2D space.
41       */
42      public static final int INHOM_COORDS = 2;
43  
44      /**
45       * Default threshold to determine whether a matrix is a valid 2x2 upper
46       * triangular one.
47       */
48      private static final double DEFAULT_VALID_THRESHOLD = 1e-8;
49  
50      /**
51       * Horizontal scale.
52       */
53      private double scaleX;
54  
55      /**
56       * Vertical scale.
57       */
58      private double scaleY;
59  
60      /**
61       * Skewness factor. If 0.0, horizontal and vertical axes remain orthogonal,
62       * otherwise they get slanted.
63       */
64      private double skewness;
65  
66      /**
67       * Constructor.
68       * Sets default scale and skewness
69       */
70      public AffineParameters2D() {
71          scaleX = scaleY = DEFAULT_SCALE;
72          skewness = DEFAULT_SKEWNESS;
73      }
74  
75      /**
76       * Constructor with scale.
77       * Sets default skewness and provided scale.
78       *
79       * @param scale Scale to be set (both, horizontal and vertical). A value
80       *              between 0.0 and 1.0 will reduce objects size, a value larger than 1.0
81       *              will enlarge objects size, and negative values will reverse objects.
82       */
83      public AffineParameters2D(final double scale) {
84          scaleX = scaleY = scale;
85          skewness = DEFAULT_SKEWNESS;
86      }
87  
88      /**
89       * Constructor with scale and skewness.
90       *
91       * @param scale    Scale to be set (both, horizontal and vertical). A value
92       *                 between 0.0 and 1.0 will reduce objects size, a value larger than 1.0
93       *                 will enlarge objects size, and negative values will reverse objects.
94       * @param skewness Skewness to be set.
95       */
96      public AffineParameters2D(final double scale, final double skewness) {
97          scaleX = scaleY = scale;
98          this.skewness = skewness;
99      }
100 
101     /**
102      * Constructor with horizontal scale, vertical scale and skewness.
103      *
104      * @param scaleX   Horizontal scale to be set. A value between 0.0 and 1.0
105      *                 will reduce objects size, a value larger than 1.0 will enlarge objects
106      *                 size, and negative values will reverse objects.
107      * @param scaleY   Vertical scale to be set. A value between 0.0 and 1.0 will
108      *                 reduce objects size, a value larger than 1.0 will enlarge objects
109      *                 size, and negative values will reverse objects.
110      * @param skewness Skewness to be set.
111      */
112     public AffineParameters2D(final double scaleX, final double scaleY, final double skewness) {
113         this.scaleX = scaleX;
114         this.scaleY = scaleY;
115         this.skewness = skewness;
116     }
117 
118     /**
119      * Constructor with matrix and threshold.
120      *
121      * @param m         Upper triangular matrix to extract affine parameters from.
122      * @param threshold Threshold to determine whether provided matrix is a
123      *                  valid upper triangular one.
124      * @throws IllegalArgumentException Raised if provided matrix is not 2x2 or
125      *                                  if it is not upper triangular, or if threshold is negative.
126      */
127     public AffineParameters2D(final Matrix m, final double threshold) {
128         fromMatrix(m, threshold);
129     }
130 
131     /**
132      * Constructor with matrix.
133      *
134      * @param m Upper triangular matrix to extract affine parameters from.
135      * @throws IllegalArgumentException Raised if provided matrix is not 2x2 or
136      *                                  if it is not upper triangular.
137      */
138     public AffineParameters2D(final Matrix m) {
139         fromMatrix(m);
140     }
141 
142     /**
143      * Returns horizontal scale.
144      * A value between 0.0 and 1.0 will reduce objects size, a value larger than
145      * 1.0 will enlarge objects size, and negative values will reverse objects.
146      *
147      * @return Horizontal scale.
148      */
149     public double getScaleX() {
150         return scaleX;
151     }
152 
153     /**
154      * Sets horizontal scale.
155      * A value between 0.0 and 1.0 will reduce objects size, a value larger than
156      * 1.0 will enlarge objects size, and negative values will reverse objects.
157      *
158      * @param scaleX Horizontal scale to be set.
159      */
160     public void setScaleX(final double scaleX) {
161         this.scaleX = scaleX;
162     }
163 
164     /**
165      * Returns vertical scale.
166      * A value between 0.0 and 1.0 will reduce objects size, a value larger than
167      * 1.0 will enlarge objects size, and negative values will reverse objects.
168      *
169      * @return Vertical scale.
170      */
171     public double getScaleY() {
172         return scaleY;
173     }
174 
175     /**
176      * Sets vertical scale.
177      * A value between 0.0 and 1.0 will reduce objects size, a value larger than
178      * 1.0 will enlarge objects size, and negative values will reverse objects.
179      *
180      * @param scaleY Vertical scale to be set.
181      */
182     public void setScaleY(final double scaleY) {
183         this.scaleY = scaleY;
184     }
185 
186     /**
187      * Sets overall scale (both, horizontal and vertical).
188      * A value between 0.0 and 1.0 will reduce objects size, a value larger than
189      * 1.0 will enlarge objects size, and negative values will reverse objects.
190      *
191      * @param scale Horizontal and vertical scale to be set.
192      */
193     public void setScale(final double scale) {
194         scaleX = scaleY = scale;
195     }
196 
197     /**
198      * Returns skewness value.
199      * A value of 0.0 indicates that horizontal and vertical axis are
200      * orthogonal, otherwise they are slanted to each other.
201      *
202      * @return Skewness value.
203      */
204     public double getSkewness() {
205         return skewness;
206     }
207 
208     /**
209      * Sets skewness value.
210      * A value of 0.0 indicates that horizontal and vertical axis are
211      * orthogonal, otherwise they are slanted to each other.
212      *
213      * @param skewness Skewness to be set.
214      */
215     public void setSkewness(final double skewness) {
216         this.skewness = skewness;
217     }
218 
219     /**
220      * Converts this affine parameters instance into matrix representation.
221      *
222      * @return A matrix representation of this instance.
223      */
224     public Matrix asMatrix() {
225         Matrix m = null;
226         try {
227             m = new Matrix(INHOM_COORDS, INHOM_COORDS);
228             asMatrix(m);
229         } catch (final WrongSizeException ignore) {
230             // never happens
231         }
232         return m;
233     }
234 
235     /**
236      * Converts this affine parameters instance into matrix representation and
237      * stores the result into provided matrix.
238      *
239      * @param m Matrix where representation of this instance will be stored.
240      * @throws IllegalArgumentException Raised if provided matrix is not 2x2.
241      */
242     public void asMatrix(final Matrix m) {
243         if (m.getRows() != INHOM_COORDS || m.getColumns() != INHOM_COORDS) {
244             throw new IllegalArgumentException();
245         }
246 
247         m.setElementAt(0, 0, scaleX);
248         m.setElementAt(1, 0, 0.0);
249         m.setElementAt(0, 1, skewness);
250         m.setElementAt(1, 1, scaleY);
251     }
252 
253     /**
254      * Sets parameters of this instance from provided matrix.
255      *
256      * @param m Matrix to set parameters from. Provided matrix must be 2x2 and
257      *          upper triangular.
258      * @throws IllegalArgumentException Raised if provided matrix is not 2x2 or
259      *                                  if it is not upper triangular.
260      */
261     public final void fromMatrix(final Matrix m) {
262         fromMatrix(m, DEFAULT_VALID_THRESHOLD);
263     }
264 
265     /**
266      * Sets parameters of this instance from provided matrix.
267      *
268      * @param m         Matrix to set parameters from. Provided matrix must be 2x2 and
269      *                  upper triangular up to provided threshold.
270      * @param threshold Threshold to determine whether provided matrix is upper
271      *                  triangular. Matrix will be considered upper triangular if its lower
272      *                  triangular elements are smaller or equal than provided threshold.
273      * @throws IllegalArgumentException Raised if provided matrix is not 2x2 or
274      *                                  if it is not upper triangular or if threshold is negative.
275      */
276     public final void fromMatrix(final Matrix m, final double threshold) {
277         if (!isValidMatrix(m, threshold)) {
278             throw new IllegalArgumentException();
279         }
280 
281         scaleX = m.getElementAt(0, 0);
282         skewness = m.getElementAt(0, 1);
283         scaleY = m.getElementAt(1, 1);
284     }
285 
286     /**
287      * Returns boolean indicating whether provided matrix is a valid matrix
288      * to set affine parameters from.
289      * Valid matrices need to be 2x2 and upper triangular.
290      *
291      * @param m A matrix to determine whether it is valid to set affine
292      *          parameters from.
293      * @return True if matrix is valid, false otherwise.
294      */
295     public static boolean isValidMatrix(final Matrix m) {
296         return isValidMatrix(m, DEFAULT_VALID_THRESHOLD);
297     }
298 
299     /**
300      * Returns boolean indicating whether provided matrix is a valid matrix to
301      * set affine parameters form.
302      * Valid matrices need to be 2x2 and upper triangular up to provided
303      * threshold. In Layman terms, a valid matrix lower triangular elements need
304      * to be smaller or equal than provided threshold.
305      *
306      * @param m         A matrix to determine whether it is valid to set affine
307      *                  parameters from.
308      * @param threshold A threshold to determine whether provided matrix is
309      *                  upper triangular. Matrix will be considered upper triangular if its lower
310      *                  triangular elements are smaller or equal than provided threshold (without
311      *                  taking into account the sign of the elements).
312      * @return True if matrix is valid, false otherwise.
313      * @throws IllegalArgumentException Raised if provided threshold is negative.
314      */
315     @SuppressWarnings("DuplicatedCode")
316     public static boolean isValidMatrix(final Matrix m, final double threshold) {
317         if (threshold < 0.0) {
318             throw new IllegalArgumentException();
319         }
320 
321         if (m.getRows() != INHOM_COORDS || m.getColumns() != INHOM_COORDS) {
322             return false;
323         }
324 
325         // check is upper triangular
326         final var rows = m.getRows();
327         final var cols = m.getColumns();
328 
329         for (var v = 0; v < cols; v++) {
330             for (var u = 0; u < rows; u++) {
331                 if (u > v && Math.abs(m.getElementAt(u, v)) > threshold) {
332                     return false;
333                 }
334             }
335         }
336 
337         return true;
338     }
339 }