Mercurial > hgrepos > Python2 > PyMuPDF
comparison src_classic/helper-geo-py.i @ 1:1d09e1dec1d9 upstream
ADD: PyMuPDF v1.26.4: the original sdist.
It does not yet contain MuPDF. This normally will be downloaded when
building PyMuPDF.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:37:51 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 1:1d09e1dec1d9 |
|---|---|
| 1 %pythoncode %{ | |
| 2 | |
| 3 # ------------------------------------------------------------------------ | |
| 4 # Copyright 2020-2022, Harald Lieder, mailto:harald.lieder@outlook.com | |
| 5 # License: GNU AFFERO GPL 3.0, https://www.gnu.org/licenses/agpl-3.0.html | |
| 6 # | |
| 7 # Part of "PyMuPDF", a Python binding for "MuPDF" (http://mupdf.com), a | |
| 8 # lightweight PDF, XPS, and E-book viewer, renderer and toolkit which is | |
| 9 # maintained and developed by Artifex Software, Inc. https://artifex.com. | |
| 10 # ------------------------------------------------------------------------ | |
| 11 | |
| 12 # largest 32bit integers surviving C float conversion roundtrips | |
| 13 # used by MuPDF to define infinite rectangles | |
| 14 FZ_MIN_INF_RECT = -0x80000000 | |
| 15 FZ_MAX_INF_RECT = 0x7fffff80 | |
| 16 | |
| 17 | |
| 18 class Matrix(object): | |
| 19 """Matrix() - all zeros | |
| 20 Matrix(a, b, c, d, e, f) | |
| 21 Matrix(zoom-x, zoom-y) - zoom | |
| 22 Matrix(shear-x, shear-y, 1) - shear | |
| 23 Matrix(degree) - rotate | |
| 24 Matrix(Matrix) - new copy | |
| 25 Matrix(sequence) - from 'sequence'""" | |
| 26 def __init__(self, *args): | |
| 27 if not args: | |
| 28 self.a = self.b = self.c = self.d = self.e = self.f = 0.0 | |
| 29 return None | |
| 30 if len(args) > 6: | |
| 31 raise ValueError("Matrix: bad seq len") | |
| 32 if len(args) == 6: # 6 numbers | |
| 33 self.a, self.b, self.c, self.d, self.e, self.f = map(float, args) | |
| 34 return None | |
| 35 if len(args) == 1: # either an angle or a sequ | |
| 36 if hasattr(args[0], "__float__"): | |
| 37 theta = math.radians(args[0]) | |
| 38 c = round(math.cos(theta), 12) | |
| 39 s = round(math.sin(theta), 12) | |
| 40 self.a = self.d = c | |
| 41 self.b = s | |
| 42 self.c = -s | |
| 43 self.e = self.f = 0.0 | |
| 44 return None | |
| 45 else: | |
| 46 self.a, self.b, self.c, self.d, self.e, self.f = map(float, args[0]) | |
| 47 return None | |
| 48 if len(args) == 2 or len(args) == 3 and args[2] == 0: | |
| 49 self.a, self.b, self.c, self.d, self.e, self.f = float(args[0]), \ | |
| 50 0.0, 0.0, float(args[1]), 0.0, 0.0 | |
| 51 return None | |
| 52 if len(args) == 3 and args[2] == 1: | |
| 53 self.a, self.b, self.c, self.d, self.e, self.f = 1.0, \ | |
| 54 float(args[1]), float(args[0]), 1.0, 0.0, 0.0 | |
| 55 return None | |
| 56 raise ValueError("Matrix: bad args") | |
| 57 | |
| 58 def invert(self, src=None): | |
| 59 """Calculate the inverted matrix. Return 0 if successful and replace | |
| 60 current one. Else return 1 and do nothing. | |
| 61 """ | |
| 62 if src is None: | |
| 63 dst = util_invert_matrix(self) | |
| 64 else: | |
| 65 dst = util_invert_matrix(src) | |
| 66 if dst[0] == 1: | |
| 67 return 1 | |
| 68 self.a, self.b, self.c, self.d, self.e, self.f = dst[1] | |
| 69 return 0 | |
| 70 | |
| 71 def pretranslate(self, tx, ty): | |
| 72 """Calculate pre translation and replace current matrix.""" | |
| 73 tx = float(tx) | |
| 74 ty = float(ty) | |
| 75 self.e += tx * self.a + ty * self.c | |
| 76 self.f += tx * self.b + ty * self.d | |
| 77 return self | |
| 78 | |
| 79 def prescale(self, sx, sy): | |
| 80 """Calculate pre scaling and replace current matrix.""" | |
| 81 sx = float(sx) | |
| 82 sy = float(sy) | |
| 83 self.a *= sx | |
| 84 self.b *= sx | |
| 85 self.c *= sy | |
| 86 self.d *= sy | |
| 87 return self | |
| 88 | |
| 89 def preshear(self, h, v): | |
| 90 """Calculate pre shearing and replace current matrix.""" | |
| 91 h = float(h) | |
| 92 v = float(v) | |
| 93 a, b = self.a, self.b | |
| 94 self.a += v * self.c | |
| 95 self.b += v * self.d | |
| 96 self.c += h * a | |
| 97 self.d += h * b | |
| 98 return self | |
| 99 | |
| 100 def prerotate(self, theta): | |
| 101 """Calculate pre rotation and replace current matrix.""" | |
| 102 theta = float(theta) | |
| 103 while theta < 0: theta += 360 | |
| 104 while theta >= 360: theta -= 360 | |
| 105 if abs(0 - theta) < EPSILON: | |
| 106 pass | |
| 107 | |
| 108 elif abs(90.0 - theta) < EPSILON: | |
| 109 a = self.a | |
| 110 b = self.b | |
| 111 self.a = self.c | |
| 112 self.b = self.d | |
| 113 self.c = -a | |
| 114 self.d = -b | |
| 115 | |
| 116 elif abs(180.0 - theta) < EPSILON: | |
| 117 self.a = -self.a | |
| 118 self.b = -self.b | |
| 119 self.c = -self.c | |
| 120 self.d = -self.d | |
| 121 | |
| 122 elif abs(270.0 - theta) < EPSILON: | |
| 123 a = self.a | |
| 124 b = self.b | |
| 125 self.a = -self.c | |
| 126 self.b = -self.d | |
| 127 self.c = a | |
| 128 self.d = b | |
| 129 | |
| 130 else: | |
| 131 rad = math.radians(theta) | |
| 132 s = math.sin(rad) | |
| 133 c = math.cos(rad) | |
| 134 a = self.a | |
| 135 b = self.b | |
| 136 self.a = c * a + s * self.c | |
| 137 self.b = c * b + s * self.d | |
| 138 self.c =-s * a + c * self.c | |
| 139 self.d =-s * b + c * self.d | |
| 140 | |
| 141 return self | |
| 142 | |
| 143 def concat(self, one, two): | |
| 144 """Multiply two matrices and replace current one.""" | |
| 145 if not len(one) == len(two) == 6: | |
| 146 raise ValueError("Matrix: bad seq len") | |
| 147 self.a, self.b, self.c, self.d, self.e, self.f = util_concat_matrix(one, two) | |
| 148 return self | |
| 149 | |
| 150 def __getitem__(self, i): | |
| 151 return (self.a, self.b, self.c, self.d, self.e, self.f)[i] | |
| 152 | |
| 153 def __setitem__(self, i, v): | |
| 154 v = float(v) | |
| 155 if i == 0: self.a = v | |
| 156 elif i == 1: self.b = v | |
| 157 elif i == 2: self.c = v | |
| 158 elif i == 3: self.d = v | |
| 159 elif i == 4: self.e = v | |
| 160 elif i == 5: self.f = v | |
| 161 else: | |
| 162 raise IndexError("index out of range") | |
| 163 return | |
| 164 | |
| 165 def __len__(self): | |
| 166 return 6 | |
| 167 | |
| 168 def __repr__(self): | |
| 169 return "Matrix" + str(tuple(self)) | |
| 170 | |
| 171 def __invert__(self): | |
| 172 """Calculate inverted matrix.""" | |
| 173 m1 = Matrix() | |
| 174 m1.invert(self) | |
| 175 return m1 | |
| 176 __inv__ = __invert__ | |
| 177 | |
| 178 def __mul__(self, m): | |
| 179 if hasattr(m, "__float__"): | |
| 180 return Matrix(self.a * m, self.b * m, self.c * m, | |
| 181 self.d * m, self.e * m, self.f * m) | |
| 182 m1 = Matrix(1,1) | |
| 183 return m1.concat(self, m) | |
| 184 | |
| 185 def __truediv__(self, m): | |
| 186 if hasattr(m, "__float__"): | |
| 187 return Matrix(self.a * 1./m, self.b * 1./m, self.c * 1./m, | |
| 188 self.d * 1./m, self.e * 1./m, self.f * 1./m) | |
| 189 m1 = util_invert_matrix(m)[1] | |
| 190 if not m1: | |
| 191 raise ZeroDivisionError("matrix not invertible") | |
| 192 m2 = Matrix(1,1) | |
| 193 return m2.concat(self, m1) | |
| 194 __div__ = __truediv__ | |
| 195 | |
| 196 def __add__(self, m): | |
| 197 if hasattr(m, "__float__"): | |
| 198 return Matrix(self.a + m, self.b + m, self.c + m, | |
| 199 self.d + m, self.e + m, self.f + m) | |
| 200 if len(m) != 6: | |
| 201 raise ValueError("Matrix: bad seq len") | |
| 202 return Matrix(self.a + m[0], self.b + m[1], self.c + m[2], | |
| 203 self.d + m[3], self.e + m[4], self.f + m[5]) | |
| 204 | |
| 205 def __sub__(self, m): | |
| 206 if hasattr(m, "__float__"): | |
| 207 return Matrix(self.a - m, self.b - m, self.c - m, | |
| 208 self.d - m, self.e - m, self.f - m) | |
| 209 if len(m) != 6: | |
| 210 raise ValueError("Matrix: bad seq len") | |
| 211 return Matrix(self.a - m[0], self.b - m[1], self.c - m[2], | |
| 212 self.d - m[3], self.e - m[4], self.f - m[5]) | |
| 213 | |
| 214 def __pos__(self): | |
| 215 return Matrix(self) | |
| 216 | |
| 217 def __neg__(self): | |
| 218 return Matrix(-self.a, -self.b, -self.c, -self.d, -self.e, -self.f) | |
| 219 | |
| 220 def __bool__(self): | |
| 221 return not (max(self) == min(self) == 0) | |
| 222 | |
| 223 def __nonzero__(self): | |
| 224 return not (max(self) == min(self) == 0) | |
| 225 | |
| 226 def __eq__(self, mat): | |
| 227 if not hasattr(mat, "__len__"): | |
| 228 return False | |
| 229 return len(mat) == 6 and bool(self - mat) is False | |
| 230 | |
| 231 def __abs__(self): | |
| 232 return math.sqrt(sum([c*c for c in self])) | |
| 233 | |
| 234 norm = __abs__ | |
| 235 | |
| 236 @property | |
| 237 def is_rectilinear(self): | |
| 238 """True if rectangles are mapped to rectangles.""" | |
| 239 return (abs(self.b) < EPSILON and abs(self.c) < EPSILON) or \ | |
| 240 (abs(self.a) < EPSILON and abs(self.d) < EPSILON); | |
| 241 | |
| 242 | |
| 243 class IdentityMatrix(Matrix): | |
| 244 """Identity matrix [1, 0, 0, 1, 0, 0]""" | |
| 245 def __init__(self): | |
| 246 Matrix.__init__(self, 1.0, 1.0) | |
| 247 def __setattr__(self, name, value): | |
| 248 if name in "ad": | |
| 249 self.__dict__[name] = 1.0 | |
| 250 elif name in "bcef": | |
| 251 self.__dict__[name] = 0.0 | |
| 252 else: | |
| 253 self.__dict__[name] = value | |
| 254 | |
| 255 def checkargs(*args): | |
| 256 raise NotImplementedError("Identity is readonly") | |
| 257 | |
| 258 prerotate = checkargs | |
| 259 preshear = checkargs | |
| 260 prescale = checkargs | |
| 261 pretranslate = checkargs | |
| 262 concat = checkargs | |
| 263 invert = checkargs | |
| 264 | |
| 265 def __repr__(self): | |
| 266 return "IdentityMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)" | |
| 267 | |
| 268 def __hash__(self): | |
| 269 return hash((1,0,0,1,0,0)) | |
| 270 | |
| 271 | |
| 272 Identity = IdentityMatrix() | |
| 273 | |
| 274 class Point(object): | |
| 275 """Point() - all zeros\nPoint(x, y)\nPoint(Point) - new copy\nPoint(sequence) - from 'sequence'""" | |
| 276 def __init__(self, *args): | |
| 277 if not args: | |
| 278 self.x = 0.0 | |
| 279 self.y = 0.0 | |
| 280 return None | |
| 281 | |
| 282 if len(args) > 2: | |
| 283 raise ValueError("Point: bad seq len") | |
| 284 if len(args) == 2: | |
| 285 self.x = float(args[0]) | |
| 286 self.y = float(args[1]) | |
| 287 return None | |
| 288 if len(args) == 1: | |
| 289 l = args[0] | |
| 290 if hasattr(l, "__getitem__") is False: | |
| 291 raise ValueError("Point: bad args") | |
| 292 if len(l) != 2: | |
| 293 raise ValueError("Point: bad seq len") | |
| 294 self.x = float(l[0]) | |
| 295 self.y = float(l[1]) | |
| 296 return None | |
| 297 raise ValueError("Point: bad args") | |
| 298 | |
| 299 def transform(self, m): | |
| 300 """Replace point by its transformation with matrix-like m.""" | |
| 301 if len(m) != 6: | |
| 302 raise ValueError("Matrix: bad seq len") | |
| 303 self.x, self.y = util_transform_point(self, m) | |
| 304 return self | |
| 305 | |
| 306 @property | |
| 307 def unit(self): | |
| 308 """Unit vector of the point.""" | |
| 309 s = self.x * self.x + self.y * self.y | |
| 310 if s < EPSILON: | |
| 311 return Point(0,0) | |
| 312 s = math.sqrt(s) | |
| 313 return Point(self.x / s, self.y / s) | |
| 314 | |
| 315 @property | |
| 316 def abs_unit(self): | |
| 317 """Unit vector with positive coordinates.""" | |
| 318 s = self.x * self.x + self.y * self.y | |
| 319 if s < EPSILON: | |
| 320 return Point(0,0) | |
| 321 s = math.sqrt(s) | |
| 322 return Point(abs(self.x) / s, abs(self.y) / s) | |
| 323 | |
| 324 def distance_to(self, *args): | |
| 325 """Return distance to rectangle or another point.""" | |
| 326 if not len(args) > 0: | |
| 327 raise ValueError("at least one parameter must be given") | |
| 328 | |
| 329 x = args[0] | |
| 330 if len(x) == 2: | |
| 331 x = Point(x) | |
| 332 elif len(x) == 4: | |
| 333 x = Rect(x) | |
| 334 else: | |
| 335 raise ValueError("arg1 must be point-like or rect-like") | |
| 336 | |
| 337 if len(args) > 1: | |
| 338 unit = args[1] | |
| 339 else: | |
| 340 unit = "px" | |
| 341 u = {"px": (1.,1.), "in": (1.,72.), "cm": (2.54, 72.), | |
| 342 "mm": (25.4, 72.)} | |
| 343 f = u[unit][0] / u[unit][1] | |
| 344 | |
| 345 if type(x) is Point: | |
| 346 return abs(self - x) * f | |
| 347 | |
| 348 # from here on, x is a rectangle | |
| 349 # as a safeguard, make a finite copy of it | |
| 350 r = Rect(x.top_left, x.top_left) | |
| 351 r = r | x.bottom_right | |
| 352 if self in r: | |
| 353 return 0.0 | |
| 354 if self.x > r.x1: | |
| 355 if self.y >= r.y1: | |
| 356 return self.distance_to(r.bottom_right, unit) | |
| 357 elif self.y <= r.y0: | |
| 358 return self.distance_to(r.top_right, unit) | |
| 359 else: | |
| 360 return (self.x - r.x1) * f | |
| 361 elif r.x0 <= self.x <= r.x1: | |
| 362 if self.y >= r.y1: | |
| 363 return (self.y - r.y1) * f | |
| 364 else: | |
| 365 return (r.y0 - self.y) * f | |
| 366 else: | |
| 367 if self.y >= r.y1: | |
| 368 return self.distance_to(r.bottom_left, unit) | |
| 369 elif self.y <= r.y0: | |
| 370 return self.distance_to(r.top_left, unit) | |
| 371 else: | |
| 372 return (r.x0 - self.x) * f | |
| 373 | |
| 374 def __getitem__(self, i): | |
| 375 return (self.x, self.y)[i] | |
| 376 | |
| 377 def __len__(self): | |
| 378 return 2 | |
| 379 | |
| 380 def __setitem__(self, i, v): | |
| 381 v = float(v) | |
| 382 if i == 0: self.x = v | |
| 383 elif i == 1: self.y = v | |
| 384 else: | |
| 385 raise IndexError("index out of range") | |
| 386 return None | |
| 387 | |
| 388 def __repr__(self): | |
| 389 return "Point" + str(tuple(self)) | |
| 390 | |
| 391 def __pos__(self): | |
| 392 return Point(self) | |
| 393 | |
| 394 def __neg__(self): | |
| 395 return Point(-self.x, -self.y) | |
| 396 | |
| 397 def __bool__(self): | |
| 398 return not (max(self) == min(self) == 0) | |
| 399 | |
| 400 def __nonzero__(self): | |
| 401 return not (max(self) == min(self) == 0) | |
| 402 | |
| 403 def __eq__(self, p): | |
| 404 if not hasattr(p, "__len__"): | |
| 405 return False | |
| 406 return len(p) == 2 and bool(self - p) is False | |
| 407 | |
| 408 def __abs__(self): | |
| 409 return math.sqrt(self.x * self.x + self.y * self.y) | |
| 410 | |
| 411 norm = __abs__ | |
| 412 | |
| 413 def __add__(self, p): | |
| 414 if hasattr(p, "__float__"): | |
| 415 return Point(self.x + p, self.y + p) | |
| 416 if len(p) != 2: | |
| 417 raise ValueError("Point: bad seq len") | |
| 418 return Point(self.x + p[0], self.y + p[1]) | |
| 419 | |
| 420 def __sub__(self, p): | |
| 421 if hasattr(p, "__float__"): | |
| 422 return Point(self.x - p, self.y - p) | |
| 423 if len(p) != 2: | |
| 424 raise ValueError("Point: bad seq len") | |
| 425 return Point(self.x - p[0], self.y - p[1]) | |
| 426 | |
| 427 def __mul__(self, m): | |
| 428 if hasattr(m, "__float__"): | |
| 429 return Point(self.x * m, self.y * m) | |
| 430 p = Point(self) | |
| 431 return p.transform(m) | |
| 432 | |
| 433 def __truediv__(self, m): | |
| 434 if hasattr(m, "__float__"): | |
| 435 return Point(self.x * 1./m, self.y * 1./m) | |
| 436 m1 = util_invert_matrix(m)[1] | |
| 437 if not m1: | |
| 438 raise ZeroDivisionError("matrix not invertible") | |
| 439 p = Point(self) | |
| 440 return p.transform(m1) | |
| 441 | |
| 442 __div__ = __truediv__ | |
| 443 | |
| 444 def __hash__(self): | |
| 445 return hash(tuple(self)) | |
| 446 | |
| 447 class Rect(object): | |
| 448 """Rect() - all zeros | |
| 449 Rect(x0, y0, x1, y1) - 4 coordinates | |
| 450 Rect(top-left, x1, y1) - point and 2 coordinates | |
| 451 Rect(x0, y0, bottom-right) - 2 coordinates and point | |
| 452 Rect(top-left, bottom-right) - 2 points | |
| 453 Rect(sequ) - new from sequence or rect-like | |
| 454 """ | |
| 455 def __init__(self, *args): | |
| 456 self.x0, self.y0, self.x1, self.y1 = util_make_rect(args) | |
| 457 return None | |
| 458 | |
| 459 def normalize(self): | |
| 460 """Replace rectangle with its valid version.""" | |
| 461 if self.x1 < self.x0: | |
| 462 self.x0, self.x1 = self.x1, self.x0 | |
| 463 if self.y1 < self.y0: | |
| 464 self.y0, self.y1 = self.y1, self.y0 | |
| 465 return self | |
| 466 | |
| 467 @property | |
| 468 def is_empty(self): | |
| 469 """True if rectangle area is empty.""" | |
| 470 return self.x0 >= self.x1 or self.y0 >= self.y1 | |
| 471 | |
| 472 @property | |
| 473 def is_valid(self): | |
| 474 """True if rectangle is valid.""" | |
| 475 return self.x0 <= self.x1 and self.y0 <= self.y1 | |
| 476 | |
| 477 @property | |
| 478 def is_infinite(self): | |
| 479 """True if this is the infinite rectangle.""" | |
| 480 return self.x0 == self.y0 == FZ_MIN_INF_RECT and self.x1 == self.y1 == FZ_MAX_INF_RECT | |
| 481 | |
| 482 @property | |
| 483 def top_left(self): | |
| 484 """Top-left corner.""" | |
| 485 return Point(self.x0, self.y0) | |
| 486 | |
| 487 @property | |
| 488 def top_right(self): | |
| 489 """Top-right corner.""" | |
| 490 return Point(self.x1, self.y0) | |
| 491 | |
| 492 @property | |
| 493 def bottom_left(self): | |
| 494 """Bottom-left corner.""" | |
| 495 return Point(self.x0, self.y1) | |
| 496 | |
| 497 @property | |
| 498 def bottom_right(self): | |
| 499 """Bottom-right corner.""" | |
| 500 return Point(self.x1, self.y1) | |
| 501 | |
| 502 tl = top_left | |
| 503 tr = top_right | |
| 504 bl = bottom_left | |
| 505 br = bottom_right | |
| 506 | |
| 507 @property | |
| 508 def quad(self): | |
| 509 """Return Quad version of rectangle.""" | |
| 510 return Quad(self.tl, self.tr, self.bl, self.br) | |
| 511 | |
| 512 def torect(self, r): | |
| 513 """Return matrix that converts to target rect.""" | |
| 514 | |
| 515 r = Rect(r) | |
| 516 if self.is_infinite or self.is_empty or r.is_infinite or r.is_empty: | |
| 517 raise ValueError("rectangles must be finite and not empty") | |
| 518 return ( | |
| 519 Matrix(1, 0, 0, 1, -self.x0, -self.y0) | |
| 520 * Matrix(r.width / self.width, r.height / self.height) | |
| 521 * Matrix(1, 0, 0, 1, r.x0, r.y0) | |
| 522 ) | |
| 523 | |
| 524 def morph(self, p, m): | |
| 525 """Morph with matrix-like m and point-like p. | |
| 526 | |
| 527 Returns a new quad.""" | |
| 528 if self.is_infinite: | |
| 529 return INFINITE_QUAD() | |
| 530 return self.quad.morph(p, m) | |
| 531 | |
| 532 def round(self): | |
| 533 """Return the IRect.""" | |
| 534 return IRect(util_round_rect(self)) | |
| 535 | |
| 536 irect = property(round) | |
| 537 | |
| 538 width = property(lambda self: self.x1 - self.x0 if self.x1 > self.x0 else 0) | |
| 539 height = property(lambda self: self.y1 - self.y0 if self.y1 > self.y0 else 0) | |
| 540 | |
| 541 def include_point(self, p): | |
| 542 """Extend to include point-like p.""" | |
| 543 if len(p) != 2: | |
| 544 raise ValueError("Point: bad seq len") | |
| 545 self.x0, self.y0, self.x1, self.y1 = util_include_point_in_rect(self, p) | |
| 546 return self | |
| 547 | |
| 548 def include_rect(self, r): | |
| 549 """Extend to include rect-like r.""" | |
| 550 if len(r) != 4: | |
| 551 raise ValueError("Rect: bad seq len") | |
| 552 r = Rect(r) | |
| 553 if r.is_infinite or self.is_infinite: | |
| 554 self.x0, self.y0, self.x1, self.y1 = FZ_MIN_INF_RECT, FZ_MIN_INF_RECT, FZ_MAX_INF_RECT, FZ_MAX_INF_RECT | |
| 555 elif r.is_empty: | |
| 556 return self | |
| 557 elif self.is_empty: | |
| 558 self.x0, self.y0, self.x1, self.y1 = r.x0, r.y0, r.x1, r.y1 | |
| 559 else: | |
| 560 self.x0, self.y0, self.x1, self.y1 = util_union_rect(self, r) | |
| 561 return self | |
| 562 | |
| 563 def intersect(self, r): | |
| 564 """Restrict to common rect with rect-like r.""" | |
| 565 if not len(r) == 4: | |
| 566 raise ValueError("Rect: bad seq len") | |
| 567 r = Rect(r) | |
| 568 if r.is_infinite: | |
| 569 return self | |
| 570 elif self.is_infinite: | |
| 571 self.x0, self.y0, self.x1, self.y1 = r.x0, r.y0, r.x1, r.y1 | |
| 572 elif r.is_empty: | |
| 573 self.x0, self.y0, self.x1, self.y1 = r.x0, r.y0, r.x1, r.y1 | |
| 574 elif self.is_empty: | |
| 575 return self | |
| 576 else: | |
| 577 self.x0, self.y0, self.x1, self.y1 = util_intersect_rect(self, r) | |
| 578 return self | |
| 579 | |
| 580 def contains(self, x): | |
| 581 """Check if containing point-like or rect-like x.""" | |
| 582 return self.__contains__(x) | |
| 583 | |
| 584 def transform(self, m): | |
| 585 """Replace with the transformation by matrix-like m.""" | |
| 586 if not len(m) == 6: | |
| 587 raise ValueError("Matrix: bad seq len") | |
| 588 self.x0, self.y0, self.x1, self.y1 = util_transform_rect(self, m) | |
| 589 return self | |
| 590 | |
| 591 def __getitem__(self, i): | |
| 592 return (self.x0, self.y0, self.x1, self.y1)[i] | |
| 593 | |
| 594 def __len__(self): | |
| 595 return 4 | |
| 596 | |
| 597 def __setitem__(self, i, v): | |
| 598 v = float(v) | |
| 599 if i == 0: self.x0 = v | |
| 600 elif i == 1: self.y0 = v | |
| 601 elif i == 2: self.x1 = v | |
| 602 elif i == 3: self.y1 = v | |
| 603 else: | |
| 604 raise IndexError("index out of range") | |
| 605 return None | |
| 606 | |
| 607 def __repr__(self): | |
| 608 return "Rect" + str(tuple(self)) | |
| 609 | |
| 610 def __pos__(self): | |
| 611 return Rect(self) | |
| 612 | |
| 613 def __neg__(self): | |
| 614 return Rect(-self.x0, -self.y0, -self.x1, -self.y1) | |
| 615 | |
| 616 def __bool__(self): | |
| 617 return not self.x0 == self.y0 == self.x1 == self.y1 == 0 | |
| 618 | |
| 619 def __nonzero__(self): | |
| 620 return not self.x0 == self.y0 == self.x1 == self.y1 == 0 | |
| 621 | |
| 622 def __eq__(self, r): | |
| 623 if not hasattr(r, "__len__"): | |
| 624 return False | |
| 625 return len(r) == 4 and self.x0 == r[0] and self.y0 == r[1] and self.x1 == r[2] and self.y1 == r[3] | |
| 626 | |
| 627 def __abs__(self): | |
| 628 if self.is_infinite or not self.is_valid: | |
| 629 return 0.0 | |
| 630 return self.width * self.height | |
| 631 | |
| 632 def norm(self): | |
| 633 return math.sqrt(sum([c*c for c in self])) | |
| 634 | |
| 635 def __add__(self, p): | |
| 636 if hasattr(p, "__float__"): | |
| 637 return Rect(self.x0 + p, self.y0 + p, self.x1 + p, self.y1 + p) | |
| 638 if len(p) != 4: | |
| 639 raise ValueError("Rect: bad seq len") | |
| 640 return Rect(self.x0 + p[0], self.y0 + p[1], self.x1 + p[2], self.y1 + p[3]) | |
| 641 | |
| 642 | |
| 643 def __sub__(self, p): | |
| 644 if hasattr(p, "__float__"): | |
| 645 return Rect(self.x0 - p, self.y0 - p, self.x1 - p, self.y1 - p) | |
| 646 if len(p) != 4: | |
| 647 raise ValueError("Rect: bad seq len") | |
| 648 return Rect(self.x0 - p[0], self.y0 - p[1], self.x1 - p[2], self.y1 - p[3]) | |
| 649 | |
| 650 | |
| 651 def __mul__(self, m): | |
| 652 if hasattr(m, "__float__"): | |
| 653 return Rect(self.x0 * m, self.y0 * m, self.x1 * m, self.y1 * m) | |
| 654 r = Rect(self) | |
| 655 r = r.transform(m) | |
| 656 return r | |
| 657 | |
| 658 def __truediv__(self, m): | |
| 659 if hasattr(m, "__float__"): | |
| 660 return Rect(self.x0 * 1./m, self.y0 * 1./m, self.x1 * 1./m, self.y1 * 1./m) | |
| 661 im = util_invert_matrix(m)[1] | |
| 662 if not im: | |
| 663 raise ZeroDivisionError("Matrix not invertible") | |
| 664 r = Rect(self) | |
| 665 r = r.transform(im) | |
| 666 return r | |
| 667 | |
| 668 __div__ = __truediv__ | |
| 669 | |
| 670 def __contains__(self, x): | |
| 671 if hasattr(x, "__float__"): | |
| 672 return x in tuple(self) | |
| 673 l = len(x) | |
| 674 if l == 2: | |
| 675 return util_is_point_in_rect(x, self) | |
| 676 if l == 4: | |
| 677 r = INFINITE_RECT() | |
| 678 try: | |
| 679 r = Rect(x) | |
| 680 except: | |
| 681 r = Quad(x).rect | |
| 682 return (self.x0 <= r.x0 <= r.x1 <= self.x1 and | |
| 683 self.y0 <= r.y0 <= r.y1 <= self.y1) | |
| 684 return False | |
| 685 | |
| 686 | |
| 687 def __or__(self, x): | |
| 688 if not hasattr(x, "__len__"): | |
| 689 raise ValueError("bad type op 2") | |
| 690 | |
| 691 r = Rect(self) | |
| 692 if len(x) == 2: | |
| 693 return r.include_point(x) | |
| 694 if len(x) == 4: | |
| 695 return r.include_rect(x) | |
| 696 raise ValueError("bad type op 2") | |
| 697 | |
| 698 def __and__(self, x): | |
| 699 if not hasattr(x, "__len__") or len(x) != 4: | |
| 700 raise ValueError("bad type op 2") | |
| 701 r = Rect(self) | |
| 702 return r.intersect(x) | |
| 703 | |
| 704 def intersects(self, x): | |
| 705 """Check if intersection with rectangle x is not empty.""" | |
| 706 r1 = Rect(x) | |
| 707 if self.is_empty or self.is_infinite or r1.is_empty or r1.is_infinite: | |
| 708 return False | |
| 709 r = Rect(self) | |
| 710 if r.intersect(r1).is_empty: | |
| 711 return False | |
| 712 return True | |
| 713 | |
| 714 def __hash__(self): | |
| 715 return hash(tuple(self)) | |
| 716 | |
| 717 class IRect(object): | |
| 718 """IRect() - all zeros | |
| 719 IRect(x0, y0, x1, y1) - 4 coordinates | |
| 720 IRect(top-left, x1, y1) - point and 2 coordinates | |
| 721 IRect(x0, y0, bottom-right) - 2 coordinates and point | |
| 722 IRect(top-left, bottom-right) - 2 points | |
| 723 IRect(sequ) - new from sequence or rect-like | |
| 724 """ | |
| 725 def __init__(self, *args): | |
| 726 self.x0, self.y0, self.x1, self.y1 = util_make_irect(args) | |
| 727 return None | |
| 728 | |
| 729 def normalize(self): | |
| 730 """Replace rectangle with its valid version.""" | |
| 731 if self.x1 < self.x0: | |
| 732 self.x0, self.x1 = self.x1, self.x0 | |
| 733 if self.y1 < self.y0: | |
| 734 self.y0, self.y1 = self.y1, self.y0 | |
| 735 return self | |
| 736 | |
| 737 @property | |
| 738 def is_empty(self): | |
| 739 """True if rectangle area is empty.""" | |
| 740 return self.x0 >= self.x1 or self.y0 >= self.y1 | |
| 741 | |
| 742 @property | |
| 743 def is_valid(self): | |
| 744 """True if rectangle is valid.""" | |
| 745 return self.x0 <= self.x1 and self.y0 <= self.y1 | |
| 746 | |
| 747 @property | |
| 748 def is_infinite(self): | |
| 749 """True if rectangle is infinite.""" | |
| 750 return self.x0 == self.y0 == FZ_MIN_INF_RECT and self.x1 == self.y1 == FZ_MAX_INF_RECT | |
| 751 | |
| 752 @property | |
| 753 def top_left(self): | |
| 754 """Top-left corner.""" | |
| 755 return Point(self.x0, self.y0) | |
| 756 | |
| 757 @property | |
| 758 def top_right(self): | |
| 759 """Top-right corner.""" | |
| 760 return Point(self.x1, self.y0) | |
| 761 | |
| 762 @property | |
| 763 def bottom_left(self): | |
| 764 """Bottom-left corner.""" | |
| 765 return Point(self.x0, self.y1) | |
| 766 | |
| 767 @property | |
| 768 def bottom_right(self): | |
| 769 """Bottom-right corner.""" | |
| 770 return Point(self.x1, self.y1) | |
| 771 | |
| 772 tl = top_left | |
| 773 tr = top_right | |
| 774 bl = bottom_left | |
| 775 br = bottom_right | |
| 776 | |
| 777 @property | |
| 778 def quad(self): | |
| 779 """Return Quad version of rectangle.""" | |
| 780 return Quad(self.tl, self.tr, self.bl, self.br) | |
| 781 | |
| 782 | |
| 783 def torect(self, r): | |
| 784 """Return matrix that converts to target rect.""" | |
| 785 | |
| 786 r = Rect(r) | |
| 787 if self.is_infinite or self.is_empty or r.is_infinite or r.is_empty: | |
| 788 raise ValueError("rectangles must be finite and not empty") | |
| 789 return ( | |
| 790 Matrix(1, 0, 0, 1, -self.x0, -self.y0) | |
| 791 * Matrix(r.width / self.width, r.height / self.height) | |
| 792 * Matrix(1, 0, 0, 1, r.x0, r.y0) | |
| 793 ) | |
| 794 | |
| 795 def morph(self, p, m): | |
| 796 """Morph with matrix-like m and point-like p. | |
| 797 | |
| 798 Returns a new quad.""" | |
| 799 if self.is_infinite: | |
| 800 return INFINITE_QUAD() | |
| 801 return self.quad.morph(p, m) | |
| 802 | |
| 803 @property | |
| 804 def rect(self): | |
| 805 return Rect(self) | |
| 806 | |
| 807 width = property(lambda self: self.x1 - self.x0 if self.x1 > self.x0 else 0) | |
| 808 height = property(lambda self: self.y1 - self.y0 if self.y1 > self.y0 else 0) | |
| 809 | |
| 810 def include_point(self, p): | |
| 811 """Extend rectangle to include point p.""" | |
| 812 rect = self.rect.include_point(p) | |
| 813 return rect.irect | |
| 814 | |
| 815 def include_rect(self, r): | |
| 816 """Extend rectangle to include rectangle r.""" | |
| 817 rect = self.rect.include_rect(r) | |
| 818 return rect.irect | |
| 819 | |
| 820 def intersect(self, r): | |
| 821 """Restrict rectangle to intersection with rectangle r.""" | |
| 822 rect = self.rect.intersect(r) | |
| 823 return rect.irect | |
| 824 | |
| 825 def __getitem__(self, i): | |
| 826 return (self.x0, self.y0, self.x1, self.y1)[i] | |
| 827 | |
| 828 def __len__(self): | |
| 829 return 4 | |
| 830 | |
| 831 def __setitem__(self, i, v): | |
| 832 v = int(v) | |
| 833 if i == 0: self.x0 = v | |
| 834 elif i == 1: self.y0 = v | |
| 835 elif i == 2: self.x1 = v | |
| 836 elif i == 3: self.y1 = v | |
| 837 else: | |
| 838 raise IndexError("index out of range") | |
| 839 return None | |
| 840 | |
| 841 def __repr__(self): | |
| 842 return "IRect" + str(tuple(self)) | |
| 843 | |
| 844 def __pos__(self): | |
| 845 return IRect(self) | |
| 846 | |
| 847 def __neg__(self): | |
| 848 return IRect(-self.x0, -self.y0, -self.x1, -self.y1) | |
| 849 | |
| 850 def __bool__(self): | |
| 851 return not self.x0 == self.y0 == self.x1 == self.y1 == 0 | |
| 852 | |
| 853 def __nonzero__(self): | |
| 854 return not self.x0 == self.y0 == self.x1 == self.y1 == 0 | |
| 855 | |
| 856 def __eq__(self, r): | |
| 857 if not hasattr(r, "__len__"): | |
| 858 return False | |
| 859 return len(r) == 4 and self.x0 == r[0] and self.y0 == r[1] and self.x1 == r[2] and self.y1 == r[3] | |
| 860 | |
| 861 def __abs__(self): | |
| 862 if self.is_infinite or not self.is_valid: | |
| 863 return 0 | |
| 864 return self.width * self.height | |
| 865 | |
| 866 def norm(self): | |
| 867 return math.sqrt(sum([c*c for c in self])) | |
| 868 | |
| 869 def __add__(self, p): | |
| 870 return Rect.__add__(self, p).round() | |
| 871 | |
| 872 def __sub__(self, p): | |
| 873 return Rect.__sub__(self, p).round() | |
| 874 | |
| 875 def transform(self, m): | |
| 876 return Rect.transform(self, m).round() | |
| 877 | |
| 878 def __mul__(self, m): | |
| 879 return Rect.__mul__(self, m).round() | |
| 880 | |
| 881 def __truediv__(self, m): | |
| 882 return Rect.__truediv__(self, m).round() | |
| 883 | |
| 884 __div__ = __truediv__ | |
| 885 | |
| 886 | |
| 887 def __contains__(self, x): | |
| 888 return Rect.__contains__(self, x) | |
| 889 | |
| 890 | |
| 891 def __or__(self, x): | |
| 892 return Rect.__or__(self, x).round() | |
| 893 | |
| 894 def __and__(self, x): | |
| 895 return Rect.__and__(self, x).round() | |
| 896 | |
| 897 def intersects(self, x): | |
| 898 return Rect.intersects(self, x) | |
| 899 | |
| 900 def __hash__(self): | |
| 901 return hash(tuple(self)) | |
| 902 | |
| 903 | |
| 904 class Quad(object): | |
| 905 """Quad() - all zero points\nQuad(ul, ur, ll, lr)\nQuad(quad) - new copy\nQuad(sequence) - from 'sequence'""" | |
| 906 def __init__(self, *args): | |
| 907 if not args: | |
| 908 self.ul = self.ur = self.ll = self.lr = Point() | |
| 909 return None | |
| 910 | |
| 911 if len(args) > 4: | |
| 912 raise ValueError("Quad: bad seq len") | |
| 913 if len(args) == 4: | |
| 914 self.ul, self.ur, self.ll, self.lr = map(Point, args) | |
| 915 return None | |
| 916 if len(args) == 1: | |
| 917 l = args[0] | |
| 918 if hasattr(l, "__getitem__") is False: | |
| 919 raise ValueError("Quad: bad args") | |
| 920 if len(l) != 4: | |
| 921 raise ValueError("Quad: bad seq len") | |
| 922 self.ul, self.ur, self.ll, self.lr = map(Point, l) | |
| 923 return None | |
| 924 raise ValueError("Quad: bad args") | |
| 925 | |
| 926 @property | |
| 927 def is_rectangular(self)->bool: | |
| 928 """Check if quad is rectangular. | |
| 929 | |
| 930 Notes: | |
| 931 Some rotation matrix can thus transform it into a rectangle. | |
| 932 This is equivalent to three corners enclose 90 degrees. | |
| 933 Returns: | |
| 934 True or False. | |
| 935 """ | |
| 936 | |
| 937 sine = util_sine_between(self.ul, self.ur, self.lr) | |
| 938 if abs(sine - 1) > EPSILON: # the sine of the angle | |
| 939 return False | |
| 940 | |
| 941 sine = util_sine_between(self.ur, self.lr, self.ll) | |
| 942 if abs(sine - 1) > EPSILON: | |
| 943 return False | |
| 944 | |
| 945 sine = util_sine_between(self.lr, self.ll, self.ul) | |
| 946 if abs(sine - 1) > EPSILON: | |
| 947 return False | |
| 948 | |
| 949 return True | |
| 950 | |
| 951 | |
| 952 @property | |
| 953 def is_convex(self)->bool: | |
| 954 """Check if quad is convex and not degenerate. | |
| 955 | |
| 956 Notes: | |
| 957 Check that for the two diagonals, the other two corners are not | |
| 958 on the same side of the diagonal. | |
| 959 Returns: | |
| 960 True or False. | |
| 961 """ | |
| 962 m = planish_line(self.ul, self.lr) # puts this diagonal on x-axis | |
| 963 p1 = self.ll * m # transform the | |
| 964 p2 = self.ur * m # other two points | |
| 965 if p1.y * p2.y > 0: | |
| 966 return False | |
| 967 m = planish_line(self.ll, self.ur) # puts other diagonal on x-axis | |
| 968 p1 = self.lr * m # tranform the | |
| 969 p2 = self.ul * m # remaining points | |
| 970 if p1.y * p2.y > 0: | |
| 971 return False | |
| 972 return True | |
| 973 | |
| 974 | |
| 975 width = property(lambda self: max(abs(self.ul - self.ur), abs(self.ll - self.lr))) | |
| 976 height = property(lambda self: max(abs(self.ul - self.ll), abs(self.ur - self.lr))) | |
| 977 | |
| 978 @property | |
| 979 def is_empty(self): | |
| 980 """Check whether all quad corners are on the same line. | |
| 981 | |
| 982 This is the case if width or height is zero. | |
| 983 """ | |
| 984 return self.width < EPSILON or self.height < EPSILON | |
| 985 | |
| 986 @property | |
| 987 def is_infinite(self): | |
| 988 """Check whether this is the infinite quad.""" | |
| 989 return self.rect.is_infinite | |
| 990 | |
| 991 @property | |
| 992 def rect(self): | |
| 993 r = Rect() | |
| 994 r.x0 = min(self.ul.x, self.ur.x, self.lr.x, self.ll.x) | |
| 995 r.y0 = min(self.ul.y, self.ur.y, self.lr.y, self.ll.y) | |
| 996 r.x1 = max(self.ul.x, self.ur.x, self.lr.x, self.ll.x) | |
| 997 r.y1 = max(self.ul.y, self.ur.y, self.lr.y, self.ll.y) | |
| 998 return r | |
| 999 | |
| 1000 | |
| 1001 def __contains__(self, x): | |
| 1002 try: | |
| 1003 l = x.__len__() | |
| 1004 except: | |
| 1005 return False | |
| 1006 if l == 2: | |
| 1007 return util_point_in_quad(x, self) | |
| 1008 if l != 4: | |
| 1009 return False | |
| 1010 if CheckRect(x): | |
| 1011 if Rect(x).is_empty: | |
| 1012 return True | |
| 1013 return util_point_in_quad(x[:2], self) and util_point_in_quad(x[2:], self) | |
| 1014 if CheckQuad(x): | |
| 1015 for i in range(4): | |
| 1016 if not util_point_in_quad(x[i], self): | |
| 1017 return False | |
| 1018 return True | |
| 1019 return False | |
| 1020 | |
| 1021 | |
| 1022 def __getitem__(self, i): | |
| 1023 return (self.ul, self.ur, self.ll, self.lr)[i] | |
| 1024 | |
| 1025 def __len__(self): | |
| 1026 return 4 | |
| 1027 | |
| 1028 def __setitem__(self, i, v): | |
| 1029 if i == 0: self.ul = Point(v) | |
| 1030 elif i == 1: self.ur = Point(v) | |
| 1031 elif i == 2: self.ll = Point(v) | |
| 1032 elif i == 3: self.lr = Point(v) | |
| 1033 else: | |
| 1034 raise IndexError("index out of range") | |
| 1035 return None | |
| 1036 | |
| 1037 def __repr__(self): | |
| 1038 return "Quad" + str(tuple(self)) | |
| 1039 | |
| 1040 def __pos__(self): | |
| 1041 return Quad(self) | |
| 1042 | |
| 1043 def __neg__(self): | |
| 1044 return Quad(-self.ul, -self.ur, -self.ll, -self.lr) | |
| 1045 | |
| 1046 def __bool__(self): | |
| 1047 return not self.is_empty | |
| 1048 | |
| 1049 def __nonzero__(self): | |
| 1050 return not self.is_empty | |
| 1051 | |
| 1052 def __eq__(self, quad): | |
| 1053 if not hasattr(quad, "__len__"): | |
| 1054 return False | |
| 1055 return len(quad) == 4 and ( | |
| 1056 self.ul == quad[0] and | |
| 1057 self.ur == quad[1] and | |
| 1058 self.ll == quad[2] and | |
| 1059 self.lr == quad[3] | |
| 1060 ) | |
| 1061 | |
| 1062 def __abs__(self): | |
| 1063 if self.is_empty: | |
| 1064 return 0.0 | |
| 1065 return abs(self.ul - self.ur) * abs(self.ul - self.ll) | |
| 1066 | |
| 1067 | |
| 1068 def morph(self, p, m): | |
| 1069 """Morph the quad with matrix-like 'm' and point-like 'p'. | |
| 1070 | |
| 1071 Return a new quad.""" | |
| 1072 if self.is_infinite: | |
| 1073 return INFINITE_QUAD() | |
| 1074 delta = Matrix(1, 1).pretranslate(p.x, p.y) | |
| 1075 q = self * ~delta * m * delta | |
| 1076 return q | |
| 1077 | |
| 1078 | |
| 1079 def transform(self, m): | |
| 1080 """Replace quad by its transformation with matrix m.""" | |
| 1081 if hasattr(m, "__float__"): | |
| 1082 pass | |
| 1083 elif len(m) != 6: | |
| 1084 raise ValueError("Matrix: bad seq len") | |
| 1085 self.ul *= m | |
| 1086 self.ur *= m | |
| 1087 self.ll *= m | |
| 1088 self.lr *= m | |
| 1089 return self | |
| 1090 | |
| 1091 def __mul__(self, m): | |
| 1092 q = Quad(self) | |
| 1093 q = q.transform(m) | |
| 1094 return q | |
| 1095 | |
| 1096 def __add__(self, q): | |
| 1097 if hasattr(q, "__float__"): | |
| 1098 return Quad(self.ul + q, self.ur + q, self.ll + q, self.lr + q) | |
| 1099 if len(p) != 4: | |
| 1100 raise ValueError("Quad: bad seq len") | |
| 1101 return Quad(self.ul + q[0], self.ur + q[1], self.ll + q[2], self.lr + q[3]) | |
| 1102 | |
| 1103 | |
| 1104 def __sub__(self, q): | |
| 1105 if hasattr(q, "__float__"): | |
| 1106 return Quad(self.ul - q, self.ur - q, self.ll - q, self.lr - q) | |
| 1107 if len(p) != 4: | |
| 1108 raise ValueError("Quad: bad seq len") | |
| 1109 return Quad(self.ul - q[0], self.ur - q[1], self.ll - q[2], self.lr - q[3]) | |
| 1110 | |
| 1111 | |
| 1112 def __truediv__(self, m): | |
| 1113 if hasattr(m, "__float__"): | |
| 1114 im = 1. / m | |
| 1115 else: | |
| 1116 im = util_invert_matrix(m)[1] | |
| 1117 if not im: | |
| 1118 raise ZeroDivisionError("Matrix not invertible") | |
| 1119 q = Quad(self) | |
| 1120 q = q.transform(im) | |
| 1121 return q | |
| 1122 | |
| 1123 __div__ = __truediv__ | |
| 1124 | |
| 1125 | |
| 1126 def __hash__(self): | |
| 1127 return hash(tuple(self)) | |
| 1128 | |
| 1129 | |
| 1130 # some special geometry objects | |
| 1131 def EMPTY_RECT(): | |
| 1132 return Rect(FZ_MAX_INF_RECT, FZ_MAX_INF_RECT, FZ_MIN_INF_RECT, FZ_MIN_INF_RECT) | |
| 1133 | |
| 1134 | |
| 1135 def INFINITE_RECT(): | |
| 1136 return Rect(FZ_MIN_INF_RECT, FZ_MIN_INF_RECT, FZ_MAX_INF_RECT, FZ_MAX_INF_RECT) | |
| 1137 | |
| 1138 | |
| 1139 def EMPTY_IRECT(): | |
| 1140 return IRect(FZ_MAX_INF_RECT, FZ_MAX_INF_RECT, FZ_MIN_INF_RECT, FZ_MIN_INF_RECT) | |
| 1141 | |
| 1142 | |
| 1143 def INFINITE_IRECT(): | |
| 1144 return IRect(FZ_MIN_INF_RECT, FZ_MIN_INF_RECT, FZ_MAX_INF_RECT, FZ_MAX_INF_RECT) | |
| 1145 | |
| 1146 | |
| 1147 def INFINITE_QUAD(): | |
| 1148 return INFINITE_RECT().quad | |
| 1149 | |
| 1150 | |
| 1151 def EMPTY_QUAD(): | |
| 1152 return EMPTY_RECT().quad | |
| 1153 | |
| 1154 | |
| 1155 %} |
