1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package math.geom2d;
27
28
29 import math.geom2d.Shape2D;
30 import math.geom2d.Angle2D;
31 import math.geom2d.Point2D;
32 import math.geom2d.Vector2D;
33 import math.geom2d.line.LinearShape2D;
34 import math.geom2d.transform.Bijection2D;
35
36
37
38
39
40
41
42
43 public class AffineTransform2D implements Bijection2D, Cloneable {
44
45
46 protected double m00, m01, m02;
47
48
49 protected double m10, m11, m12;
50
51
52
53
54
55
56
57 public final static AffineTransform2D createIdentity() {
58 return new AffineTransform2D(1, 0, 0, 0, 1, 0);
59 }
60
61
62
63
64
65 public final static AffineTransform2D create(AffineTransform2D trans) {
66 double[][] mat = trans.getAffineMatrix();
67 return new AffineTransform2D(
68 mat[0][0], mat[0][1], mat[0][2],
69 mat[1][0], mat[1][1], mat[1][2]);
70 }
71
72
73
74
75 public final static AffineTransform2D create(double[] coefs) {
76 if (coefs.length==4) {
77 return new AffineTransform2D(
78 coefs[0], coefs[1], 0,
79 coefs[2], coefs[3], 0);
80 } else if (coefs.length==6) {
81 return new AffineTransform2D(
82 coefs[0], coefs[1], coefs[2],
83 coefs[3], coefs[4], coefs[5]);
84 } else {
85 throw new IllegalArgumentException();
86 }
87 }
88
89
90
91
92 public final static AffineTransform2D create(
93 double xx, double yx, double tx,
94 double xy, double yy, double ty) {
95 return new AffineTransform2D(xx, yx, tx, xy, yy, ty);
96 }
97
98 public final static AffineTransform2D createGlideReflection(
99 LinearShape2D line, double distance) {
100
101 Vector2D vector = line.getVector().getNormalizedVector();
102 Point2D origin = line.getOrigin();
103
104
105 double dx = vector.getX();
106 double dy = vector.getY();
107 double x0 = origin.getX();
108 double y0 = origin.getY();
109
110
111 double tx = dx*distance;
112 double ty = dy*distance;
113
114
115 double delta = dx*dx+dy*dy;
116 double dx2 = dx*dx;
117 double dy2 = dy*dy;
118 double dxy = dx*dy;
119 double dxy0 = dx*y0;
120 double dyx0 = dy*x0;
121
122
123 return new AffineTransform2D(
124 (dx2-dy2)/delta, 2*dxy/delta, 2*dy*(dyx0-dxy0)/delta+tx,
125 2*dxy/delta, (dy2-dx2)/delta, 2*dx*(dxy0-dyx0)/delta+ty);
126 }
127
128 public final static AffineTransform2D createHomothecy(Point2D center,
129 double k) {
130 return createScaling(center, k, k);
131 }
132
133 public final static AffineTransform2D createLineReflection(
134 LinearShape2D line) {
135 Vector2D vector = line.getVector();
136 Point2D origin = line.getOrigin();
137 double dx = vector.getX();
138 double dy = vector.getY();
139 double x0 = origin.getX();
140 double y0 = origin.getY();
141 double delta = dx*dx+dy*dy;
142
143 return new AffineTransform2D((dx*dx-dy*dy)/delta, 2*dx*dy/delta, 2*dy
144 *(dy*x0-dx*y0)/delta, 2*dx*dy/delta, (dy*dy-dx*dx)/delta, 2*dx
145 *(dx*y0-dy*x0)/delta);
146 }
147
148
149
150
151
152
153
154 public final static AffineTransform2D createPointReflection(Point2D center) {
155 return AffineTransform2D.createScaling(center, -1, -1);
156 }
157
158 public final static AffineTransform2D createQuadrantRotation(int numQuadrant) {
159 int n = ((numQuadrant%4)+4)%4;
160 switch (n) {
161 case 0:
162 return new AffineTransform2D(1, 0, 0, 0, 1, 0);
163 case 1:
164 return new AffineTransform2D(0, -1, 0, 1, 0, 0);
165 case 2:
166 return new AffineTransform2D(-1, 0, 0, 0, -1, 0);
167 case 3:
168 return new AffineTransform2D(0, 1, 0, -1, 0, 0);
169 default:
170 return new AffineTransform2D(1, 0, 0, 0, 1, 0);
171 }
172 }
173
174
175
176
177 public final static AffineTransform2D createRotation(double angle) {
178 return AffineTransform2D.createRotation(0, 0, angle);
179 }
180
181
182
183
184 public final static AffineTransform2D createRotation(Point2D center,
185 double angle) {
186 return AffineTransform2D.createRotation(center.getX(), center.getY(),
187 angle);
188 }
189
190
191
192
193
194
195 public final static AffineTransform2D createRotation(double cx, double cy,
196 double angle) {
197 angle = Angle2D.formatAngle(angle);
198
199
200 double cot = 1, sit = 0;
201
202
203 int k = (int) Math.round(angle*2/Math.PI);
204 if (Math.abs(k*Math.PI/2-angle)<Shape2D.ACCURACY) {
205 assert k>=0 : "k should be positive";
206 assert k<5 : "k should be between 0 and 4";
207 switch (k) {
208 case 0:
209 cot = 1;
210 sit = 0;
211 break;
212 case 1:
213 cot = 0;
214 sit = 1;
215 break;
216 case 2:
217 cot = -1;
218 sit = 0;
219 break;
220 case 3:
221 cot = 0;
222 sit = -1;
223 break;
224 case 4:
225 cot = 1;
226 sit = 0;
227 break;
228 }
229 } else {
230 cot = Math.cos(angle);
231 sit = Math.sin(angle);
232 }
233
234
235 return new AffineTransform2D(
236 cot, -sit, (1-cot)*cx+sit*cy,
237 sit, cot, (1-cot)*cy-sit*cx);
238 }
239
240
241
242
243 public final static AffineTransform2D createScaling(double sx, double sy) {
244 return AffineTransform2D.createScaling(new Point2D(0, 0), sx, sy);
245 }
246
247
248
249
250 public final static AffineTransform2D createScaling(Point2D center,
251 double sx, double sy) {
252 return new AffineTransform2D(sx, 0, (1-sx)*center.getX(), 0, sy, (1-sy)
253 *center.getY());
254 }
255
256
257
258
259
260
261
262
263 public final static AffineTransform2D createShear(double shx, double shy) {
264 return new AffineTransform2D(1, shx, 0, shy, 1, 0);
265 }
266
267
268
269
270 public final static AffineTransform2D createTranslation(Vector2D vect) {
271 return new AffineTransform2D(1, 0, vect.getX(), 0, 1, vect.getY());
272 }
273
274
275
276
277 public final static AffineTransform2D createTranslation(double dx, double dy) {
278 return new AffineTransform2D(1, 0, dx, 0, 1, dy);
279 }
280
281
282
283
284
285
286
287 public final static boolean isIdentity(AffineTransform2D trans) {
288 double[] coefs = trans.getCoefficients();
289 if (Math.abs(coefs[0]-1)>Shape2D.ACCURACY)
290 return false;
291 if (Math.abs(coefs[1])>Shape2D.ACCURACY)
292 return false;
293 if (Math.abs(coefs[2])>Shape2D.ACCURACY)
294 return false;
295 if (Math.abs(coefs[3])>Shape2D.ACCURACY)
296 return false;
297 if (Math.abs(coefs[4]-1)>Shape2D.ACCURACY)
298 return false;
299 if (Math.abs(coefs[5])>Shape2D.ACCURACY)
300 return false;
301 return true;
302 }
303
304
305
306
307
308
309
310 public final static boolean isDirect(AffineTransform2D trans) {
311 double[][] mat = trans.getAffineMatrix();
312 return mat[0][0]*mat[1][1]-mat[0][1]*mat[1][0]>0;
313 }
314
315
316
317
318
319
320
321
322 public final static boolean isIsometry(AffineTransform2D trans) {
323
324 double[][] mat = trans.getAffineMatrix();
325 double a = mat[0][0];
326 double b = mat[0][1];
327 double d = mat[1][0];
328 double e = mat[1][1];
329
330
331 if (Math.abs(a*a+d*d-1)>Shape2D.ACCURACY)
332 return false;
333 if (Math.abs(b*b+e*e-1)>Shape2D.ACCURACY)
334 return false;
335 if (Math.abs(a*b+d*e)>Shape2D.ACCURACY)
336 return false;
337
338
339 return true;
340 }
341
342
343
344
345
346
347
348
349 public final static boolean isMotion(AffineTransform2D trans) {
350 double[][] mat = trans.getAffineMatrix();
351 double det = mat[0][0]*mat[1][1]-mat[0][1]*mat[1][0];
352 return Math.abs(det-1)<Shape2D.ACCURACY;
353 }
354
355
356
357
358
359
360
361 public final static boolean isSimilarity(AffineTransform2D trans) {
362 double[][] mat = trans.getAffineMatrix();
363
364 double a = mat[0][0];
365 double b = mat[1][0];
366 double c = mat[0][1];
367 double d = mat[1][1];
368
369
370 double k2 = Math.abs(a*d-b*c);
371
372
373 if (Math.abs(a*a+b*b-k2)>Shape2D.ACCURACY)
374 return false;
375 if (Math.abs(c*c+d*d-k2)>Shape2D.ACCURACY)
376 return false;
377 if (Math.abs(a*a+c*c-k2)>Shape2D.ACCURACY)
378 return false;
379 if (Math.abs(b*b+d*d-k2)>Shape2D.ACCURACY)
380 return false;
381
382
383 return true;
384 }
385
386
387
388
389
390 public AffineTransform2D() {
391
392 m00 = m11 = 1;
393 m01 = m10 = 0;
394 m02 = m12 = 0;
395 }
396
397
398 public AffineTransform2D(AffineTransform2D trans) {
399 double[][] mat = trans.getAffineMatrix();
400 this.m00 = mat[0][0];
401 this.m01 = mat[0][1];
402 this.m02 = mat[0][2];
403 this.m10 = mat[1][0];
404 this.m11 = mat[1][1];
405 this.m12 = mat[1][2];
406 }
407
408 public AffineTransform2D(double[] coefs) {
409 if (coefs.length==4) {
410 m00 = coefs[0];
411 m01 = coefs[1];
412 m10 = coefs[2];
413 m11 = coefs[3];
414 } else {
415 m00 = coefs[0];
416 m01 = coefs[1];
417 m02 = coefs[2];
418 m10 = coefs[3];
419 m11 = coefs[4];
420 m12 = coefs[5];
421 }
422 }
423
424 public AffineTransform2D(double xx, double yx, double tx, double xy,
425 double yy, double ty) {
426 m00 = xx;
427 m01 = yx;
428 m02 = tx;
429 m10 = xy;
430 m11 = yy;
431 m12 = ty;
432 }
433
434
435
436
437
438
439
440 public double[] getCoefficients() {
441 double[] tab = { m00, m01, m02, m10, m11, m12 };
442 return tab;
443 }
444
445
446
447
448
449
450 public double[][] getAffineMatrix() {
451 double[][] tab = new double[][] {
452 new double[] { m00, m01, m02 },
453 new double[] { m10, m11, m12 },
454 new double[] { 0, 0, 1 } };
455 return tab;
456 }
457
458
459
460
461
462
463
464
465
466 @Deprecated
467 public AffineTransform2D compose(AffineTransform2D that) {
468 return this.concatenate(that);
469 }
470
471
472
473
474
475
476
477
478
479
480
481 public AffineTransform2D concatenate(AffineTransform2D that) {
482 double[][] m2 = that.getAffineMatrix();
483 double n00 = this.m00*m2[0][0]+this.m01*m2[1][0];
484 double n01 = this.m00*m2[0][1]+this.m01*m2[1][1];
485 double n02 = this.m00*m2[0][2]+this.m01*m2[1][2]+this.m02;
486 double n10 = this.m10*m2[0][0]+this.m11*m2[1][0];
487 double n11 = this.m10*m2[0][1]+this.m11*m2[1][1];
488 double n12 = this.m10*m2[0][2]+this.m11*m2[1][2]+this.m12;
489 return new AffineTransform2D(n00, n01, n02, n10, n11, n12);
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508 public AffineTransform2D chain(AffineTransform2D that) {
509 double[][] m2 = that.getAffineMatrix();
510 return new AffineTransform2D(
511 m2[0][0]*this.m00+m2[0][1]*this.m10,
512 m2[0][0]*this.m01+m2[0][1]*this.m11,
513 m2[0][0]*this.m02+m2[0][1]*this.m12+m2[0][2],
514 m2[1][0]*this.m00+m2[1][1]*this.m10,
515 m2[1][0]*this.m01+m2[1][1]*this.m11,
516 m2[1][0]*this.m02+m2[1][1]*this.m12+m2[1][2]);
517 }
518
519
520
521
522
523
524
525
526
527
528
529 public AffineTransform2D preConcatenate(AffineTransform2D that) {
530 return this.chain(that);
531 }
532
533
534
535
536 public boolean isSimilarity() {
537 return AffineTransform2D.isSimilarity(this);
538 }
539
540 public boolean isMotion() {
541 return AffineTransform2D.isMotion(this);
542 }
543
544 public boolean isIsometry() {
545 return AffineTransform2D.isIsometry(this);
546 }
547
548 public boolean isDirect() {
549 return AffineTransform2D.isDirect(this);
550 }
551
552 public boolean isIdentity() {
553 return AffineTransform2D.isIdentity(this);
554 }
555
556
557
558
559
560
561
562
563
564
565 public AffineTransform2D invert() {
566 double det = m00*m11-m10*m01;
567
568 if (Math.abs(det)<Shape2D.ACCURACY)
569 throw new NonInvertibleTransformException();
570
571 return new AffineTransform2D(
572 m11/det, -m01/det, (m01*m12-m02*m11)/det,
573 -m10/det, m00/det, (m02*m10-m00*m12)/det);
574 }
575
576
577
578
579
580
581
582 @Deprecated
583 public AffineTransform2D getInverseTransform() {
584 return this.invert();
585 }
586
587
588
589
590 public Point2D[] transform(java.awt.geom.Point2D[] src, Point2D[] dst) {
591 if (dst==null)
592 dst = new Point2D[src.length];
593 if (dst[0]==null)
594 for (int i = 0; i<src.length; i++)
595 dst[i] = new Point2D();
596
597 double coef[] = getCoefficients();
598
599 for (int i = 0; i<src.length; i++)
600 dst[i].setLocation(new Point2D(
601 src[i].getX()*coef[0]+src[i].getY()*coef[1]+coef[2],
602 src[i].getX()*coef[3]+src[i].getY()*coef[4]+coef[5]));
603 return dst;
604 }
605
606 public Point2D transform(java.awt.geom.Point2D src) {
607 double coef[] = this.getCoefficients();
608 Point2D dst = new Point2D(
609 src.getX()*coef[0]+src.getY()*coef[1]+coef[2],
610 src.getX()*coef[3]+src.getY()*coef[4]+coef[5]);
611 return dst;
612 }
613
614
615
616
617 @Deprecated
618 public Point2D transform(java.awt.geom.Point2D src, Point2D dst) {
619 double coef[] = getCoefficients();
620 if (dst==null)
621 dst = new Point2D();
622 dst.setLocation(
623 src.getX()*coef[0]+src.getY()*coef[1]+coef[2],
624 src.getX()*coef[3]+src.getY()*coef[4]+coef[5]);
625 return dst;
626 }
627
628
629
630
631
632
633
634 @Override
635 public String toString() {
636 return new String("AffineTransform2D(" +
637 m00 + "," + m01 + "," + m02 + "," +
638 m10 + "," + m11 + "," + m12 + "," );
639 }
640
641 @Override
642 public boolean equals(Object obj) {
643 if (!(obj instanceof AffineTransform2D))
644 return false;
645
646 double[] tab1 = this.getCoefficients();
647 double[] tab2 = ((AffineTransform2D) obj).getCoefficients();
648
649 for (int i = 0; i<6; i++)
650 if (Math.abs(tab1[i]-tab2[i])>Shape2D.ACCURACY)
651 return false;
652
653 return true;
654 }
655
656 @Override
657 public AffineTransform2D clone() {
658 return new AffineTransform2D(m00, m01, m02, m10, m11, m12);
659 }
660 }