Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/geometry.c @ 3:2c135c81b16c
MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:44:09 +0200 |
| parents | b50eed0cc0ef |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/geometry.c Mon Sep 15 11:44:09 2025 +0200 @@ -0,0 +1,885 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" + +#include <math.h> +#include <float.h> +#include <limits.h> + +#define MAX4(a,b,c,d) fz_max(fz_max(a,b), fz_max(c,d)) +#define MIN4(a,b,c,d) fz_min(fz_min(a,b), fz_min(c,d)) + +/* A useful macro to add with overflow detection and clamping. + + We want to do "b = a + x", but to allow for overflow. Consider the + top bits, and the cases in which overflow occurs: + + overflow a x b ~a^x a^b (~a^x)&(a^b) + no 0 0 0 1 0 0 + yes 0 0 1 1 1 1 + no 0 1 0 0 0 0 + no 0 1 1 0 1 0 + no 1 0 0 0 1 0 + no 1 0 1 0 0 0 + yes 1 1 0 1 1 1 + no 1 1 1 1 0 0 +*/ +#define ADD_WITH_SAT(b,a,x) \ + ((b) = (a) + (x), (b) = (((~(a)^(x))&((a)^(b))) < 0 ? ((x) < 0 ? INT_MIN : INT_MAX) : (b))) + +/* Matrices, points and affine transformations */ + +const fz_matrix fz_identity = { 1, 0, 0, 1, 0, 0 }; + +fz_matrix +fz_concat(fz_matrix one, fz_matrix two) +{ + fz_matrix dst; + dst.a = one.a * two.a + one.b * two.c; + dst.b = one.a * two.b + one.b * two.d; + dst.c = one.c * two.a + one.d * two.c; + dst.d = one.c * two.b + one.d * two.d; + dst.e = one.e * two.a + one.f * two.c + two.e; + dst.f = one.e * two.b + one.f * two.d + two.f; + return dst; +} + +fz_matrix +fz_scale(float sx, float sy) +{ + fz_matrix m; + m.a = sx; m.b = 0; + m.c = 0; m.d = sy; + m.e = 0; m.f = 0; + return m; +} + +fz_matrix +fz_pre_scale(fz_matrix m, float sx, float sy) +{ + m.a *= sx; + m.b *= sx; + m.c *= sy; + m.d *= sy; + return m; +} + +fz_matrix +fz_post_scale(fz_matrix m, float sx, float sy) +{ + m.a *= sx; + m.b *= sy; + m.c *= sx; + m.d *= sy; + m.e *= sx; + m.f *= sy; + return m; +} + +fz_matrix +fz_shear(float h, float v) +{ + fz_matrix m; + m.a = 1; m.b = v; + m.c = h; m.d = 1; + m.e = 0; m.f = 0; + return m; +} + +fz_matrix +fz_pre_shear(fz_matrix m, float h, float v) +{ + float a = m.a; + float b = m.b; + m.a += v * m.c; + m.b += v * m.d; + m.c += h * a; + m.d += h * b; + return m; +} + +fz_matrix +fz_rotate(float theta) +{ + fz_matrix m; + float s; + float c; + + while (theta < 0) + theta += 360; + while (theta >= 360) + theta -= 360; + + if (fabsf(0 - theta) < FLT_EPSILON) + { + s = 0; + c = 1; + } + else if (fabsf(90.0f - theta) < FLT_EPSILON) + { + s = 1; + c = 0; + } + else if (fabsf(180.0f - theta) < FLT_EPSILON) + { + s = 0; + c = -1; + } + else if (fabsf(270.0f - theta) < FLT_EPSILON) + { + s = -1; + c = 0; + } + else + { + s = sinf(theta * FZ_PI / 180); + c = cosf(theta * FZ_PI / 180); + } + + m.a = c; m.b = s; + m.c = -s; m.d = c; + m.e = 0; m.f = 0; + return m; +} + +fz_matrix +fz_pre_rotate(fz_matrix m, float theta) +{ + while (theta < 0) + theta += 360; + while (theta >= 360) + theta -= 360; + + if (fabsf(0 - theta) < FLT_EPSILON) + { + /* Nothing to do */ + } + else if (fabsf(90.0f - theta) < FLT_EPSILON) + { + float a = m.a; + float b = m.b; + m.a = m.c; + m.b = m.d; + m.c = -a; + m.d = -b; + } + else if (fabsf(180.0f - theta) < FLT_EPSILON) + { + m.a = -m.a; + m.b = -m.b; + m.c = -m.c; + m.d = -m.d; + } + else if (fabsf(270.0f - theta) < FLT_EPSILON) + { + float a = m.a; + float b = m.b; + m.a = -m.c; + m.b = -m.d; + m.c = a; + m.d = b; + } + else + { + float s = sinf(theta * FZ_PI / 180); + float c = cosf(theta * FZ_PI / 180); + float a = m.a; + float b = m.b; + m.a = c * a + s * m.c; + m.b = c * b + s * m.d; + m.c =-s * a + c * m.c; + m.d =-s * b + c * m.d; + } + + return m; +} + +fz_matrix +fz_translate(float tx, float ty) +{ + fz_matrix m; + m.a = 1; m.b = 0; + m.c = 0; m.d = 1; + m.e = tx; m.f = ty; + return m; +} + +fz_matrix +fz_pre_translate(fz_matrix m, float tx, float ty) +{ + m.e += tx * m.a + ty * m.c; + m.f += tx * m.b + ty * m.d; + return m; +} + +fz_matrix +fz_transform_page(fz_rect mediabox, float resolution, float rotate) +{ + float user_w, user_h, pixel_w, pixel_h; + fz_rect pixel_box; + fz_matrix matrix; + + /* Adjust scaling factors to cover whole pixels */ + user_w = mediabox.x1 - mediabox.x0; + user_h = mediabox.y1 - mediabox.y0; + pixel_w = floorf(user_w * resolution / 72 + 0.5f); + pixel_h = floorf(user_h * resolution / 72 + 0.5f); + + matrix = fz_pre_rotate(fz_scale(pixel_w / user_w, pixel_h / user_h), rotate); + + /* Adjust the page origin to sit at 0,0 after rotation */ + pixel_box = fz_transform_rect(mediabox, matrix); + matrix.e -= pixel_box.x0; + matrix.f -= pixel_box.y0; + + return matrix; +} + +fz_matrix +fz_invert_matrix(fz_matrix src) +{ + float a = src.a; + float det = a * src.d - src.b * src.c; + if (det < -FLT_EPSILON || det > FLT_EPSILON) + { + fz_matrix dst; + float rdet = 1 / det; + dst.a = src.d * rdet; + dst.b = -src.b * rdet; + dst.c = -src.c * rdet; + dst.d = a * rdet; + a = -src.e * dst.a - src.f * dst.c; + dst.f = -src.e * dst.b - src.f * dst.d; + dst.e = a; + return dst; + } + return src; +} + +int +fz_try_invert_matrix(fz_matrix *dst, fz_matrix src) +{ + double sa = (double)src.a; + double sb = (double)src.b; + double sc = (double)src.c; + double sd = (double)src.d; + double da, db, dc, dd; + double det = sa * sd - sb * sc; + if (det >= -DBL_EPSILON && det <= DBL_EPSILON) + return 1; + det = 1 / det; + da = sd * det; + dst->a = (float)da; + db = -sb * det; + dst->b = (float)db; + dc = -sc * det; + dst->c = (float)dc; + dd = sa * det; + dst->d = (float)dd; + da = -src.e * da - src.f * dc; + dst->f = (float)(-src.e * db - src.f * dd); + dst->e = (float)da; + return 0; +} + +int +fz_is_rectilinear(fz_matrix m) +{ + return (fabsf(m.b) < FLT_EPSILON && fabsf(m.c) < FLT_EPSILON) || + (fabsf(m.a) < FLT_EPSILON && fabsf(m.d) < FLT_EPSILON); +} + +float +fz_matrix_expansion(fz_matrix m) +{ + return sqrtf(fabsf(m.a * m.d - m.b * m.c)); +} + +float +fz_matrix_max_expansion(fz_matrix m) +{ + float max = fabsf(m.a); + float x = fabsf(m.b); + if (max < x) + max = x; + x = fabsf(m.c); + if (max < x) + max = x; + x = fabsf(m.d); + if (max < x) + max = x; + return max; +} + +fz_point +fz_transform_point(fz_point p, fz_matrix m) +{ + float x = p.x; + p.x = x * m.a + p.y * m.c + m.e; + p.y = x * m.b + p.y * m.d + m.f; + return p; +} + +fz_point +fz_transform_point_xy(float x, float y, fz_matrix m) +{ + fz_point p; + p.x = x * m.a + y * m.c + m.e; + p.y = x * m.b + y * m.d + m.f; + return p; +} + +fz_point +fz_transform_vector(fz_point p, fz_matrix m) +{ + float x = p.x; + p.x = x * m.a + p.y * m.c; + p.y = x * m.b + p.y * m.d; + return p; +} + +fz_point +fz_normalize_vector(fz_point p) +{ + float len = p.x * p.x + p.y * p.y; + if (len != 0) + { + len = sqrtf(len); + p.x /= len; + p.y /= len; + } + return p; +} + +/* Rectangles and bounding boxes */ + +/* biggest and smallest integers that a float can represent perfectly (i.e. 24 bits) */ +#define MAX_SAFE_INT 16777216 +#define MIN_SAFE_INT -16777216 + +const fz_rect fz_infinite_rect = { FZ_MIN_INF_RECT, FZ_MIN_INF_RECT, FZ_MAX_INF_RECT, FZ_MAX_INF_RECT }; +const fz_rect fz_empty_rect = { FZ_MAX_INF_RECT, FZ_MAX_INF_RECT, FZ_MIN_INF_RECT, FZ_MIN_INF_RECT }; +const fz_rect fz_invalid_rect = { 0, 0, -1, -1 }; +const fz_rect fz_unit_rect = { 0, 0, 1, 1 }; + +const fz_irect fz_infinite_irect = { FZ_MIN_INF_RECT, FZ_MIN_INF_RECT, FZ_MAX_INF_RECT, FZ_MAX_INF_RECT }; +const fz_irect fz_empty_irect = { FZ_MAX_INF_RECT, FZ_MAX_INF_RECT, FZ_MIN_INF_RECT, FZ_MIN_INF_RECT }; +const fz_irect fz_invalid_irect = { 0, 0, -1, -1 }; +const fz_irect fz_unit_bbox = { 0, 0, 1, 1 }; + +const fz_quad fz_infinite_quad = { { -INFINITY, INFINITY}, {INFINITY, INFINITY}, {-INFINITY, -INFINITY}, {INFINITY, -INFINITY} }; +const fz_quad fz_invalid_quad = { {NAN, NAN}, {NAN, NAN}, {NAN, NAN}, {NAN, NAN} }; + +fz_irect +fz_irect_from_rect(fz_rect r) +{ + fz_irect b; + if (fz_is_infinite_rect(r)) + return fz_infinite_irect; + if (!fz_is_valid_rect(r)) + return fz_invalid_irect; + + b.x0 = fz_clamp(floorf(r.x0), MIN_SAFE_INT, MAX_SAFE_INT); + b.y0 = fz_clamp(floorf(r.y0), MIN_SAFE_INT, MAX_SAFE_INT); + b.x1 = fz_clamp(ceilf(r.x1), MIN_SAFE_INT, MAX_SAFE_INT); + b.y1 = fz_clamp(ceilf(r.y1), MIN_SAFE_INT, MAX_SAFE_INT); + + return b; +} + +fz_rect +fz_rect_from_irect(fz_irect a) +{ + fz_rect r; + + if (fz_is_infinite_irect(a)) + return fz_infinite_rect; + + r.x0 = a.x0; + r.y0 = a.y0; + r.x1 = a.x1; + r.y1 = a.y1; + return r; +} + +fz_irect +fz_round_rect(fz_rect r) +{ + fz_irect b; + float f; + + f = floorf(r.x0 + 0.001f); + b.x0 = fz_clamp(f, MIN_SAFE_INT, MAX_SAFE_INT); + f = floorf(r.y0 + 0.001f); + b.y0 = fz_clamp(f, MIN_SAFE_INT, MAX_SAFE_INT); + f = ceilf(r.x1 - 0.001f); + b.x1 = fz_clamp(f, MIN_SAFE_INT, MAX_SAFE_INT); + f = ceilf(r.y1 - 0.001f); + b.y1 = fz_clamp(f, MIN_SAFE_INT, MAX_SAFE_INT); + + return b; +} + +fz_rect +fz_intersect_rect(fz_rect a, fz_rect b) +{ + if (fz_is_infinite_rect(b)) return a; + if (fz_is_infinite_rect(a)) return b; + if (a.x0 < b.x0) + a.x0 = b.x0; + if (a.y0 < b.y0) + a.y0 = b.y0; + if (a.x1 > b.x1) + a.x1 = b.x1; + if (a.y1 > b.y1) + a.y1 = b.y1; + return a; +} + +fz_irect +fz_intersect_irect(fz_irect a, fz_irect b) +{ + if (fz_is_infinite_irect(b)) return a; + if (fz_is_infinite_irect(a)) return b; + if (a.x0 < b.x0) + a.x0 = b.x0; + if (a.y0 < b.y0) + a.y0 = b.y0; + if (a.x1 > b.x1) + a.x1 = b.x1; + if (a.y1 > b.y1) + a.y1 = b.y1; + return a; +} + +fz_rect +fz_union_rect(fz_rect a, fz_rect b) +{ + /* Check for empty box before infinite box */ + if (!fz_is_valid_rect(b)) return a; + if (!fz_is_valid_rect(a)) return b; + if (fz_is_infinite_rect(a)) return a; + if (fz_is_infinite_rect(b)) return b; + if (a.x0 > b.x0) + a.x0 = b.x0; + if (a.y0 > b.y0) + a.y0 = b.y0; + if (a.x1 < b.x1) + a.x1 = b.x1; + if (a.y1 < b.y1) + a.y1 = b.y1; + return a; +} + +fz_rect +fz_translate_rect(fz_rect a, float xoff, float yoff) +{ + if (fz_is_infinite_rect(a)) return a; + a.x0 += xoff; + a.y0 += yoff; + a.x1 += xoff; + a.y1 += yoff; + return a; +} + +fz_irect +fz_translate_irect(fz_irect a, int xoff, int yoff) +{ + int t; + + if (fz_is_empty_irect(a)) return a; + if (fz_is_infinite_irect(a)) return a; + a.x0 = ADD_WITH_SAT(t, a.x0, xoff); + a.y0 = ADD_WITH_SAT(t, a.y0, yoff); + a.x1 = ADD_WITH_SAT(t, a.x1, xoff); + a.y1 = ADD_WITH_SAT(t, a.y1, yoff); + return a; +} + +fz_rect +fz_transform_rect(fz_rect r, fz_matrix m) +{ + fz_point s, t, u, v; + int invalid; + + if (fz_is_infinite_rect(r)) + return r; + + if (fabsf(m.b) < FLT_EPSILON && fabsf(m.c) < FLT_EPSILON) + { + if (m.a < 0) + { + float f = r.x0; + r.x0 = r.x1; + r.x1 = f; + } + if (m.d < 0) + { + float f = r.y0; + r.y0 = r.y1; + r.y1 = f; + } + s = fz_transform_point_xy(r.x0, r.y0, m); + t = fz_transform_point_xy(r.x1, r.y1, m); + r.x0 = s.x; r.y0 = s.y; + r.x1 = t.x; r.y1 = t.y; + /* If r was invalid coming in, it'll still be invalid now. */ + return r; + } + else if (fabsf(m.a) < FLT_EPSILON && fabsf(m.d) < FLT_EPSILON) + { + if (m.b < 0) + { + float f = r.x0; + r.x0 = r.x1; + r.x1 = f; + } + if (m.c < 0) + { + float f = r.y0; + r.y0 = r.y1; + r.y1 = f; + } + s = fz_transform_point_xy(r.x0, r.y0, m); + t = fz_transform_point_xy(r.x1, r.y1, m); + r.x0 = s.x; r.y0 = s.y; + r.x1 = t.x; r.y1 = t.y; + /* If r was invalid coming in, it'll still be invalid now. */ + return r; + } + + invalid = (r.x0 > r.x1) || (r.y0 > r.y1); + s.x = r.x0; s.y = r.y0; + t.x = r.x0; t.y = r.y1; + u.x = r.x1; u.y = r.y1; + v.x = r.x1; v.y = r.y0; + s = fz_transform_point(s, m); + t = fz_transform_point(t, m); + u = fz_transform_point(u, m); + v = fz_transform_point(v, m); + r.x0 = MIN4(s.x, t.x, u.x, v.x); + r.y0 = MIN4(s.y, t.y, u.y, v.y); + r.x1 = MAX4(s.x, t.x, u.x, v.x); + r.y1 = MAX4(s.y, t.y, u.y, v.y); + + /* If we were called with an invalid rectangle, return an + * invalid rectangle after transformation. */ + if (invalid) + { + float f; + f = r.x0; r.x0 = r.x1; r.x1 = f; + f = r.y0; r.y0 = r.y1; r.y1 = f; + } + return r; +} + +fz_irect +fz_expand_irect(fz_irect a, int expand) +{ + if (fz_is_infinite_irect(a)) return a; + if (!fz_is_valid_irect(a)) return a; + a.x0 -= expand; + a.y0 -= expand; + a.x1 += expand; + a.y1 += expand; + return a; +} + +fz_rect +fz_expand_rect(fz_rect a, float expand) +{ + if (fz_is_infinite_rect(a)) return a; + if (!fz_is_valid_rect(a)) return a; + a.x0 -= expand; + a.y0 -= expand; + a.x1 += expand; + a.y1 += expand; + return a; +} + +/* Adding a point to an invalid rectangle makes the zero area rectangle + * that contains just that point. */ +fz_rect fz_include_point_in_rect(fz_rect r, fz_point p) +{ + if (fz_is_infinite_rect(r)) return r; + if (p.x < r.x0) r.x0 = p.x; + if (p.x > r.x1) r.x1 = p.x; + if (p.y < r.y0) r.y0 = p.y; + if (p.y > r.y1) r.y1 = p.y; + return r; +} + +int fz_contains_rect(fz_rect a, fz_rect b) +{ + /* An invalid rect can't contain anything */ + if (!fz_is_valid_rect(a)) + return 0; + /* Any valid rect contains all invalid rects */ + if (!fz_is_valid_rect(b)) + return 1; + return ((a.x0 <= b.x0) && + (a.y0 <= b.y0) && + (a.x1 >= b.x1) && + (a.y1 >= b.y1)); +} + +int +fz_is_valid_quad(fz_quad q) +{ + return (!isnan(q.ll.x) && + !isnan(q.ll.y) && + !isnan(q.ul.x) && + !isnan(q.ul.y) && + !isnan(q.lr.x) && + !isnan(q.lr.y) && + !isnan(q.ur.x) && + !isnan(q.ur.y)); +} + +int +fz_is_infinite_quad(fz_quad q) +{ + /* For a quad to be infinite, all the ordinates need to be infinite. */ + if (!isinf(q.ll.x) || + !isinf(q.ll.y) || + !isinf(q.ul.x) || + !isinf(q.ul.y) || + !isinf(q.lr.x) || + !isinf(q.lr.y) || + !isinf(q.ur.x) || + !isinf(q.ur.y)) + return 0; + + /* Just because all the ordinates are infinite, we don't necessarily have an infinite quad. */ + /* The quad points in order are: ll, ul, ur, lr OR reversed: ll, lr, ur, ul */ + /* The required points are: (-inf, -inf) (-inf, inf) (inf, inf) (inf, -inf) */ + + /* Not the fastest way to code it, but easy to understand! */ +#define INF_QUAD_TEST(A,B,C,D) \ + if (q.A.x < 0 && q.A.y < 0 && q.B.x < 0 && q.B.y > 0 && q.C.x > 0 && q.C.y > 0 && q.D.x > 0 && q.D.y < 0) return 1 + + INF_QUAD_TEST(ll, ul, ur, lr); + INF_QUAD_TEST(ul, ur, lr, ll); + INF_QUAD_TEST(ur, lr, ll, ul); + INF_QUAD_TEST(lr, ll, ul, ur); + INF_QUAD_TEST(ll, lr, ur, ul); + INF_QUAD_TEST(lr, ur, ul, ll); + INF_QUAD_TEST(ur, ul, ll, lr); + INF_QUAD_TEST(ul, ll, lr, ur); +#undef INF_QUAD_TEST + + return 0; +} + +int fz_is_empty_quad(fz_quad q) +{ + /* Using "Shoelace" formula */ + float area; + + if (fz_is_infinite_quad(q)) + return 0; + if (!fz_is_valid_quad(q)) + return 1; /* All invalid quads are empty */ + + area = q.ll.x * q.lr.y + + q.lr.x * q.ur.y + + q.ur.x * q.ul.y + + q.ul.x * q.ll.y - + q.lr.x * q.ll.y - + q.ur.x * q.lr.y - + q.ul.x * q.ur.y - + q.ll.x * q.ul.y; + + return area == 0; +} + +fz_rect +fz_rect_from_quad(fz_quad q) +{ + fz_rect r; + + if (!fz_is_valid_quad(q)) + return fz_invalid_rect; + if (fz_is_infinite_quad(q)) + return fz_infinite_rect; + + r.x0 = MIN4(q.ll.x, q.lr.x, q.ul.x, q.ur.x); + r.y0 = MIN4(q.ll.y, q.lr.y, q.ul.y, q.ur.y); + r.x1 = MAX4(q.ll.x, q.lr.x, q.ul.x, q.ur.x); + r.y1 = MAX4(q.ll.y, q.lr.y, q.ul.y, q.ur.y); + return r; +} + +fz_quad +fz_transform_quad(fz_quad q, fz_matrix m) +{ + if (!fz_is_valid_quad(q)) + return q; + if (fz_is_infinite_quad(q)) + return q; + + q.ul = fz_transform_point(q.ul, m); + q.ur = fz_transform_point(q.ur, m); + q.ll = fz_transform_point(q.ll, m); + q.lr = fz_transform_point(q.lr, m); + return q; +} + +fz_quad +fz_quad_from_rect(fz_rect r) +{ + fz_quad q; + + if (!fz_is_valid_rect(r)) + return fz_invalid_quad; + if (fz_is_infinite_rect(r)) + return fz_infinite_quad; + + q.ul = fz_make_point(r.x0, r.y0); + q.ur = fz_make_point(r.x1, r.y0); + q.ll = fz_make_point(r.x0, r.y1); + q.lr = fz_make_point(r.x1, r.y1); + return q; +} + + +int fz_is_point_inside_rect(fz_point p, fz_rect r) +{ + return (p.x >= r.x0 && p.x < r.x1 && p.y >= r.y0 && p.y < r.y1); +} + +int fz_is_point_inside_irect(int x, int y, fz_irect r) +{ + return (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1); +} + +int fz_is_rect_inside_rect(fz_rect inner, fz_rect outer) +{ + if (!fz_is_valid_rect(outer)) + return 0; + if (!fz_is_valid_rect(inner)) + return 0; + if (inner.x0 >= outer.x0 && inner.x1 <= outer.x1 && inner.y0 >= outer.y0 && inner.y1 <= outer.y1) + return 1; + return 0; +} + +int fz_is_irect_inside_irect(fz_irect inner, fz_irect outer) +{ + if (!fz_is_valid_irect(outer)) + return 0; + if (!fz_is_valid_irect(inner)) + return 0; + if (inner.x0 >= outer.x0 && inner.x1 <= outer.x1 && inner.y0 >= outer.y0 && inner.y1 <= outer.y1) + return 1; + return 0; +} + +/* cross (b-a) with (p-a) */ +static float +cross(fz_point a, fz_point b, fz_point p) +{ + b.x -= a.x; + b.y -= a.y; + p.x -= a.x; + p.y -= a.y; + return b.x * p.y - b.y * p.x; +} + +static int fz_is_point_inside_triangle(fz_point p, fz_point a, fz_point b, fz_point c) +{ + /* Consider the following: + * + * P + * /| + * / | + * / | + * A +---+-------+ B + * M + * + * The cross product of vector AB and vector AP is the distance PM. + * The sign of this distance depends on what side of the line AB, P lies on. + * + * So, for a triangle ABC, if we take cross products of: + * + * AB and AP + * BC and BP + * CA and CP + * + * P can only be inside the triangle if the signs are all identical. + * + * One of the cross products being 0 indicates that the point is on a line. + * Two of the cross products being 0 indicates that the point is on a vertex. + * + * If 2 of the vertices are the same, the algorithm still works. + * Iff all 3 of the vertices are the same, the cross products are all zero. The + * value of p is irrelevant. + */ + float crossa = cross(a, b, p); + float crossb = cross(b, c, p); + float crossc = cross(c, a, p); + + /* Check for degenerate case. All vertices the same. */ + if (crossa == 0 && crossb == 0 && crossc == 0) + return a.x == p.x && a.y == p.y; + + if (crossa >= 0 && crossb >= 0 && crossc >= 0) + return 1; + if (crossa <= 0 && crossb <= 0 && crossc <= 0) + return 1; + + return 0; +} + +int fz_is_point_inside_quad(fz_point p, fz_quad q) +{ + if (!fz_is_valid_quad(q)) + return 0; + if (fz_is_infinite_quad(q)) + return 1; + + return + fz_is_point_inside_triangle(p, q.ul, q.ur, q.lr) || + fz_is_point_inside_triangle(p, q.ul, q.lr, q.ll); +} + +int fz_is_quad_inside_quad(fz_quad needle, fz_quad haystack) +{ + if (!fz_is_valid_quad(needle) || !fz_is_valid_quad(haystack)) + return 0; + if (fz_is_infinite_quad(haystack)) + return 1; + + return + fz_is_point_inside_quad(needle.ul, haystack) && + fz_is_point_inside_quad(needle.ur, haystack) && + fz_is_point_inside_quad(needle.ll, haystack) && + fz_is_point_inside_quad(needle.lr, haystack); +} + +int fz_is_quad_intersecting_quad(fz_quad a, fz_quad b) +{ + fz_rect ar = fz_rect_from_quad(a); + fz_rect br = fz_rect_from_quad(b); + return !fz_is_empty_rect(fz_intersect_rect(ar, br)); +}
