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 void 173 Transformable::Transform(double* x, double* y) const 174 { 175 transform(x, y); 176 } 177 178 179 void 180 Transformable::Transform(BPoint* point) const 181 { 182 if (point) { 183 double x = point->x; 184 double y = point->y; 185 186 transform(&x, &y); 187 188 point->x = x; 189 point->y = y; 190 } 191 } 192 193 194 BPoint 195 Transformable::Transform(const BPoint& point) const 196 { 197 BPoint p(point); 198 Transform(&p); 199 return p; 200 } 201 202 203 void 204 Transformable::InverseTransform(double* x, double* y) const 205 { 206 inverse_transform(x, y); 207 } 208 209 210 void 211 Transformable::InverseTransform(BPoint* point) const 212 { 213 if (point) { 214 double x = point->x; 215 double y = point->y; 216 217 inverse_transform(&x, &y); 218 219 point->x = x; 220 point->y = y; 221 } 222 } 223 224 225 BPoint 226 Transformable::InverseTransform(const BPoint& point) const 227 { 228 BPoint p(point); 229 InverseTransform(&p); 230 return p; 231 } 232 233 234 BRect 235 Transformable::TransformBounds(const BRect& bounds) const 236 { 237 if (bounds.IsValid()) { 238 BPoint lt(bounds.left, bounds.top); 239 BPoint rt(bounds.right, bounds.top); 240 BPoint lb(bounds.left, bounds.bottom); 241 BPoint rb(bounds.right, bounds.bottom); 242 243 Transform(<); 244 Transform(&rt); 245 Transform(&lb); 246 Transform(&rb); 247 248 return BRect(floorf(min4(lt.x, rt.x, lb.x, rb.x)), 249 floorf(min4(lt.y, rt.y, lb.y, rb.y)), 250 ceilf(max4(lt.x, rt.x, lb.x, rb.x)), 251 ceilf(max4(lt.y, rt.y, lb.y, rb.y))); 252 } 253 return bounds; 254 } 255 256 257 bool 258 Transformable::IsTranslationOnly() const 259 { 260 double matrix[6]; 261 store_to(matrix); 262 return matrix[0] == 1.0 && matrix[1] == 0.0 263 && matrix[2] == 0.0 && matrix[3] == 1.0; 264 } 265 266 267 268 void 269 Transformable::TranslateBy(BPoint offset) 270 { 271 if (offset.x != 0.0 || offset.y != 0.0) { 272 multiply(agg::trans_affine_translation(offset.x, offset.y)); 273 TransformationChanged(); 274 } 275 } 276 277 278 void 279 Transformable::RotateBy(BPoint origin, double radians) 280 { 281 if (radians != 0.0) { 282 multiply(agg::trans_affine_translation(-origin.x, -origin.y)); 283 multiply(agg::trans_affine_rotation(radians)); 284 multiply(agg::trans_affine_translation(origin.x, origin.y)); 285 TransformationChanged(); 286 } 287 } 288 289 290 void 291 Transformable::ScaleBy(BPoint origin, double xScale, double yScale) 292 { 293 if (xScale != 1.0 || yScale != 1.0) { 294 multiply(agg::trans_affine_translation(-origin.x, -origin.y)); 295 multiply(agg::trans_affine_scaling(xScale, yScale)); 296 multiply(agg::trans_affine_translation(origin.x, origin.y)); 297 TransformationChanged(); 298 } 299 } 300 301 302 void 303 Transformable::ShearBy(BPoint origin, double xShear, double yShear) 304 { 305 if (xShear != 0.0 || yShear != 0.0) { 306 multiply(agg::trans_affine_translation(-origin.x, -origin.y)); 307 multiply(agg::trans_affine_skewing(xShear, yShear)); 308 multiply(agg::trans_affine_translation(origin.x, origin.y)); 309 TransformationChanged(); 310 } 311 } 312