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 }