Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zxing-cpp/core/src/Quadrilateral.h @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * Copyright 2020 Axel Waggershauser | |
| 3 */ | |
| 4 // SPDX-License-Identifier: Apache-2.0 | |
| 5 | |
| 6 #pragma once | |
| 7 | |
| 8 #include "Point.h" | |
| 9 #include "ZXAlgorithms.h" | |
| 10 | |
| 11 #include <array> | |
| 12 #include <cmath> | |
| 13 #include <string> | |
| 14 | |
| 15 namespace ZXing { | |
| 16 | |
| 17 template <typename T> | |
| 18 class Quadrilateral : public std::array<T, 4> | |
| 19 { | |
| 20 using Base = std::array<T, 4>; | |
| 21 using Base::at; | |
| 22 public: | |
| 23 using Point = T; | |
| 24 | |
| 25 Quadrilateral() = default; | |
| 26 Quadrilateral(T tl, T tr, T br, T bl) : Base{tl, tr, br, bl} {} | |
| 27 template <typename U> | |
| 28 Quadrilateral(PointT<U> tl, PointT<U> tr, PointT<U> br, PointT<U> bl) | |
| 29 : Quadrilateral(Point(tl), Point(tr), Point(br), Point(bl)) | |
| 30 {} | |
| 31 | |
| 32 constexpr Point topLeft() const noexcept { return at(0); } | |
| 33 constexpr Point topRight() const noexcept { return at(1); } | |
| 34 constexpr Point bottomRight() const noexcept { return at(2); } | |
| 35 constexpr Point bottomLeft() const noexcept { return at(3); } | |
| 36 | |
| 37 double orientation() const | |
| 38 { | |
| 39 auto centerLine = (topRight() + bottomRight()) - (topLeft() + bottomLeft()); | |
| 40 if (centerLine == Point{}) | |
| 41 return 0.; | |
| 42 auto centerLineF = normalized(centerLine); | |
| 43 return std::atan2(centerLineF.y, centerLineF.x); | |
| 44 } | |
| 45 }; | |
| 46 | |
| 47 using QuadrilateralF = Quadrilateral<PointF>; | |
| 48 using QuadrilateralI = Quadrilateral<PointI>; | |
| 49 | |
| 50 template <typename PointT = PointF> | |
| 51 Quadrilateral<PointT> Rectangle(int width, int height, typename PointT::value_t margin = 0) | |
| 52 { | |
| 53 return { | |
| 54 PointT{margin, margin}, {width - margin, margin}, {width - margin, height - margin}, {margin, height - margin}}; | |
| 55 } | |
| 56 | |
| 57 template <typename PointT = PointF> | |
| 58 Quadrilateral<PointT> CenteredSquare(int size) | |
| 59 { | |
| 60 return Scale(Quadrilateral(PointT{-1, -1}, {1, -1}, {1, 1}, {-1, 1}), size / 2); | |
| 61 } | |
| 62 | |
| 63 template <typename PointT = PointI> | |
| 64 Quadrilateral<PointT> Line(int y, int xStart, int xStop) | |
| 65 { | |
| 66 return {PointT{xStart, y}, {xStop, y}, {xStop, y}, {xStart, y}}; | |
| 67 } | |
| 68 | |
| 69 template <typename PointT> | |
| 70 bool IsConvex(const Quadrilateral<PointT>& poly) | |
| 71 { | |
| 72 const int N = Size(poly); | |
| 73 bool sign = false; | |
| 74 | |
| 75 typename PointT::value_t m = INFINITY, M = 0; | |
| 76 | |
| 77 for(int i = 0; i < N; i++) | |
| 78 { | |
| 79 auto d1 = poly[(i + 2) % N] - poly[(i + 1) % N]; | |
| 80 auto d2 = poly[i] - poly[(i + 1) % N]; | |
| 81 auto cp = cross(d1, d2); | |
| 82 | |
| 83 // TODO: see if the isInside check for all boundary points in GridSampler is still required after fixing the wrong fabs() | |
| 84 // application in the following line | |
| 85 UpdateMinMax(m, M, std::fabs(cp)); | |
| 86 | |
| 87 if (i == 0) | |
| 88 sign = cp > 0; | |
| 89 else if (sign != (cp > 0)) | |
| 90 return false; | |
| 91 } | |
| 92 | |
| 93 // It turns out being convex is not enough to prevent a "numerical instability" | |
| 94 // that can cause the corners being projected inside the image boundaries but | |
| 95 // some points near the corners being projected outside. This has been observed | |
| 96 // where one corner is almost in line with two others. The M/m ratio is below 2 | |
| 97 // for the complete existing sample set. For very "skewed" QRCodes a value of | |
| 98 // around 3 is realistic. A value of 14 has been observed to trigger the | |
| 99 // instability. | |
| 100 return M / m < 4.0; | |
| 101 } | |
| 102 | |
| 103 template <typename PointT> | |
| 104 Quadrilateral<PointT> Scale(const Quadrilateral<PointT>& q, int factor) | |
| 105 { | |
| 106 return {factor * q[0], factor * q[1], factor * q[2], factor * q[3]}; | |
| 107 } | |
| 108 | |
| 109 template <typename PointT> | |
| 110 PointT Center(const Quadrilateral<PointT>& q) | |
| 111 { | |
| 112 return Reduce(q) / Size(q); | |
| 113 } | |
| 114 | |
| 115 template <typename PointT> | |
| 116 Quadrilateral<PointT> RotatedCorners(const Quadrilateral<PointT>& q, int n = 1, bool mirror = false) | |
| 117 { | |
| 118 Quadrilateral<PointT> res; | |
| 119 std::rotate_copy(q.begin(), q.begin() + ((n + 4) % 4), q.end(), res.begin()); | |
| 120 if (mirror) | |
| 121 std::swap(res[1], res[3]); | |
| 122 return res; | |
| 123 } | |
| 124 | |
| 125 template <typename PointT> | |
| 126 bool IsInside(const PointT& p, const Quadrilateral<PointT>& q) | |
| 127 { | |
| 128 // Test if p is on the same side (right or left) of all polygon segments | |
| 129 int pos = 0, neg = 0; | |
| 130 for (int i = 0; i < Size(q); ++i) | |
| 131 (cross(p - q[i], q[(i + 1) % Size(q)] - q[i]) < 0 ? neg : pos)++; | |
| 132 return pos == 0 || neg == 0; | |
| 133 } | |
| 134 | |
| 135 template <typename PointT> | |
| 136 Quadrilateral<PointT> BoundingBox(const Quadrilateral<PointT>& q) | |
| 137 { | |
| 138 auto [minX, maxX] = std::minmax({q[0].x, q[1].x, q[2].x, q[3].x}); | |
| 139 auto [minY, maxY] = std::minmax({q[0].y, q[1].y, q[2].y, q[3].y}); | |
| 140 return {PointT{minX, minY}, {maxX, minY}, {maxX, maxY}, {minX, maxY}}; | |
| 141 } | |
| 142 | |
| 143 template <typename PointT> | |
| 144 bool HaveIntersectingBoundingBoxes(const Quadrilateral<PointT>& a, const Quadrilateral<PointT>& b) | |
| 145 { | |
| 146 auto bba = BoundingBox(a), bbb = BoundingBox(b); | |
| 147 | |
| 148 bool x = bbb.topRight().x < bba.topLeft().x || bbb.topLeft().x > bba.topRight().x; | |
| 149 bool y = bbb.bottomLeft().y < bba.topLeft().y || bbb.topLeft().y > bba.bottomLeft().y; | |
| 150 return !(x || y); | |
| 151 } | |
| 152 | |
| 153 template <typename PointT> | |
| 154 Quadrilateral<PointT> Blend(const Quadrilateral<PointT>& a, const Quadrilateral<PointT>& b) | |
| 155 { | |
| 156 auto dist2First = [r = a[0]](auto s, auto t) { return distance(s, r) < distance(t, r); }; | |
| 157 // rotate points such that the the two topLeft points are closest to each other | |
| 158 auto offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin(); | |
| 159 | |
| 160 Quadrilateral<PointT> res; | |
| 161 for (int i = 0; i < 4; ++i) | |
| 162 res[i] = (a[i] + b[(i + offset) % 4]) / 2; | |
| 163 | |
| 164 return res; | |
| 165 } | |
| 166 | |
| 167 template <typename T> | |
| 168 std::string ToString(const Quadrilateral<PointT<T>>& points) | |
| 169 { | |
| 170 std::string res; | |
| 171 for (const auto& p : points) | |
| 172 res += std::to_string(p.x) + "x" + std::to_string(p.y) + (&p == &points.back() ? "" : " "); | |
| 173 return res; | |
| 174 } | |
| 175 | |
| 176 } // ZXing | |
| 177 |
