diff --git a/src/enums.h b/src/enums.h new file mode 100644 index 0000000..f4e0c72 --- /dev/null +++ b/src/enums.h @@ -0,0 +1,32 @@ +#pragma once + +namespace ege +{ + +enum Alignment +{ + Alignment_LEFT = 0x01, + Alignment_HMID = 0x02, + Alignment_RIGHT = 0x04, + + Alignment_TOP = 0x10, + Alignment_VMID = 0x20, + Alignment_BOTTOM = 0x40, + + Alignment_LEFT_TOP = Alignment_LEFT | Alignment_TOP, + Alignment_LEFT_MID = Alignment_LEFT | Alignment_VMID, + Alignment_LEFT_BOTTOM = Alignment_LEFT | Alignment_BOTTOM, + + Alignment_MID_TOP = Alignment_HMID | Alignment_TOP, + Alignment_CENTER = Alignment_HMID | Alignment_VMID, + Alignment_MID_BOTTOM = Alignment_HMID | Alignment_BOTTOM, + + Alignment_RIGHT_TOP = Alignment_RIGHT | Alignment_TOP, + Alignment_RIGHT_MID = Alignment_RIGHT | Alignment_VMID, + Alignment_RIGHT_BOTTOM = Alignment_RIGHT | Alignment_BOTTOM +}; + +const unsigned int ALIGNMENT_HORIZONTAL_MASK = 0x0F; +const unsigned int ALIGNMENT_VERTICAL_MASK = 0xF0; + +} diff --git a/src/image.cpp b/src/image.cpp index d896230..d22e1d3 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -809,121 +809,56 @@ void IMAGE::putimage(PIMAGE imgDest, int xDest, int yDest, int widthDest, int he CONVERT_IMAGE_END; } -/** - * @brief 对两个区域进行裁剪(目标区域大小和源区域相同) - * - * @param[in,out] dest 目标位置 - * @param[in,out] src 源区域 - * @param[in] destClip 目标裁剪区域(需保证边界和宽高在 int 范围内) - * @param[in] srcClip 源裁剪区域(需保证边界和宽高在 int 范围内) - * - * @return true 结果区域不为空 - * @return false 结果区域为空 - */ -static bool fixRect(Point& dest, Rect& src, const Rect& destClip, const Rect& srcClip) -{ - if ((dest.x < destClip.right()) && dest.y < destClip.bottom()) { - Rect srcIntersection = intersect(src, srcClip); - - if (srcIntersection.isValid()) { - /* 偏移量,满足 >= 0 关系*/ - int srcOffsetX = srcIntersection.x - src.x; - int srcOffsetY = srcIntersection.y - src.y; - - /* 溢出校验 */ - if ((dest.x > INT_MAX - srcOffsetX) || (dest.y > INT_MAX - srcOffsetY)) { - src.setEmpty(); - return false; - } - - dest.offset(srcOffsetX, srcOffsetY); - Rect destRect(dest, srcIntersection.size()); - - /* 超出边界则进行裁剪 */ - if (destRect.x > INT_MAX - destRect.width) { - destRect.width = INT_MAX - destRect.x; - } - - if (destRect.y > INT_MAX - destRect.height) { - destRect.height = INT_MAX - destRect.y; - } - - Rect destIntersection = intersect(destRect, destClip); - - if (destIntersection.isValid()) { - srcIntersection.setSize(destIntersection.size()); - int xOffset = destIntersection.x - dest.x; - int yOffset = destIntersection.y - dest.y; - srcIntersection.offset(xOffset, yOffset); - - src = srcIntersection; - dest = destIntersection.topLeft(); - return true; - } - } - } - - // 经裁剪后区域为空 - src.setEmpty(); - return false; -} - -/* private function */ static void fix_rect_1size(PCIMAGE imgDest, PCIMAGE imgSrc, int* xDest, int* yDest, int* xSrc, int* ySrc, int* width, int* height) { - /* 区域无效 */ - if (*xSrc >= imgSrc->m_width || (*ySrc >= imgSrc->m_height)) { + viewporttype vpt = imgDest->m_vpt; + Point srcPos(*xSrc, *ySrc); + Point dstPos(*xDest + vpt.left, *yDest + vpt.top); + + /* 区域位于图像右下角,此区域内无内容 */ + if ( (srcPos.x >= imgSrc->m_width) || (srcPos.y >= imgSrc->m_height) + || (dstPos.x >= imgDest->m_width) || (dstPos.y >= imgDest->m_height)) + { *width = *height = 0; return; } - /* prepare viewport region and carry out coordinate transformation */ - struct viewporttype vpt = imgDest->m_vpt; - *xDest += vpt.left; - *yDest += vpt.top; + Bound srcBound; + srcBound.setTopLeft(*xSrc, *ySrc); - int left = (*xSrc < 0) ? 0 : *xSrc; - int top = (*ySrc < 0) ? 0 : *ySrc; - - int right = imgSrc->m_width, bottom = imgSrc->m_height;; - if (*width > 0) { - right = sumIsOverflow(*xSrc, *width) ? INT_MAX: (*xSrc + *width); - } + /* 调整区域: 宽高参数 <= 0 则将区域扩展至图像右下边缘,否则截断在 int 范围内*/ + (*width <= 0) ? srcBound.setRight(imgSrc->m_width) : (void)srcBound.setLargeWidth(*width); + (*height <= 0) ? srcBound.setBottom(imgSrc->m_height) : (void)srcBound.setLargeHeight(*height); - if (*height > 0) { - bottom = sumIsOverflow(*ySrc, *height) ? INT_MAX : (*ySrc + *height); - } + /* 绘制区域 */ + Bound dstBound; + dstBound.setTopLeft(dstPos); + dstBound.setLargeSize(srcBound.width(), srcBound.height()); - /* 偏移量,范围: [0, INT_MAX+1] */ - unsigned int xOffset = left - *xSrc; - unsigned int yOffset = top - *ySrc; + /* 由视口区域计算绘制目标裁剪区域 */ + Bound srcClip(0, 0, imgSrc->m_width, imgSrc->m_height); + Bound dstClip(0, 0, imgDest->m_width, imgDest->m_height); + dstClip.intersect(Bound(vpt.left, vpt.top, vpt.right, vpt.bottom)); - Point dstPos(*xDest + vpt.left, *yDest + vpt.top); + srcBound.intersect(srcClip); + dstBound.intersect(dstClip); - if ((dstPos.x > (int)(INT_MAX - xOffset)) || (dstPos.y > (int)(INT_MAX - yOffset))) { - *width = *height = 0; - return; - } else { - dstPos.offset((int)xOffset, (int)yOffset); - } + /* 由共同区域求实际绘制区域 */ + srcBound.offset(-srcPos.x, -srcPos.y); + srcBound.offset(-dstPos.x, -dstPos.y); + Bound actualBound = intersect(srcBound, dstBound); - Rect srcRect(Point(left, top), Point(right, bottom)); - // 设定裁剪区域,源图像 - Rect dstClip(Point(vpt.left, vpt.top), Point(vpt.right, vpt.bottom)); - Rect srcClip(Point(0, 0), Point(imgSrc->m_width, imgSrc->m_height)); + if (actualBound.isValid()) { + *xDest = actualBound.x() + dstPos.x; + *yDest = actualBound.y() + dstPos.y; - bool isValid = fixRect(dstPos, srcRect, dstClip, srcClip); - if (!isValid) { - *width = *height = 0; - return; + *xSrc = actualBound.x() + srcPos.x; + *ySrc = actualBound.y() + srcPos.y; + *width = actualBound.width(); + *height = actualBound.height(); } else { - *xDest = dstPos.x; - *yDest = dstPos.y; - *xSrc = srcRect.x; - *ySrc = srcRect.y; - *width = srcRect.width; - *height = srcRect.height; + *width = *height = 0; } } @@ -2920,8 +2855,8 @@ void putimage(PIMAGE imgDest, int xDest, int yDest, PCIMAGE pSrcImg, DWORD dwRop pSrcImg->putimage(imgDest, xDest, yDest, dwRop); } -void putimage( - PIMAGE imgDest, int xDest, int yDest, int widthDest, int heightDest, PCIMAGE pSrcImg, int xSrc, int ySrc, DWORD dwRop) +void putimage(PIMAGE imgDest, int xDest, int yDest, int widthDest, int heightDest, + PCIMAGE pSrcImg, int xSrc, int ySrc, DWORD dwRop) { pSrcImg = CONVERT_IMAGE_CONST(pSrcImg); pSrcImg->putimage(imgDest, xDest, yDest, widthDest, heightDest, xSrc, ySrc, dwRop); @@ -2951,15 +2886,15 @@ int getimage(PIMAGE imgDest, const wchar_t* resType, const wchar_t* resName, int return imgDest->getimage(resType, resName, zoomWidth, zoomHeight); } -void putimage(PIMAGE imgDest, int xDest, int yDest, int widthDest, int heightDest, PCIMAGE pSrcImg, int xSrc, int ySrc, - int srcWidth, int srcHeight, DWORD dwRop) +void putimage(PIMAGE imgDest, int xDest, int yDest, int widthDest, int heightDest, + PCIMAGE pSrcImg, int xSrc, int ySrc, int srcWidth, int srcHeight, DWORD dwRop) { pSrcImg = CONVERT_IMAGE_CONST(pSrcImg); pSrcImg->putimage(imgDest, xDest, yDest, widthDest, heightDest, xSrc, ySrc, srcWidth, srcHeight, dwRop); } -void putimage(int xDest, int yDest, int widthDest, int heightDest, PCIMAGE pSrcImg, int xSrc, int ySrc, int srcWidth, - int srcHeight, DWORD dwRop) +void putimage(int xDest, int yDest, int widthDest, int heightDest, + PCIMAGE pSrcImg, int xSrc, int ySrc, int srcWidth, int srcHeight, DWORD dwRop) { pSrcImg = CONVERT_IMAGE_CONST(pSrcImg); pSrcImg->putimage(NULL, xDest, yDest, widthDest, heightDest, xSrc, ySrc, srcWidth, srcHeight, dwRop); diff --git a/src/types.cpp b/src/types.cpp index 033deae..2d91691 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -6,280 +6,10 @@ namespace ege { - //------------------------------------------------------------------------------ // Rect //------------------------------------------------------------------------------ -/** - * @brief 将起点在 x 长度为 length(整数范围)的线段进行裁剪,使线段在限制范围内 - * @note 线段起点为 x (包含), 终点为 x + length (不包含) - * - * @param[in, out] x 起始点 - * @param[in, out] length 线段长度(可正可负) - * @param[in] minval 裁剪后线段的最小值 - * @param[in] maxval 裁剪后线段的最大值 - */ -static void clipByLimits(int& x, int& length, int minval, int maxval) -{ - if (minval > maxval) { - length = 0; - } else if (length == 0) { - x = minval; - } else { - /* 指示范围的端点(包含) */ - int start, end; - if (length > 0) { // x ----xMin ---- xMax ----> right() - /* 右边界截断到 INT_MAX */ - end = ((unsigned int)(length - 1) > (unsigned int)(INT_MAX - x)) ? INT_MAX : (x + length - 1); - start = MAX(x, minval); - end = MIN(end, maxval); - - x = start; - length = (start > end) ? 0 : (end - start + 1); - } else if (length < 0) { // left <----xMin ---- xMax ---- x - /* 左边界截断到 INT_MIN */ - end = ((unsigned int)(-length - 1) > (unsigned int)(x - INT_MIN)) ? INT_MIN : (x + length + 1); - start = MIN(x, maxval); - end = MAX(end, minval); - - x = start; - length = (start < end) ? 0 : (end - start - 1); - } - } -} - -Rect normalize(const Rect& rect) -{ - return Rect(rect).normalize(); -} - -Rect intersect(const Rect& a, const Rect& b) -{ - return Rect(a).intersect(b); -} - -/** - * @brief 求原对象与 rect 的交集,结果写入原对象 - * - * @param rect - * @return Rect& 原对象的引用 - * @note 目前求交集需要先求取两个矩形的边界,计算时可能会由于溢出而得到 - * 错误的结果,所以要求矩形边界在 int 范围内。 - */ -Rect& Rect::intersect(const Rect& rect) -{ - if (isEmpty() || rect.isEmpty()) - return setEmpty(); - - Rect a(*this), b(rect); - a.normalize(); - b.normalize(); - - if ( (a.right() <= b.left()) || (b.right() <= a.left()) - || (a.bottom() <= b.top()) || (b.bottom() <= a.top() )) { - return setEmpty(); - } - - int left = MAX(a.left(), b.left()); - int top = MAX(a.top(), b.top()); - int right = MIN(a.right(), b.right()); - int bottom = MIN(a.bottom(), b.bottom()); - - setBounds(left, top, right, bottom); - - return *this; -} - -/** - * @brief 求原对象与 rect 的并集,结果写入原对象 - * - * @param rect - * @return Rect& 原对象的引用 - * @note 目前求并集需要先求取两个矩形的边界,计算时可能会由于溢出而得到 - * 错误的结果,所以要求矩形边界在 int 范围内。 - */ -Rect& Rect::unite(const Rect& rect) -{ - if (rect.isEmpty()) - return *this;; - - if (isEmpty()) - return *this = rect; - - Rect a(*this), b(rect); - a.normalize(); - b.normalize(); - - int left = MIN(a.left(), b.left()); - int top = MIN(a.top(), b.top()); - int right = MAX(a.right(), b.right()); - int bottom = MAX(a.bottom(), b.bottom()); - - setBounds(left, top, right, bottom); - return *this; -} - -Rect& Rect::unite(int x, int y, int width, int height) -{ - unite(Rect(x, y, width, height)); - return *this; -} - -Rect& Rect::unite(const Point& point) -{ - if (isEmpty()) { - set(point, Size(1, 1)); - } else { - if (point.x < left()) { - setLeft(point.x); - } else if (point.x >= right()) { - setRight(point.x + 1); - } - - if (point.y < top()) { - setTop(point.y); - } else if (point.y >= bottom()) { - setBottom(point.y + 1); - } - } - - return *this; -} - -Rect& Rect::unite(const Point points[], int count) -{ - unite(bounds(points, count)); - return *this; -} - -Rect unite(const Rect& a, const Rect& b) -{ - return Rect(a).unite(b); -} - -/** - * @brief 求包含所有点的最小包围矩形 - * @param a - * @param b - * @return 包含所有点的包围矩形 - * @warning 矩形的宽高为 int 型,因此要求所有点在 x 和 y 上的最大最小值之差 - * 需要小于 INT_MAX,否则会得到错误的结果 - */ -Rect bounds(const Point points[], int count) -{ - if (count <= 0) - return Rect(); - - int xMin = points[0].x, xMax = xMin; - int yMin = points[0].y, yMax = yMin; - - for (int i = 1; i < count; i++) { - if (points[i].x < xMin) { - xMin = points[i].x; - } else if (points[i].x > xMax) { - xMax = points[i].x; - } - - if (points[i].y < yMin) { - yMin = points[i].y; - } else if (points[i].y > yMax) { - yMax = points[i].y; - } - } - - assert((unsigned int)(xMax - xMin) < INT_MAX); - assert((unsigned int)(yMax - xMin) < INT_MAX); - - Point leftTop(xMin, yMin); - Point bottomRight(xMax + 1, yMax + 1); - - return Rect(leftTop, bottomRight, false); -} - -/** - * @brief 求包含两个点的最小矩形 - * @param a - * @param b - * @return 矩形 - * @warning 矩形的宽高为 int 型,因此两个点的在 x 和 y 上的差值需要小于 INT_MAX - */ -Rect bounds(const Point& a, const Point& b) -{ - int xMin, xMax; - if (a.x <= b.x) { - xMin = a.x; - xMax = b.x; - } else { - xMin = b.x; - xMax = a.x; - } - - int yMin, yMax; - if (a.y <= b.y) { - yMin = a.y; - yMax = b.y; - } else { - yMin = b.y; - yMax = a.y; - } - - assert((unsigned int)(xMax - xMin) < INT_MAX); - assert((unsigned int)(yMax - xMin) < INT_MAX); - - Point leftTop(xMin, yMin); - Point bottomRight(xMax + 1, yMax + 1); - - return Rect(leftTop, bottomRight, false); -} - -Rect& Rect::clip(int xMin, int xMax, int yMin, int yMax) -{ - return clipX(xMin, xMax).clipY(yMin, yMax); -} - -Rect& Rect::clipX(int xMin, int xMax) -{ - clipByLimits(x, width, xMin, xMax); - return *this; -} - -Rect& Rect::clipY(int yMin, int yMax) -{ - clipByLimits(y, height, yMin, yMax); - return *this; -} - -Rect& Rect::clip () -{ - return clipX().clipY(); - -} -Rect& Rect::clipX() -{ - if (width > 0) { - if ((unsigned int)(width - 1) > (unsigned int)(INT_MAX - x)) - width = INT_MAX - x + 1; - } else if (width < 0) { - if ((unsigned)(x - INT_MIN) < (unsigned)(-width - 1)) - width = INT_MIN - x - 1; - } - - return *this; -} - -Rect& Rect::clipY() -{ - if (height > 0) { - if ((unsigned int)(height - 1) > (unsigned int)(INT_MAX - x)) - height = INT_MAX - y + 1; - } else if (height < 0) { - if ((unsigned)(y - INT_MIN) < (unsigned)(-height - 1)) - height = INT_MIN - y - 1; - } - - return *this; -} //------------------------------------------------------------------------------ diff --git a/src/types.h b/src/types.h index dc01d05..36d5bf2 100644 --- a/src/types.h +++ b/src/types.h @@ -10,7 +10,7 @@ typedef unsigned uint32_t; #endif -#include +#include #include @@ -48,20 +48,45 @@ typedef intptr_t POINTER_SIZE; typedef long POINTER_SIZE; #endif + +#ifndef EGE_TEMP_MIN +#define EGE_TEMP_MIN(a, b) ((b) < (a) ? (b) : (a)) +#endif + +#ifndef EGE_TEMP_MAX +#define EGE_TEMP_MAX(a, b) ((b) > (a) ? (b) : (a)) +#endif + +#ifndef EGE_TEMP_DIFF +#define EGE_TEMP_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#endif + +#ifndef EGE_TEMP_DIFF_UINT +#define EGE_TEMP_DIFF_UINT(a, b) ((unsigned int)EGE_TEMP_DIFF(a, b)) +#endif + +#ifndef EGE_TEMP_MIDPOINT_INT +#define EGE_TEMP_MIDPOINT_INT(a, b) \ + ((a) < (b) ? ((a) + ((unsigned)(b) - (a)) / 2) : ((a) - ((unsigned)(a) - (b)) / 2)) +#endif + +#ifndef EGE_TEMP_ROUND +#define EGE_TEMP_ROUND(x) ((int)((x) > 0.0 ? ((x) + 0.5) : ((x) - 0.5))) +#endif + +#include "enums.h" namespace ege { - #ifndef EGE_BYTE_TYPEDEF #define EGE_BYTE_TYPEDEF typedef unsigned char byte; #endif //------------------------------------------------------------------------------ -// Point +// Point & Pointf //------------------------------------------------------------------------------ +struct Pointf; -#ifndef EGE_Point_TYPEDEF -#define EGE_Point_TYPEDEF struct Point { int x; @@ -70,33 +95,106 @@ struct Point public: Point() : x(0), y(0) {} Point(int x, int y) : x(x), y(y) {} + explicit Point(Pointf point); - Point& set(int x, int y); - Point& offset(int dx, int dy); + void set(int x, int y); + void set(const Point& point); + void offset(int dx, int dy); }; -inline Point& Point::set(int x, int y) +struct Pointf { - this->x = x; - this->y = y; - return *this; + float x; + float y; + +public: + Pointf() : x(0.0f), y(0.0f) {} + Pointf(float x, float y) : x(x), y(y) {} + explicit Pointf(Point point); + + void set(float x, float y); + void set(const Pointf& point); + void offset(float dx, float dy); + + bool nearEquals(const Pointf& point, float error = 1E-5f) const; +}; + +inline Point::Point(Pointf point) : x((int)point.x), y((int)point.y) {} +inline Pointf::Pointf(Point point) : x((float)point.x), y((float)point.y) {} + +inline void Point::set(int x, int y) +{ + set(Point(x, y)); } -inline Point& Point::offset(int dx, int dy) +inline void Point::set(const Point& point) +{ + x = point.x; + y = point.y; +} + +inline void Point::offset(int dx, int dy) { x += dx; y += dy; - return *this; } -#endif +inline bool operator== (const Point& a, const Point& b) +{ + return (a.x == b.x) && (a.y == b.y); +} + +inline bool operator!= (const Point& a, const Point& b) +{ + return !(a == b); +} + +inline void Pointf::set(float x, float y) +{ + set(Pointf(x, y)); +} + +inline void Pointf::set(const Pointf& point) +{ + x = point.x; + y = point.y; +} + +inline void Pointf::offset(float dx, float dy) +{ + x += dx; + y += dy; +} + +inline Pointf offset(const Pointf& point, float dx, float dy) +{ + return Pointf(point.x + dx, point.y + dy); +} + +inline Point offset(const Point& point, int dx, int dy) +{ + return Point(point.x + dx, point.y + dy); +} + +inline bool operator== (const Pointf& a, const Pointf& b) +{ + return (a.x == b.x) && (a.y == b.y); +} + +inline bool operator!= (const Pointf& a, const Pointf& b) +{ + return !(a == b); +} + +inline bool Pointf:: nearEquals(const Pointf& point, float error) const +{ + return (EGE_TEMP_DIFF(x, point.x) <= error) + && (EGE_TEMP_DIFF(y, point.y) <= error); +} //------------------------------------------------------------------------------ -// Size +// Size & Sizef //------------------------------------------------------------------------------ - -#ifndef EGE_Size_TYPEDEF -#define EGE_Size_TYPEDEF struct Size { int width; @@ -106,33 +204,66 @@ struct Size Size() : width(0), height(0) {} Size(int width, int height) : width(width), height(height) {} - Size& set(int width, int height); - Size& setEmpty(); + void set(int width, int height); + void setEmpty(); - bool isNull() const { return (width == 0) && (height == 0); } - bool isEmpty() const { return (width <= 0) || (height <= 0); } - bool isValid() const { return (width > 0) && (height > 0); } - bool isNormalized() const { return (width >= 0) && (height >= 0); } + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + bool isNormalized() const; - Size& tranpose(); - Size& normalize(); + void tranpose(); + void normalize(); }; bool operator== (const Size& a, const Size& b); bool operator!= (const Size& a, const Size& b); + Size normalize(const Size& size); +struct Sizef +{ + float width; + float height; + +public: + Sizef() : width(0.0f), height(0.0f) {} + Sizef(float width, float height) : width(width), height(height) {} + + void set(float width, float height); + void setEmpty(); + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + bool isNormalized() const; + + bool nearEquals(const Sizef& size, float error) const; + + void tranpose(); + void normalize(); +}; + +bool operator== (const Sizef& a, const Sizef& b); +bool operator!= (const Sizef& a, const Sizef& b); + +Sizef normalize(const Sizef& size); + //------------------------------------------------------------------------------ -inline Size& Size::set(int width, int height) +inline bool Size::isNull() const { return (width == 0) && (height == 0); } +inline bool Size::isEmpty() const { return (width == 0) || (height == 0); } +inline bool Size::isValid() const { return (width > 0) && (height > 0); } +inline bool Size::isNormalized() const { return (width >= 0) && (height >= 0); } + +inline void Size::set(int width, int height) { this->width = width; this->height = height; - return *this; } -inline Size& Size::setEmpty() +inline void Size::setEmpty() { return set(0, 0); } @@ -147,15 +278,20 @@ inline bool operator!= (const Size& a, const Size& b) return !(a == b); } -inline Size& Size::tranpose() +inline bool Sizef::nearEquals(const Sizef& size, float error) const +{ + return (EGE_TEMP_DIFF(width, size.width) <= error) + && (EGE_TEMP_DIFF(height, size.height) <= error); +} + +inline void Size::tranpose() { int temp = width; width = height; height = temp; - return *this; } -inline Size& Size::normalize() +inline void Size::normalize() { if (width < 0) { width = -width; @@ -164,22 +300,203 @@ inline Size& Size::normalize() if (height < 0) { height = -height; } - - return *this; } inline Size normalize(const Size& size) { - return Size(size).tranpose(); + Size s(size); + s.normalize(); + return s; } -#endif +//------------------------------------------------------------------------------ + +inline bool Sizef::isNull() const { return (width == 0.0f) && (height == 0.0f); } +inline bool Sizef::isEmpty() const { return (width == 0.0f) || (height == 0.0f); } +inline bool Sizef::isValid() const { return (width > 0.0f) && (height > 0.0f); } +inline bool Sizef::isNormalized() const { return (width >= 0.0f) && (height >= 0.0f); } + +inline void Sizef::set(float width, float height) +{ + this->width = width; + this->height = height; +} + +inline void Sizef::setEmpty() +{ + return set(0.0f, 0.0f); +} + +inline bool operator== (const Sizef& a, const Sizef& b) +{ + return (a.width == b.width) && (a.height == b.height); +} + +inline bool operator!= (const Sizef& a, const Sizef& b) +{ + return !(a == b); +} + +inline void Sizef::tranpose() +{ + float temp = width; + width = height; + height = temp; +} + +inline void Sizef::normalize() +{ + if (width < 0.0f) { + width = -width; + } + + if (height < 0.0f) { + height = -height; + } +} + +inline Sizef normalize(const Sizef& size) +{ + Sizef s(size); + s.normalize(); + return s; +} //------------------------------------------------------------------------------ -// Rect +// Bound & Rect //------------------------------------------------------------------------------ -#ifndef EGE_Rect_TYPEDEF -#define EGE_Rect_TYPEDEF +struct Rect; + +struct Bound +{ + int left; + int top; + int right; + int bottom; + +public: + Bound(); + Bound(int left, int top, int right, int bottom, bool normalize = true); + Bound(const Point& topLeft, const Point& bottomRight, bool normalize = true); + explicit Bound(const Rect& rect); + + void set(int left, int top, int right, int bottom, bool normalize = true); + void setRect(const Rect& rect); + void setRect(int x, int y, int width, int height, bool normalize = true); + void setRect(Point topLeft, Size size, bool normalize = true); + void setEmpty(); + + void setX(int x); + void setY(int y); + void setWidth(int width); + void setHeight(int height); + + void setXY(int x, int y); + void setSize(const Size& size); + void setSize(int width, int height); + + bool setLargeWidth(unsigned int width); + bool setLargeHeight(unsigned int height); + bool setLargeSize(unsigned int width, unsigned int height); + + void setLeft(int left); + void setTop(int top); + void setRight(int right); + void setBottom(int bottom); + + void setTopLeft (int x, int y); + void setTopRight (int x, int y); + void setBottomLeft (int x, int y); + void setBottomRight(int x, int y); + + void setTopLeft (const Point& point); + void setTopRight (const Point& point); + void setBottomLeft (const Point& point); + void setBottomRight(const Point& point); + + void setLeftRight (int left, int right); + void setTopBottom (int top, int bottom); + + int x() const; + int y() const; + int width() const; + int height() const; + + Point xy() const; + Size size() const; + + Point topLeft() const; + Point topRight() const; + Point bottomLeft() const; + Point bottomRight() const; + + int centerX() const; + int centerY() const; + Point center() const; + + double exactCenterX() const; + double exactCenterY() const; + Pointf exactCenter() const; + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + bool isNormalized() const; + + bool isWidthOutOfRange() const; + bool isHeightOutOfRange() const; + bool isOutOfRange() const; + + bool isContains(int x, int y) const; + bool isContains(const Point& point) const; + bool isContains(const Bound& bound) const; + bool isContains(int left, int top, int right, int bottom) const; + + bool isOverlaps(const Bound& bound) const; + bool isOverlaps(int left, int top, int right, int bottom) const; + + void transpose(); + void offset (int dx, int dy); + void offsetTo(int x, int y); + void offsetTo(const Point& point); + + bool normalize(); + bool fixedNormalize(); + + void flipHorizonal(); + void flipVertical(); + + void inset (int margin); + void inset (int dx, int dy); + void inset (int leftMargin, int topMargin, int rightMargin, int bottomMargin); + void outset (int margin); + void outset (int dx, int dy); + void outset (int leftMargin, int topMargin, int rightMargin, int bottomMargin); + + void leftAlign (int left); + void topAlign (int top); + void rightAlign (int right); + void bottomAlign(int bottom); + void centerAlign(int x, int y); + void alignTo(const Point& point, Alignment alignment); + void alignTo(int x, int y, Alignment alignment); + void alignTo(const Bound& bound, Alignment alignment); + + void scale(float scale); + void scale(float xScale, float yScale); + void scale(float xScale, float yScale, Pointf center); + + bool intersect(const Bound& bound); + bool intersect(const Rect& rect); + bool intersect(int left, int top, int right, int bottom); + + void unite(const Bound& bound); + void unite(int x, int y); + void unite(const Point& point); + void unite(const Point points[], int length); +}; + + struct Rect { int x; @@ -189,401 +506,1389 @@ struct Rect public: Rect(); - Rect(int x, int y, int width, int height); - Rect(const Point& topLeft, const Size& size); - Rect(const Point& topLeft, const Point& bottomRight, bool normalize = false); + Rect(int x, int y, int width, int height, bool normalize = true); + Rect(const Point& topLeft, const Size& size, bool normalize = true); + explicit Rect(const Bound& bound); + + void set(int x, int y, int width, int height, bool normalize = true); + void set(const Point& topLeft, const Size& size, bool normalize = true); + void setBound(const Bound& bound); + void setBound(int left, int top, int right, int bottom, bool normalize = true); + void setBound(const Point& topLeft, const Point& bottomRight, bool normalize = true); + void setEmpty(); + + void setX(int x); + void setY(int y); + void setXY(int x, int y); + void setWidth(int width); + void setHeight(int height); + + void setSize(const Size& size); + void setSize(int width, int height); + + void setLeft(int left); + void setTop(int top); + void setRight(int right); + void setBottom(int bottom); + + void setTopLeft (int x, int y); + void setTopRight (int x, int y); + void setBottomLeft (int x, int y); + void setBottomRight(int x, int y); + + void setTopLeft (const Point& point); + void setTopRight (const Point& point); + void setBottomLeft (const Point& point); + void setBottomRight(const Point& point); + + void setLeftRight (int left, int right); + void setTopBottom (int top, int bottom); int left() const; int top() const; int right() const; int bottom() const; - Point topLeft() const; - Point bottomRight() const; - Point center() const; - Size size() const; - - Rect& setTopLeft(const Point& topLeft); - Rect& setTopLeft(int x, int y); - Rect& setBottomRight(const Point& bottomRight); - Rect& setBottomRight(int x, int y); - Rect& setLeftRight(int left, int right); - Rect& setTopBottom(int top, int bottom); - Rect& setSize(const Size& size); - Rect& setSize(int width, int height); - Rect& setX(int x); - Rect& setY(int y); - Rect& setWidth(int width); - Rect& setHeight(int height); - Rect& setLeft(int left); - Rect& setTop(int top); - Rect& setRight(int right); - Rect& setBottom(int bottom); - Rect& set(int x, int y, int width, int height); - Rect& set(const Point& topLeft, const Size& size); - Rect& setBounds(const Point& topLeft, const Point& bottomRight, bool normalize = true); - Rect& setBounds(int left, int top, int right, int bottom, bool normalize = true); - Rect& setEmpty(); - - Rect& normalize(); - Rect& transpose(); - Rect& offset (int dx, int dy); - Rect& offsetTo(int x, int y); - Rect& inset (int dx, int dy); - Rect& outset (int dx, int dy); - - Rect& intersect(const Rect& rect); - Rect& unite(const Rect& rect); - Rect& unite(int x, int y, int width, int height); - Rect& unite(const Point& point); - Rect& unite(const Point points[], int count); - - Rect& clip (int xMin, int xMax, int yMin, int yMax); - Rect& clipX(int xMin, int xMax); - Rect& clipY(int yMin, int yMax); - Rect& clip (); - Rect& clipX(); - Rect& clipY(); - - bool isNull() const; - bool isEmpty() const; - bool isValid() const; - bool isNormalized() const; - - bool contains (int x, int y) const; - bool contains (const Point& point) const; - bool contains (const Rect& rect) const; - bool isOverlap(const Rect& rect) const; - + Point xy() const; + Size size() const; + + Point topLeft() const; + Point topRight() const; + Point bottomLeft() const; + Point bottomRight() const; + + int centerX() const; + int centerY() const; + Point center() const; + + double exactCenterX() const; + double exactCenterY() const; + Pointf exactCenter() const; + + bool isNull() const; + bool isEmpty() const; + bool isValid() const; + bool isNormalized() const; + + bool isContains(int x, int y) const; + bool isContains(const Point& point) const; + bool isContains(const Rect& rect) const; + bool isContains(int x, int y, int width, int height) const; + + bool isOverlaps(const Rect& rect) const; + bool isOverlaps(int x, int y, int width, int height) const; + + void transpose(); + void offset (int dx, int dy); + void offsetTo(int x, int y); + void offsetTo(const Point& point); + + bool normalize(); + bool fixedNormalize(); + + void inset (int margin); + void inset (int dx, int dy); + void inset (int leftMargin, int topMargin, int rightMargin, int bottomMargin); + void outset (int margin); + void outset (int dx, int dy); + void outset (int leftMargin, int topMargin, int rightMargin, int bottomMargin); + + void leftAlign (int left); + void topAlign (int top); + void rightAlign (int right); + void bottomAlign(int bottom); + void centerAlign(int x, int y); + void centerAlign(Point point); + + void alignTo(const Point& point, Alignment alignment); + void alignTo(int x, int y, Alignment alignment); + void alignTo(const Rect& rect, Alignment alignment); + + void scale(float scale); + void scale(float xScale, float yScale); + void scale(float xScale, float yScale, Pointf center); + + bool intersect(const Rect& rect); + bool intersect(const Bound& bound); + bool intersect(int x, int y, int width, int height); + + bool unite(const Rect& rect); + bool unite(int x, int y); + bool unite(const Point& point); + bool unite(const Point points[], int length); + bool unite(int x, int y, int width, int height); + + void clip (int xMin, int xMax, int yMin, int yMax); + void clipX(int xMin, int xMax); + void clipY(int yMin, int yMax); + void clip (); + void clipX(); + void clipY(); + + bool isOutOfRange(int xMin, int xMax, int yMin, int yMax) const; + bool isOutOfRange() const; bool isXOutOfRange(int xMin, int xMax) const; bool isYOutOfRange(int yMin, int yMax) const; bool isXOutOfRange() const; bool isYOutOfRange() const; - bool isOutOfRange (int xMin, int xMax, int yMin, int yMax) const; - bool isOutOfRange() const; +private: + static bool clipByLimits(int& x, int& length, int minval, int maxval); }; // Rect -Rect normalize(const Rect& rect); +//------------------------------------------------------------------------------ -Rect intersect(const Rect& a, const Rect& b); +Bound offset(const Bound& bound, int dx, int dy); -Rect unite(const Rect& a, const Rect& b); +Bound intersect(const Bound& a, const Bound& b); + +Bound intersect(const Rect& a, const Bound& b); -Rect bounds(const Point point[], int count); +Bound intersect(const Bound& a, const Rect& b); -Rect bounds(const Point& a, const Point& b); +bool intersect(const Bound& a, const Bound& b, Bound& c); + +Bound unite(const Bound& a, const Bound& b); + +Bound getBounds(const Point points[], int length); //------------------------------------------------------------------------------ -inline Rect::Rect(): x(0), y(0), width(0), height(0) {} +Rect clip(const Rect& rect); -inline Rect::Rect(int x, int y, int width, int height) : x(x), y(y), width(width), height(height) {} +Rect offset(const Rect& rect, int dx, int dy); -inline Rect::Rect(const Point& topLeft, const Size& size) - : x(topLeft.x), y(topLeft.y), width(size.width), height(size.height) -{ } +Rect normalize(const Rect& rect); + +Rect intersect(const Rect& a, const Rect& b); -inline Rect::Rect(const Point& topLeft, const Point& bottomRight, bool normalize) - : x(topLeft.x), y(topLeft.y), width(bottomRight.x - topLeft.x), height(bottomRight.y - topLeft.y) +Rect unite(const Rect& a, const Rect& b); + +//------------------------------------------------------------------------------ + +inline Bound::Bound(): left(0), top(0), right(0), bottom(0) {} + +inline Bound::Bound(int left, int top, int right, int bottom, bool normalize) + :left(left), top(top), right(right), bottom(bottom) { if (normalize) this->normalize(); } -inline int Rect::left() const { return x; } -inline int Rect::top() const { return y; } -inline int Rect::right() const { return x + width; } -inline int Rect::bottom() const { return y + height; } +inline Bound::Bound(const Point& topLeft, const Point& bottomRight, bool normalize) + : left(topLeft.x), top(topLeft.y), right(bottomRight.x), bottom(bottomRight.y) +{ + if (normalize) + this->normalize(); +} -inline Point Rect::topLeft() const { return Point(x, y); } -inline Point Rect::bottomRight() const { return Point(x + width, y + height); } -inline Size Rect::size() const { return Size(width, height); } -inline Point Rect::center() const { return Point(x + width / 2, y + height / 2);} +inline Bound::Bound(const Rect& rect) + : left(rect.x), top(rect.y), right(rect.x + rect.width), bottom(rect.y + rect.height) +{ } -inline Rect& Rect::setX(int x) { this->x = x; return *this;} -inline Rect& Rect::setY(int y) { this->x = y; return *this; } -inline Rect& Rect::setWidth(int width) { this->width = width; return *this;} -inline Rect& Rect::setHeight(int height) { this->height = height; return *this;} +inline void Bound::setX(int x) { right += x - left; left = x; } +inline void Bound::setY(int y) { bottom += y - top; top = y; } +inline void Bound::setWidth(int width) { right = left + width; } +inline void Bound::setHeight(int height) { bottom = top + height; } -inline Rect& Rect::setLeft(int left) -{ - int diff = left - this->left(); - width -= diff; - x = left; +inline void Bound::setLeft(int left) { this->left = left; } +inline void Bound::setTop(int top) { this->top = top; } +inline void Bound::setRight(int right) { this->right = right; } +inline void Bound::setBottom(int bottom) { this->bottom = bottom; } - return *this; -} +inline int Bound::x() const { return left; } +inline int Bound::y() const { return top; } +inline int Bound::width() const { return right - left; } +inline int Bound::height() const { return bottom - top; } -inline Rect& Rect::setTop(int top) +inline int Bound::centerX() const { - int diff = top - this->top(); - height -= diff; - y = top; - return *this; + return EGE_TEMP_MIDPOINT_INT(left, right); } -inline Rect& Rect::setRight(int right) +inline int Bound::centerY() const { - width = right - left(); - return *this; + return EGE_TEMP_MIDPOINT_INT(top, bottom); } -inline Rect& Rect::setBottom(int bottom) +inline double Bound::exactCenterX() const { return 0.5 * left + 0.5 * right; } +inline double Bound::exactCenterY() const { return 0.5 * top + 0.5 * bottom; } + +inline bool Bound::isNull() const { return (left == right) && (top == bottom); } +inline bool Bound::isEmpty() const { return (left == right) || (top == bottom); } +inline bool Bound::isValid() const { return (left < right) && (top < bottom); } +inline bool Bound::isNormalized() const { return (left <= right) && (top <= bottom); } + +inline bool Bound::isWidthOutOfRange() const { - height = bottom - top(); - return *this; + if (left < right) + return ((unsigned)right - left) > (unsigned)INT_MAX; + else + return ((unsigned)left - right) > (unsigned)INT_MIN; } -inline Rect& Rect::setSize(const Size& size) +inline bool Bound::isHeightOutOfRange() const { - setSize(size.width, size.height); - return *this; + if (top < bottom) + return (unsigned)bottom - top > (unsigned)INT_MAX; + else + return (unsigned)top - bottom > (unsigned)INT_MIN; } -inline Rect& Rect::setSize(int width, int height) +inline bool Bound::isOutOfRange() const { - this->width = width; - this->height = height; - return *this; + return isWidthOutOfRange() || isHeightOutOfRange(); } -inline Rect& Rect::setTopLeft(const Point& topLeft) +inline void Bound::set(int left, int top, int right, int bottom, bool normalize) { - setTopLeft(topLeft.x, topLeft.y); - return *this; + this->left = left; + this->top = top; + this->right = right; + this->bottom = bottom; + + if (normalize) + this->normalize(); } -inline Rect& Rect::setTopLeft(int x, int y) +inline void Bound::setRect(const Rect& rect) { - setBounds(Point(x, y), bottomRight(), false); - return *this; + left = rect.x; + top = rect.y; + right = rect.x + rect.width; + bottom = rect.y + rect.height; } -inline Rect& Rect::setBottomRight(const Point& bottomRight) +inline void Bound::setRect(int x, int y, int width, int height, bool normalize) { - return setBottomRight(bottomRight.x, bottomRight.y); + setRect(Rect(x, y, width, height, normalize)); } -inline Rect& Rect::setBottomRight(int x, int y) +inline void Bound::setRect(Point topLeft, Size size, bool normalize) { - setRight(x); - setBottom(y); - return *this; + setRect(Rect(topLeft, size, normalize)); } -inline Rect& Rect::setLeftRight(int left, int right) +inline void Bound::setEmpty() { - x = left; - width = right - left; - return *this; + left = top = right = bottom = 0; } -inline Rect& Rect::setTopBottom(int top, int bottom) +inline bool Bound::isContains(const Point& point) const { - y = top; - height = bottom - top; - return *this; + return isContains(point.x, point.y); } -inline Rect& Rect::set(int x, int y, int width, int height) +inline bool Bound::isContains(int x, int y) const { - this->x = x; - this->y = y; - this->width = width; - this->height = height; - - return *this; + return (left <= x) && (x < right) + && (top <= y) && (y < bottom); } -inline Rect& Rect::set(const Point& topLeft, const Size& size) +inline bool Bound::isContains(const Bound& bound) const { - setTopLeft(topLeft); - setSize(size); - return *this; + return isContains(bound.left, bound.top, bound.right, bound.bottom); } -inline Rect& Rect::setBounds(const Point& topLeft, const Point& bottomRight, bool normalize) +inline bool Bound::isContains(int left, int top, int right, int bottom) const { - x = topLeft.x; - y = topLeft.y; - width = bottomRight.x - topLeft.x; - height = bottomRight.y - topLeft.y; - - if (normalize) - this->normalize(); - - return *this; + return (this->left <= left) && (this->right >= right) + && (this->top <= top) && (this->bottom >= bottom); } -inline Rect& Rect::setBounds(int left, int top, int right, int bottom, bool normalize) +inline bool Bound::isOverlaps(const Bound& bound) const { - x = left; - y = top; - setRight(right); - setBottom(bottom); - - if (normalize) - this->normalize(); - - return *this; + return isOverlaps(bound.left, bound.top, bound.right, bound.bottom); } -inline Rect& Rect::setEmpty() +inline bool Bound::isOverlaps(int left, int top, int right, int bottom) const { - x = y = width = height = 0; - return *this; + return (this->left < right) && (this->right > left) + && (this->top < bottom) && (this->bottom > top); } -inline Rect& Rect::normalize() +inline bool Bound::normalize() { - if (width < 0) { - x += width + 1; - width = -width; + bool changed = false; + if (left > right) { + flipHorizonal(); + changed = false; } - if (height < 0) { - y += height + 1; - height = -height; + if (top > bottom) { + flipVertical(); + changed = true; } - - return *this; + return changed; } -inline Rect& Rect::transpose() +inline bool Bound::fixedNormalize() { - int temp = width; - width = height; - height = temp; - return *this; + bool changed = false; + if (left > right) { + int temp = left; + left = right + 1; + right = temp + 1; + changed = true; + } + + if (top > bottom) { + int temp = top; + top = bottom + 1; + bottom = temp + 1; + changed = true; + } + return changed; } -inline Rect& Rect::offset(int dx, int dy) +inline void Bound::flipHorizonal() { - x += dx; - y += dy; - return *this; + int temp = left; + left = right; + right = temp; } -inline Rect& Rect::offsetTo(int x, int y) +inline void Bound::flipVertical() { - this->x = x; - this->y = y; - return *this; + int temp = top; + top = bottom; + bottom = temp; } -inline Rect& Rect::inset(int dx, int dy) +inline Point Bound::xy() const { return Point(left, top); } +inline Size Bound::size() const { return Size(width(), height()); } + +inline Point Bound::center() const { return Point(centerX(), centerY()); } +inline Pointf Bound::exactCenter() const { return Pointf((float)exactCenterX(), (float)exactCenterY()); } +inline Point Bound::topLeft() const { return Point(left, top); } +inline Point Bound::topRight() const { return Point(right, top); } +inline Point Bound::bottomLeft() const { return Point(left, bottom); } +inline Point Bound::bottomRight() const { return Point(right, bottom); }; + +inline void Bound::transpose() { - x += dx; - y += dy; - width += 2 * dx; - height += 2 * dy; - return *this; + int w = width(); + setWidth(height()); + setHeight(w); } -inline Rect& Rect::outset(int dx, int dy) +inline void Bound::offset(int dx, int dy) { - inset(-dx, -dy); - return *this; + left += dx; + top += dy; + right += dx; + bottom += dy; } -inline bool Rect::isNull() const { return (width == 0) && (height == 0); } -inline bool Rect::isEmpty() const { return (width == 0) || (height == 0); } -inline bool Rect::isValid() const { return (width > 0) && (height > 0); } -inline bool Rect::isNormalized() const { return (width >= 0) && (height >= 0); } +inline void Bound::offsetTo(int x, int y) { setTopLeft(x, y); } +inline void Bound::offsetTo(const Point& point) { offsetTo(point.x, point.y); } -inline bool Rect::isOutOfRange() const +inline void Bound::inset(int margin) { inset(margin, margin, margin, margin); } +inline void Bound::inset(int dx, int dy) { inset(dx, dy, dx, dy); } + +inline void Bound::inset(int leftMargin, int topMargin, int rightMargin, int bottomMargin) { - return isOutOfRange(INT_MIN, INT_MAX, INT_MIN, INT_MAX); + left += leftMargin; + top += topMargin; + right -= rightMargin; + bottom -= bottomMargin; } -inline bool Rect::isXOutOfRange(int xMin, int xMax) const +inline void Bound::outset(int margin) { outset(margin, margin, margin, margin); } +inline void Bound::outset(int dx, int dy) { outset(dx, dy, dx, dy); } + +inline void Bound::outset(int leftMargin, int topMargin, int rightMargin, int bottomMargin) { - if ((xMin > xMax) || ((x < xMin) || (x > xMax))) - return true; + inset(-leftMargin, -topMargin, -rightMargin, -bottomMargin); +} - if (width > 0) { - if ((unsigned)(xMax - x) < (unsigned)(width - 1)) - return true; - } else if (width < 0) { - if ((unsigned)(x - xMin) < (unsigned)(-width - 1)) - return true; - } +inline void Bound::setXY(int x, int y) { setX(x); setY(y);} +inline void Bound::setSize(const Size& size) { setSize(size.width, size.height); } - return false; +inline void Bound::setSize(int width, int height) +{ + setWidth(width); + setHeight(height); } -inline bool Rect::isXOutOfRange() const +inline bool Bound::setLargeWidth(unsigned int width) { - return isXOutOfRange(INT_MIN, INT_MAX); + bool overflow = left > (int)(INT_MAX - width); + right = overflow ? INT_MAX : (int)(left + width); + + return !overflow; } -inline bool Rect::isYOutOfRange() const +inline bool Bound::setLargeHeight(unsigned int height) { - return isYOutOfRange(INT_MIN, INT_MAX); + bool overflow = top > (int)(INT_MAX - height); + bottom = overflow ? INT_MAX : (int)(top + height); + + return !overflow; } -inline bool Rect::isYOutOfRange(int yMin, int yMax) const +inline bool Bound::setLargeSize(unsigned int width, unsigned int height) { - if (yMin > yMax) - return true; + return setLargeHeight(height) & setLargeWidth(width); // Not && +} - if ((y < yMin) || (y > yMax)) - return true; - if (height > 0) { - if ((unsigned)(yMax - y) < (unsigned)(height - 1)) - return true; - } else if (height < 0) { - if ((unsigned)(y - yMin) < (unsigned)(-height - 1)) - return true; - } +inline void Bound::setTopLeft (int x, int y) { setLeft(x); setTop(y); } +inline void Bound::setTopRight (int x, int y) { setRight(x); setTop(y); } +inline void Bound::setBottomLeft (int x, int y) { setLeft(x); setBottom(y); } +inline void Bound::setBottomRight(int x, int y) { setRight(x); setBottom(y); } - return false; -} +inline void Bound::setTopLeft (const Point& point) { setTopLeft(point.x, point.y); } +inline void Bound::setTopRight (const Point& point) { setTopRight(point.x, point.y); } +inline void Bound::setBottomLeft (const Point& point) { setBottomLeft(point.x, point.y); } +inline void Bound::setBottomRight(const Point& point) { setBottomRight(point.x, point.y); } -inline bool Rect::isOutOfRange(int xMin, int xMax, int yMin, int yMax) const +inline void Bound::setLeftRight (int left, int right) { - return isXOutOfRange(xMin, xMax) || isYOutOfRange(yMin, yMax); + this->left = left; + this->right = right; } -inline bool Rect::contains(int x, int y) const +inline void Bound::setTopBottom (int top, int bottom) { - return ((x >= left()) && (x < right()) && (y >= top() && y < bottom())); + this->top = top; + this->bottom = bottom; } -inline bool Rect::contains(const Point& point) const +inline void Bound::leftAlign (int left) { setX(left); } +inline void Bound::topAlign (int top) { setY(top); } +inline void Bound::rightAlign (int right) { setX(right - width()); } +inline void Bound::bottomAlign(int bottom) { setY(bottom - height()); } + +inline void Bound::centerAlign(int x, int y) { - return contains(point.x, point.y); + setX(x + left - centerX()); + setY(y - top - centerY()); } -inline bool Rect::contains(const Rect& rect) const +inline void Bound::alignTo(const Point& point, Alignment alignment) { - return ( left() <= rect.left()) && ( top() <= rect.top()) - && (right() >= rect.right()) && (bottom() >= rect.bottom()); + alignTo(point.x, point.y, alignment); } -inline bool Rect::isOverlap(const Rect& rect) const +inline void Bound::alignTo(int x, int y, Alignment alignment) { - if (isEmpty() || rect.isEmpty()) - return false; - - if ((left() >= rect.right()) || (top() >= rect.bottom()) || (right() <= rect.left() || bottom() <= rect.top())) - return false; + unsigned int horizontalAlignment = (unsigned int)alignment & ALIGNMENT_HORIZONTAL_MASK; + if (horizontalAlignment != 0) { + switch(horizontalAlignment & (~horizontalAlignment + 1)) { + case Alignment_LEFT: leftAlign(x); break; + case Alignment_HMID: setX(x + left - centerX()); break; + case Alignment_RIGHT: rightAlign(x); break; + default: break; // Do nothing + } + } - return true; + unsigned int verticalAlignment = (unsigned int)alignment & ALIGNMENT_VERTICAL_MASK; + if (verticalAlignment != 0) { + switch(verticalAlignment & (~verticalAlignment + 1)) { + case Alignment_TOP: topAlign(y); break; + case Alignment_VMID: setY(y + top - centerY()); break; + case Alignment_BOTTOM: bottomAlign(y); break; + default: break; // Do nothing + } + } } -inline bool operator== (const Rect& a, const Rect& b) +inline void Bound::alignTo(const Bound& bound, Alignment alignment) { - return (a.x == b.x) && (a.y == b.y) && (a.width == b.width) && (a.height == b.height); + unsigned int horizontalAlignment = (unsigned int)alignment & ALIGNMENT_HORIZONTAL_MASK; + if (horizontalAlignment != 0) { + switch(horizontalAlignment & (~horizontalAlignment + 1)) { + case Alignment_LEFT: leftAlign(bound.left); break; + case Alignment_HMID: setX(bound.centerX() + left - centerX()); break; + case Alignment_RIGHT: rightAlign(bound.right); break; + default: break; // Do nothing + } + } + + unsigned int verticalAlignment = (unsigned int)alignment & ALIGNMENT_VERTICAL_MASK; + if (verticalAlignment != 0) { + switch(verticalAlignment & (~verticalAlignment + 1)) { + case Alignment_TOP: topAlign(bound.top); break; + case Alignment_VMID: setY(bound.centerY() + top - centerY()); break; + case Alignment_BOTTOM: bottomAlign(bound.bottom); break; + default: break; // Do nothing + } + } } -inline bool operator!= (const Rect& a, const Rect& b) +inline void Bound::scale(float scale) { this->scale(scale, scale); } + +inline void Bound::scale(float xScale, float yScale) { - return !(a == b); + right = left + EGE_TEMP_ROUND(((double)right - left) * xScale); + bottom = top + EGE_TEMP_ROUND(((double)bottom - top) * yScale); } -#endif + +inline void Bound::scale(float xScale, float yScale, Pointf center) +{ + left = EGE_TEMP_ROUND(center.x + ((double)left - center.x) * xScale); + top = EGE_TEMP_ROUND(center.y + ((double)top - center.y) * yScale); + right = EGE_TEMP_ROUND(center.x + ((double)right - center.x) * xScale); + bottom = EGE_TEMP_ROUND(center.y + ((double)bottom - center.y) * yScale); +} + +inline bool Bound::intersect(const Bound& bound) +{ + /* Unlike isOverlaps(), this only checks for no overlaps at all + * and allows edges to overlap. */ + if ((left > bound.right) || (top > bound.bottom) || (right < bound.left) || (bottom < bound.top)) + { + setEmpty(); + return false; + } + + int left = EGE_TEMP_MAX(this->left, bound.left); + int top = EGE_TEMP_MAX(this->top, bound.top); + int right = EGE_TEMP_MIN(this->right, bound.right); + int bottom = EGE_TEMP_MIN(this->bottom, bound.bottom); + + set(left, top, right, bottom); + + return !isEmpty(); +} + +inline bool Bound::intersect(const Rect& rect) +{ + return intersect(Bound(clip(rect))); +} + +inline bool Bound::intersect(int left, int top, int right, int bottom) +{ + return intersect(Bound(left, top, right, bottom)); +} + +inline void Bound::unite(const Point& point) +{ + unite(point.x, point.y); +} + +inline void Bound::unite(int x, int y) +{ + if (x < left) { + setLeft(x); + } else if (x > right) { + setRight(x); + } + + if (y < top) { + setTop(y); + } else if (y > bottom) { + setBottom(y); + } +} + +inline void Bound::unite(const Point points[], int length) +{ + for (int i = 0; i < length; i++) + unite(points[i]); +} + +inline void Bound::unite(const Bound& bound) +{ + if (bound.isEmpty()) { + return; + } + + if (isEmpty()) { + *this = bound; + return; + } + + int left = EGE_TEMP_MIN(this->left, bound.left); + int top = EGE_TEMP_MIN(this->top, bound.top); + int right = EGE_TEMP_MAX(this->right, bound.right); + int bottom = EGE_TEMP_MAX(this->bottom, bound.bottom); + + set(left, top, right, bottom, false); +} + +inline Bound unite(const Bound& a, const Bound& b) +{ + Bound c(a); + c.unite(b); + return c; +} + +inline Bound getBounds(const Point points[], int length) +{ + if (length <= 0) + return Bound(); + + int left = points[0].x; + int top = points[0].y; + int right = left; + int bottom = top; + + for (int i = 1; i < length; i++) { + if (points[i].x < left) { + left = points[i].x; + } else if (points[i].x > right) { + right = points[i].x; + } + + if (points[i].y < top) { + top = points[i].y; + } else if (points[i].y > bottom) { + bottom = points[i].y; + } + } + + return Bound(left, top, right, bottom, false); +} + +inline Bound intersect(const Bound& a, const Bound& b) +{ + Bound c(a); + c.intersect(b); + return c; +} + +inline Bound intersect(const Rect& a, const Bound& b) +{ + return intersect(Bound(clip(a)), b); +} + +inline Bound intersect(const Bound& a, const Rect& b) +{ + return intersect(a, Bound(clip(b))); +} + +inline Bound offset(const Bound& bound, int dx, int dy) +{ + return Bound(bound.left + dx, bound.top + dy, bound.right + dx, bound.bottom + dy, false); +} + +//------------------------------------------------------------------------------ +// Rect //------------------------------------------------------------------------------ +inline Rect::Rect(): x(0), y(0), width(0), height(0) {} + +inline Rect::Rect(int x, int y, int width, int height, bool normalize) + : x(x), y(y), width(width), height(height) +{ + if (normalize) + this->normalize(); +} +inline Rect::Rect(const Point& topLeft, const Size& size, bool normalize) + : x(topLeft.x), y(topLeft.y), width(size.width), height(size.height) +{ + if (normalize) + this->normalize(); } + +inline Rect::Rect(const Bound& bound) + : x(bound.x()), y(bound.y()), width(bound.width()), height(bound.height()) +{ } + +inline int Rect::left() const { return x; } +inline int Rect::top() const { return y; } +inline int Rect::right() const { return x + width; } +inline int Rect::bottom() const { return y + height; } + +inline Point Rect::topLeft() const { return Point(x, y); } +inline Point Rect::topRight() const { return Point(x + width, y); } +inline Point Rect::bottomLeft() const { return Point(x, y + height); } +inline Point Rect::bottomRight() const { return Point(x + width, y + height); } + +inline Point Rect::xy() const { return Point(x, y); } +inline Size Rect::size() const { return Size(width, height); } + +inline int Rect::centerX() const { return x + width / 2; } +inline int Rect::centerY() const { return y + height / 2; } +inline Point Rect::center() const { return Point(centerX(), centerY()); } + +inline double Rect::exactCenterX() const { return x + 0.5 * width; } +inline double Rect::exactCenterY() const { return y + 0.5 * height; } +inline Pointf Rect::exactCenter() const { return Pointf((float)exactCenterX(), (float)exactCenterY()); } + +inline void Rect::setX(int x) { this->x = x; } +inline void Rect::setY(int y) { this->x = y; } +inline void Rect::setXY(int x, int y) { this->x = x; this->y = y; } +inline void Rect::setWidth(int width) { this->width = width; } +inline void Rect::setHeight(int height){ this->height = height; } + +inline void Rect::setLeft(int left) +{ + width -= left - this->left(); + x = left; +} + +inline void Rect::setTop(int top) +{ + height -= top - this->top(); + y = top; +} + +inline void Rect::setRight(int right) +{ + width = right - left(); +} + +inline void Rect::setBottom(int bottom) +{ + height = bottom - top(); +} + +inline void Rect::setSize(const Size& size) +{ + setSize(size.width, size.height); +} + +inline void Rect::setSize(int width, int height) +{ + this->width = width; + this->height = height; +} + +inline void Rect::setTopLeft (const Point& point) { setTopLeft(point.x, point.y); } +inline void Rect::setTopRight (const Point& point) { setTopRight(point.x, point.y); } +inline void Rect::setBottomLeft (const Point& point) { setBottomLeft(point.x, point.y); } +inline void Rect::setBottomRight(const Point& point) { setBottomRight(point.x, point.y); } +inline void Rect::setTopLeft (int x, int y) { setLeft(x); setTop(y); } +inline void Rect::setTopRight (int x, int y) { setRight(x); setTop(y); } +inline void Rect::setBottomLeft (int x, int y) { setLeft(x); setBottom(y); } +inline void Rect::setBottomRight(int x, int y) { setRight(x); setBottom(y); } + +inline void Rect::setLeftRight(int left, int right) +{ + x = left; + width = right - left; +} + +inline void Rect::setTopBottom(int top, int bottom) +{ + y = top; + height = bottom - top; +} + +inline void Rect::set(int x, int y, int width, int height, bool normalize) +{ + this->x = x; + this->y = y; + this->width = width; + this->height = height; + + if (normalize) + this->normalize(); +} + +inline void Rect::set(const Point& topLeft, const Size& size, bool normalize) +{ + setTopLeft(topLeft); + setSize(size); + + if (normalize) + this->normalize(); +} + +inline void Rect::setBound(const Bound& bound) +{ + x = bound.left; + y = bound.top; + width = bound.right - bound.left; + height = bound.bottom - bound.top; +} + +inline void Rect::setBound(int left, int top, int right, int bottom, bool normalize) +{ + setBound(Bound(left, top, right, bottom, normalize)); +} + +inline void Rect::setBound(const Point& topLeft, const Point& bottomRight, bool normalize) +{ + setBound(Bound(topLeft, bottomRight, normalize)); +} + +inline void Rect::setEmpty() +{ + x = y = width = height = 0; +} + +inline void Rect::leftAlign (int left) { x = left; } +inline void Rect::topAlign (int top) { y = top; } +inline void Rect::rightAlign (int right) { x = right - width; } +inline void Rect::bottomAlign(int bottom) { y = bottom - height; } + +inline void Rect::centerAlign(int x, int y) +{ + centerAlign(Point(x, y)); +} + +inline void Rect::centerAlign(Point point) +{ + x = point.x - width / 2; + y = point.y - height / 2; +} + +inline void Rect::alignTo(const Point& point, Alignment alignment) +{ + unsigned int horizontalAlignment = (unsigned int)alignment & ALIGNMENT_HORIZONTAL_MASK; + if (horizontalAlignment != 0) { + switch(horizontalAlignment & (~horizontalAlignment + 1)) { + case Alignment_LEFT: leftAlign(point.x); break; + case Alignment_HMID: x = point.x - width / 2; break; + case Alignment_RIGHT: rightAlign(point.x); break; + default: break; // Do nothing + } + } + + unsigned int verticalAlignment = (unsigned int)alignment & ALIGNMENT_VERTICAL_MASK; + if (verticalAlignment != 0) { + switch(verticalAlignment & (~verticalAlignment + 1)) { + case Alignment_TOP: topAlign(point.y); break; + case Alignment_VMID: y = point.y - height / 2; break; + case Alignment_BOTTOM: bottomAlign(point.y); break; + default: break; // Do nothing + } + } +} + +inline void Rect::alignTo(const Rect& rect, Alignment alignment) +{ + unsigned int horizontalAlignment = ((unsigned int)alignment & ALIGNMENT_HORIZONTAL_MASK); + if (horizontalAlignment != 0) { + switch(horizontalAlignment & (~horizontalAlignment + 1)) { + case Alignment_LEFT: leftAlign(rect.left()); break; + case Alignment_HMID: x = rect.centerX() - width / 2; break; + case Alignment_RIGHT: rightAlign(rect.right()); break; + default: break; // Do nothing. + } + } + + unsigned int verticalAlignment = ((unsigned int)alignment & ALIGNMENT_VERTICAL_MASK); + if (verticalAlignment != 0) { + switch(verticalAlignment & (~horizontalAlignment + 1)) { + case Alignment_TOP: topAlign(rect.top()); break; + case Alignment_VMID: y = rect.centerY() - height / 2; break; + case Alignment_BOTTOM: bottomAlign(rect.bottom()); break; + default: break; // Do nothing. + } + } +} + +inline bool Rect::normalize() +{ + bool changed = false; + if (width < 0) { + x += width; + width = -width; + changed = true; + } + + if (height < 0) { + y += height; + height = -height; + changed = true; + } + + return changed; +} + +inline bool Rect::fixedNormalize() +{ + bool changed = false; + if (width < 0) { + x = x + width + 1; + width = -width; + changed = true; + } + + if (height < 0) { + y = y + height + 1; + height = -height; + changed = true; + } + + return changed; +} + +inline void Rect::transpose() +{ + int temp = width; + width = height; + height = temp; +} + +inline void Rect::offset(int dx, int dy) +{ + x += dx; + y += dy; +} + +inline void Rect::offsetTo(int x, int y) +{ + offsetTo(Point(x, y)); +} + +inline void Rect::offsetTo(const Point& point) +{ + x = point.x; + y = point.y; +} + +inline void Rect::inset(int margin) { inset(margin, margin); } +inline void Rect::inset(int dx, int dy) { inset(dx, dy, dx, dy); } + +inline void Rect::inset(int leftMargin, int topMargin, int rightMargin, int bottomMargin) +{ + x += leftMargin; + y += topMargin; + width -= (leftMargin + rightMargin); + height -= (rightMargin + bottomMargin); +} + +inline void Rect::outset(int margin) { inset(-margin); } +inline void Rect::outset(int dx, int dy) { inset(-dx, -dy); } +inline void Rect::outset(int leftMargin, int topMargin, int rightMargin, int bottomMargin) +{ + inset(-leftMargin, -topMargin, -rightMargin, -bottomMargin); +} + +inline void Rect::scale(float scale) +{ + this->scale(scale, scale); +} + +inline void Rect::scale(float xScale, float yScale) +{ + double w = ((double)width * xScale); + double h = ((double)height * yScale); + width = (int)((w > 0.0) ? (w + 0.5) : (w - 0.5)); + height = (int)((h > 0.0) ? (h + 0.5) : (h - 0.5)); +} + +inline void Rect::scale(float xScale, float yScale, Pointf center) +{ + double x1 = ((double)x - center.x) * xScale + center.x; + double y1 = ((double)y - center.y) * xScale + center.y; + double w = (double)width * xScale; + double h = (double)height * yScale; + + x = (int)((x1 > 0.0) ? (x1 + 0.5) : (x1 - 0.5)); + y = (int)((y1 > 0.0) ? (y1 + 0.5) : (y1 - 0.5)); + width = (int)((w > 0.0) ? (w + 0.5) : (w - 0.5)); + height = (int)((h > 0.0) ? (h + 0.5) : (h - 0.5)); +} + +inline bool Rect::isNull() const { return (width == 0) && (height == 0); } +inline bool Rect::isEmpty() const { return (width == 0) || (height == 0); } +inline bool Rect::isValid() const { return (width > 0) && (height > 0); } +inline bool Rect::isNormalized() const { return (width >= 0) && (height >= 0); } + +inline bool Rect::isOutOfRange() const +{ + return isXOutOfRange() || isYOutOfRange(); +} + +inline bool Rect::isXOutOfRange(int xMin, int xMax) const +{ + if ((xMin > xMax) || (x < xMin) || (x > xMax)) + return true; + + if (width == 0) { + return false; + } else if (width > 0) { + return (unsigned)(xMax - x) < (unsigned)(width - 1); + } else { + return (unsigned)(x - xMin) < (unsigned)(-(width + 1)); + } +} + +inline bool Rect::isXOutOfRange() const +{ + return (width >= 0) ? (x > INT_MAX - width) : (x < INT_MIN - width); +} + +inline bool Rect::isYOutOfRange() const +{ + return (height >= 0) ? (y > INT_MAX - height) : (y < INT_MIN - height); +} + +inline bool Rect::isYOutOfRange(int yMin, int yMax) const +{ + if ((yMin > yMax) || (y < yMin) || (y > yMax)) + return true; + + if (height == 0) { + return false; + } else if (height > 0) { + return (unsigned)(yMax - y) < (unsigned)(height - 1); + } else { + return (unsigned)(y - yMin) < (unsigned)(-(height + 1)); + } +} + +inline bool Rect::isOutOfRange(int xMin, int xMax, int yMin, int yMax) const +{ + return isXOutOfRange(xMin, xMax) || isYOutOfRange(yMin, yMax); +} + +inline bool Rect::isContains(int x, int y) const +{ + return isContains(Point(x, y)); +} + +inline bool Rect::isContains(const Point& point) const +{ + return (point.x >= x) && ((unsigned)(point.x - x) < width) + && (point.y >= x) && ((unsigned)(point.y - y) < height); +} + +inline bool Rect::isContains(int x, int y, int width, int height) const +{ + return isContains(Rect(x, y, width, height, false)); +} + +/** + * @brief Determines whether this rectangle contains another rectangle. + * @param + * @return {true: This rectangle contains the specified rectangle. } + * {false: This rectangle does not contain the specified rectangle. } + * @pre Both rectangles must be normalized. + * + */ +inline bool Rect::isContains(const Rect& rect) const +{ + if ((rect.x < x) || (rect.y < y)) + return false; + + Rect a(*this), b(rect); + a.offset(INT_MIN - x, INT_MIN - y); + b.offset(INT_MIN - x, INT_MIN - y); + + return (a.right() > b.left()) && (a.bottom() > b.top()) + && (a.right() >= b.right()) && (a.bottom() >= b.bottom()); +} + +/** + * @brief Determine whether two rectangular regions overlap (i.e., the intersection is non-empty). + * @param rect The rectangle to test for intersection with this rectangle. + * @pre Both rectangles must be normalized. + */ +inline bool Rect::isOverlaps(const Rect& rect) const +{ + if ((EGE_TEMP_DIFF_UINT(x, rect.x) >= INT_MAX) || (EGE_TEMP_DIFF_UINT(y, rect.y) >= INT_MAX)) + return false; + + int xOffset = INT_MIN - EGE_TEMP_MIN(left(), rect.left()); + int yOffset = INT_MIN - EGE_TEMP_MIN(top(), rect.top()); + + Rect a(*this), b(rect); + a.offset(xOffset, yOffset); + b.offset(xOffset, yOffset); + + return (a.left() < b.right()) && (b.left() < a.right()) + && (a.top() < b.bottom()) && (b.top() < a.bottom()); +} + +inline bool Rect::isOverlaps(int x, int y, int width, int height) const +{ + return isOverlaps(Rect(x, y, width, height, false)); +} + +inline bool Rect::intersect(int x, int y, int width, int height) +{ + return intersect(Rect(x, y, width, height, false)); +} + +/** + * @brief Update this rect to be the intersection with another rect. + * @param rect Target rect. + * @return Whether the intersection is empty. + * @pre Both rect must be normalized. + */ +inline bool Rect::intersect(const Rect& rect) +{ + if ((EGE_TEMP_DIFF_UINT(x, rect.x) > INT_MAX) || (EGE_TEMP_DIFF_UINT(y, rect.y) > INT_MAX)) { + setEmpty(); + return false; + } + + int xOffset = INT_MIN - EGE_TEMP_MIN(left(), rect.left()); + int yOffset = INT_MIN - EGE_TEMP_MIN(top(), rect.top()); + + Rect a(*this), b(rect); + a.offset(xOffset, yOffset); + b.offset(xOffset, yOffset); + + int left = EGE_TEMP_MAX(a.left(), b.left()); + int top = EGE_TEMP_MAX(a.top(), b.top()); + int right = EGE_TEMP_MIN(a.right(), b.right()); + int bottom = EGE_TEMP_MIN(a.bottom(), b.bottom()); + + set(left - xOffset, top + yOffset, right - left, bottom - top); + + return !isEmpty(); +} + +inline bool Rect::intersect(const Bound& bound) +{ + Bound b(bound); + bool nonEmpty = b.intersect(*this); + setBound(b); + return nonEmpty; +} + +/** + * @brief update this rect to be the union with another rect. + * @param rect target rect. + * @return The width and height of the union are within the range([0, INT_MAX]). + * @pre both rect must be normalized. + */ +inline bool Rect::unite(const Rect& rect) +{ + if (rect.isEmpty()) + return true; + + if (isEmpty()) { + *this = rect; + return true; + } + + int xOffset = INT_MIN - EGE_TEMP_MIN(left(), rect.left()); + int yOffset = INT_MIN - EGE_TEMP_MIN(top(), rect.top()); + + Rect a(*this), b(rect); + a.offset(xOffset, yOffset); + b.offset(xOffset, yOffset); + + int left = EGE_TEMP_MIN(a.left(), b.left()); + int top = EGE_TEMP_MIN(a.top(), b.top()); + int right = EGE_TEMP_MAX(a.right(), b.right()); + int bottom = EGE_TEMP_MAX(a.bottom(), b.bottom()); + + set(left - xOffset, top - yOffset, right - left, bottom - top); + + return (EGE_TEMP_DIFF_UINT(a.x, b.x) <= INT_MAX) + && (EGE_TEMP_DIFF_UINT(a.y, b.y) <= INT_MAX) + && ((unsigned)right - left <= INT_MAX) + && ((unsigned)bottom - top <= INT_MAX); +} + +inline bool Rect::unite(int x, int y, int width, int height) +{ + return unite(Rect(x, y, width, height)); +} + +inline bool Rect::unite(int x, int y) +{ + return unite(Point(x, y)); +} + +/** + * @brief + * @param point target point + * @return The width and height of the union are within the range([0, INT_MAX]). + * @pre The rectangle must be normalized. + */ +inline bool Rect::unite(const Point& point) +{ + int xOffset = INT_MIN - EGE_TEMP_MIN(x, point.x); + int yOffset = INT_MIN - EGE_TEMP_MIN(y, point.y); + + Rect r(x + xOffset, y + yOffset, width, height); + Point p(point.x + xOffset, point.y + yOffset); + + int left = INT_MIN; + int top = INT_MIN; + int right = EGE_TEMP_MAX(this->right(), p.x); + int bottom = EGE_TEMP_MAX(this->bottom(), p.y); + + set(left - xOffset, top - yOffset, right - left, bottom - top); + + return (INT_MAX - r.width >= r.x) + && (INT_MAX - r.height >= r.y) + && ((unsigned)right - left <= INT_MAX) + && ((unsigned)bottom - top <= INT_MAX); +} + +inline bool Rect::unite(const Point points[], int length) +{ + if (length <= 0) + return true; + + Bound bound = getBounds(points, length); + + return unite(Rect(bound)) && !bound.isOutOfRange(); +} + +inline void Rect::clip(int xMin, int xMax, int yMin, int yMax) +{ + clipX(xMin, xMax); + clipY(yMin, yMax); +} + +inline void Rect::clipX(int xMin, int xMax) +{ + clipByLimits(x, width, xMin, xMax); +} + +inline void Rect::clipY(int yMin, int yMax) +{ + clipByLimits(y, height, yMin, yMax); +} + +inline void Rect::clip() +{ + clipX(); + clipY(); +} + +inline void Rect::clipX() +{ + if (width >= 0) { + if (INT_MAX - width < x) + width = INT_MAX - x; + } else { + if (INT_MIN - width > x) + width = INT_MIN - x; + } +} + +inline void Rect::clipY() +{ + if (height >= 0) { + if (INT_MAX - height < y) + height = INT_MAX - y; + } else { + if (INT_MIN - height > y) + height = INT_MIN - y; + } +} + +/** + * @brief 对起点为 x 、长度为 length(整数范围)的线段进行裁剪,使线段限制在指定范围内, + * @details - 如果线段在指定范围内,经裁剪后的线段由 x, length 返回,返回值为 true。 + * - 如果指定范围为空集(即 minval < maxval) 或者线段完全在范围外,则不改变线段并返回 false。 + * - 裁剪后线段保持原来方向。 + * + * @param[in, out] x 线段起点,裁剪后为新线段的起点 + * @param[in, out] length 线段长度(可正可负),裁剪后为新线段的长度 + * @param[in] minval 裁剪后线段的最小值(包含) + * @param[in] maxval 裁剪后线段的最大值(包含) + * + * @return false: 线段完全位于指定范围之外 + * @return true: 线段有一部分()位于指定范围内 + * + * @note 线段起点 x, 终点 x+length (包含起点,不包含终点)。 + */ +inline bool Rect::clipByLimits(int& x, int& length, int minval, int maxval) +{ + if (minval > maxval) + return false; + + if (length == 0) { + return (minval <= x) && (x <= maxval); + } + + int left, right; + + if (length > 0) { + left = x; + right = (INT_MAX - (length - 1) < x) ? INT_MAX : (x + length - 1); + } else { + left = (INT_MIN - (length + 1) > x) ? INT_MIN : (x + length + 1); + right = x; + } + + if ((left > maxval) || (right < maxval)) + return false; + + int clipLeft = EGE_TEMP_MAX(left, minval); + int clipRight = EGE_TEMP_MIN(right, maxval); + + if (length > 0) { + x = clipLeft; + length = clipRight - clipLeft + 1; + } else { + x = clipRight; + length = clipLeft - clipRight - 1; + } + + return true; +} + +//------------------------------------------------------------------------------ + +inline bool operator== (const Rect& a, const Rect& b) +{ + return (a.x == b.x) && (a.y == b.y) && (a.width == b.width) && (a.height == b.height); +} + +inline bool operator!= (const Rect& a, const Rect& b) +{ + return !(a == b); +} + +inline Rect clip(const Rect& rect) +{ + Rect r(rect); + r.clip(); + return r; +} + +inline Rect offset(const Rect& rect, int dx, int dy) +{ + return Rect(rect.x + dx, rect.y + dy, rect.width, rect.height, false); +} + +inline Rect normalize(const Rect& rect) +{ + Rect r(rect); + r.normalize(); + return r; +} + + +inline Rect intersect(const Rect& a, const Rect& b) +{ + Rect c(a); + c.intersect(b); + return c; +} + +inline Rect unite(const Rect& a, const Rect& b) +{ + Rect c(a); + c.unite(b); + return c; +} + +inline Bound getBounds(const Point& a, const Point& b) +{ + int left, top, right, bottom; + if (a.x <= b.x) { + left = a.x; + right = b.x; + } else { + left = b.x; + right = a.x; + } + + if (a.y <= b.y) { + top = a.y; + bottom = b.y; + } else { + top = b.y; + bottom = a.y; + } + + return Bound(left, top, right, bottom, false); +} + +//------------------------------------------------------------------------------ + +} + +#undef EGE_TEMP_MIN +#undef EGE_TEMP_MAX +#undef EGE_TEMP_MIDPOINT_INT +#undef EGE_TEMP_DIFF_UINT +#undef EGE_TEMP_ROUND