1/*
2 * Copyright 2006-2009, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include "ChannelTransform.h"
10
11#include <math.h>
12#include <stdio.h>
13
14
15// constructor
16ChannelTransform::ChannelTransform()
17	:
18	Transformable(),
19	fPivot(0.0, 0.0),
20	fTranslation(0.0, 0.0),
21	fRotation(0.0),
22	fXScale(1.0),
23	fYScale(1.0)
24{
25}
26
27// copy constructor
28ChannelTransform::ChannelTransform(const ChannelTransform& other)
29	:
30	Transformable(other),
31	fPivot(other.fPivot),
32	fTranslation(other.fTranslation),
33	fRotation(other.fRotation),
34	fXScale(other.fXScale),
35	fYScale(other.fYScale)
36{
37}
38
39
40// destructor
41ChannelTransform::~ChannelTransform()
42{
43}
44
45
46// SetTransformation
47void
48ChannelTransform::SetTransformation(const Transformable& other)
49{
50	// calc affine parameters
51
52	// translation
53	double tx;
54	double ty;
55	other.translation(&tx, &ty);
56
57	// rotation
58	double rotation = agg::rad2deg(other.rotation());
59
60	// scale
61	double scaleX;
62	double scaleY;
63	other.scaling(&scaleX, &scaleY);
64
65	if (isnanf(tx) || isnanf(ty) || isnanf(scaleX) || isnanf(scaleY))
66		return;
67
68	SetTransformation(B_ORIGIN, BPoint(tx, ty), rotation, scaleX, scaleY);
69}
70
71
72// SetTransformation
73void
74ChannelTransform::SetTransformation(BPoint pivot, BPoint translation,
75	double rotation, double xScale, double yScale)
76{
77//printf("SetTransformation(BPoint(%.1f, %.1f), BPoint(%.1f, %.1f), "
78//"%.2f, %.2f, %.2f)\n", pivot.x, pivot.y, translation.x, translation.y,
79//rotation, xScale, yScale);
80
81	if (fTranslation != translation ||
82		fPivot != pivot ||
83		fRotation != rotation ||
84		fXScale != xScale ||
85		fYScale != yScale) {
86
87		fPivot = pivot;
88		fTranslation = translation;
89		fRotation = rotation;
90		fXScale = xScale;
91		fYScale = yScale;
92
93		_UpdateMatrix();
94	}
95}
96
97
98// SetPivot
99void
100ChannelTransform::SetPivot(BPoint pivot)
101{
102	if (pivot == fPivot)
103		return;
104
105	fPivot = pivot;
106
107	_UpdateMatrix();
108}
109
110
111// TranslateBy
112void
113ChannelTransform::TranslateBy(BPoint offset)
114{
115	if (offset.x == 0.0 && offset.y == 0.0)
116		return;
117
118	fTranslation += offset;
119
120	_UpdateMatrix();
121}
122
123
124// RotateBy
125/*!	Converts a rotation in world coordinates into
126	a combined local rotation and a translation.
127*/
128void
129ChannelTransform::RotateBy(BPoint origin, double degrees)
130{
131	if (degrees == 0.0)
132		return;
133
134	origin -= fPivot;
135
136	fRotation += degrees;
137
138	// rotate fTranslation
139	double xOffset = fTranslation.x - origin.x;
140	double yOffset = fTranslation.y - origin.y;
141
142	agg::trans_affine_rotation m(degrees * M_PI / 180.0);
143	m.transform(&xOffset, &yOffset);
144
145	fTranslation.x = origin.x + xOffset;
146	fTranslation.y = origin.y + yOffset;
147
148	_UpdateMatrix();
149}
150
151
152// RotateBy
153void
154ChannelTransform::RotateBy(double degrees)
155{
156	if (degrees == 0.0)
157		return;
158
159	fRotation += degrees;
160
161	_UpdateMatrix();
162}
163
164
165// ScaleBy
166//
167// converts a scalation in world coordinates into
168// a combined local scalation and a translation
169void
170ChannelTransform::ScaleBy(BPoint origin, double xScale, double yScale)
171{
172	// TODO: Untested?
173	if (xScale == 1.0 && yScale == 1.0)
174		return;
175
176	fXScale *= xScale;
177	fYScale *= yScale;
178
179	// scale fTranslation
180	double xOffset = fTranslation.x - origin.x;
181	double yOffset = fTranslation.y - origin.y;
182
183	fTranslation.x = origin.x + (xOffset * xScale);
184	fTranslation.y = origin.y + (yOffset * yScale);
185
186	_UpdateMatrix();
187}
188
189// ScaleBy
190void
191ChannelTransform::ScaleBy(double xScale, double yScale)
192{
193	if (xScale == 1.0 && yScale == 1.0)
194		return;
195
196	fXScale *= xScale;
197	fYScale *= yScale;
198
199	_UpdateMatrix();
200}
201
202
203// SetTranslationAndScale
204void
205ChannelTransform::SetTranslationAndScale(BPoint offset, double xScale,
206	double yScale)
207{
208	if (fTranslation == offset && fXScale == xScale && fYScale == yScale)
209		return;
210
211	fTranslation = offset;
212
213	fXScale = xScale;
214	fYScale = yScale;
215
216	_UpdateMatrix();
217}
218
219
220// Reset
221void
222ChannelTransform::Reset()
223{
224	SetTransformation(B_ORIGIN, B_ORIGIN, 0.0, 1.0, 1.0);
225}
226
227
228// =
229ChannelTransform&
230ChannelTransform::operator=(const ChannelTransform& other)
231{
232	fTranslation = other.fTranslation;
233	fRotation = other.fRotation;
234	fXScale = other.fXScale;
235	fYScale = other.fYScale;
236
237	Transformable::operator=(other);
238
239	return *this;
240}
241
242
243// _UpdateMatrix
244void
245ChannelTransform::_UpdateMatrix()
246{
247	// fix up scales in case any is zero
248	double xScale = fXScale;
249	if (xScale == 0.0)
250		xScale = 0.000001;
251	double yScale = fYScale;
252	if (yScale == 0.0)
253		yScale = 0.000001;
254
255	// start clean
256	reset();
257	// the "pivot" is like the offset from world to local
258	// coordinate system and is the center for rotation and scale
259	multiply(agg::trans_affine_translation(-fPivot.x, -fPivot.y));
260	multiply(agg::trans_affine_scaling(xScale, yScale));
261	multiply(agg::trans_affine_rotation(fRotation * M_PI / 180.0));
262
263	multiply(agg::trans_affine_translation(fPivot.x + fTranslation.x,
264		fPivot.y + fTranslation.y));
265
266	// call hook function
267	Update();
268}
269
270