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 bool 173 Transformable::IsDilation() const 174 { 175 double m[6]; 176 store_to(m); 177 return m[1] == 0.0 && m[2] == 0.0; 178 } 179 180 181 void 182 Transformable::Transform(double* x, double* y) const 183 { 184 transform(x, y); 185 } 186 187 188 void 189 Transformable::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 203 BPoint 204 Transformable::Transform(const BPoint& point) const 205 { 206 BPoint p(point); 207 Transform(&p); 208 return p; 209 } 210 211 212 void 213 Transformable::InverseTransform(double* x, double* y) const 214 { 215 inverse_transform(x, y); 216 } 217 218 219 void 220 Transformable::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 234 BPoint 235 Transformable::InverseTransform(const BPoint& point) const 236 { 237 BPoint p(point); 238 InverseTransform(&p); 239 return p; 240 } 241 242 243 BRect 244 Transformable::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 266 bool 267 Transformable::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 277 void 278 Transformable::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 287 void 288 Transformable::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 299 void 300 Transformable::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 311 void 312 Transformable::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