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