1 /* ScummVM - Graphic Adventure Engine 2 * 3 * ScummVM is the legal property of its developers, whose names 4 * are too numerous to list here. Please refer to the COPYRIGHT 5 * file distributed with this source distribution. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #ifndef COMMON_RECT_H 24 #define COMMON_RECT_H 25 26 #include "common/scummsys.h" 27 #include "common/util.h" 28 #include "common/debug.h" 29 30 #define PRINT_RECT(x) (x).left,(x).top,(x).right,(x).bottom 31 32 namespace Common { 33 34 /** 35 * @defgroup common_rect Rectangular zones 36 * @ingroup common 37 * 38 * @brief API for operations on rectangular zones. 39 * 40 * @{ 41 */ 42 43 /** 44 * Simple class for handling both 2D position and size. 45 */ 46 struct Point { 47 int16 x; /*!< The horizontal position of the point. */ 48 int16 y; /*!< The vertical position of the point. */ 49 PointPoint50 Point() : x(0), y(0) {} 51 52 /** 53 * Create a point with position defined by @p x1 and @p y1. 54 */ PointPoint55 Point(int16 x1, int16 y1) : x(x1), y(y1) {} 56 /** 57 * Determine whether the position of two points is the same. 58 */ 59 bool operator==(const Point &p) const { return x == p.x && y == p.y; } 60 /** 61 * Determine whether the position of two points is not the same. 62 */ 63 bool operator!=(const Point &p) const { return x != p.x || y != p.y; } 64 /** 65 * Create a point by adding the @p delta value to a point. 66 */ 67 Point operator+(const Point &delta) const { return Point(x + delta.x, y + delta.y); } 68 /** 69 * Create a point by subtracting the @p delta value from a point. 70 */ 71 Point operator-(const Point &delta) const { return Point(x - delta.x, y - delta.y); } 72 /** 73 * Create a point by dividing a point by the (int) @p divisor value. 74 */ 75 Point operator/(int divisor) const { return Point(x / divisor, y / divisor); } 76 /** 77 * Create a point by multiplying a point by the (int) @p multiplier value. 78 */ 79 Point operator*(int multiplier) const { return Point(x * multiplier, y * multiplier); } 80 /** 81 * Create a point by dividing a point by the (double) @p divisor value. 82 */ 83 Point operator/(double divisor) const { return Point((int16)(x / divisor), (int16)(y / divisor)); } 84 /** 85 * Create a point by multiplying a point by the (double) @p multiplier value. 86 */ 87 Point operator*(double multiplier) const { return Point((int16)(x * multiplier), (int16)(y * multiplier)); } 88 89 /** 90 * Change a point's position by adding @p delta to its x and y coordinates. 91 */ 92 void operator+=(const Point &delta) { 93 x += delta.x; 94 y += delta.y; 95 } 96 97 /** 98 * Change a point's position by subtracting @p delta from its x and y arguments. 99 */ 100 void operator-=(const Point &delta) { 101 x -= delta.x; 102 y -= delta.y; 103 } 104 105 /** 106 * Return the square of the distance between this point and the point @p p. 107 * 108 * @param p The other point. 109 * @return The distance between this and @p p. 110 */ sqrDistPoint111 uint sqrDist(const Point &p) const { 112 int diffx = ABS(p.x - x); 113 if (diffx >= 0x1000) 114 return 0xFFFFFF; 115 116 int diffy = ABS(p.y - y); 117 if (diffy >= 0x1000) 118 return 0xFFFFFF; 119 120 return uint(diffx * diffx + diffy * diffy); 121 } 122 }; 123 124 static inline Point operator*(int multiplier, const Point &p) { return Point(p.x * multiplier, p.y * multiplier); } 125 static inline Point operator*(double multiplier, const Point &p) { return Point((int16)(p.x * multiplier), (int16)(p.y * multiplier)); } 126 127 /** 128 * Simple class for handling a rectangular zone. 129 * 130 * Note: This implementation is built around the assumption that (top,left) is 131 * part of the rectangle, but (bottom,right) is not. This is reflected in 132 * various methods, including contains(), intersects(), and others. 133 * 134 * Another very widespread approach to rectangle classes treats (bottom,right) 135 * also as a part of the rectangle. 136 * 137 * Conceptually, both are sound, but the approach we use saves many intermediate 138 * computations (like computing the height in our case is done by doing this: 139 * height = bottom - top; 140 * while in the alternate system, it would be 141 * height = bottom - top + 1; 142 * 143 * When writing code using our Rect class, always keep this principle in mind! 144 */ 145 struct Rect { 146 int16 top, left; /*!< The point at the top left of the rectangle (part of the Rect). */ 147 int16 bottom, right; /*!< The point at the bottom right of the rectangle (not part of the Rect). */ 148 RectRect149 Rect() : top(0), left(0), bottom(0), right(0) {} 150 /** 151 * Create a rectangle with the top-left corner at position (0, 0) and the given width @p w and height @p h. 152 */ RectRect153 Rect(int16 w, int16 h) : top(0), left(0), bottom(h), right(w) {} 154 /** 155 * Create a rectangle with the top-left corner at the given position (x1, y1) 156 * and the bottom-right corner at the position (x2, y2). 157 * 158 * The @p x2 value must be greater or equal @p x1 and @p y2 must be greater or equal @p y1. 159 */ RectRect160 Rect(int16 x1, int16 y1, int16 x2, int16 y2) : top(y1), left(x1), bottom(y2), right(x2) { 161 assert(isValidRect()); 162 } 163 /** 164 * Check if two rectangles are identical. 165 * 166 * @return True if the rectangles are identical, false otherwise. 167 */ 168 bool operator==(const Rect &rhs) const { return equals(rhs); } 169 /** 170 * Check if two rectangles are different. 171 * 172 * @return True if the rectangles are different, false otherwise. 173 */ 174 bool operator!=(const Rect &rhs) const { return !equals(rhs); } 175 widthRect176 int16 width() const { return right - left; } /*!< Return the width of a rectangle. */ heightRect177 int16 height() const { return bottom - top; } /*!< Return the height of a rectangle. */ 178 setWidthRect179 void setWidth(int16 aWidth) { /*!< Set the width to @p aWidth value. */ 180 right = left + aWidth; 181 } 182 setHeightRect183 void setHeight(int16 aHeight) { /*!< Set the height to @p aHeight value. */ 184 bottom = top + aHeight; 185 } 186 187 /** 188 * Check if the given position is inside this rectangle. 189 * 190 * @param x The horizontal position to check. 191 * @param y The vertical position to check. 192 * 193 * @return True if the given position is inside this rectangle, false otherwise. 194 */ containsRect195 bool contains(int16 x, int16 y) const { 196 return (left <= x) && (x < right) && (top <= y) && (y < bottom); 197 } 198 199 /** 200 * Check if the given point is inside this rectangle. 201 * 202 * @param p The point to check. 203 * 204 * @return True if the given point is inside this rectangle, false otherwise. 205 */ containsRect206 bool contains(const Point &p) const { 207 return contains(p.x, p.y); 208 } 209 210 /** 211 * Check if the given Rect is contained inside this rectangle. 212 * 213 * @param r The rectangle to check. 214 * 215 * @return True if the given Rect is inside, false otherwise. 216 */ containsRect217 bool contains(const Rect &r) const { 218 return (left <= r.left) && (r.right <= right) && (top <= r.top) && (r.bottom <= bottom); 219 } 220 221 /** 222 * Check if the given Rect is equal to this one. 223 * 224 * @param r The rectangle to check. 225 * 226 * @return true If the given Rect is equal, false otherwise. 227 */ equalsRect228 bool equals(const Rect &r) const { 229 return (left == r.left) && (right == r.right) && (top == r.top) && (bottom == r.bottom); 230 } 231 232 /** 233 * Check if the given rectangle intersects with this rectangle. 234 * 235 * @param r The rectangle to check. 236 * 237 * @return True if the given rectangle has a non-empty intersection with 238 * this rectangle, false otherwise. 239 */ intersectsRect240 bool intersects(const Rect &r) const { 241 return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom); 242 } 243 244 /** 245 * Find the intersecting rectangle between this rectangle and the given rectangle. 246 * 247 * @param r The intersecting rectangle. 248 * 249 * @return The intersection of the rectangles or an empty rectangle if not intersecting. 250 */ findIntersectingRectRect251 Rect findIntersectingRect(const Rect &r) const { 252 if (!intersects(r)) 253 return Rect(); 254 255 return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom)); 256 } 257 258 /** 259 * Extend this rectangle so that it contains @p r. 260 * 261 * @param r The rectangle to extend by. 262 */ extendRect263 void extend(const Rect &r) { 264 left = MIN(left, r.left); 265 right = MAX(right, r.right); 266 top = MIN(top, r.top); 267 bottom = MAX(bottom, r.bottom); 268 } 269 270 /** 271 * Extend this rectangle in all four directions by the given number of pixels. 272 * 273 * @param offset The size to grow by. 274 */ growRect275 void grow(int16 offset) { 276 top -= offset; 277 left -= offset; 278 bottom += offset; 279 right += offset; 280 } 281 282 /** 283 * Clip this rectangle with another rectangle @p r. 284 */ clipRect285 void clip(const Rect &r) { 286 assert(isValidRect()); 287 assert(r.isValidRect()); 288 289 if (top < r.top) top = r.top; 290 else if (top > r.bottom) top = r.bottom; 291 292 if (left < r.left) left = r.left; 293 else if (left > r.right) left = r.right; 294 295 if (bottom > r.bottom) bottom = r.bottom; 296 else if (bottom < r.top) bottom = r.top; 297 298 if (right > r.right) right = r.right; 299 else if (right < r.left) right = r.left; 300 } 301 302 /** 303 * Reduce the dimensions of this rectangle by setting max width and max heigth. 304 */ clipRect305 void clip(int16 maxw, int16 maxh) { 306 clip(Rect(0, 0, maxw, maxh)); 307 } 308 309 /** 310 * Check if the rectangle is empty (its width or length is 0) or invalid (its width or length are negative). 311 * 312 * @retval true The rectangle is empty or invalid. 313 * @retval false The rectangle is valid and not empty. 314 */ isEmptyRect315 bool isEmpty() const { 316 return (left >= right || top >= bottom); 317 } 318 319 /** 320 * Check if this is a valid rectangle. 321 */ isValidRectRect322 bool isValidRect() const { 323 return (left <= right && top <= bottom); 324 } 325 326 /** 327 * Move this rectangle to the position defined by @p x, @p y. 328 */ moveToRect329 void moveTo(int16 x, int16 y) { 330 bottom += y - top; 331 right += x - left; 332 top = y; 333 left = x; 334 } 335 336 /** 337 * Move the rectangle by the given delta x and y values. 338 */ translateRect339 void translate(int16 dx, int16 dy) { 340 left += dx; right += dx; 341 top += dy; bottom += dy; 342 } 343 344 /** 345 * Move this rectangle to the position of the point @p p. 346 */ moveToRect347 void moveTo(const Point &p) { 348 moveTo(p.x, p.y); 349 } 350 351 /** 352 * Print debug messages related to this class. 353 */ 354 void debugPrint(int debuglevel = 0, const char *caption = "Rect:") const { 355 debug(debuglevel, "%s %d, %d, %d, %d", caption, left, top, right, bottom); 356 } 357 358 /** 359 * Create a rectangle around the given center. 360 * @note The center point is rounded up and left when given an odd width and height. 361 */ centerRect362 static Rect center(int16 cx, int16 cy, int16 w, int16 h) { 363 int x = cx - w / 2, y = cy - h / 2; 364 return Rect(x, y, x + w, y + h); 365 } 366 367 /** 368 * Given target surface with size clip, this function ensures that 369 * blit arguments @p dst and @p rect are within the @p clip rectangle. 370 * @param dst Blit destination coordinates. 371 * @param rect Blit source rectangle. 372 * @param clip Clip rectangle (size of destination surface). 373 */ getBlitRectRect374 static bool getBlitRect(Point &dst, Rect &rect, const Rect &clip) { 375 if (dst.x < clip.left) { 376 rect.left += clip.left - dst.x; 377 dst.x = clip.left; 378 } 379 380 if (dst.y < clip.top) { 381 rect.top += clip.top - dst.y; 382 dst.y = clip.top; 383 } 384 385 int right = dst.x + rect.right; 386 if (right > clip.right) 387 rect.right -= right - clip.right; 388 389 int bottom = dst.y + rect.bottom; 390 if (bottom > clip.bottom) 391 rect.bottom -= bottom - clip.bottom; 392 return !rect.isEmpty(); 393 } 394 }; 395 396 /** @} */ 397 398 } // End of namespace Common 399 400 #endif 401