SkPoint.h revision 0bb18bb264b26afca45452910437c09445e23a3c
1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#ifndef SkPoint_DEFINED 11#define SkPoint_DEFINED 12 13#include "SkMath.h" 14#include "SkScalar.h" 15 16/** \struct SkIPoint 17 18 SkIPoint holds two 32 bit integer coordinates 19*/ 20struct SkIPoint { 21 int32_t fX, fY; 22 23 static SkIPoint Make(int32_t x, int32_t y) { 24 SkIPoint pt; 25 pt.set(x, y); 26 return pt; 27 } 28 29 int32_t x() const { return fX; } 30 int32_t y() const { return fY; } 31 void setX(int32_t x) { fX = x; } 32 void setY(int32_t y) { fY = y; } 33 34 /** 35 * Returns true iff fX and fY are both zero. 36 */ 37 bool isZero() const { return (fX | fY) == 0; } 38 39 /** 40 * Set both fX and fY to zero. Same as set(0, 0) 41 */ 42 void setZero() { fX = fY = 0; } 43 44 /** Set the x and y values of the point. */ 45 void set(int32_t x, int32_t y) { fX = x; fY = y; } 46 47 /** Rotate the point clockwise, writing the new point into dst 48 It is legal for dst == this 49 */ 50 void rotateCW(SkIPoint* dst) const; 51 52 /** Rotate the point clockwise, writing the new point back into the point 53 */ 54 55 void rotateCW() { this->rotateCW(this); } 56 57 /** Rotate the point counter-clockwise, writing the new point into dst. 58 It is legal for dst == this 59 */ 60 void rotateCCW(SkIPoint* dst) const; 61 62 /** Rotate the point counter-clockwise, writing the new point back into 63 the point 64 */ 65 void rotateCCW() { this->rotateCCW(this); } 66 67 /** Negate the X and Y coordinates of the point. 68 */ 69 void negate() { fX = -fX; fY = -fY; } 70 71 /** Return a new point whose X and Y coordinates are the negative of the 72 original point's 73 */ 74 SkIPoint operator-() const { 75 SkIPoint neg; 76 neg.fX = -fX; 77 neg.fY = -fY; 78 return neg; 79 } 80 81 /** Add v's coordinates to this point's */ 82 void operator+=(const SkIPoint& v) { 83 fX += v.fX; 84 fY += v.fY; 85 } 86 87 /** Subtract v's coordinates from this point's */ 88 void operator-=(const SkIPoint& v) { 89 fX -= v.fX; 90 fY -= v.fY; 91 } 92 93 /** Returns true if the point's coordinates equal (x,y) */ 94 bool equals(int32_t x, int32_t y) const { 95 return fX == x && fY == y; 96 } 97 98 friend bool operator==(const SkIPoint& a, const SkIPoint& b) { 99 return a.fX == b.fX && a.fY == b.fY; 100 } 101 102 friend bool operator!=(const SkIPoint& a, const SkIPoint& b) { 103 return a.fX != b.fX || a.fY != b.fY; 104 } 105 106 /** Returns a new point whose coordinates are the difference between 107 a and b (i.e. a - b) 108 */ 109 friend SkIPoint operator-(const SkIPoint& a, const SkIPoint& b) { 110 SkIPoint v; 111 v.set(a.fX - b.fX, a.fY - b.fY); 112 return v; 113 } 114 115 /** Returns a new point whose coordinates are the sum of a and b (a + b) 116 */ 117 friend SkIPoint operator+(const SkIPoint& a, const SkIPoint& b) { 118 SkIPoint v; 119 v.set(a.fX + b.fX, a.fY + b.fY); 120 return v; 121 } 122 123 /** Returns the dot product of a and b, treating them as 2D vectors 124 */ 125 static int32_t DotProduct(const SkIPoint& a, const SkIPoint& b) { 126 return a.fX * b.fX + a.fY * b.fY; 127 } 128 129 /** Returns the cross product of a and b, treating them as 2D vectors 130 */ 131 static int32_t CrossProduct(const SkIPoint& a, const SkIPoint& b) { 132 return a.fX * b.fY - a.fY * b.fX; 133 } 134}; 135 136struct SK_API SkPoint { 137 SkScalar fX, fY; 138 139 static SkPoint Make(SkScalar x, SkScalar y) { 140 SkPoint pt; 141 pt.set(x, y); 142 return pt; 143 } 144 145 SkScalar x() const { return fX; } 146 SkScalar y() const { return fY; } 147 148 /** 149 * Returns true iff fX and fY are both zero. 150 */ 151 bool isZero() const { return (0 == fX) & (0 == fY); } 152 153 /** Set the point's X and Y coordinates */ 154 void set(SkScalar x, SkScalar y) { fX = x; fY = y; } 155 156 /** Set the point's X and Y coordinates by automatically promoting (x,y) to 157 SkScalar values. 158 */ 159 void iset(int32_t x, int32_t y) { 160 fX = SkIntToScalar(x); 161 fY = SkIntToScalar(y); 162 } 163 164 /** Set the point's X and Y coordinates by automatically promoting p's 165 coordinates to SkScalar values. 166 */ 167 void iset(const SkIPoint& p) { 168 fX = SkIntToScalar(p.fX); 169 fY = SkIntToScalar(p.fY); 170 } 171 172 void setAbs(const SkPoint& pt) { 173 fX = SkScalarAbs(pt.fX); 174 fY = SkScalarAbs(pt.fY); 175 } 176 177 // counter-clockwise fan 178 void setIRectFan(int l, int t, int r, int b) { 179 SkPoint* v = this; 180 v[0].set(SkIntToScalar(l), SkIntToScalar(t)); 181 v[1].set(SkIntToScalar(l), SkIntToScalar(b)); 182 v[2].set(SkIntToScalar(r), SkIntToScalar(b)); 183 v[3].set(SkIntToScalar(r), SkIntToScalar(t)); 184 } 185 void setIRectFan(int l, int t, int r, int b, size_t stride); 186 187 // counter-clockwise fan 188 void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { 189 SkPoint* v = this; 190 v[0].set(l, t); 191 v[1].set(l, b); 192 v[2].set(r, b); 193 v[3].set(r, t); 194 } 195 void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride); 196 197 static void Offset(SkPoint points[], int count, const SkPoint& offset) { 198 Offset(points, count, offset.fX, offset.fY); 199 } 200 201 static void Offset(SkPoint points[], int count, SkScalar dx, SkScalar dy) { 202 for (int i = 0; i < count; ++i) { 203 points[i].offset(dx, dy); 204 } 205 } 206 207 void offset(SkScalar dx, SkScalar dy) { 208 fX += dx; 209 fY += dy; 210 } 211 212 /** Return the euclidian distance from (0,0) to the point 213 */ 214 SkScalar length() const { return SkPoint::Length(fX, fY); } 215 SkScalar distanceToOrigin() const { return this->length(); } 216 217 /** 218 * Return true if the computed length of the vector is >= the internal 219 * tolerance (used to avoid dividing by tiny values). 220 */ 221 static bool CanNormalize(SkScalar dx, SkScalar dy) 222#ifdef SK_SCALAR_IS_FLOAT 223 // Simple enough (and performance critical sometimes) so we inline it. 224 { return (dx*dx + dy*dy) > (SK_ScalarNearlyZero * SK_ScalarNearlyZero); } 225#else 226 ; 227#endif 228 229 bool canNormalize() const { 230 return CanNormalize(fX, fY); 231 } 232 233 /** Set the point (vector) to be unit-length in the same direction as it 234 already points. If the point has a degenerate length (i.e. nearly 0) 235 then return false and do nothing; otherwise return true. 236 */ 237 bool normalize(); 238 239 /** Set the point (vector) to be unit-length in the same direction as the 240 x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0) 241 then return false and do nothing, otherwise return true. 242 */ 243 bool setNormalize(SkScalar x, SkScalar y); 244 245 /** Scale the point (vector) to have the specified length, and return that 246 length. If the original length is degenerately small (nearly zero), 247 do nothing and return false, otherwise return true. 248 */ 249 bool setLength(SkScalar length); 250 251 /** Set the point (vector) to have the specified length in the same 252 direction as (x,y). If the vector (x,y) has a degenerate length 253 (i.e. nearly 0) then return false and do nothing, otherwise return true. 254 */ 255 bool setLength(SkScalar x, SkScalar y, SkScalar length); 256 257 /** Scale the point's coordinates by scale, writing the answer into dst. 258 It is legal for dst == this. 259 */ 260 void scale(SkScalar scale, SkPoint* dst) const; 261 262 /** Scale the point's coordinates by scale, writing the answer back into 263 the point. 264 */ 265 void scale(SkScalar value) { this->scale(value, this); } 266 267 /** Rotate the point clockwise by 90 degrees, writing the answer into dst. 268 It is legal for dst == this. 269 */ 270 void rotateCW(SkPoint* dst) const; 271 272 /** Rotate the point clockwise by 90 degrees, writing the answer back into 273 the point. 274 */ 275 void rotateCW() { this->rotateCW(this); } 276 277 /** Rotate the point counter-clockwise by 90 degrees, writing the answer 278 into dst. It is legal for dst == this. 279 */ 280 void rotateCCW(SkPoint* dst) const; 281 282 /** Rotate the point counter-clockwise by 90 degrees, writing the answer 283 back into the point. 284 */ 285 void rotateCCW() { this->rotateCCW(this); } 286 287 /** Negate the point's coordinates 288 */ 289 void negate() { 290 fX = -fX; 291 fY = -fY; 292 } 293 294 /** Returns a new point whose coordinates are the negative of the point's 295 */ 296 SkPoint operator-() const { 297 SkPoint neg; 298 neg.fX = -fX; 299 neg.fY = -fY; 300 return neg; 301 } 302 303 /** Add v's coordinates to the point's 304 */ 305 void operator+=(const SkPoint& v) { 306 fX += v.fX; 307 fY += v.fY; 308 } 309 310 /** Subtract v's coordinates from the point's 311 */ 312 void operator-=(const SkPoint& v) { 313 fX -= v.fX; 314 fY -= v.fY; 315 } 316 317 /** 318 * Returns true if both X and Y are finite (not infinity or NaN) 319 */ 320 bool isFinite() const { 321#ifdef SK_SCALAR_IS_FLOAT 322 SkScalar accum = 0; 323 accum *= fX; 324 accum *= fY; 325 326 // accum is either NaN or it is finite (zero). 327 SkASSERT(0 == accum || !(accum == accum)); 328 329 // value==value will be true iff value is not NaN 330 // TODO: is it faster to say !accum or accum==accum? 331 return accum == accum; 332#else 333 // use bit-or for speed, since we don't care about short-circuting the 334 // tests, and we expect the common case will be that we need to check all. 335 int isNaN = (SK_FixedNaN == fX) | (SK_FixedNaN == fX)); 336 return !isNaN; 337#endif 338 } 339 340 /** Returns true if the point's coordinates equal (x,y) 341 */ 342 bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; } 343 344 friend bool operator==(const SkPoint& a, const SkPoint& b) { 345 return a.fX == b.fX && a.fY == b.fY; 346 } 347 348 friend bool operator!=(const SkPoint& a, const SkPoint& b) { 349 return a.fX != b.fX || a.fY != b.fY; 350 } 351 352 /** Return true if this point and the given point are far enough apart 353 such that a vector between them would be non-degenerate. 354 355 WARNING: Unlike the deprecated version of equalsWithinTolerance(), 356 this method does not use componentwise comparison. Instead, it 357 uses a comparison designed to match judgments elsewhere regarding 358 degeneracy ("points A and B are so close that the vector between them 359 is essentially zero"). 360 */ 361 bool equalsWithinTolerance(const SkPoint& p) const { 362 return !CanNormalize(fX - p.fX, fY - p.fY); 363 } 364 365 /** DEPRECATED: Return true if this and the given point are componentwise 366 within tolerance "tol". 367 368 WARNING: There is no guarantee that the result will reflect judgments 369 elsewhere regarding degeneracy ("points A and B are so close that the 370 vector between them is essentially zero"). 371 */ 372 bool equalsWithinTolerance(const SkPoint& p, SkScalar tol) const { 373 return SkScalarNearlyZero(fX - p.fX, tol) 374 && SkScalarNearlyZero(fY - p.fY, tol); 375 } 376 377 /** Returns a new point whose coordinates are the difference between 378 a's and b's (a - b) 379 */ 380 friend SkPoint operator-(const SkPoint& a, const SkPoint& b) { 381 SkPoint v; 382 v.set(a.fX - b.fX, a.fY - b.fY); 383 return v; 384 } 385 386 /** Returns a new point whose coordinates are the sum of a's and b's (a + b) 387 */ 388 friend SkPoint operator+(const SkPoint& a, const SkPoint& b) { 389 SkPoint v; 390 v.set(a.fX + b.fX, a.fY + b.fY); 391 return v; 392 } 393 394 /** Returns the euclidian distance from (0,0) to (x,y) 395 */ 396 static SkScalar Length(SkScalar x, SkScalar y); 397 398 /** Normalize pt, returning its previous length. If the prev length is too 399 small (degenerate), return 0 and leave pt unchanged. This uses the same 400 tolerance as CanNormalize. 401 402 Note that this method may be significantly more expensive than 403 the non-static normalize(), because it has to return the previous length 404 of the point. If you don't need the previous length, call the 405 non-static normalize() method instead. 406 */ 407 static SkScalar Normalize(SkPoint* pt); 408 409 /** Returns the euclidian distance between a and b 410 */ 411 static SkScalar Distance(const SkPoint& a, const SkPoint& b) { 412 return Length(a.fX - b.fX, a.fY - b.fY); 413 } 414 415 /** Returns the dot product of a and b, treating them as 2D vectors 416 */ 417 static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) { 418 return SkScalarMul(a.fX, b.fX) + SkScalarMul(a.fY, b.fY); 419 } 420 421 /** Returns the cross product of a and b, treating them as 2D vectors 422 */ 423 static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) { 424 return SkScalarMul(a.fX, b.fY) - SkScalarMul(a.fY, b.fX); 425 } 426 427 SkScalar cross(const SkPoint& vec) const { 428 return CrossProduct(*this, vec); 429 } 430 431 SkScalar dot(const SkPoint& vec) const { 432 return DotProduct(*this, vec); 433 } 434 435 SkScalar lengthSqd() const { 436 return DotProduct(*this, *this); 437 } 438 439 SkScalar distanceToSqd(const SkPoint& pt) const { 440 SkScalar dx = fX - pt.fX; 441 SkScalar dy = fY - pt.fY; 442 return SkScalarMul(dx, dx) + SkScalarMul(dy, dy); 443 } 444 445 /** 446 * The side of a point relative to a line. If the line is from a to b then 447 * the values are consistent with the sign of (b-a) cross (pt-a) 448 */ 449 enum Side { 450 kLeft_Side = -1, 451 kOn_Side = 0, 452 kRight_Side = 1 453 }; 454 455 /** 456 * Returns the squared distance to the infinite line between two pts. Also 457 * optionally returns the side of the line that the pt falls on (looking 458 * along line from a to b) 459 */ 460 SkScalar distanceToLineBetweenSqd(const SkPoint& a, 461 const SkPoint& b, 462 Side* side = NULL) const; 463 464 /** 465 * Returns the distance to the infinite line between two pts. Also 466 * optionally returns the side of the line that the pt falls on (looking 467 * along the line from a to b) 468 */ 469 SkScalar distanceToLineBetween(const SkPoint& a, 470 const SkPoint& b, 471 Side* side = NULL) const { 472 return SkScalarSqrt(this->distanceToLineBetweenSqd(a, b, side)); 473 } 474 475 /** 476 * Returns the squared distance to the line segment between pts a and b 477 */ 478 SkScalar distanceToLineSegmentBetweenSqd(const SkPoint& a, 479 const SkPoint& b) const; 480 481 /** 482 * Returns the distance to the line segment between pts a and b. 483 */ 484 SkScalar distanceToLineSegmentBetween(const SkPoint& a, 485 const SkPoint& b) const { 486 return SkScalarSqrt(this->distanceToLineSegmentBetweenSqd(a, b)); 487 } 488 489 /** 490 * Make this vector be orthogonal to vec. Looking down vec the 491 * new vector will point in direction indicated by side (which 492 * must be kLeft_Side or kRight_Side). 493 */ 494 void setOrthog(const SkPoint& vec, Side side = kLeft_Side) { 495 // vec could be this 496 SkScalar tmp = vec.fX; 497 if (kRight_Side == side) { 498 fX = -vec.fY; 499 fY = tmp; 500 } else { 501 SkASSERT(kLeft_Side == side); 502 fX = vec.fY; 503 fY = -tmp; 504 } 505 } 506}; 507 508typedef SkPoint SkVector; 509 510#endif 511