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