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