xref: /haiku/src/servers/app/drawing/Painter/Transformable.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
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 
17 inline float
18 min4(float a, float b, float c, float d)
19 {
20 	return min_c(a, min_c(b, min_c(c, d)));
21 }
22 
23 
24 inline float
25 max4(float a, float b, float c, float d)
26 {
27 	return max_c(a, max_c(b, max_c(c, d)));
28 }
29 
30 
31 Transformable::Transformable()
32 	: agg::trans_affine()
33 {
34 }
35 
36 
37 Transformable::Transformable(const Transformable& other)
38 	: agg::trans_affine(other)
39 {
40 }
41 
42 
43 Transformable::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 
60 Transformable::~Transformable()
61 {
62 }
63 
64 
65 status_t
66 Transformable::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 
85 void
86 Transformable::StoreTo(double matrix[6]) const
87 {
88 	store_to(matrix);
89 }
90 
91 
92 void
93 Transformable::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 
106 void
107 Transformable::SetTransformable(const Transformable& other)
108 {
109 	if (*this != other) {
110 		*this = other;
111 		TransformationChanged();
112 	}
113 }
114 
115 
116 Transformable&
117 Transformable::operator=(const Transformable& other)
118 {
119 	if (other != *this) {
120 		agg::trans_affine::operator=(other);
121 		TransformationChanged();
122 	}
123 	return *this;
124 }
125 
126 
127 Transformable&
128 Transformable::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 
138 Transformable&
139 Transformable::Multiply(const Transformable& other)
140 {
141 	if (!other.IsIdentity()) {
142 		multiply(other);
143 		TransformationChanged();
144 	}
145 	return *this;
146 }
147 
148 
149 void
150 Transformable::Reset()
151 {
152 	reset();
153 }
154 
155 
156 bool
157 Transformable::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 
172 bool
173 Transformable::IsDilation() const
174 {
175 	double m[6];
176 	store_to(m);
177 	return m[1] == 0.0 && m[2] == 0.0;
178 }
179 
180 
181 void
182 Transformable::Transform(double* x, double* y) const
183 {
184 	transform(x, y);
185 }
186 
187 
188 void
189 Transformable::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 
203 BPoint
204 Transformable::Transform(const BPoint& point) const
205 {
206 	BPoint p(point);
207 	Transform(&p);
208 	return p;
209 }
210 
211 
212 void
213 Transformable::InverseTransform(double* x, double* y) const
214 {
215 	inverse_transform(x, y);
216 }
217 
218 
219 void
220 Transformable::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 
234 BPoint
235 Transformable::InverseTransform(const BPoint& point) const
236 {
237 	BPoint p(point);
238 	InverseTransform(&p);
239 	return p;
240 }
241 
242 
243 BRect
244 Transformable::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 
266 bool
267 Transformable::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 
277 void
278 Transformable::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 
287 void
288 Transformable::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 
299 void
300 Transformable::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 
311 void
312 Transformable::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