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