xref: /haiku/src/servers/app/drawing/Painter/Transformable.cpp (revision 644fa5a93845dc4a1bc155f1fd0f94ebdf0b47bc)
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 void
173 Transformable::Transform(double* x, double* y) const
174 {
175 	transform(x, y);
176 }
177 
178 
179 void
180 Transformable::Transform(BPoint* point) const
181 {
182 	if (point) {
183 		double x = point->x;
184 		double y = point->y;
185 
186 		transform(&x, &y);
187 
188 		point->x = x;
189 		point->y = y;
190 	}
191 }
192 
193 
194 BPoint
195 Transformable::Transform(const BPoint& point) const
196 {
197 	BPoint p(point);
198 	Transform(&p);
199 	return p;
200 }
201 
202 
203 void
204 Transformable::InverseTransform(double* x, double* y) const
205 {
206 	inverse_transform(x, y);
207 }
208 
209 
210 void
211 Transformable::InverseTransform(BPoint* point) const
212 {
213 	if (point) {
214 		double x = point->x;
215 		double y = point->y;
216 
217 		inverse_transform(&x, &y);
218 
219 		point->x = x;
220 		point->y = y;
221 	}
222 }
223 
224 
225 BPoint
226 Transformable::InverseTransform(const BPoint& point) const
227 {
228 	BPoint p(point);
229 	InverseTransform(&p);
230 	return p;
231 }
232 
233 
234 BRect
235 Transformable::TransformBounds(const BRect& bounds) const
236 {
237 	if (bounds.IsValid()) {
238 		BPoint lt(bounds.left, bounds.top);
239 		BPoint rt(bounds.right, bounds.top);
240 		BPoint lb(bounds.left, bounds.bottom);
241 		BPoint rb(bounds.right, bounds.bottom);
242 
243 		Transform(&lt);
244 		Transform(&rt);
245 		Transform(&lb);
246 		Transform(&rb);
247 
248 		return BRect(floorf(min4(lt.x, rt.x, lb.x, rb.x)),
249 					 floorf(min4(lt.y, rt.y, lb.y, rb.y)),
250 					 ceilf(max4(lt.x, rt.x, lb.x, rb.x)),
251 					 ceilf(max4(lt.y, rt.y, lb.y, rb.y)));
252 	}
253 	return bounds;
254 }
255 
256 
257 bool
258 Transformable::IsTranslationOnly() const
259 {
260 	double matrix[6];
261 	store_to(matrix);
262 	return matrix[0] == 1.0 && matrix[1] == 0.0
263 		&& matrix[2] == 0.0 && matrix[3] == 1.0;
264 }
265 
266 
267 
268 void
269 Transformable::TranslateBy(BPoint offset)
270 {
271 	if (offset.x != 0.0 || offset.y != 0.0) {
272 		multiply(agg::trans_affine_translation(offset.x, offset.y));
273 		TransformationChanged();
274 	}
275 }
276 
277 
278 void
279 Transformable::RotateBy(BPoint origin, double radians)
280 {
281 	if (radians != 0.0) {
282 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
283 		multiply(agg::trans_affine_rotation(radians));
284 		multiply(agg::trans_affine_translation(origin.x, origin.y));
285 		TransformationChanged();
286 	}
287 }
288 
289 
290 void
291 Transformable::ScaleBy(BPoint origin, double xScale, double yScale)
292 {
293 	if (xScale != 1.0 || yScale != 1.0) {
294 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
295 		multiply(agg::trans_affine_scaling(xScale, yScale));
296 		multiply(agg::trans_affine_translation(origin.x, origin.y));
297 		TransformationChanged();
298 	}
299 }
300 
301 
302 void
303 Transformable::ShearBy(BPoint origin, double xShear, double yShear)
304 {
305 	if (xShear != 0.0 || yShear != 0.0) {
306 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
307 		multiply(agg::trans_affine_skewing(xShear, yShear));
308 		multiply(agg::trans_affine_translation(origin.x, origin.y));
309 		TransformationChanged();
310 	}
311 }
312