michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #ifndef SkPoint_DEFINED michael@0: #define SkPoint_DEFINED michael@0: michael@0: #include "SkMath.h" michael@0: #include "SkScalar.h" michael@0: michael@0: /** \struct SkIPoint michael@0: michael@0: SkIPoint holds two 32 bit integer coordinates michael@0: */ michael@0: struct SkIPoint { michael@0: int32_t fX, fY; michael@0: michael@0: static SkIPoint Make(int32_t x, int32_t y) { michael@0: SkIPoint pt; michael@0: pt.set(x, y); michael@0: return pt; michael@0: } michael@0: michael@0: int32_t x() const { return fX; } michael@0: int32_t y() const { return fY; } michael@0: void setX(int32_t x) { fX = x; } michael@0: void setY(int32_t y) { fY = y; } michael@0: michael@0: /** michael@0: * Returns true iff fX and fY are both zero. michael@0: */ michael@0: bool isZero() const { return (fX | fY) == 0; } michael@0: michael@0: /** michael@0: * Set both fX and fY to zero. Same as set(0, 0) michael@0: */ michael@0: void setZero() { fX = fY = 0; } michael@0: michael@0: /** Set the x and y values of the point. */ michael@0: void set(int32_t x, int32_t y) { fX = x; fY = y; } michael@0: michael@0: /** Rotate the point clockwise, writing the new point into dst michael@0: It is legal for dst == this michael@0: */ michael@0: void rotateCW(SkIPoint* dst) const; michael@0: michael@0: /** Rotate the point clockwise, writing the new point back into the point michael@0: */ michael@0: michael@0: void rotateCW() { this->rotateCW(this); } michael@0: michael@0: /** Rotate the point counter-clockwise, writing the new point into dst. michael@0: It is legal for dst == this michael@0: */ michael@0: void rotateCCW(SkIPoint* dst) const; michael@0: michael@0: /** Rotate the point counter-clockwise, writing the new point back into michael@0: the point michael@0: */ michael@0: void rotateCCW() { this->rotateCCW(this); } michael@0: michael@0: /** Negate the X and Y coordinates of the point. michael@0: */ michael@0: void negate() { fX = -fX; fY = -fY; } michael@0: michael@0: /** Return a new point whose X and Y coordinates are the negative of the michael@0: original point's michael@0: */ michael@0: SkIPoint operator-() const { michael@0: SkIPoint neg; michael@0: neg.fX = -fX; michael@0: neg.fY = -fY; michael@0: return neg; michael@0: } michael@0: michael@0: /** Add v's coordinates to this point's */ michael@0: void operator+=(const SkIPoint& v) { michael@0: fX += v.fX; michael@0: fY += v.fY; michael@0: } michael@0: michael@0: /** Subtract v's coordinates from this point's */ michael@0: void operator-=(const SkIPoint& v) { michael@0: fX -= v.fX; michael@0: fY -= v.fY; michael@0: } michael@0: michael@0: /** Returns true if the point's coordinates equal (x,y) */ michael@0: bool equals(int32_t x, int32_t y) const { michael@0: return fX == x && fY == y; michael@0: } michael@0: michael@0: friend bool operator==(const SkIPoint& a, const SkIPoint& b) { michael@0: return a.fX == b.fX && a.fY == b.fY; michael@0: } michael@0: michael@0: friend bool operator!=(const SkIPoint& a, const SkIPoint& b) { michael@0: return a.fX != b.fX || a.fY != b.fY; michael@0: } michael@0: michael@0: /** Returns a new point whose coordinates are the difference between michael@0: a and b (i.e. a - b) michael@0: */ michael@0: friend SkIPoint operator-(const SkIPoint& a, const SkIPoint& b) { michael@0: SkIPoint v; michael@0: v.set(a.fX - b.fX, a.fY - b.fY); michael@0: return v; michael@0: } michael@0: michael@0: /** Returns a new point whose coordinates are the sum of a and b (a + b) michael@0: */ michael@0: friend SkIPoint operator+(const SkIPoint& a, const SkIPoint& b) { michael@0: SkIPoint v; michael@0: v.set(a.fX + b.fX, a.fY + b.fY); michael@0: return v; michael@0: } michael@0: michael@0: /** Returns the dot product of a and b, treating them as 2D vectors michael@0: */ michael@0: static int32_t DotProduct(const SkIPoint& a, const SkIPoint& b) { michael@0: return a.fX * b.fX + a.fY * b.fY; michael@0: } michael@0: michael@0: /** Returns the cross product of a and b, treating them as 2D vectors michael@0: */ michael@0: static int32_t CrossProduct(const SkIPoint& a, const SkIPoint& b) { michael@0: return a.fX * b.fY - a.fY * b.fX; michael@0: } michael@0: }; michael@0: michael@0: struct SK_API SkPoint { michael@0: SkScalar fX, fY; michael@0: michael@0: static SkPoint Make(SkScalar x, SkScalar y) { michael@0: SkPoint pt; michael@0: pt.set(x, y); michael@0: return pt; michael@0: } michael@0: michael@0: SkScalar x() const { return fX; } michael@0: SkScalar y() const { return fY; } michael@0: michael@0: /** michael@0: * Returns true iff fX and fY are both zero. michael@0: */ michael@0: bool isZero() const { return (0 == fX) & (0 == fY); } michael@0: michael@0: /** Set the point's X and Y coordinates */ michael@0: void set(SkScalar x, SkScalar y) { fX = x; fY = y; } michael@0: michael@0: /** Set the point's X and Y coordinates by automatically promoting (x,y) to michael@0: SkScalar values. michael@0: */ michael@0: void iset(int32_t x, int32_t y) { michael@0: fX = SkIntToScalar(x); michael@0: fY = SkIntToScalar(y); michael@0: } michael@0: michael@0: /** Set the point's X and Y coordinates by automatically promoting p's michael@0: coordinates to SkScalar values. michael@0: */ michael@0: void iset(const SkIPoint& p) { michael@0: fX = SkIntToScalar(p.fX); michael@0: fY = SkIntToScalar(p.fY); michael@0: } michael@0: michael@0: void setAbs(const SkPoint& pt) { michael@0: fX = SkScalarAbs(pt.fX); michael@0: fY = SkScalarAbs(pt.fY); michael@0: } michael@0: michael@0: // counter-clockwise fan michael@0: void setIRectFan(int l, int t, int r, int b) { michael@0: SkPoint* v = this; michael@0: v[0].set(SkIntToScalar(l), SkIntToScalar(t)); michael@0: v[1].set(SkIntToScalar(l), SkIntToScalar(b)); michael@0: v[2].set(SkIntToScalar(r), SkIntToScalar(b)); michael@0: v[3].set(SkIntToScalar(r), SkIntToScalar(t)); michael@0: } michael@0: void setIRectFan(int l, int t, int r, int b, size_t stride); michael@0: michael@0: // counter-clockwise fan michael@0: void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { michael@0: SkPoint* v = this; michael@0: v[0].set(l, t); michael@0: v[1].set(l, b); michael@0: v[2].set(r, b); michael@0: v[3].set(r, t); michael@0: } michael@0: void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b, size_t stride); michael@0: michael@0: static void Offset(SkPoint points[], int count, const SkPoint& offset) { michael@0: Offset(points, count, offset.fX, offset.fY); michael@0: } michael@0: michael@0: static void Offset(SkPoint points[], int count, SkScalar dx, SkScalar dy) { michael@0: for (int i = 0; i < count; ++i) { michael@0: points[i].offset(dx, dy); michael@0: } michael@0: } michael@0: michael@0: void offset(SkScalar dx, SkScalar dy) { michael@0: fX += dx; michael@0: fY += dy; michael@0: } michael@0: michael@0: /** Return the euclidian distance from (0,0) to the point michael@0: */ michael@0: SkScalar length() const { return SkPoint::Length(fX, fY); } michael@0: SkScalar distanceToOrigin() const { return this->length(); } michael@0: michael@0: /** michael@0: * Return true if the computed length of the vector is >= the internal michael@0: * tolerance (used to avoid dividing by tiny values). michael@0: */ michael@0: static bool CanNormalize(SkScalar dx, SkScalar dy) { michael@0: // Simple enough (and performance critical sometimes) so we inline it. michael@0: return (dx*dx + dy*dy) > (SK_ScalarNearlyZero * SK_ScalarNearlyZero); michael@0: } michael@0: michael@0: bool canNormalize() const { michael@0: return CanNormalize(fX, fY); michael@0: } michael@0: michael@0: /** Set the point (vector) to be unit-length in the same direction as it michael@0: already points. If the point has a degenerate length (i.e. nearly 0) michael@0: then return false and do nothing; otherwise return true. michael@0: */ michael@0: bool normalize(); michael@0: michael@0: /** Set the point (vector) to be unit-length in the same direction as the michael@0: x,y params. If the vector (x,y) has a degenerate length (i.e. nearly 0) michael@0: then return false and do nothing, otherwise return true. michael@0: */ michael@0: bool setNormalize(SkScalar x, SkScalar y); michael@0: michael@0: /** Scale the point (vector) to have the specified length, and return that michael@0: length. If the original length is degenerately small (nearly zero), michael@0: do nothing and return false, otherwise return true. michael@0: */ michael@0: bool setLength(SkScalar length); michael@0: michael@0: /** Set the point (vector) to have the specified length in the same michael@0: direction as (x,y). If the vector (x,y) has a degenerate length michael@0: (i.e. nearly 0) then return false and do nothing, otherwise return true. michael@0: */ michael@0: bool setLength(SkScalar x, SkScalar y, SkScalar length); michael@0: michael@0: /** Same as setLength, but favoring speed over accuracy. michael@0: */ michael@0: bool setLengthFast(SkScalar length); michael@0: michael@0: /** Same as setLength, but favoring speed over accuracy. michael@0: */ michael@0: bool setLengthFast(SkScalar x, SkScalar y, SkScalar length); michael@0: michael@0: /** Scale the point's coordinates by scale, writing the answer into dst. michael@0: It is legal for dst == this. michael@0: */ michael@0: void scale(SkScalar scale, SkPoint* dst) const; michael@0: michael@0: /** Scale the point's coordinates by scale, writing the answer back into michael@0: the point. michael@0: */ michael@0: void scale(SkScalar value) { this->scale(value, this); } michael@0: michael@0: /** Rotate the point clockwise by 90 degrees, writing the answer into dst. michael@0: It is legal for dst == this. michael@0: */ michael@0: void rotateCW(SkPoint* dst) const; michael@0: michael@0: /** Rotate the point clockwise by 90 degrees, writing the answer back into michael@0: the point. michael@0: */ michael@0: void rotateCW() { this->rotateCW(this); } michael@0: michael@0: /** Rotate the point counter-clockwise by 90 degrees, writing the answer michael@0: into dst. It is legal for dst == this. michael@0: */ michael@0: void rotateCCW(SkPoint* dst) const; michael@0: michael@0: /** Rotate the point counter-clockwise by 90 degrees, writing the answer michael@0: back into the point. michael@0: */ michael@0: void rotateCCW() { this->rotateCCW(this); } michael@0: michael@0: /** Negate the point's coordinates michael@0: */ michael@0: void negate() { michael@0: fX = -fX; michael@0: fY = -fY; michael@0: } michael@0: michael@0: /** Returns a new point whose coordinates are the negative of the point's michael@0: */ michael@0: SkPoint operator-() const { michael@0: SkPoint neg; michael@0: neg.fX = -fX; michael@0: neg.fY = -fY; michael@0: return neg; michael@0: } michael@0: michael@0: /** Add v's coordinates to the point's michael@0: */ michael@0: void operator+=(const SkPoint& v) { michael@0: fX += v.fX; michael@0: fY += v.fY; michael@0: } michael@0: michael@0: /** Subtract v's coordinates from the point's michael@0: */ michael@0: void operator-=(const SkPoint& v) { michael@0: fX -= v.fX; michael@0: fY -= v.fY; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if both X and Y are finite (not infinity or NaN) michael@0: */ michael@0: bool isFinite() const { michael@0: SkScalar accum = 0; michael@0: accum *= fX; michael@0: accum *= fY; michael@0: michael@0: // accum is either NaN or it is finite (zero). michael@0: SkASSERT(0 == accum || !(accum == accum)); michael@0: michael@0: // value==value will be true iff value is not NaN michael@0: // TODO: is it faster to say !accum or accum==accum? michael@0: return accum == accum; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if the point's coordinates equal (x,y) michael@0: */ michael@0: bool equals(SkScalar x, SkScalar y) const { michael@0: return fX == x && fY == y; michael@0: } michael@0: michael@0: friend bool operator==(const SkPoint& a, const SkPoint& b) { michael@0: return a.fX == b.fX && a.fY == b.fY; michael@0: } michael@0: michael@0: friend bool operator!=(const SkPoint& a, const SkPoint& b) { michael@0: return a.fX != b.fX || a.fY != b.fY; michael@0: } michael@0: michael@0: /** Return true if this point and the given point are far enough apart michael@0: such that a vector between them would be non-degenerate. michael@0: michael@0: WARNING: Unlike the explicit tolerance version, michael@0: this method does not use componentwise comparison. Instead, it michael@0: uses a comparison designed to match judgments elsewhere regarding michael@0: degeneracy ("points A and B are so close that the vector between them michael@0: is essentially zero"). michael@0: */ michael@0: bool equalsWithinTolerance(const SkPoint& p) const { michael@0: return !CanNormalize(fX - p.fX, fY - p.fY); michael@0: } michael@0: michael@0: /** WARNING: There is no guarantee that the result will reflect judgments michael@0: elsewhere regarding degeneracy ("points A and B are so close that the michael@0: vector between them is essentially zero"). michael@0: */ michael@0: bool equalsWithinTolerance(const SkPoint& p, SkScalar tol) const { michael@0: return SkScalarNearlyZero(fX - p.fX, tol) michael@0: && SkScalarNearlyZero(fY - p.fY, tol); michael@0: } michael@0: michael@0: /** Returns a new point whose coordinates are the difference between michael@0: a's and b's (a - b) michael@0: */ michael@0: friend SkPoint operator-(const SkPoint& a, const SkPoint& b) { michael@0: SkPoint v; michael@0: v.set(a.fX - b.fX, a.fY - b.fY); michael@0: return v; michael@0: } michael@0: michael@0: /** Returns a new point whose coordinates are the sum of a's and b's (a + b) michael@0: */ michael@0: friend SkPoint operator+(const SkPoint& a, const SkPoint& b) { michael@0: SkPoint v; michael@0: v.set(a.fX + b.fX, a.fY + b.fY); michael@0: return v; michael@0: } michael@0: michael@0: /** Returns the euclidian distance from (0,0) to (x,y) michael@0: */ michael@0: static SkScalar Length(SkScalar x, SkScalar y); michael@0: michael@0: /** Normalize pt, returning its previous length. If the prev length is too michael@0: small (degenerate), return 0 and leave pt unchanged. This uses the same michael@0: tolerance as CanNormalize. michael@0: michael@0: Note that this method may be significantly more expensive than michael@0: the non-static normalize(), because it has to return the previous length michael@0: of the point. If you don't need the previous length, call the michael@0: non-static normalize() method instead. michael@0: */ michael@0: static SkScalar Normalize(SkPoint* pt); michael@0: michael@0: /** Returns the euclidian distance between a and b michael@0: */ michael@0: static SkScalar Distance(const SkPoint& a, const SkPoint& b) { michael@0: return Length(a.fX - b.fX, a.fY - b.fY); michael@0: } michael@0: michael@0: /** Returns the dot product of a and b, treating them as 2D vectors michael@0: */ michael@0: static SkScalar DotProduct(const SkPoint& a, const SkPoint& b) { michael@0: return a.fX * b.fX + a.fY * b.fY; michael@0: } michael@0: michael@0: /** Returns the cross product of a and b, treating them as 2D vectors michael@0: */ michael@0: static SkScalar CrossProduct(const SkPoint& a, const SkPoint& b) { michael@0: return a.fX * b.fY - a.fY * b.fX; michael@0: } michael@0: michael@0: SkScalar cross(const SkPoint& vec) const { michael@0: return CrossProduct(*this, vec); michael@0: } michael@0: michael@0: SkScalar dot(const SkPoint& vec) const { michael@0: return DotProduct(*this, vec); michael@0: } michael@0: michael@0: SkScalar lengthSqd() const { michael@0: return DotProduct(*this, *this); michael@0: } michael@0: michael@0: SkScalar distanceToSqd(const SkPoint& pt) const { michael@0: SkScalar dx = fX - pt.fX; michael@0: SkScalar dy = fY - pt.fY; michael@0: return dx * dx + dy * dy; michael@0: } michael@0: michael@0: /** michael@0: * The side of a point relative to a line. If the line is from a to b then michael@0: * the values are consistent with the sign of (b-a) cross (pt-a) michael@0: */ michael@0: enum Side { michael@0: kLeft_Side = -1, michael@0: kOn_Side = 0, michael@0: kRight_Side = 1 michael@0: }; michael@0: michael@0: /** michael@0: * Returns the squared distance to the infinite line between two pts. Also michael@0: * optionally returns the side of the line that the pt falls on (looking michael@0: * along line from a to b) michael@0: */ michael@0: SkScalar distanceToLineBetweenSqd(const SkPoint& a, michael@0: const SkPoint& b, michael@0: Side* side = NULL) const; michael@0: michael@0: /** michael@0: * Returns the distance to the infinite line between two pts. Also michael@0: * optionally returns the side of the line that the pt falls on (looking michael@0: * along the line from a to b) michael@0: */ michael@0: SkScalar distanceToLineBetween(const SkPoint& a, michael@0: const SkPoint& b, michael@0: Side* side = NULL) const { michael@0: return SkScalarSqrt(this->distanceToLineBetweenSqd(a, b, side)); michael@0: } michael@0: michael@0: /** michael@0: * Returns the squared distance to the line segment between pts a and b michael@0: */ michael@0: SkScalar distanceToLineSegmentBetweenSqd(const SkPoint& a, michael@0: const SkPoint& b) const; michael@0: michael@0: /** michael@0: * Returns the distance to the line segment between pts a and b. michael@0: */ michael@0: SkScalar distanceToLineSegmentBetween(const SkPoint& a, michael@0: const SkPoint& b) const { michael@0: return SkScalarSqrt(this->distanceToLineSegmentBetweenSqd(a, b)); michael@0: } michael@0: michael@0: /** michael@0: * Make this vector be orthogonal to vec. Looking down vec the michael@0: * new vector will point in direction indicated by side (which michael@0: * must be kLeft_Side or kRight_Side). michael@0: */ michael@0: void setOrthog(const SkPoint& vec, Side side = kLeft_Side) { michael@0: // vec could be this michael@0: SkScalar tmp = vec.fX; michael@0: if (kRight_Side == side) { michael@0: fX = -vec.fY; michael@0: fY = tmp; michael@0: } else { michael@0: SkASSERT(kLeft_Side == side); michael@0: fX = vec.fY; michael@0: fY = -tmp; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * cast-safe way to treat the point as an array of (2) SkScalars. michael@0: */ michael@0: const SkScalar* asScalars() const { return &fX; } michael@0: }; michael@0: michael@0: typedef SkPoint SkVector; michael@0: michael@0: #endif