xref: /haiku/src/kits/interface/AffineTransform.cpp (revision ad33fd78a50fc20fc1ff3f8df85f6c09c0a619a1)
1 /*
2  * Copyright 2008-2010, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephen Deken, stephen.deken@gmail.com
7  *		Stephan Aßmus <superstippi@gmx.de>
8  */
9 //----------------------------------------------------------------------------
10 // Anti-Grain Geometry - Version 2.4
11 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
12 //
13 // Permission to copy, use, modify, sell and distribute this software
14 // is granted provided this copyright notice appears in all copies.
15 // This software is provided "as is" without express or implied
16 // warranty, and with no claim as to its suitability for any purpose.
17 //
18 //----------------------------------------------------------------------------
19 // Contact: mcseem@antigrain.com
20 //          mcseemagg@yahoo.com
21 //          http://www.antigrain.com
22 //----------------------------------------------------------------------------
23 
24 #include <AffineTransform.h>
25 
26 #include <TypeConstants.h>
27 
28 
29 const BAffineTransform B_AFFINE_IDENTITY_TRANSFORM;
30 
31 
BAffineTransform()32 BAffineTransform::BAffineTransform()
33 	:
34 	sx(1.0),
35 	shy(0.0),
36 	shx(0.0),
37 	sy(1.0),
38 	tx(0.0),
39 	ty(0.0)
40 {
41 }
42 
43 
BAffineTransform(double sx,double shy,double shx,double sy,double tx,double ty)44 BAffineTransform::BAffineTransform(double sx, double shy, double shx,
45 		double sy, double tx, double ty)
46 	:
47 	sx(sx),
48 	shy(shy),
49 	shx(shx),
50 	sy(sy),
51 	tx(tx),
52 	ty(ty)
53 {
54 }
55 
56 
BAffineTransform(const BAffineTransform & other)57 BAffineTransform::BAffineTransform(const BAffineTransform& other)
58 	:
59 	sx(other.sx),
60 	shy(other.shy),
61 	shx(other.shx),
62 	sy(other.sy),
63 	tx(other.tx),
64 	ty(other.ty)
65 {
66 }
67 
68 
~BAffineTransform()69 BAffineTransform::~BAffineTransform()
70 {
71 }
72 
73 
74 // #pragma mark -
75 
76 
77 bool
IsFixedSize() const78 BAffineTransform::IsFixedSize() const
79 {
80 	return true;
81 }
82 
83 
84 type_code
TypeCode() const85 BAffineTransform::TypeCode() const
86 {
87 	return B_AFFINE_TRANSFORM_TYPE;
88 }
89 
90 
91 ssize_t
FlattenedSize() const92 BAffineTransform::FlattenedSize() const
93 {
94 	return 6 * sizeof(double);
95 }
96 
97 
98 status_t
Flatten(void * _buffer,ssize_t size) const99 BAffineTransform::Flatten(void* _buffer, ssize_t size) const
100 {
101 	if (_buffer == NULL || size < BAffineTransform::FlattenedSize())
102 		return B_BAD_VALUE;
103 
104 	double* buffer = reinterpret_cast<double*>(_buffer);
105 
106 	buffer[0] = sx;
107 	buffer[1] = shy;
108 	buffer[2] = shx;
109 	buffer[3] = sy;
110 	buffer[4] = tx;
111 	buffer[5] = ty;
112 
113 	return B_OK;
114 }
115 
116 
117 status_t
Unflatten(type_code code,const void * _buffer,ssize_t size)118 BAffineTransform::Unflatten(type_code code, const void* _buffer, ssize_t size)
119 {
120 	if (_buffer == NULL || size < BAffineTransform::FlattenedSize()
121 			|| code != BAffineTransform::TypeCode()) {
122 		return B_BAD_VALUE;
123 	}
124 
125 	const double* buffer = reinterpret_cast<const double*>(_buffer);
126 
127 	sx = buffer[0];
128 	shy = buffer[1];
129 	shx = buffer[2];
130 	sy = buffer[3];
131 	tx = buffer[4];
132 	ty = buffer[5];
133 
134 	return B_OK;
135 }
136 
137 
138 // #pragma mark -
139 
140 
141 /*static*/ BAffineTransform
AffineTranslation(double x,double y)142 BAffineTransform::AffineTranslation(double x, double y)
143 {
144 	return BAffineTransform(1.0, 0.0, 0.0, 1.0, x, y);
145 }
146 
147 
148 /*static*/ BAffineTransform
AffineRotation(double angle)149 BAffineTransform::AffineRotation(double angle)
150 {
151 	return BAffineTransform(cos(angle), sin(angle), -sin(angle), cos(angle),
152 		0.0, 0.0);
153 }
154 
155 
156 /*static*/ BAffineTransform
AffineScaling(double x,double y)157 BAffineTransform::AffineScaling(double x, double y)
158 {
159 	return BAffineTransform(x, 0.0, 0.0, y, 0.0, 0.0);
160 }
161 
162 
163 /*static*/ BAffineTransform
AffineScaling(double scale)164 BAffineTransform::AffineScaling(double scale)
165 {
166 	return BAffineTransform(scale, 0.0, 0.0, scale, 0.0, 0.0);
167 }
168 
169 
170 /*static*/ BAffineTransform
AffineShearing(double x,double y)171 BAffineTransform::AffineShearing(double x, double y)
172 {
173 	return BAffineTransform(1.0, tan(y), tan(x), 1.0, 0.0, 0.0);
174 }
175 
176 
177 // #pragma mark -
178 
179 
180 BPoint
Apply(const BPoint & point) const181 BAffineTransform::Apply(const BPoint& point) const
182 {
183 	double x = point.x;
184 	double y = point.y;
185 	Apply(&x, &y);
186 	return BPoint(x, y);
187 }
188 
189 
190 BPoint
ApplyInverse(const BPoint & point) const191 BAffineTransform::ApplyInverse(const BPoint& point) const
192 {
193 	double x = point.x;
194 	double y = point.y;
195 	ApplyInverse(&x, &y);
196 	return BPoint(x, y);
197 }
198 
199 
200 void
Apply(BPoint * point) const201 BAffineTransform::Apply(BPoint* point) const
202 {
203 	if (point == NULL)
204 		return;
205 	double x = point->x;
206 	double y = point->y;
207 	Apply(&x, &y);
208 	point->x = x;
209 	point->y = y;
210 }
211 
212 
213 void
ApplyInverse(BPoint * point) const214 BAffineTransform::ApplyInverse(BPoint* point) const
215 {
216 	if (point == NULL)
217 		return;
218 	double x = point->x;
219 	double y = point->y;
220 	ApplyInverse(&x, &y);
221 	point->x = x;
222 	point->y = y;
223 }
224 
225 
226 void
Apply(BPoint * points,uint32 count) const227 BAffineTransform::Apply(BPoint* points, uint32 count) const
228 {
229 	if (points != NULL) {
230 		for (uint32 i = 0; i < count; ++i)
231 			Apply(&points[i]);
232 	}
233 }
234 
235 
236 void
ApplyInverse(BPoint * points,uint32 count) const237 BAffineTransform::ApplyInverse(BPoint* points, uint32 count) const
238 {
239 	if (points != NULL) {
240 		for (uint32 i = 0; i < count; ++i)
241 			ApplyInverse(&points[i]);
242 	}
243 }
244 
245 
246 // #pragma mark -
247 
248 
249 const BAffineTransform&
TranslateBy(const BPoint & delta)250 BAffineTransform::TranslateBy(const BPoint& delta)
251 {
252 	return TranslateBy(delta.x, delta.y);
253 }
254 
255 
256 BAffineTransform
TranslateByCopy(double x,double y) const257 BAffineTransform::TranslateByCopy(double x, double y) const
258 {
259 	BAffineTransform copy(*this);
260 	copy.TranslateBy(x, y);
261 	return copy;
262 }
263 
264 
265 BAffineTransform
TranslateByCopy(const BPoint & delta) const266 BAffineTransform::TranslateByCopy(const BPoint& delta) const
267 {
268 	return TranslateByCopy(delta.x, delta.y);
269 }
270 
271 
272 // #pragma mark -
273 
274 
275 const BAffineTransform&
RotateBy(const BPoint & center,double angle)276 BAffineTransform::RotateBy(const BPoint& center, double angle)
277 {
278 	TranslateBy(-center.x, -center.y);
279 	RotateBy(angle);
280 	return TranslateBy(center.x, center.y);
281 }
282 
283 
284 BAffineTransform
RotateByCopy(double angle) const285 BAffineTransform::RotateByCopy(double angle) const
286 {
287 	BAffineTransform copy(*this);
288 	copy.RotateBy(angle);
289 	return copy;
290 }
291 
292 
293 BAffineTransform
RotateByCopy(const BPoint & center,double angle) const294 BAffineTransform::RotateByCopy(const BPoint& center, double angle) const
295 {
296 	BAffineTransform copy(*this);
297 	copy.RotateBy(center, angle);
298 	return copy;
299 }
300 
301 
302 // #pragma mark -
303 
304 
305 const BAffineTransform&
ScaleBy(const BPoint & center,double scale)306 BAffineTransform::ScaleBy(const BPoint& center, double scale)
307 {
308 	return ScaleBy(center, scale, scale);
309 }
310 
311 
312 const BAffineTransform&
ScaleBy(const BPoint & center,double x,double y)313 BAffineTransform::ScaleBy(const BPoint& center, double x, double y)
314 {
315 	TranslateBy(-center.x, -center.y);
316 	ScaleBy(x, y);
317 	return TranslateBy(center.x, center.y);
318 }
319 
320 
321 const BAffineTransform&
ScaleBy(const BPoint & scale)322 BAffineTransform::ScaleBy(const BPoint& scale)
323 {
324 	return ScaleBy(scale.x, scale.y);
325 }
326 
327 
328 const BAffineTransform&
ScaleBy(const BPoint & center,const BPoint & scale)329 BAffineTransform::ScaleBy(const BPoint& center, const BPoint& scale)
330 {
331 	return ScaleBy(center, scale.x, scale.y);
332 }
333 
334 
335 BAffineTransform
ScaleByCopy(double scale) const336 BAffineTransform::ScaleByCopy(double scale) const
337 {
338 	return ScaleByCopy(scale, scale);
339 }
340 
341 
342 BAffineTransform
ScaleByCopy(const BPoint & center,double scale) const343 BAffineTransform::ScaleByCopy(const BPoint& center, double scale) const
344 {
345 	return ScaleByCopy(center, scale, scale);
346 }
347 
348 
349 BAffineTransform
ScaleByCopy(double x,double y) const350 BAffineTransform::ScaleByCopy(double x, double y) const
351 {
352 	BAffineTransform copy(*this);
353 	copy.ScaleBy(x, y);
354 	return copy;
355 }
356 
357 
358 BAffineTransform
ScaleByCopy(const BPoint & center,double x,double y) const359 BAffineTransform::ScaleByCopy(const BPoint& center, double x, double y) const
360 {
361 	BAffineTransform copy(*this);
362 	copy.ScaleBy(center, x, y);
363 	return copy;
364 }
365 
366 
367 BAffineTransform
ScaleByCopy(const BPoint & scale) const368 BAffineTransform::ScaleByCopy(const BPoint& scale) const
369 {
370 	return ScaleByCopy(scale.x, scale.y);
371 }
372 
373 
374 BAffineTransform
ScaleByCopy(const BPoint & center,const BPoint & scale) const375 BAffineTransform::ScaleByCopy(const BPoint& center, const BPoint& scale) const
376 {
377 	return ScaleByCopy(center, scale.x, scale.y);
378 }
379 
380 
381 const BAffineTransform&
SetScale(double scale)382 BAffineTransform::SetScale(double scale)
383 {
384 	return SetScale(scale, scale);
385 }
386 
387 
388 const BAffineTransform&
SetScale(double x,double y)389 BAffineTransform::SetScale(double x, double y)
390 {
391 	double tx;
392 	double ty;
393 	double rotation;
394 	double shearX;
395 	double shearY;
396 	if (!GetAffineParameters(&tx, &ty, &rotation, NULL, NULL,
397 			&shearX, &shearY)) {
398 		return *this;
399 	}
400 
401 	BAffineTransform result;
402 	result.ShearBy(shearX, shearY);
403 	result.ScaleBy(x, y);
404 	result.RotateBy(rotation);
405 	result.TranslateBy(tx, ty);
406 
407 	return *this = result;
408 }
409 
410 
411 // #pragma mark -
412 
413 
414 const BAffineTransform&
ShearBy(const BPoint & center,double x,double y)415 BAffineTransform::ShearBy(const BPoint& center, double x, double y)
416 {
417 	TranslateBy(-center.x, -center.y);
418 	ShearBy(x, y);
419 	return TranslateBy(center.x, center.y);
420 }
421 
422 
423 const BAffineTransform&
ShearBy(const BPoint & shear)424 BAffineTransform::ShearBy(const BPoint& shear)
425 {
426 	return ShearBy(shear.x, shear.y);
427 }
428 
429 
430 const BAffineTransform&
ShearBy(const BPoint & center,const BPoint & shear)431 BAffineTransform::ShearBy(const BPoint& center, const BPoint& shear)
432 {
433 	return ShearBy(center, shear.x, shear.y);
434 }
435 
436 
437 BAffineTransform
ShearByCopy(double x,double y) const438 BAffineTransform::ShearByCopy(double x, double y) const
439 {
440 	BAffineTransform copy(*this);
441 	copy.ShearBy(x, y);
442 	return copy;
443 }
444 
445 
446 BAffineTransform
ShearByCopy(const BPoint & center,double x,double y) const447 BAffineTransform::ShearByCopy(const BPoint& center, double x, double y) const
448 {
449 	BAffineTransform copy(*this);
450 	copy.ShearBy(center, x, y);
451 	return copy;
452 }
453 
454 
455 BAffineTransform
ShearByCopy(const BPoint & shear) const456 BAffineTransform::ShearByCopy(const BPoint& shear) const
457 {
458 	BAffineTransform copy(*this);
459 	copy.ShearBy(shear);
460 	return copy;
461 }
462 
463 
464 BAffineTransform
ShearByCopy(const BPoint & center,const BPoint & shear) const465 BAffineTransform::ShearByCopy(const BPoint& center, const BPoint& shear) const
466 {
467 	BAffineTransform copy(*this);
468 	copy.ShearBy(center, shear);
469 	return copy;
470 }
471 
472 
473 // #pragma mark -
474 
475 
476 const BAffineTransform&
PreMultiply(const BAffineTransform & other)477 BAffineTransform::PreMultiply(const BAffineTransform& other)
478 {
479 	double t0 = sx * other.sx + shy * other.shx;
480 	double t2 = shx * other.sx + sy * other.shx;
481 	double t4 = tx * other.sx + ty * other.shx + other.tx;
482 	shy = sx * other.shy + shy * other.sy;
483 	sy = shx * other.shy + sy * other.sy;
484 	ty = tx * other.shy + ty * other.sy + other.ty;
485 	sx = t0;
486 	shx = t2;
487 	tx = t4;
488 	return *this;
489 }
490 
491 
492 bool
IsValid(double epsilon) const493 BAffineTransform::IsValid(double epsilon) const
494 {
495 	return fabs(sx) > epsilon && fabs(sy) > epsilon;
496 }
497 
498 
499 static inline bool
IsEqualEpsilon(double v1,double v2,double epsilon)500 IsEqualEpsilon(double v1, double v2, double epsilon)
501 {
502     return fabs(v1 - v2) <= double(epsilon);
503 }
504 
505 
506 bool
IsIdentity(double epsilon) const507 BAffineTransform::IsIdentity(double epsilon) const
508 {
509 	return IsEqualEpsilon(sx, 1.0, epsilon)
510 		&& IsEqualEpsilon(shy, 0.0, epsilon)
511 		&& IsEqualEpsilon(shx, 0.0, epsilon)
512 		&& IsEqualEpsilon(sy, 1.0, epsilon)
513 		&& IsEqualEpsilon(tx, 0.0, epsilon)
514 		&& IsEqualEpsilon(ty, 0.0, epsilon);
515 }
516 
517 
518 bool
IsDilation(double epsilon) const519 BAffineTransform::IsDilation(double epsilon) const
520 {
521 	return IsEqualEpsilon(shy, 0.0, epsilon)
522 		&& IsEqualEpsilon(shx, 0.0, epsilon);
523 }
524 
525 
526 bool
IsEqual(const BAffineTransform & other,double epsilon) const527 BAffineTransform::IsEqual(const BAffineTransform& other, double epsilon) const
528 {
529 	return IsEqualEpsilon(sx, other.sx, epsilon)
530 		&& IsEqualEpsilon(shy, other.shy, epsilon)
531 		&& IsEqualEpsilon(shx, other.shx, epsilon)
532 		&& IsEqualEpsilon(sy, other.sy, epsilon)
533 		&& IsEqualEpsilon(tx, other.tx, epsilon)
534 		&& IsEqualEpsilon(ty, other.ty, epsilon);
535 }
536 
537 
538 const BAffineTransform&
Invert()539 BAffineTransform::Invert()
540 {
541 	double d  = InverseDeterminant();
542 
543 	double t0 = sy * d;
544 	sy =  sx * d;
545 	shy = -shy * d;
546 	shx = -shx * d;
547 
548 	double t4 = -tx * t0 - ty * shx;
549 	ty = -tx * shy - ty * sy;
550 
551 	sx = t0;
552 	tx = t4;
553 
554     return *this;
555 }
556 
557 
558 const BAffineTransform&
FlipX()559 BAffineTransform::FlipX()
560 {
561 	sx = -sx;
562 	shy = -shy;
563 	tx = -tx;
564 	return *this;
565 }
566 
567 
568 const BAffineTransform&
FlipY()569 BAffineTransform::FlipY()
570 {
571 	shx = -shx;
572 	sy = -sy;
573 	ty = -ty;
574 	return *this;
575 }
576 
577 
578 const BAffineTransform&
Reset()579 BAffineTransform::Reset()
580 {
581 	sx = sy = 1.0;
582 	shy = shx = tx = ty = 0.0;
583 	return *this;
584 }
585 
586 
587 void
GetTranslation(double * _tx,double * _ty) const588 BAffineTransform::GetTranslation(double* _tx, double* _ty) const
589 {
590 	if (_tx)
591 		*_tx = tx;
592 	if (_ty)
593 		*_ty = ty;
594 }
595 
596 
597 double
Rotation() const598 BAffineTransform::Rotation() const
599 {
600 	double x1 = 0.0;
601 	double y1 = 0.0;
602 	double x2 = 1.0;
603 	double y2 = 0.0;
604 	Apply(&x1, &y1);
605 	Apply(&x2, &y2);
606 	return atan2(y2 - y1, x2 - x1);
607 }
608 
609 
610 double
Scale() const611 BAffineTransform::Scale() const
612 {
613 	double x = 0.707106781 * sx + 0.707106781 * shx;
614 	double y = 0.707106781 * shy + 0.707106781 * sy;
615 	return sqrt(x * x + y * y);
616 }
617 
618 
619 void
GetScale(double * _sx,double * _sy) const620 BAffineTransform::GetScale(double* _sx, double* _sy) const
621 {
622 	double x1 = 0.0;
623 	double y1 = 0.0;
624 	double x2 = 1.0;
625 	double y2 = 1.0;
626 	BAffineTransform t(*this);
627 	t.PreMultiply(AffineRotation(-Rotation()));
628 	t.Apply(&x1, &y1);
629 	t.Apply(&x2, &y2);
630 	if (_sx)
631 		*_sx = x2 - x1;
632 	if (_sy)
633 		*_sy = y2 - y1;
634 }
635 
636 
637 void
GetScaleAbs(double * _sx,double * _sy) const638 BAffineTransform::GetScaleAbs(double* _sx, double* _sy) const
639 {
640 	// When there is considerable shear this method gives us much
641 	// better estimation than just sx, sy.
642 	if (_sx)
643 		*_sx = sqrt(sx * sx + shx * shx);
644 	if (_sy)
645 		*_sy = sqrt(shy * shy + sy * sy);
646 }
647 
648 
649 bool
GetAffineParameters(double * _translationX,double * _translationY,double * _rotation,double * _scaleX,double * _scaleY,double * _shearX,double * _shearY) const650 BAffineTransform::GetAffineParameters(double* _translationX,
651 	double* _translationY, double* _rotation, double* _scaleX, double* _scaleY,
652 	double* _shearX, double* _shearY) const
653 {
654 	GetTranslation(_translationX, _translationY);
655 
656 	double rotation = Rotation();
657 	if (_rotation != NULL)
658 		*_rotation = rotation;
659 
660 	// Calculate shear
661 	double x1 = 0.0;
662 	double y1 = 0.0;
663 	double x2 = 1.0;
664 	double y2 = 0.0;
665 	double x3 = 0.0;
666 	double y3 = 1.0;
667 
668 	// Reverse the effects of any rotation
669 	BAffineTransform t(*this);
670 	t.PreMultiply(AffineRotation(-rotation));
671 
672 	t.Apply(&x1, &y1);
673 	t.Apply(&x2, &y2);
674 	t.Apply(&x3, &y3);
675 
676 	double shearX = y2 - y1;
677 	double shearY = x3 - x1;
678 
679 	// Calculate scale
680 	x1 = 0.0;
681 	y1 = 0.0;
682 	x2 = 1.0;
683 	y2 = 0.0;
684 	x3 = 0.0;
685 	y3 = 1.0;
686 
687 	// Reverse the effects of any shear
688 	t.PreMultiplyInverse(AffineShearing(shearX, shearY));
689 
690 	t.Apply(&x1, &y1);
691 	t.Apply(&x2, &y2);
692 	t.Apply(&x3, &y3);
693 
694 	double scaleX = x2 - x1;
695 	double scaleY = y3 - y1;
696 
697 	if (_scaleX != NULL)
698 		*_scaleX = scaleX;
699 	if (_scaleY != NULL)
700 		*_scaleY = scaleY;
701 
702 	// Since scale was calculated last, the shear values are still scaled.
703 	// We cannot get the affine parameters from a matrix with 0 scale.
704 	if (scaleX == 0.0 && scaleY == 0.0)
705 		return false;
706 
707 	if (_shearX != NULL)
708 		*_shearX = shearX / scaleX;
709 	if (_shearY != NULL)
710 		*_shearY = shearY / scaleY;
711 
712 	return true;
713 }
714 
715