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