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*/
18struct 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*/
40struct 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
156struct 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    void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride);
216
217    static void Offset(SkPoint points[], int count, const SkPoint& offset) {
218        Offset(points, count, offset.fX, offset.fY);
219    }
220
221    static void Offset(SkPoint points[], int count, SkScalar dx, SkScalar dy) {
222        for (int i = 0; i < count; ++i) {
223            points[i].offset(dx, dy);
224        }
225    }
226
227    void offset(SkScalar dx, SkScalar dy) {
228        fX += dx;
229        fY += dy;
230    }
231
232    /** Return the euclidian distance from (0,0) to the point
233    */
234    SkScalar length() const { return SkPoint::Length(fX, fY); }
235    SkScalar distanceToOrigin() const { return this->length(); }
236
237    /**
238     *  Return true if the computed length of the vector is >= the internal
239     *  tolerance (used to avoid dividing by tiny values).
240     */
241    static bool CanNormalize(SkScalar dx, SkScalar dy) {
242        // Simple enough (and performance critical sometimes) so we inline it.
243        return (dx*dx + dy*dy) > (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
244    }
245
246    bool canNormalize() const {
247        return CanNormalize(fX, fY);
248    }
249
250    /** Set the point (vector) to be unit-length in the same direction as it
251        already points.  If the point has a degenerate length (i.e. nearly 0)
252        then return false and do nothing; otherwise return true.
253    */
254    bool normalize();
255
256    /** Set the point (vector) to be unit-length in the same direction as the
257        x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0)
258        then return false and do nothing, otherwise return true.
259    */
260    bool setNormalize(SkScalar x, SkScalar y);
261
262    /** Scale the point (vector) to have the specified length, and return that
263        length. If the original length is degenerately small (nearly zero),
264        do nothing and return false, otherwise return true.
265    */
266    bool setLength(SkScalar length);
267
268    /** Set the point (vector) to have the specified length in the same
269     direction as (x,y). If the vector (x,y) has a degenerate length
270     (i.e. nearly 0) then return false and do nothing, otherwise return true.
271    */
272    bool setLength(SkScalar x, SkScalar y, SkScalar length);
273
274    /** Same as setLength, but favoring speed over accuracy.
275    */
276    bool setLengthFast(SkScalar length);
277
278    /** Same as setLength, but favoring speed over accuracy.
279    */
280    bool setLengthFast(SkScalar x, SkScalar y, SkScalar length);
281
282    /** Scale the point's coordinates by scale, writing the answer into dst.
283        It is legal for dst == this.
284    */
285    void scale(SkScalar scale, SkPoint* dst) const;
286
287    /** Scale the point's coordinates by scale, writing the answer back into
288        the point.
289    */
290    void scale(SkScalar value) { this->scale(value, this); }
291
292    /** Rotate the point clockwise by 90 degrees, writing the answer into dst.
293        It is legal for dst == this.
294    */
295    void rotateCW(SkPoint* dst) const;
296
297    /** Rotate the point clockwise by 90 degrees, writing the answer back into
298        the point.
299    */
300    void rotateCW() { this->rotateCW(this); }
301
302    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
303        into dst. It is legal for dst == this.
304    */
305    void rotateCCW(SkPoint* dst) const;
306
307    /** Rotate the point counter-clockwise by 90 degrees, writing the answer
308        back into the point.
309    */
310    void rotateCCW() { this->rotateCCW(this); }
311
312    /** Negate the point's coordinates
313    */
314    void negate() {
315        fX = -fX;
316        fY = -fY;
317    }
318
319    /** Returns a new point whose coordinates are the negative of the point's
320    */
321    SkPoint operator-() const {
322        SkPoint neg;
323        neg.fX = -fX;
324        neg.fY = -fY;
325        return neg;
326    }
327
328    /** Add v's coordinates to the point's
329    */
330    void operator+=(const SkPoint& v) {
331        fX += v.fX;
332        fY += v.fY;
333    }
334
335    /** Subtract v's coordinates from the point's
336    */
337    void operator-=(const SkPoint& v) {
338        fX -= v.fX;
339        fY -= v.fY;
340    }
341
342    /**
343     *  Returns true if both X and Y are finite (not infinity or NaN)
344     */
345    bool isFinite() const {
346        SkScalar accum = 0;
347        accum *= fX;
348        accum *= fY;
349
350        // accum is either NaN or it is finite (zero).
351        SkASSERT(0 == accum || !(accum == accum));
352
353        // value==value will be true iff value is not NaN
354        // TODO: is it faster to say !accum or accum==accum?
355        return accum == accum;
356    }
357
358    /**
359     *  Returns true if the point's coordinates equal (x,y)
360     */
361    bool equals(SkScalar x, SkScalar y) const {
362        return fX == x && fY == y;
363    }
364
365    friend bool operator==(const SkPoint& a, const SkPoint& b) {
366        return a.fX == b.fX && a.fY == b.fY;
367    }
368
369    friend bool operator!=(const SkPoint& a, const SkPoint& b) {
370        return a.fX != b.fX || a.fY != b.fY;
371    }
372
373    /** Return true if this point and the given point are far enough apart
374        such that a vector between them would be non-degenerate.
375
376        WARNING: Unlike the explicit tolerance version,
377        this method does not use componentwise comparison.  Instead, it
378        uses a comparison designed to match judgments elsewhere regarding
379        degeneracy ("points A and B are so close that the vector between them
380        is essentially zero").
381    */
382    bool equalsWithinTolerance(const SkPoint& p) const {
383        return !CanNormalize(fX - p.fX, fY - p.fY);
384    }
385
386    /** WARNING: There is no guarantee that the result will reflect judgments
387        elsewhere regarding degeneracy ("points A and B are so close that the
388        vector between them is essentially zero").
389    */
390    bool equalsWithinTolerance(const SkPoint& p, SkScalar tol) const {
391        return SkScalarNearlyZero(fX - p.fX, tol)
392               && SkScalarNearlyZero(fY - p.fY, tol);
393    }
394
395    /** Returns a new point whose coordinates are the difference between
396        a's and b's (a - b)
397    */
398    friend SkPoint operator-(const SkPoint& a, const SkPoint& b) {
399        SkPoint v;
400        v.set(a.fX - b.fX, a.fY - b.fY);
401        return v;
402    }
403
404    /** Returns a new point whose coordinates are the sum of a's and b's (a + b)
405    */
406    friend SkPoint operator+(const SkPoint& a, const SkPoint& b) {
407        SkPoint v;
408        v.set(a.fX + b.fX, a.fY + b.fY);
409        return v;
410    }
411
412    /** Returns the euclidian distance from (0,0) to (x,y)
413    */
414    static SkScalar Length(SkScalar x, SkScalar y);
415
416    /** Normalize pt, returning its previous length. If the prev length is too
417        small (degenerate), return 0 and leave pt unchanged. This uses the same
418        tolerance as CanNormalize.
419
420        Note that this method may be significantly more expensive than
421        the non-static normalize(), because it has to return the previous length
422        of the point.  If you don't need the previous length, call the
423        non-static normalize() method instead.
424     */
425    static SkScalar Normalize(SkPoint* pt);
426
427    /** Returns the euclidian distance between a and b
428    */
429    static SkScalar Distance(const SkPoint& a, const SkPoint& b) {
430        return Length(a.fX - b.fX, a.fY - b.fY);
431    }
432
433    /** Returns the dot product of a and b, treating them as 2D vectors
434    */
435    static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) {
436        return a.fX * b.fX + a.fY * b.fY;
437    }
438
439    /** Returns the cross product of a and b, treating them as 2D vectors
440    */
441    static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) {
442        return a.fX * b.fY - a.fY * b.fX;
443    }
444
445    SkScalar cross(const SkPoint& vec) const {
446        return CrossProduct(*this, vec);
447    }
448
449    SkScalar dot(const SkPoint& vec) const {
450        return DotProduct(*this, vec);
451    }
452
453    SkScalar lengthSqd() const {
454        return DotProduct(*this, *this);
455    }
456
457    SkScalar distanceToSqd(const SkPoint& pt) const {
458        SkScalar dx = fX - pt.fX;
459        SkScalar dy = fY - pt.fY;
460        return dx * dx + dy * dy;
461    }
462
463    /**
464     * The side of a point relative to a line. If the line is from a to b then
465     * the values are consistent with the sign of (b-a) cross (pt-a)
466     */
467    enum Side {
468        kLeft_Side  = -1,
469        kOn_Side    =  0,
470        kRight_Side =  1
471    };
472
473    /**
474     * Returns the squared distance to the infinite line between two pts. Also
475     * optionally returns the side of the line that the pt falls on (looking
476     * along line from a to b)
477     */
478    SkScalar distanceToLineBetweenSqd(const SkPoint& a,
479                                      const SkPoint& b,
480                                      Side* side = NULL) const;
481
482    /**
483     * Returns the distance to the infinite line between two pts. Also
484     * optionally returns the side of the line that the pt falls on (looking
485     * along the line from a to b)
486     */
487    SkScalar distanceToLineBetween(const SkPoint& a,
488                                   const SkPoint& b,
489                                   Side* side = NULL) const {
490        return SkScalarSqrt(this->distanceToLineBetweenSqd(a, b, side));
491    }
492
493    /**
494     * Returns the squared distance to the line segment between pts a and b
495     */
496    SkScalar distanceToLineSegmentBetweenSqd(const SkPoint& a,
497                                             const SkPoint& b) const;
498
499    /**
500     * Returns the distance to the line segment between pts a and b.
501     */
502    SkScalar distanceToLineSegmentBetween(const SkPoint& a,
503                                          const SkPoint& b) const {
504        return SkScalarSqrt(this->distanceToLineSegmentBetweenSqd(a, b));
505    }
506
507    /**
508     * Make this vector be orthogonal to vec. Looking down vec the
509     * new vector will point in direction indicated by side (which
510     * must be kLeft_Side or kRight_Side).
511     */
512    void setOrthog(const SkPoint& vec, Side side = kLeft_Side) {
513        // vec could be this
514        SkScalar tmp = vec.fX;
515        if (kRight_Side == side) {
516            fX = -vec.fY;
517            fY = tmp;
518        } else {
519            SkASSERT(kLeft_Side == side);
520            fX = vec.fY;
521            fY = -tmp;
522        }
523    }
524
525    /**
526     *  cast-safe way to treat the point as an array of (2) SkScalars.
527     */
528    const SkScalar* asScalars() const { return &fX; }
529};
530
531typedef SkPoint SkVector;
532
533#endif
534