1/*
2 * Copyright 2006-2009, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "Transformable.h"
10
11#include <stdio.h>
12#include <string.h>
13
14// constructor
15Transformable::Transformable()
16	: agg::trans_affine()
17{
18}
19
20// copy constructor
21Transformable::Transformable(const Transformable& other)
22	: agg::trans_affine(other)
23{
24}
25
26// destructor
27Transformable::~Transformable()
28{
29}
30
31// StoreTo
32void
33Transformable::StoreTo(double matrix[matrix_size]) const
34{
35	store_to(matrix);
36}
37
38// LoadFrom
39void
40Transformable::LoadFrom(const double matrix[matrix_size])
41{
42	// before calling the potentially heavy TransformationChanged()
43	// hook function, make sure that the transformation
44	// really changed
45	Transformable t;
46	t.load_from(matrix);
47	if (*this != t) {
48		load_from(matrix);
49		TransformationChanged();
50	}
51}
52
53// SetTransform
54void
55Transformable::SetTransform(const Transformable& other)
56{
57	if (*this != other) {
58		*this = other;
59		TransformationChanged();
60	}
61}
62
63// operator=
64Transformable&
65Transformable::operator=(const Transformable& other)
66{
67	if (other != *this) {
68		reset();
69		multiply(other);
70		TransformationChanged();
71	}
72	return *this;
73}
74
75// Multiply
76Transformable&
77Transformable::Multiply(const Transformable& other)
78{
79	if (!other.IsIdentity()) {
80		multiply(other);
81		TransformationChanged();
82	}
83	return *this;
84}
85
86// Reset
87void
88Transformable::Reset()
89{
90	if (!IsIdentity()) {
91		reset();
92		TransformationChanged();
93	}
94}
95
96// Invert
97void
98Transformable::Invert()
99{
100	if (!IsIdentity()) {
101		invert();
102		TransformationChanged();
103	}
104}
105
106// IsIdentity
107bool
108Transformable::IsIdentity() const
109{
110	double m[matrix_size];
111	store_to(m);
112	if (m[0] == 1.0 &&
113		m[1] == 0.0 &&
114		m[2] == 0.0 &&
115		m[3] == 1.0 &&
116		m[4] == 0.0 &&
117		m[5] == 0.0)
118		return true;
119	return false;
120}
121
122// IsTranslationOnly
123bool
124Transformable::IsTranslationOnly() const
125{
126	double m[matrix_size];
127	store_to(m);
128	if (m[0] == 1.0 &&
129		m[1] == 0.0 &&
130		m[2] == 0.0 &&
131		m[3] == 1.0)
132		return true;
133	return false;
134}
135
136// IsNotDistorted
137bool
138Transformable::IsNotDistorted() const
139{
140	double m[matrix_size];
141	store_to(m);
142	return (m[0] == m[3]);
143}
144
145// IsValid
146bool
147Transformable::IsValid() const
148{
149	double m[matrix_size];
150	store_to(m);
151	return ((m[0] * m[3] - m[1] * m[2]) != 0.0);
152}
153
154// operator==
155bool
156Transformable::operator==(const Transformable& other) const
157{
158	double m1[matrix_size];
159	other.store_to(m1);
160	double m2[matrix_size];
161	store_to(m2);
162	return memcmp(m1, m2, sizeof(m1)) == 0;
163}
164
165// operator!=
166bool
167Transformable::operator!=(const Transformable& other) const
168{
169	return !(*this == other);
170}
171
172// Transform
173void
174Transformable::Transform(double* x, double* y) const
175{
176	transform(x, y);
177}
178
179// Transform
180void
181Transformable::Transform(BPoint* point) const
182{
183	if (point) {
184		double x = point->x;
185		double y = point->y;
186
187		transform(&x, &y);
188
189		point->x = x;
190		point->y = y;
191	}
192}
193
194// Transform
195BPoint
196Transformable::Transform(const BPoint& point) const
197{
198	BPoint p(point);
199	Transform(&p);
200	return p;
201}
202
203// InverseTransform
204void
205Transformable::InverseTransform(double* x, double* y) const
206{
207	inverse_transform(x, y);
208}
209
210// InverseTransform
211void
212Transformable::InverseTransform(BPoint* point) const
213{
214	if (point) {
215		double x = point->x;
216		double y = point->y;
217
218		inverse_transform(&x, &y);
219
220		point->x = x;
221		point->y = y;
222	}
223}
224
225// InverseTransform
226BPoint
227Transformable::InverseTransform(const BPoint& point) const
228{
229	BPoint p(point);
230	InverseTransform(&p);
231	return p;
232}
233
234inline float
235min4(float a, float b, float c, float d)
236{
237	return min_c(a, min_c(b, min_c(c, d)));
238}
239
240inline float
241max4(float a, float b, float c, float d)
242{
243	return max_c(a, max_c(b, max_c(c, d)));
244}
245
246// TransformBounds
247BRect
248Transformable::TransformBounds(BRect bounds) const
249{
250	if (bounds.IsValid()) {
251		BPoint lt(bounds.left, bounds.top);
252		BPoint rt(bounds.right, bounds.top);
253		BPoint lb(bounds.left, bounds.bottom);
254		BPoint rb(bounds.right, bounds.bottom);
255
256		Transform(&lt);
257		Transform(&rt);
258		Transform(&lb);
259		Transform(&rb);
260
261		return BRect(floorf(min4(lt.x, rt.x, lb.x, rb.x)),
262					 floorf(min4(lt.y, rt.y, lb.y, rb.y)),
263					 ceilf(max4(lt.x, rt.x, lb.x, rb.x)),
264					 ceilf(max4(lt.y, rt.y, lb.y, rb.y)));
265	}
266	return bounds;
267}
268
269// TranslateBy
270void
271Transformable::TranslateBy(BPoint offset)
272{
273	if (offset.x != 0.0 || offset.y != 0.0) {
274		multiply(agg::trans_affine_translation(offset.x, offset.y));
275		TransformationChanged();
276	}
277}
278
279// RotateBy
280void
281Transformable::RotateBy(BPoint origin, double degrees)
282{
283	if (degrees != 0.0) {
284		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
285		multiply(agg::trans_affine_rotation(degrees * (M_PI / 180.0)));
286		multiply(agg::trans_affine_translation(origin.x, origin.y));
287		TransformationChanged();
288	}
289}
290
291// ScaleBy
292void
293Transformable::ScaleBy(BPoint origin, double xScale, double yScale)
294{
295	if (xScale != 1.0 || yScale != 1.0) {
296		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
297		multiply(agg::trans_affine_scaling(xScale, yScale));
298		multiply(agg::trans_affine_translation(origin.x, origin.y));
299		TransformationChanged();
300	}
301}
302
303// ShearBy
304void
305Transformable::ShearBy(BPoint origin, double xShear, double yShear)
306{
307	if (xShear != 0.0 || yShear != 0.0) {
308		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
309		multiply(agg::trans_affine_skewing(xShear, yShear));
310		multiply(agg::trans_affine_translation(origin.x, origin.y));
311		TransformationChanged();
312	}
313}
314
315// TransformationChanged
316void
317Transformable::TransformationChanged()
318{
319	// default implementation doesn't care
320}
321
322