xref: /haiku/src/servers/app/drawing/Painter/Transformable.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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 // min4
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 // max4
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 // constructor
31 Transformable::Transformable()
32 	: agg::trans_affine()
33 {
34 }
35 
36 // copy constructor
37 Transformable::Transformable(const Transformable& other)
38 	: agg::trans_affine(other)
39 {
40 }
41 
42 // constructor
43 Transformable::Transformable(const BMessage* archive)
44 	: agg::trans_affine()
45 {
46 	if (archive) {
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 // destructor
60 Transformable::~Transformable()
61 {
62 }
63 
64 // Archive
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 // StoreTo
85 void
86 Transformable::StoreTo(double matrix[6]) const
87 {
88 	store_to(matrix);
89 }
90 
91 // LoadFrom
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 // SetTransformable
106 void
107 Transformable::SetTransformable(const Transformable& other)
108 {
109 	if (*this != other) {
110 		*this = other;
111 		TransformationChanged();
112 	}
113 }
114 
115 // operator=
116 Transformable&
117 Transformable::operator=(const Transformable& other)
118 {
119 	if (other != *this) {
120 		reset();
121 		multiply(other);
122 		TransformationChanged();
123 	}
124 	return *this;
125 }
126 
127 // Multiply
128 Transformable&
129 Transformable::Multiply(const Transformable& other)
130 {
131 	if (!other.IsIdentity()) {
132 		multiply(other);
133 		TransformationChanged();
134 	}
135 	return *this;
136 }
137 
138 // Reset
139 void
140 Transformable::Reset()
141 {
142 	reset();
143 }
144 
145 // IsIdentity
146 bool
147 Transformable::IsIdentity() const
148 {
149 	double m[6];
150 	store_to(m);
151 	if (m[0] == 1.0 &&
152 		m[1] == 0.0 &&
153 		m[2] == 0.0 &&
154 		m[3] == 1.0 &&
155 		m[4] == 0.0 &&
156 		m[5] == 0.0)
157 		return true;
158 	return false;
159 }
160 
161 // operator==
162 bool
163 Transformable::operator==(const Transformable& other) const
164 {
165 	double m1[6];
166 	other.store_to(m1);
167 	double m2[6];
168 	store_to(m2);
169 	if (m1[0] == m2[0] &&
170 		m1[1] == m2[1] &&
171 		m1[2] == m2[2] &&
172 		m1[3] == m2[3] &&
173 		m1[4] == m2[4] &&
174 		m1[5] == m2[5])
175 		return true;
176 	return false;
177 }
178 
179 // operator!=
180 bool
181 Transformable::operator!=(const Transformable& other) const
182 {
183 	return !(*this == other);
184 }
185 
186 // Transform
187 void
188 Transformable::Transform(double* x, double* y) const
189 {
190 	transform(x, y);
191 }
192 
193 // Transform
194 void
195 Transformable::Transform(BPoint* point) const
196 {
197 	if (point) {
198 		double x = point->x;
199 		double y = point->y;
200 
201 		transform(&x, &y);
202 
203 		point->x = x;
204 		point->y = y;
205 	}
206 }
207 
208 // Transform
209 BPoint
210 Transformable::Transform(const BPoint& point) const
211 {
212 	BPoint p(point);
213 	Transform(&p);
214 	return p;
215 }
216 
217 // InverseTransform
218 void
219 Transformable::InverseTransform(double* x, double* y) const
220 {
221 	inverse_transform(x, y);
222 }
223 
224 // InverseTransform
225 void
226 Transformable::InverseTransform(BPoint* point) const
227 {
228 	if (point) {
229 		double x = point->x;
230 		double y = point->y;
231 
232 		inverse_transform(&x, &y);
233 
234 		point->x = x;
235 		point->y = y;
236 	}
237 }
238 
239 // InverseTransform
240 BPoint
241 Transformable::InverseTransform(const BPoint& point) const
242 {
243 	BPoint p(point);
244 	InverseTransform(&p);
245 	return p;
246 }
247 
248 // TransformBounds
249 BRect
250 Transformable::TransformBounds(const BRect& bounds) const
251 {
252 	if (bounds.IsValid()) {
253 		BPoint lt(bounds.left, bounds.top);
254 		BPoint rt(bounds.right, bounds.top);
255 		BPoint lb(bounds.left, bounds.bottom);
256 		BPoint rb(bounds.right, bounds.bottom);
257 
258 		Transform(&lt);
259 		Transform(&rt);
260 		Transform(&lb);
261 		Transform(&rb);
262 
263 		return BRect(floorf(min4(lt.x, rt.x, lb.x, rb.x)),
264 					 floorf(min4(lt.y, rt.y, lb.y, rb.y)),
265 					 ceilf(max4(lt.x, rt.x, lb.x, rb.x)),
266 					 ceilf(max4(lt.y, rt.y, lb.y, rb.y)));
267 	}
268 	return bounds;
269 }
270 
271 // TranslateBy
272 void
273 Transformable::TranslateBy(BPoint offset)
274 {
275 	if (offset.x != 0.0 || offset.y != 0.0) {
276 		multiply(agg::trans_affine_translation(offset.x, offset.y));
277 		TransformationChanged();
278 	}
279 }
280 
281 // RotateBy
282 void
283 Transformable::RotateBy(BPoint origin, double radians)
284 {
285 	if (radians != 0.0) {
286 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
287 		multiply(agg::trans_affine_rotation(radians));
288 		multiply(agg::trans_affine_translation(origin.x, origin.y));
289 		TransformationChanged();
290 	}
291 }
292 
293 // ScaleBy
294 void
295 Transformable::ScaleBy(BPoint origin, double xScale, double yScale)
296 {
297 	if (xScale != 1.0 || yScale != 1.0) {
298 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
299 		multiply(agg::trans_affine_scaling(xScale, yScale));
300 		multiply(agg::trans_affine_translation(origin.x, origin.y));
301 		TransformationChanged();
302 	}
303 }
304 
305 // ShearBy
306 void
307 Transformable::ShearBy(BPoint origin, double xShear, double yShear)
308 {
309 	if (xShear != 0.0 || yShear != 0.0) {
310 		multiply(agg::trans_affine_translation(-origin.x, -origin.y));
311 		multiply(agg::trans_affine_skewing(xShear, yShear));
312 		multiply(agg::trans_affine_translation(origin.x, origin.y));
313 		TransformationChanged();
314 	}
315 }
316