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