JOGL v2.6.0-rc-20250712
JOGL, High-Performance Graphics Binding for Java™ (public API).
AffineTransform.java
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/**
18 * @author Denis M. Kishenko
19 * @author Sven Gothel, (c) 2010-2023
20 */
21package com.jogamp.math.geom.plane;
22
23// import jogamp.opengl.util.HashCode;
24
25import com.jogamp.graph.geom.Vertex;
26import com.jogamp.math.FloatUtil;
27import com.jogamp.math.Vec2f;
28import com.jogamp.math.Vec3f;
29import com.jogamp.math.geom.AABBox;
30
31public final class AffineTransform {
32
33 static final String determinantIsZero = "Determinant is zero";
34
35 public static final int TYPE_IDENTITY = 0;
36 public static final int TYPE_TRANSLATION = 1;
37 public static final int TYPE_UNIFORM_SCALE = 2;
38 public static final int TYPE_GENERAL_SCALE = 4;
39 public static final int TYPE_QUADRANT_ROTATION = 8;
40 public static final int TYPE_GENERAL_ROTATION = 16;
41 public static final int TYPE_GENERAL_TRANSFORM = 32;
42 public static final int TYPE_FLIP = 64;
45
46 /**
47 * The <code>TYPE_UNKNOWN</code> is an initial type value
48 */
49 static final int TYPE_UNKNOWN = -1;
50
51 /**
52 * The min value equivalent to zero. If absolute value less then ZERO it considered as zero.
53 */
54 static final float ZERO = (float) 1E-10;
55
56 /**
57 * The values of transformation matrix
58 */
59 private float m00;
60 private float m10;
61 private float m01;
62 private float m11;
63 private float m02;
64 private float m12;
65
66 /**
67 * The transformation <code>type</code>
68 */
69 private transient int type;
70
71 public AffineTransform() {
73 }
74
76 this.type = t.type;
77 this.m00 = t.m00;
78 this.m10 = t.m10;
79 this.m01 = t.m01;
80 this.m11 = t.m11;
81 this.m02 = t.m02;
82 this.m12 = t.m12;
83 }
84
85 public AffineTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12) {
86 this.type = TYPE_UNKNOWN;
87 this.m00 = m00;
88 this.m10 = m10;
89 this.m01 = m01;
90 this.m11 = m11;
91 this.m02 = m02;
92 this.m12 = m12;
93 }
94
95 public AffineTransform(final float[] matrix) {
96 this.type = TYPE_UNKNOWN;
97 m00 = matrix[0];
98 m10 = matrix[1];
99 m01 = matrix[2];
100 m11 = matrix[3];
101 if (matrix.length > 4) {
102 m02 = matrix[4];
103 m12 = matrix[5];
104 }
105 }
106
107 /*
108 * Method returns type of affine transformation.
109 *
110 * Transform matrix is
111 * m00 m01 m02
112 * m10 m11 m12
113 *
114 * According analytic geometry new basis vectors are (m00, m01) and (m10, m11),
115 * translation vector is (m02, m12). Original basis vectors are (1, 0) and (0, 1).
116 * Type transformations classification:
117 * TYPE_IDENTITY - new basis equals original one and zero translation
118 * TYPE_TRANSLATION - translation vector isn't zero
119 * TYPE_UNIFORM_SCALE - vectors length of new basis equals
120 * TYPE_GENERAL_SCALE - vectors length of new basis doesn't equal
121 * TYPE_FLIP - new basis vector orientation differ from original one
122 * TYPE_QUADRANT_ROTATION - new basis is rotated by 90, 180, 270, or 360 degrees
123 * TYPE_GENERAL_ROTATION - new basis is rotated by arbitrary angle
124 * TYPE_GENERAL_TRANSFORM - transformation can't be inversed
125 */
126 public int getType() {
127 if (type != TYPE_UNKNOWN) {
128 return type;
129 }
130
131 int type = 0;
132
133 if (m00 * m01 + m10 * m11 != 0.0) {
135 return type;
136 }
137
138 if (m02 != 0.0 || m12 != 0.0) {
139 type |= TYPE_TRANSLATION;
140 } else
141 if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
142 type = TYPE_IDENTITY;
143 return type;
144 }
145
146 if (m00 * m11 - m01 * m10 < 0.0) {
147 type |= TYPE_FLIP;
148 }
149
150 final float dx = m00 * m00 + m10 * m10;
151 final float dy = m01 * m01 + m11 * m11;
152 if (dx != dy) {
153 type |= TYPE_GENERAL_SCALE;
154 } else
155 if (dx != 1.0) {
156 type |= TYPE_UNIFORM_SCALE;
157 }
158
159 if ((m00 == 0.0 && m11 == 0.0) ||
160 (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0)))
161 {
163 } else
164 if (m01 != 0.0 || m10 != 0.0) {
165 type |= TYPE_GENERAL_ROTATION;
166 }
167
168 return type;
169 }
170
171 public final float getScaleX() {
172 return m00;
173 }
174
175 public final float getScaleY() {
176 return m11;
177 }
178
179 public final float getShearX() {
180 return m01;
181 }
182
183 public final float getShearY() {
184 return m10;
185 }
186
187 public final float getTranslateX() {
188 return m02;
189 }
190
191 public final float getTranslateY() {
192 return m12;
193 }
194
195 public final boolean isIdentity() {
196 return getType() == TYPE_IDENTITY;
197 }
198
199 public final void getMatrix(final float[] matrix) {
200 matrix[0] = m00;
201 matrix[1] = m10;
202 matrix[2] = m01;
203 matrix[3] = m11;
204 if (matrix.length > 4) {
205 matrix[4] = m02;
206 matrix[5] = m12;
207 }
208 }
209
210 public final float getDeterminant() {
211 return m00 * m11 - m01 * m10;
212 }
213
214 public final AffineTransform setTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12) {
215 this.type = TYPE_UNKNOWN;
216 this.m00 = m00;
217 this.m10 = m10;
218 this.m01 = m01;
219 this.m11 = m11;
220 this.m02 = m02;
221 this.m12 = m12;
222 return this;
223 }
224
226 type = t.type;
227 setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
228 return this;
229 }
230
232 type = TYPE_IDENTITY;
233 m00 = m11 = 1.0f;
234 m10 = m01 = m02 = m12 = 0.0f;
235 return this;
236 }
237
238 public final AffineTransform setToTranslation(final float mx, final float my) {
239 m00 = m11 = 1.0f;
240 m01 = m10 = 0.0f;
241 m02 = mx;
242 m12 = my;
243 if (mx == 0.0f && my == 0.0f) {
244 type = TYPE_IDENTITY;
245 } else {
246 type = TYPE_TRANSLATION;
247 }
248 return this;
249 }
250
251 public final AffineTransform setToScale(final float scx, final float scy) {
252 m00 = scx;
253 m11 = scy;
254 m10 = m01 = m02 = m12 = 0.0f;
255 if (scx != 1.0f || scy != 1.0f) {
256 type = TYPE_UNKNOWN;
257 } else {
258 type = TYPE_IDENTITY;
259 }
260 return this;
261 }
262
263 public final AffineTransform setToShear(final float shx, final float shy) {
264 m00 = m11 = 1.0f;
265 m02 = m12 = 0.0f;
266 m01 = shx;
267 m10 = shy;
268 if (shx != 0.0f || shy != 0.0f) {
269 type = TYPE_UNKNOWN;
270 } else {
271 type = TYPE_IDENTITY;
272 }
273 return this;
274 }
275
276 public final AffineTransform setToRotation(final float angle) {
277 float sin = FloatUtil.sin(angle);
278 float cos = FloatUtil.cos(angle);
279 if (Math.abs(cos) < ZERO) {
280 cos = 0.0f;
281 sin = sin > 0.0f ? 1.0f : -1.0f;
282 } else
283 if (Math.abs(sin) < ZERO) {
284 sin = 0.0f;
285 cos = cos > 0.0f ? 1.0f : -1.0f;
286 }
287 m00 = m11 = cos;
288 m01 = -sin;
289 m10 = sin;
290 m02 = m12 = 0.0f;
291 type = TYPE_UNKNOWN;
292 return this;
293 }
294
295 public final AffineTransform setToRotation(final float angle, final float px, final float py) {
296 setToRotation(angle);
297 m02 = px * (1.0f - m00) + py * m10;
298 m12 = py * (1.0f - m00) - px * m10;
299 type = TYPE_UNKNOWN;
300 return this;
301 }
302
303 public final AffineTransform translate(final float mx, final float my, final AffineTransform tmp) {
304 return concatenate(tmp.setToTranslation(mx, my));
305 }
306
307 public final AffineTransform scale(final float scx, final float scy, final AffineTransform tmp) {
308 return concatenate(tmp.setToScale(scx, scy));
309 }
310
311 public final AffineTransform shear(final float shx, final float shy, final AffineTransform tmp) {
312 return concatenate(tmp.setToShear(shx, shy));
313 }
314
315 public final AffineTransform rotate(final float angle, final AffineTransform tmp) {
316 return concatenate(tmp.setToRotation(angle));
317 }
318
319 public final AffineTransform rotate(final float angle, final float px, final float py, final AffineTransform tmp) {
320 return concatenate(tmp.setToRotation(angle, px, py));
321 }
322
323 /**
324 * Multiply matrix of two AffineTransform objects.
325 * @param tL - the AffineTransform object is a multiplicand (left argument)
326 * @param tR - the AffineTransform object is a multiplier (right argument)
327 *
328 * @return A new AffineTransform object containing the result of [tL] X [tR].
329 */
330 public final static AffineTransform multiply(final AffineTransform tL, final AffineTransform tR) {
331 return new AffineTransform(
332 tR.m00 * tL.m00 + tR.m10 * tL.m01, // m00
333 tR.m00 * tL.m10 + tR.m10 * tL.m11, // m10
334 tR.m01 * tL.m00 + tR.m11 * tL.m01, // m01
335 tR.m01 * tL.m10 + tR.m11 * tL.m11, // m11
336 tR.m02 * tL.m00 + tR.m12 * tL.m01 + tL.m02, // m02
337 tR.m02 * tL.m10 + tR.m12 * tL.m11 + tL.m12);// m12
338 }
339
340 /**
341 * Concatenates the given matrix to this.
342 * <p>
343 * Implementations performs the matrix multiplication:
344 * <pre>
345 * [this] = [this] X [tR]
346 * </pre>
347 * </p>
348 * @param tR the right-argument of the matrix multiplication
349 * @return this transform for chaining
350 */
352 // setTransform(multiply(this, tR));
353 type = TYPE_UNKNOWN;
355 tR.m00 * m00 + tR.m10 * m01, // m00
356 tR.m00 * m10 + tR.m10 * m11, // m10
357 tR.m01 * m00 + tR.m11 * m01, // m01
358 tR.m01 * m10 + tR.m11 * m11, // m11
359 tR.m02 * m00 + tR.m12 * m01 + m02, // m02
360 tR.m02 * m10 + tR.m12 * m11 + m12);// m12
361 return this;
362 }
363
364 /**
365 * Pre-concatenates the given matrix to this.
366 * <p>
367 * Implementations performs the matrix multiplication:
368 * <pre>
369 * [this] = [tL] X [this]
370 * </pre>
371 * </p>
372 * @param tL the left-argument of the matrix multiplication
373 * @return this transform for chaining
374 */
376 // setTransform(multiply(tL, this));
377 type = TYPE_UNKNOWN;
379 m00 * tL.m00 + m10 * tL.m01, // m00
380 m00 * tL.m10 + m10 * tL.m11, // m10
381 m01 * tL.m00 + m11 * tL.m01, // m01
382 m01 * tL.m10 + m11 * tL.m11, // m11
383 m02 * tL.m00 + m12 * tL.m01 + tL.m02, // m02
384 m02 * tL.m10 + m12 * tL.m11 + tL.m12);// m12
385 return this;
386 }
387
389 final float det = getDeterminant();
390 if (Math.abs(det) < ZERO) {
391 throw new NoninvertibleTransformException(determinantIsZero);
392 }
393 return new AffineTransform(
394 m11 / det, // m00
395 -m10 / det, // m10
396 -m01 / det, // m01
397 m00 / det, // m11
398 (m01 * m12 - m11 * m02) / det, // m02
399 (m10 * m02 - m00 * m12) / det // m12
400 );
401 }
402
403 /**
404 *
405 * @param src
406 * @param dst
407 * @return dst for chaining
408 */
409 public final AABBox transform(final AABBox src, final AABBox dst) {
410 final Vec3f lo = src.getLow();
411 final Vec3f hi = src.getHigh();
412 dst.setSize(lo.x() * m00 + lo.y() * m01 + m02, lo.x() * m10 + lo.y() * m11 + m12, lo.z(),
413 hi.x() * m00 + hi.y() * m01 + m02, hi.x() * m10 + hi.y() * m11 + m12, hi.z());
414 return dst;
415 }
416
417 /**
418 * @param src
419 * @param dst
420 * @return dst for chaining
421 */
422 public final Vertex transform(final Vertex src, final Vertex dst) {
423 final float x = src.x();
424 final float y = src.y();
425 dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, src.z());
426 return dst;
427 }
428
429 public final void transform(final Vertex[] src, int srcOff, final Vertex[] dst, int dstOff, int length) {
430 while (--length >= 0) {
431 final Vertex srcPoint = src[srcOff++];
432 final Vertex dstPoint = dst[dstOff];
433 if (dstPoint == null) {
434 throw new IllegalArgumentException("dst["+dstOff+"] is null");
435 }
436 final float x = srcPoint.x();
437 final float y = srcPoint.y();
438 dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, srcPoint.z());
439 dst[dstOff++] = dstPoint;
440 }
441 }
442
443 /**
444 * @param src float[2] source of transformation
445 * @param dst float[2] destination of transformation, maybe be equal to <code>src</code>
446 * @return dst for chaining
447 */
448 public final float[] transform(final float[] src, final float[] dst) {
449 final float x = src[0];
450 final float y = src[1];
451 dst[0] = x * m00 + y * m01 + m02;
452 dst[1] = x * m10 + y * m11 + m12;
453 return dst;
454 }
455
456 public final void transform(final float[] src, final int srcOff, final float[] dst, final int dstOff) {
457 final float x = src[srcOff + 0];
458 final float y = src[srcOff + 1];
459 dst[dstOff + 0] = x * m00 + y * m01 + m02;
460 dst[dstOff + 1] = x * m10 + y * m11 + m12;
461 }
462
463 public final void transform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) {
464 int step = 2;
465 if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
466 srcOff = srcOff + length * 2 - 2;
467 dstOff = dstOff + length * 2 - 2;
468 step = -2;
469 }
470 while (--length >= 0) {
471 final float x = src[srcOff + 0];
472 final float y = src[srcOff + 1];
473 dst[dstOff + 0] = x * m00 + y * m01 + m02;
474 dst[dstOff + 1] = x * m10 + y * m11 + m12;
475 srcOff += step;
476 dstOff += step;
477 }
478 }
479
480 /**
481 * @param src source of transformation
482 * @param dst destination of transformation, maybe be equal to <code>src</code>
483 * @return dst for chaining
484 */
485 public final Vec2f transform(final Vec2f src, final Vec2f dst) {
486 final float x = src.x();
487 final float y = src.y();
488 dst.setX( x * m00 + y * m01 + m02 );
489 dst.setY( x * m10 + y * m11 + m12 );
490 return dst;
491 }
492
493 /**
494 * @param src source of transformation
495 * @param dst destination of transformation, maybe be equal to <code>src</code>
496 * @return dst for chaining
497 */
498 public final Vec3f transform(final Vec3f src, final Vec3f dst) {
499 final float x = src.x();
500 final float y = src.y();
501 dst.setX( x * m00 + y * m01 + m02 );
502 dst.setY( x * m10 + y * m11 + m12 );
503 dst.setZ( src.z() ); // just copy z
504 return dst;
505 }
506
507 /**
508 *
509 * @param src
510 * @param dst
511 * @return return dst for chaining
512 */
513 public final Vertex deltaTransform(final Vertex src, final Vertex dst) {
514 final float x = src.x();
515 final float y = src.y();
516 dst.setCoord(x * m00 + y * m01, x * m10 + y * m11, src.z());
517 return dst;
518 }
519
520 public final void deltaTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length) {
521 while (--length >= 0) {
522 final float x = src[srcOff++];
523 final float y = src[srcOff++];
524 dst[dstOff++] = x * m00 + y * m01;
525 dst[dstOff++] = x * m10 + y * m11;
526 }
527 }
528
529 /**
530 *
531 * @param src
532 * @param dst
533 * @return return dst for chaining
534 * @throws NoninvertibleTransformException
535 */
536 public final Vertex inverseTransform(final Vertex src, final Vertex dst) throws NoninvertibleTransformException {
537 final float det = getDeterminant();
538 if (Math.abs(det) < ZERO) {
539 throw new NoninvertibleTransformException(determinantIsZero);
540 }
541 final float x = src.x() - m02;
542 final float y = src.y() - m12;
543 dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det, src.z());
544 return dst;
545 }
546
547 public final void inverseTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length)
549 {
550 final float det = getDeterminant();
551 if (Math.abs(det) < ZERO) {
552 throw new NoninvertibleTransformException(determinantIsZero);
553 }
554
555 while (--length >= 0) {
556 final float x = src[srcOff++] - m02;
557 final float y = src[srcOff++] - m12;
558 dst[dstOff++] = (x * m11 - y * m01) / det;
559 dst[dstOff++] = (y * m00 - x * m10) / det;
560 }
561 }
562
563 @Override
564 public final String toString() {
565 return
566 getClass().getName() +
567 "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
568 + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
569 }
570
571 /** @Override
572 public int hashCode() {
573 HashCode hash = new HashCode();
574 hash.append(m00);
575 hash.append(m01);
576 hash.append(m02);
577 hash.append(m10);
578 hash.append(m11);
579 hash.append(m12);
580 return hash.hashCode();
581 } */
582
583 @Override
584 public final boolean equals(final Object obj) {
585 if (obj == this) {
586 return true;
587 }
588 if (obj instanceof AffineTransform) {
589 final AffineTransform t = (AffineTransform)obj;
590 return
591 m00 == t.m00 && m01 == t.m01 &&
592 m02 == t.m02 && m10 == t.m10 &&
593 m11 == t.m11 && m12 == t.m12;
594 }
595 return false;
596 }
597 @Override
598 public final int hashCode() {
599 throw new InternalError("hashCode not designed");
600 }
601}
602
A Vertex exposing Vec3f vertex- and texture-coordinates.
Definition: Vertex.java:37
final void setCoord(final Vec3f coord)
Definition: Vertex.java:93
Basic Float math utility functions.
Definition: FloatUtil.java:83
static float sin(final float a)
static float cos(final float a)
2D Vector based upon two float components.
Definition: Vec2f.java:37
void setY(final float y)
Definition: Vec2f.java:139
void setX(final float x)
Definition: Vec2f.java:138
3D Vector based upon three float components.
Definition: Vec3f.java:37
void setX(final float x)
Definition: Vec3f.java:158
void setZ(final float z)
Definition: Vec3f.java:160
void setY(final float y)
Definition: Vec3f.java:159
Axis Aligned Bounding Box.
Definition: AABBox.java:54
final Vec3f getHigh()
Returns the maximum right-top-near (xyz) coordinate.
Definition: AABBox.java:131
final Vec3f getLow()
Returns the minimum left-bottom-far (xyz) coordinate.
Definition: AABBox.java:140
final AABBox setSize(final float[] low, final float[] high)
Set size of the AABBox specifying the coordinates of the low and high.
Definition: AABBox.java:173
static final AffineTransform multiply(final AffineTransform tL, final AffineTransform tR)
Multiply matrix of two AffineTransform objects.
final float[] transform(final float[] src, final float[] dst)
final AffineTransform shear(final float shx, final float shy, final AffineTransform tmp)
final void getMatrix(final float[] matrix)
final AffineTransform concatenate(final AffineTransform tR)
Concatenates the given matrix to this.
final AffineTransform rotate(final float angle, final float px, final float py, final AffineTransform tmp)
final void transform(final float[] src, int srcOff, final float[] dst, int dstOff, int length)
final AffineTransform setTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12)
final Vec2f transform(final Vec2f src, final Vec2f dst)
final AffineTransform translate(final float mx, final float my, final AffineTransform tmp)
final AffineTransform setTransform(final AffineTransform t)
final AffineTransform setToTranslation(final float mx, final float my)
final AffineTransform setToScale(final float scx, final float scy)
final AffineTransform setToShear(final float shx, final float shy)
final void transform(final Vertex[] src, int srcOff, final Vertex[] dst, int dstOff, int length)
final Vertex deltaTransform(final Vertex src, final Vertex dst)
final Vec3f transform(final Vec3f src, final Vec3f dst)
final AABBox transform(final AABBox src, final AABBox dst)
final void inverseTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length)
AffineTransform(final float m00, final float m10, final float m01, final float m11, final float m02, final float m12)
final void deltaTransform(final float[] src, int srcOff, final float[] dst, int dstOff, int length)
final AffineTransform setToRotation(final float angle)
final boolean equals(final Object obj)
@Override public int hashCode() { HashCode hash = new HashCode(); hash.append(m00); hash....
final void transform(final float[] src, final int srcOff, final float[] dst, final int dstOff)
final AffineTransform setToRotation(final float angle, final float px, final float py)
final AffineTransform preConcatenate(final AffineTransform tL)
Pre-concatenates the given matrix to this.
final AffineTransform rotate(final float angle, final AffineTransform tmp)
final Vertex inverseTransform(final Vertex src, final Vertex dst)
final Vertex transform(final Vertex src, final Vertex dst)
final AffineTransform scale(final float scx, final float scy, final AffineTransform tmp)