已合并
feat(utils): add selection geometry utils for rect/point operations #158
no86创建于 7月31日
feat(utils): add selection geometry utils for rect/point operations #158
已合并
共 2 个文件变更+1887-0
| @@ -0,0 +1,186 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +namespace OHOS { | ||
| 25 | +namespace SelectionFwk { | ||
| 26 | + | ||
| 27 | +struct SelectionPoint { | ||
| 28 | + int32_t x = 0; | ||
| 29 | + int32_t y = 0; | ||
| 30 | + | ||
| 31 | + SelectionPoint() = default; | ||
| 32 | + SelectionPoint(int32_t px, int32_t py) : x(px), y(py) {} | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +struct SelectionRect { | ||
| 36 | + int32_t x = 0; | ||
| 37 | + int32_t y = 0; | ||
| 38 | + int32_t width = 0; | ||
| 39 | + int32_t height = 0; | ||
| 40 | + | ||
| 41 | + SelectionRect() = default; | ||
| 42 | + SelectionRect(int32_t px, int32_t py, int32_t pw, int32_t ph) | ||
| 43 | + : x(px), y(py), width(pw), height(ph) {} | ||
| 44 | + | ||
| 45 | + bool IsEmpty() const; | ||
| 46 | + bool IsNormalized() const; | ||
| 47 | + int64_t Area() const; | ||
| 48 | + bool operator==(const SelectionRect &other) const; | ||
| 49 | + bool operator!=(const SelectionRect &other) const; | ||
| 50 | +}; | ||
| 51 | + | ||
| 52 | +class SelectionGeometryUtils { | ||
| 53 | +public: | ||
| 54 | + static int32_t ClampInt(int32_t value, int32_t lo, int32_t hi); | ||
| 55 | + static int32_t ClampToInt32(int64_t value); | ||
| 56 | + static int32_t SnapToPixel(double value); | ||
| 57 | + static int32_t SafeAddInt32(int32_t a, int32_t b); | ||
| 58 | + static int32_t SafeSubInt32(int32_t a, int32_t b); | ||
| 59 | + static int64_t SafeMulInt64(int32_t a, int32_t b); | ||
| 60 | + | ||
| 61 | + static SelectionRect MakeEmptyRect(); | ||
| 62 | + static SelectionRect NormalizeRect(const SelectionRect &rect); | ||
| 63 | + static int64_t RightEdge(const SelectionRect &rect); | ||
| 64 | + static int64_t BottomEdge(const SelectionRect &rect); | ||
| 65 | + static int64_t CenterX(const SelectionRect &rect); | ||
| 66 | + static int64_t CenterY(const SelectionRect &rect); | ||
| 67 | + static int64_t Perimeter(const SelectionRect &rect); | ||
| 68 | + static double AspectRatio(const SelectionRect &rect); | ||
| 69 | + | ||
| 70 | + static bool ContainsPoint(const SelectionRect &rect, const SelectionPoint &point); | ||
| 71 | + static bool ContainsPoint(const SelectionRect &rect, int32_t px, int32_t py); | ||
| 72 | + static bool ContainsRect(const SelectionRect &outer, const SelectionRect &inner); | ||
| 73 | + static bool Intersects(const SelectionRect &a, const SelectionRect &b); | ||
| 74 | + static bool EqualsTolerance(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 75 | + | ||
| 76 | + static SelectionRect IntersectRect(const SelectionRect &a, const SelectionRect &b); | ||
| 77 | + static SelectionRect UnionRect(const SelectionRect &a, const SelectionRect &b); | ||
| 78 | + static std::vector<SelectionRect> SubtractRect(const SelectionRect &a, const SelectionRect &b); | ||
| 79 | + static std::vector<SelectionRect> IntersectListWithRect(const std::vector<SelectionRect> &list, | ||
| 80 | + const SelectionRect &rect); | ||
| 81 | + static SelectionRect UnionListBounds(const std::vector<SelectionRect> &list); | ||
| 82 | + | ||
| 83 | + static double DistancePointRect(const SelectionPoint &point, const SelectionRect &rect); | ||
| 84 | + static int32_t SignedDistanceXPointRect(const SelectionPoint &point, const SelectionRect &rect); | ||
| 85 | + static int32_t SignedDistanceYPointRect(const SelectionPoint &point, const SelectionRect &rect); | ||
| 86 | + static double DistanceRectRect(const SelectionRect &a, const SelectionRect &b); | ||
| 87 | + static int32_t HorizontalGap(const SelectionRect &a, const SelectionRect &b); | ||
| 88 | + static int32_t VerticalGap(const SelectionRect &a, const SelectionRect &b); | ||
| 89 | + | ||
| 90 | + static bool IsAdjacent(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 91 | + static bool IsHorizontallyAligned(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 92 | + static bool IsVerticallyAligned(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 93 | + static bool AreOnSameRow(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 94 | + static bool AreOnSameColumn(const SelectionRect &a, const SelectionRect &b, int32_t tol); | ||
| 95 | + | ||
| 96 | + static SelectionRect Inflate(const SelectionRect &rect, int32_t left, int32_t top, | ||
| 97 | + int32_t right, int32_t bottom); | ||
| 98 | + static SelectionRect InflateUniform(const SelectionRect &rect, int32_t amount); | ||
| 99 | + static SelectionRect Deflate(const SelectionRect &rect, int32_t left, int32_t top, | ||
| 100 | + int32_t right, int32_t bottom); | ||
| 101 | + static SelectionRect DeflateUniform(const SelectionRect &rect, int32_t amount); | ||
| 102 | + static SelectionRect Offset(const SelectionRect &rect, int32_t dx, int32_t dy); | ||
| 103 | + static SelectionRect Scale(const SelectionRect &rect, double sx, double sy); | ||
| 104 | + static SelectionRect Transpose(const SelectionRect &rect); | ||
| 105 | + static SelectionRect MirrorHorizontal(const SelectionRect &rect, int32_t axisX); | ||
| 106 | + static SelectionRect MirrorVertical(const SelectionRect &rect, int32_t axisY); | ||
| 107 | + static SelectionRect Rotate90(const SelectionRect &rect, const SelectionPoint &pivot); | ||
| 108 | + | ||
| 109 | + static std::vector<SelectionRect> MergeOverlapping(const std::vector<SelectionRect> &list, | ||
| 110 | + int32_t tol); | ||
| 111 | + static int64_t TotalArea(const std::vector<SelectionRect> &list); | ||
| 112 | + static SelectionRect BoundsOfList(const std::vector<SelectionRect> &list); | ||
| 113 | + static bool ContainsAny(const std::vector<SelectionRect> &list, const SelectionRect &rect); | ||
| 114 | + static bool ContainsAll(const std::vector<SelectionRect> &list, const SelectionRect &rect); | ||
| 115 | + static std::vector<SelectionRect> RemoveContained(const std::vector<SelectionRect> &list); | ||
| 116 | + static std::vector<SelectionRect> Deduplicate(const std::vector<SelectionRect> &list, int32_t tol); | ||
| 117 | + | ||
| 118 | + static std::vector<std::vector<SelectionRect>> GroupByRows(const std::vector<SelectionRect> &list, | ||
| 119 | + int32_t rowTol); | ||
| 120 | + static std::vector<SelectionRect> SortByRow(const std::vector<SelectionRect> &list); | ||
| 121 | + | ||
| 122 | + static SelectionRect ClipToBounds(const SelectionRect &rect, const SelectionRect &bounds); | ||
| 123 | + static std::vector<SelectionRect> ClipRectListToBounds(const std::vector<SelectionRect> &list, | ||
| 124 | + const SelectionRect &bounds); | ||
| 125 | + | ||
| 126 | + static bool ClipSegmentToRect(SelectionPoint &p1, SelectionPoint &p2, const SelectionRect &rect); | ||
| 127 | + static bool SegmentIntersectsRect(const SelectionPoint &p1, const SelectionPoint &p2, | ||
| 128 | + const SelectionRect &rect); | ||
| 129 | + | ||
| 130 | + static std::vector<SelectionPoint> ClipPolygonToRect(const std::vector<SelectionPoint> &polygon, | ||
| 131 | + const SelectionRect &rect); | ||
| 132 | + | ||
| 133 | + static int64_t UnionArea(const std::vector<SelectionRect> &list); | ||
| 134 | + | ||
| 135 | + static SelectionRect MapFromParent(const SelectionRect &rect, const SelectionPoint &parentOrigin); | ||
| 136 | + static SelectionRect MapToParent(const SelectionRect &rect, const SelectionPoint &parentOrigin); | ||
| 137 | + static SelectionRect NormalizeForRTL(const SelectionRect &rect, int32_t parentWidth); | ||
| 138 | + | ||
| 139 | + static SelectionRect MinRectByArea(const SelectionRect &a, const SelectionRect &b); | ||
| 140 | + static SelectionRect MaxRectByArea(const SelectionRect &a, const SelectionRect &b); | ||
| 141 | + | ||
| 142 | + static double PointDistance(const SelectionPoint &a, const SelectionPoint &b); | ||
| 143 | + static bool PointEqualsTolerance(const SelectionPoint &a, const SelectionPoint &b, int32_t tol); | ||
| 144 | + static SelectionPoint PointLerp(const SelectionPoint &a, const SelectionPoint &b, double t); | ||
| 145 | + static SelectionPoint PointTranslate(const SelectionPoint &pt, int32_t dx, int32_t dy); | ||
| 146 | + static SelectionPoint PointRotate90(const SelectionPoint &pt, const SelectionPoint &pivot); | ||
| 147 | + static SelectionPoint PointMirrorHorizontal(const SelectionPoint &pt, int32_t axisX); | ||
| 148 | + static SelectionPoint PointMirrorVertical(const SelectionPoint &pt, int32_t axisY); | ||
| 149 | + static double SegmentLength(const SelectionPoint &a, const SelectionPoint &b); | ||
| 150 | + static double PointToSegmentDistance(const SelectionPoint &p, const SelectionPoint &s, | ||
| 151 | + const SelectionPoint &e); | ||
| 152 | + static SelectionPoint FootOfPerpendicular(const SelectionPoint &p, const SelectionPoint &s, | ||
| 153 | + const SelectionPoint &e); | ||
| 154 | + static bool IsPointOnSegment(const SelectionPoint &p, const SelectionPoint &s, | ||
| 155 | + const SelectionPoint &e); | ||
| 156 | + static std::vector<SelectionPoint> RectCorners(const SelectionRect &rect); | ||
| 157 | + static std::vector<SelectionPoint> RectEdgeMidpoints(const SelectionRect &rect); | ||
| 158 | + static int32_t ClosestEdgeIndex(const SelectionRect &rect, const SelectionPoint &point); | ||
| 159 | + static SelectionRect ExpandToContain(const SelectionRect &rect, const SelectionPoint &point); | ||
| 160 | + static SelectionRect ShrinkToAspectRatio(const SelectionRect &rect, double targetRatio); | ||
| 161 | + static std::vector<SelectionRect> SplitGrid(const SelectionRect &rect, int32_t rows, int32_t cols); | ||
| 162 | + static bool IsSquare(const SelectionRect &rect); | ||
| 163 | + static int32_t LargerAxis(const SelectionRect &rect); | ||
| 164 | + static int32_t SmallerAxis(const SelectionRect &rect); | ||
| 165 | + static SelectionRect CenterAt(const SelectionRect &rect, const SelectionPoint ¢er); | ||
| 166 | +}; | ||
| 167 | + | ||
| 168 | +class SelectionRectCombiner { | ||
| 169 | +public: | ||
| 170 | + SelectionRectCombiner() = default; | ||
| 171 | + ~SelectionRectCombiner() = default; | ||
| 172 | + | ||
| 173 | + void Add(const SelectionRect &rect); | ||
| 174 | + std::vector<SelectionRect> Result() const; | ||
| 175 | + void Clear(); | ||
| 176 | + size_t Size() const; | ||
| 177 | + bool Empty() const; | ||
| 178 | + | ||
| 179 | +private: | ||
| 180 | + void TryMergeAdjacent(); | ||
| 181 | + | ||
| 182 | + std::vector<SelectionRect> rects_; | ||
| 183 | +}; | ||
| 184 | +} // namespace SelectionFwk | ||
| 185 | +} // namespace OHOS | ||
| 186 | + | ||
| @@ -0,0 +1,1701 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 Huawei Device Co., Ltd. | ||
| 3 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | + * you may not use this file except in compliance with the License. | ||
| 5 | + * You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * Unless required by applicable law or agreed to in writing, software | ||
| 10 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | + * See the License for the specific language governing permissions and | ||
| 13 | + * limitations under the License. | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +namespace OHOS { | ||
| 27 | +namespace SelectionFwk { | ||
| 28 | + | ||
| 29 | +namespace { | ||
| 30 | +constexpr int32_t INT32_MIN_VALUE = std::numeric_limits<int32_t>::min(); | ||
| 31 | +constexpr int32_t INT32_MAX_VALUE = std::numeric_limits<int32_t>::max(); | ||
| 32 | + | ||
| 33 | +constexpr size_t MIN_POLYGON_VERTICES = 3; | ||
| 34 | +constexpr int32_t RECT_EDGE_COUNT = 4; | ||
| 35 | +constexpr int32_t Y_BOUNDS_PER_RECT = 2; | ||
| 36 | +constexpr int64_t HALF_FACTOR = 2; | ||
| 37 | +constexpr int64_t PERIMETER_FACTOR = 2; | ||
| 38 | +constexpr int64_t MIRROR_FACTOR = 2; | ||
| 39 | +constexpr double ROUND_HALF = 0.5; | ||
| 40 | +constexpr double CLIP_T_MIN = 0.0; | ||
| 41 | +constexpr double CLIP_T_MAX = 1.0; | ||
| 42 | + | ||
| 43 | +constexpr int EDGE_LEFT = 0; | ||
| 44 | +constexpr int EDGE_RIGHT = 1; | ||
| 45 | +constexpr int EDGE_TOP = 2; | ||
| 46 | +constexpr int EDGE_BOTTOM = 3; | ||
| 47 | + | ||
| 48 | +int64_t AbsInt64(int64_t value) | ||
| 49 | +{ | ||
| 50 | + return value < 0 ? -value : value; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +struct Interval { | ||
| 54 | + int64_t start; | ||
| 55 | + int64_t end; | ||
| 56 | +}; | ||
| 57 | + | ||
| 58 | +bool OverlapOrAdjacent1D(int64_t a0, int64_t a1, int64_t b0, int64_t b1, int32_t tol) | ||
| 59 | +{ | ||
| 60 | + if (a0 > a1) { | ||
| 61 | + std::swap(a0, a1); | ||
| 62 | + } | ||
| 63 | + if (b0 > b1) { | ||
| 64 | + std::swap(b0, b1); | ||
| 65 | + } | ||
| 66 | + int64_t tol64 = static_cast<int64_t>(tol); | ||
| 67 | + if (tol64 < 0) { | ||
| 68 | + tol64 = 0; | ||
| 69 | + } | ||
| 70 | + return !(a1 + tol64 < b0 || b1 + tol64 < a0); | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +bool Overlap1D(int64_t a0, int64_t a1, int64_t b0, int64_t b1) | ||
| 74 | +{ | ||
| 75 | + if (a0 > a1) { | ||
| 76 | + std::swap(a0, a1); | ||
| 77 | + } | ||
| 78 | + if (b0 > b1) { | ||
| 79 | + std::swap(b0, b1); | ||
| 80 | + } | ||
| 81 | + return !(a1 <= b0 || b1 <= a0); | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +SelectionRect ClampRect(int64_t x, int64_t y, int64_t width, int64_t height) | ||
| 85 | +{ | ||
| 86 | + int64_t nx = x; | ||
| 87 | + int64_t ny = y; | ||
| 88 | + int64_t nw = width; | ||
| 89 | + int64_t nh = height; | ||
| 90 | + if (nw < 0) { | ||
| 91 | + nx += nw; | ||
| 92 | + nw = -nw; | ||
| 93 | + } | ||
| 94 | + if (nh < 0) { | ||
| 95 | + ny += nh; | ||
| 96 | + nh = -nh; | ||
| 97 | + } | ||
| 98 | + int64_t left = nx; | ||
| 99 | + int64_t top = ny; | ||
| 100 | + int64_t right = nx + nw; | ||
| 101 | + int64_t bottom = ny + nh; | ||
| 102 | + if (left < INT32_MIN_VALUE) { | ||
| 103 | + left = INT32_MIN_VALUE; | ||
| 104 | + } | ||
| 105 | + if (left > INT32_MAX_VALUE) { | ||
| 106 | + left = INT32_MAX_VALUE; | ||
| 107 | + } | ||
| 108 | + if (top < INT32_MIN_VALUE) { | ||
| 109 | + top = INT32_MIN_VALUE; | ||
| 110 | + } | ||
| 111 | + if (top > INT32_MAX_VALUE) { | ||
| 112 | + top = INT32_MAX_VALUE; | ||
| 113 | + } | ||
| 114 | + if (right < INT32_MIN_VALUE) { | ||
| 115 | + right = INT32_MIN_VALUE; | ||
| 116 | + } | ||
| 117 | + if (right > INT32_MAX_VALUE) { | ||
| 118 | + right = INT32_MAX_VALUE; | ||
| 119 | + } | ||
| 120 | + if (bottom < INT32_MIN_VALUE) { | ||
| 121 | + bottom = INT32_MIN_VALUE; | ||
| 122 | + } | ||
| 123 | + if (bottom > INT32_MAX_VALUE) { | ||
| 124 | + bottom = INT32_MAX_VALUE; | ||
| 125 | + } | ||
| 126 | + if (right < left) { | ||
| 127 | + right = left; | ||
| 128 | + } | ||
| 129 | + if (bottom < top) { | ||
| 130 | + bottom = top; | ||
| 131 | + } | ||
| 132 | + return SelectionRect(static_cast<int32_t>(left), static_cast<int32_t>(top), | ||
| 133 | + static_cast<int32_t>(right - left), static_cast<int32_t>(bottom - top)); | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +bool PolygonInsidePoint(const SelectionPoint &pt, int edge, const SelectionRect &bounds) | ||
| 137 | +{ | ||
| 138 | + int64_t x = pt.x; | ||
| 139 | + int64_t y = pt.y; | ||
| 140 | + int64_t l = bounds.x; | ||
| 141 | + int64_t r = SelectionGeometryUtils::RightEdge(bounds); | ||
| 142 | + int64_t t = bounds.y; | ||
| 143 | + int64_t b = SelectionGeometryUtils::BottomEdge(bounds); | ||
| 144 | + switch (edge) { | ||
| 145 | + case EDGE_LEFT: | ||
| 146 | + return x >= l; | ||
| 147 | + case EDGE_RIGHT: | ||
| 148 | + return x < r; | ||
| 149 | + case EDGE_TOP: | ||
| 150 | + return y >= t; | ||
| 151 | + case EDGE_BOTTOM: | ||
| 152 | + return y < b; | ||
| 153 | + default: | ||
| 154 | + return false; | ||
| 155 | + } | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +SelectionPoint PolygonEdgeIntersect(const SelectionPoint &s, const SelectionPoint &e, | ||
| 159 | + int edge, const SelectionRect &bounds) | ||
| 160 | +{ | ||
| 161 | + int64_t x1 = s.x; | ||
| 162 | + int64_t y1 = s.y; | ||
| 163 | + int64_t x2 = e.x; | ||
| 164 | + int64_t y2 = e.y; | ||
| 165 | + int64_t dx = x2 - x1; | ||
| 166 | + int64_t dy = y2 - y1; | ||
| 167 | + int64_t l = bounds.x; | ||
| 168 | + int64_t r = SelectionGeometryUtils::RightEdge(bounds); | ||
| 169 | + int64_t t = bounds.y; | ||
| 170 | + int64_t b = SelectionGeometryUtils::BottomEdge(bounds); | ||
| 171 | + switch (edge) { | ||
| 172 | + case EDGE_LEFT: { | ||
| 173 | + if (dx == 0) { | ||
| 174 | + return SelectionPoint(static_cast<int32_t>(l), static_cast<int32_t>(y1)); | ||
| 175 | + } | ||
| 176 | + double tt = static_cast<double>(l - x1) / static_cast<double>(dx); | ||
| 177 | + int64_t ny = y1 + static_cast<int64_t>(tt * static_cast<double>(dy)); | ||
| 178 | + return SelectionPoint(static_cast<int32_t>(l), static_cast<int32_t>(ny)); | ||
| 179 | + } | ||
| 180 | + case EDGE_RIGHT: { | ||
| 181 | + if (dx == 0) { | ||
| 182 | + return SelectionPoint(static_cast<int32_t>(r), static_cast<int32_t>(y1)); | ||
| 183 | + } | ||
| 184 | + double tt = static_cast<double>(r - x1) / static_cast<double>(dx); | ||
| 185 | + int64_t ny = y1 + static_cast<int64_t>(tt * static_cast<double>(dy)); | ||
| 186 | + return SelectionPoint(static_cast<int32_t>(r), static_cast<int32_t>(ny)); | ||
| 187 | + } | ||
| 188 | + case EDGE_TOP: { | ||
| 189 | + if (dy == 0) { | ||
| 190 | + return SelectionPoint(static_cast<int32_t>(x1), static_cast<int32_t>(t)); | ||
| 191 | + } | ||
| 192 | + double tt = static_cast<double>(t - y1) / static_cast<double>(dy); | ||
| 193 | + int64_t nx = x1 + static_cast<int64_t>(tt * static_cast<double>(dx)); | ||
| 194 | + return SelectionPoint(static_cast<int32_t>(nx), static_cast<int32_t>(t)); | ||
| 195 | + } | ||
| 196 | + case EDGE_BOTTOM: { | ||
| 197 | + if (dy == 0) { | ||
| 198 | + return SelectionPoint(static_cast<int32_t>(x1), static_cast<int32_t>(b)); | ||
| 199 | + } | ||
| 200 | + double tt = static_cast<double>(b - y1) / static_cast<double>(dy); | ||
| 201 | + int64_t nx = x1 + static_cast<int64_t>(tt * static_cast<double>(dx)); | ||
| 202 | + return SelectionPoint(static_cast<int32_t>(nx), static_cast<int32_t>(b)); | ||
| 203 | + } | ||
| 204 | + default: | ||
| 205 | + return e; | ||
| 206 | + } | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +std::vector<SelectionPoint> ClipPolygonSingleEdge(const std::vector<SelectionPoint> &input, | ||
| 210 | + int edge, const SelectionRect &bounds) | ||
| 211 | +{ | ||
| 212 | + std::vector<SelectionPoint> output; | ||
| 213 | + size_t n = input.size(); | ||
| 214 | + if (n == 0) { | ||
| 215 | + return output; | ||
| 216 | + } | ||
| 217 | + for (size_t i = 0; i < n; ++i) { | ||
| 218 | + SelectionPoint cur = input[i]; | ||
| 219 | + SelectionPoint prev = input[(i + n - 1) % n]; | ||
| 220 | + bool curIn = PolygonInsidePoint(cur, edge, bounds); | ||
| 221 | + bool prevIn = PolygonInsidePoint(prev, edge, bounds); | ||
| 222 | + if (curIn) { | ||
| 223 | + if (!prevIn) { | ||
| 224 | + output.push_back(PolygonEdgeIntersect(prev, cur, edge, bounds)); | ||
| 225 | + } | ||
| 226 | + output.push_back(cur); | ||
| 227 | + } else if (prevIn) { | ||
| 228 | + output.push_back(PolygonEdgeIntersect(prev, cur, edge, bounds)); | ||
| 229 | + } | ||
| 230 | + } | ||
| 231 | + return output; | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +bool ApplySegmentClipEdge(double p, double q, double &t0, double &t1) | ||
| 235 | +{ | ||
| 236 | + if (p == 0.0) { | ||
| 237 | + return q >= 0.0; | ||
| 238 | + } | ||
| 239 | + double t = q / p; | ||
| 240 | + if (p < 0.0) { | ||
| 241 | + if (t > t1) { | ||
| 242 | + return false; | ||
| 243 | + } | ||
| 244 | + if (t > t0) { | ||
| 245 | + t0 = t; | ||
| 246 | + } | ||
| 247 | + } else { | ||
| 248 | + if (t < t0) { | ||
| 249 | + return false; | ||
| 250 | + } | ||
| 251 | + if (t < t1) { | ||
| 252 | + t1 = t; | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + return true; | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +int64_t MergeIntervalLengths(std::vector<Interval> &intervals) | ||
| 259 | +{ | ||
| 260 | + if (intervals.empty()) { | ||
| 261 | + return 0; | ||
| 262 | + } | ||
| 263 | + std::sort(intervals.begin(), intervals.end(), | ||
| 264 | + [](const Interval &a, const Interval &b) { return a.start < b.start; }); | ||
| 265 | + int64_t mergedLen = 0; | ||
| 266 | + int64_t curStart = intervals[0].start; | ||
| 267 | + int64_t curEnd = intervals[0].end; | ||
| 268 | + for (size_t k = 1; k < intervals.size(); ++k) { | ||
| 269 | + if (intervals[k].start <= curEnd) { | ||
| 270 | + if (intervals[k].end > curEnd) { | ||
| 271 | + curEnd = intervals[k].end; | ||
| 272 | + } | ||
| 273 | + } else { | ||
| 274 | + mergedLen += curEnd - curStart; | ||
| 275 | + curStart = intervals[k].start; | ||
| 276 | + curEnd = intervals[k].end; | ||
| 277 | + } | ||
| 278 | + } | ||
| 279 | + mergedLen += curEnd - curStart; | ||
| 280 | + return mergedLen; | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +bool CanMergeRects(const SelectionRect &ri, const SelectionRect &rj, int32_t tol) | ||
| 284 | +{ | ||
| 285 | + int32_t hx = SelectionGeometryUtils::HorizontalGap(ri, rj); | ||
| 286 | + int32_t vy = SelectionGeometryUtils::VerticalGap(ri, rj); | ||
| 287 | + bool xOverlap = (hx <= 0); | ||
| 288 | + bool yOverlap = (vy <= 0); | ||
| 289 | + bool xAdj = (hx >= 0 && hx <= tol); | ||
| 290 | + bool yAdj = (vy >= 0 && vy <= tol); | ||
| 291 | + return (xOverlap && (yOverlap || yAdj)) || (yOverlap && (xOverlap || xAdj)); | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +size_t FindMergePartner(const std::vector<SelectionRect> &rects, size_t i, int32_t tol) | ||
| 295 | +{ | ||
| 296 | + for (size_t j = i + 1; j < rects.size(); ++j) { | ||
| 297 | + if (CanMergeRects(rects[i], rects[j], tol)) { | ||
| 298 | + return j; | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + return rects.size(); | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +bool IsContainedByAny(size_t idx, const std::vector<SelectionRect> &list) | ||
| 305 | +{ | ||
| 306 | + for (size_t j = 0; j < list.size(); ++j) { | ||
| 307 | + if (idx == j) { | ||
| 308 | + continue; | ||
| 309 | + } | ||
| 310 | + if (list[idx] == list[j]) { | ||
| 311 | + if (j < idx) { | ||
| 312 | + return true; | ||
| 313 | + } | ||
| 314 | + continue; | ||
| 315 | + } | ||
| 316 | + if (SelectionGeometryUtils::ContainsRect(list[j], list[idx])) { | ||
| 317 | + return true; | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + return false; | ||
| 321 | +} | ||
| 322 | + | ||
| 323 | +size_t FindRowGroup(const SelectionRect &rect, const std::vector<std::vector<SelectionRect>> &groups, | ||
| 324 | + int32_t rowTol) | ||
| 325 | +{ | ||
| 326 | + for (size_t g = 0; g < groups.size(); ++g) { | ||
| 327 | + if (groups[g].empty()) { | ||
| 328 | + continue; | ||
| 329 | + } | ||
| 330 | + SelectionRect bounds = SelectionGeometryUtils::UnionListBounds(groups[g]); | ||
| 331 | + if (OverlapOrAdjacent1D(bounds.y, SelectionGeometryUtils::BottomEdge(bounds), rect.y, | ||
| 332 | + SelectionGeometryUtils::BottomEdge(rect), rowTol)) { | ||
| 333 | + return g; | ||
| 334 | + } | ||
| 335 | + } | ||
| 336 | + return groups.size(); | ||
| 337 | +} | ||
| 338 | +} // namespace | ||
| 339 | + | ||
| 340 | +bool SelectionRect::IsEmpty() const | ||
| 341 | +{ | ||
| 342 | + return width <= 0 || height <= 0; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +bool SelectionRect::IsNormalized() const | ||
| 346 | +{ | ||
| 347 | + return width >= 0 && height >= 0; | ||
| 348 | +} | ||
| 349 | + | ||
| 350 | +int64_t SelectionRect::Area() const | ||
| 351 | +{ | ||
| 352 | + int64_t w = AbsInt64(static_cast<int64_t>(width)); | ||
| 353 | + int64_t h = AbsInt64(static_cast<int64_t>(height)); | ||
| 354 | + return w * h; | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +bool SelectionRect::operator==(const SelectionRect &other) const | ||
| 358 | +{ | ||
| 359 | + return x == other.x && y == other.y && width == other.width && height == other.height; | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +bool SelectionRect::operator!=(const SelectionRect &other) const | ||
| 363 | +{ | ||
| 364 | + return !(*this == other); | ||
| 365 | +} | ||
| 366 | + | ||
| 367 | +int32_t SelectionGeometryUtils::ClampInt(int32_t value, int32_t lo, int32_t hi) | ||
| 368 | +{ | ||
| 369 | + if (lo > hi) { | ||
| 370 | + std::swap(lo, hi); | ||
| 371 | + } | ||
| 372 | + if (value < lo) { | ||
| 373 | + return lo; | ||
| 374 | + } | ||
| 375 | + if (value > hi) { | ||
| 376 | + return hi; | ||
| 377 | + } | ||
| 378 | + return value; | ||
| 379 | +} | ||
| 380 | + | ||
| 381 | +int32_t SelectionGeometryUtils::ClampToInt32(int64_t value) | ||
| 382 | +{ | ||
| 383 | + if (value < INT32_MIN_VALUE) { | ||
| 384 | + return INT32_MIN_VALUE; | ||
| 385 | + } | ||
| 386 | + if (value > INT32_MAX_VALUE) { | ||
| 387 | + return INT32_MAX_VALUE; | ||
| 388 | + } | ||
| 389 | + return static_cast<int32_t>(value); | ||
| 390 | +} | ||
| 391 | + | ||
| 392 | +int32_t SelectionGeometryUtils::SnapToPixel(double value) | ||
| 393 | +{ | ||
| 394 | + if (value <= static_cast<double>(INT32_MIN_VALUE)) { | ||
| 395 | + return INT32_MIN_VALUE; | ||
| 396 | + } | ||
| 397 | + if (value >= static_cast<double>(INT32_MAX_VALUE)) { | ||
| 398 | + return INT32_MAX_VALUE; | ||
| 399 | + } | ||
| 400 | + if (value >= 0.0) { | ||
| 401 | + return static_cast<int32_t>(std::floor(value + ROUND_HALF)); | ||
| 402 | + } | ||
| 403 | + return static_cast<int32_t>(std::ceil(value - ROUND_HALF)); | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +int32_t SelectionGeometryUtils::SafeAddInt32(int32_t a, int32_t b) | ||
| 407 | +{ | ||
| 408 | + int64_t sum = static_cast<int64_t>(a) + static_cast<int64_t>(b); | ||
| 409 | + return ClampToInt32(sum); | ||
| 410 | +} | ||
| 411 | + | ||
| 412 | +int32_t SelectionGeometryUtils::SafeSubInt32(int32_t a, int32_t b) | ||
| 413 | +{ | ||
| 414 | + int64_t diff = static_cast<int64_t>(a) - static_cast<int64_t>(b); | ||
| 415 | + return ClampToInt32(diff); | ||
| 416 | +} | ||
| 417 | + | ||
| 418 | +int64_t SelectionGeometryUtils::SafeMulInt64(int32_t a, int32_t b) | ||
| 419 | +{ | ||
| 420 | + return static_cast<int64_t>(a) * static_cast<int64_t>(b); | ||
| 421 | +} | ||
| 422 | + | ||
| 423 | +SelectionRect SelectionGeometryUtils::MakeEmptyRect() | ||
| 424 | +{ | ||
| 425 | + return SelectionRect(0, 0, 0, 0); | ||
| 426 | +} | ||
| 427 | + | ||
| 428 | +SelectionRect SelectionGeometryUtils::NormalizeRect(const SelectionRect &rect) | ||
| 429 | +{ | ||
| 430 | + return ClampRect(static_cast<int64_t>(rect.x), static_cast<int64_t>(rect.y), | ||
| 431 | + static_cast<int64_t>(rect.width), static_cast<int64_t>(rect.height)); | ||
| 432 | +} | ||
| 433 | + | ||
| 434 | +int64_t SelectionGeometryUtils::RightEdge(const SelectionRect &rect) | ||
| 435 | +{ | ||
| 436 | + return static_cast<int64_t>(rect.x) + static_cast<int64_t>(rect.width); | ||
| 437 | +} | ||
| 438 | + | ||
| 439 | +int64_t SelectionGeometryUtils::BottomEdge(const SelectionRect &rect) | ||
| 440 | +{ | ||
| 441 | + return static_cast<int64_t>(rect.y) + static_cast<int64_t>(rect.height); | ||
| 442 | +} | ||
| 443 | + | ||
| 444 | +int64_t SelectionGeometryUtils::CenterX(const SelectionRect &rect) | ||
| 445 | +{ | ||
| 446 | + int64_t w = AbsInt64(static_cast<int64_t>(rect.width)); | ||
| 447 | + return static_cast<int64_t>(rect.x) + w / HALF_FACTOR; | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +int64_t SelectionGeometryUtils::CenterY(const SelectionRect &rect) | ||
| 451 | +{ | ||
| 452 | + int64_t h = AbsInt64(static_cast<int64_t>(rect.height)); | ||
| 453 | + return static_cast<int64_t>(rect.y) + h / HALF_FACTOR; | ||
| 454 | +} | ||
| 455 | + | ||
| 456 | +int64_t SelectionGeometryUtils::Perimeter(const SelectionRect &rect) | ||
| 457 | +{ | ||
| 458 | + int64_t w = AbsInt64(static_cast<int64_t>(rect.width)); | ||
| 459 | + int64_t h = AbsInt64(static_cast<int64_t>(rect.height)); | ||
| 460 | + return PERIMETER_FACTOR * (w + h); | ||
| 461 | +} | ||
| 462 | + | ||
| 463 | +double SelectionGeometryUtils::AspectRatio(const SelectionRect &rect) | ||
| 464 | +{ | ||
| 465 | + int64_t w = AbsInt64(static_cast<int64_t>(rect.width)); | ||
| 466 | + int64_t h = AbsInt64(static_cast<int64_t>(rect.height)); | ||
| 467 | + if (h == 0) { | ||
| 468 | + return 0.0; | ||
| 469 | + } | ||
| 470 | + return static_cast<double>(w) / static_cast<double>(h); | ||
| 471 | +} | ||
| 472 | + | ||
| 473 | +bool SelectionGeometryUtils::ContainsPoint(const SelectionRect &rect, const SelectionPoint &point) | ||
| 474 | +{ | ||
| 475 | + return ContainsPoint(rect, point.x, point.y); | ||
| 476 | +} | ||
| 477 | + | ||
| 478 | +bool SelectionGeometryUtils::ContainsPoint(const SelectionRect &rect, int32_t px, int32_t py) | ||
| 479 | +{ | ||
| 480 | + if (rect.IsEmpty()) { | ||
| 481 | + return false; | ||
| 482 | + } | ||
| 483 | + int64_t x = static_cast<int64_t>(px); | ||
| 484 | + int64_t y = static_cast<int64_t>(py); | ||
| 485 | + int64_t rx = static_cast<int64_t>(rect.x); | ||
| 486 | + int64_t ry = static_cast<int64_t>(rect.y); | ||
| 487 | + int64_t r = RightEdge(rect); | ||
| 488 | + int64_t b = BottomEdge(rect); | ||
| 489 | + if (r < rx) { | ||
| 490 | + std::swap(rx, r); | ||
| 491 | + } | ||
| 492 | + if (b < ry) { | ||
| 493 | + std::swap(ry, b); | ||
| 494 | + } | ||
| 495 | + return x >= rx && x < r && y >= ry && y < b; | ||
| 496 | +} | ||
| 497 | + | ||
| 498 | +bool SelectionGeometryUtils::ContainsRect(const SelectionRect &outer, const SelectionRect &inner) | ||
| 499 | +{ | ||
| 500 | + if (outer.IsEmpty() || inner.IsEmpty()) { | ||
| 501 | + return false; | ||
| 502 | + } | ||
| 503 | + SelectionRect normOuter = NormalizeRect(outer); | ||
| 504 | + SelectionRect normInner = NormalizeRect(inner); | ||
| 505 | + int64_t orx = normOuter.x; | ||
| 506 | + int64_t ory = normOuter.y; | ||
| 507 | + int64_t orr = RightEdge(normOuter); | ||
| 508 | + int64_t orb = BottomEdge(normOuter); | ||
| 509 | + int64_t irx = normInner.x; | ||
| 510 | + int64_t iry = normInner.y; | ||
| 511 | + int64_t irr = RightEdge(normInner); | ||
| 512 | + int64_t irb = BottomEdge(normInner); | ||
| 513 | + return irx >= orx && iry >= ory && irr <= orr && irb <= orb; | ||
| 514 | +} | ||
| 515 | + | ||
| 516 | +bool SelectionGeometryUtils::Intersects(const SelectionRect &a, const SelectionRect &b) | ||
| 517 | +{ | ||
| 518 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 519 | + return false; | ||
| 520 | + } | ||
| 521 | + SelectionRect na = NormalizeRect(a); | ||
| 522 | + SelectionRect nb = NormalizeRect(b); | ||
| 523 | + int64_t al = na.x; | ||
| 524 | + int64_t at = na.y; | ||
| 525 | + int64_t ar = RightEdge(na); | ||
| 526 | + int64_t ab = BottomEdge(na); | ||
| 527 | + int64_t bl = nb.x; | ||
| 528 | + int64_t bt = nb.y; | ||
| 529 | + int64_t br = RightEdge(nb); | ||
| 530 | + int64_t bb = BottomEdge(nb); | ||
| 531 | + return al < br && bl < ar && at < bb && bt < ab; | ||
| 532 | +} | ||
| 533 | + | ||
| 534 | +bool SelectionGeometryUtils::EqualsTolerance(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 535 | +{ | ||
| 536 | + int32_t absTol = tol < 0 ? -tol : tol; | ||
| 537 | + int32_t dx = SafeSubInt32(a.x, b.x); | ||
| 538 | + if (dx < 0) { | ||
| 539 | + dx = -dx; | ||
| 540 | + } | ||
| 541 | + int32_t dy = SafeSubInt32(a.y, b.y); | ||
| 542 | + if (dy < 0) { | ||
| 543 | + dy = -dy; | ||
| 544 | + } | ||
| 545 | + int32_t dw = SafeSubInt32(a.width, b.width); | ||
| 546 | + if (dw < 0) { | ||
| 547 | + dw = -dw; | ||
| 548 | + } | ||
| 549 | + int32_t dh = SafeSubInt32(a.height, b.height); | ||
| 550 | + if (dh < 0) { | ||
| 551 | + dh = -dh; | ||
| 552 | + } | ||
| 553 | + return dx <= absTol && dy <= absTol && dw <= absTol && dh <= absTol; | ||
| 554 | +} | ||
| 555 | + | ||
| 556 | +SelectionRect SelectionGeometryUtils::IntersectRect(const SelectionRect &a, const SelectionRect &b) | ||
| 557 | +{ | ||
| 558 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 559 | + return MakeEmptyRect(); | ||
| 560 | + } | ||
| 561 | + SelectionRect na = NormalizeRect(a); | ||
| 562 | + SelectionRect nb = NormalizeRect(b); | ||
| 563 | + int64_t l = std::max(static_cast<int64_t>(na.x), static_cast<int64_t>(nb.x)); | ||
| 564 | + int64_t t = std::max(static_cast<int64_t>(na.y), static_cast<int64_t>(nb.y)); | ||
| 565 | + int64_t r = std::min(RightEdge(na), RightEdge(nb)); | ||
| 566 | + int64_t btm = std::min(BottomEdge(na), BottomEdge(nb)); | ||
| 567 | + if (r <= l || btm <= t) { | ||
| 568 | + return MakeEmptyRect(); | ||
| 569 | + } | ||
| 570 | + return ClampRect(l, t, r - l, btm - t); | ||
| 571 | +} | ||
| 572 | + | ||
| 573 | +SelectionRect SelectionGeometryUtils::UnionRect(const SelectionRect &a, const SelectionRect &b) | ||
| 574 | +{ | ||
| 575 | + if (a.IsEmpty()) { | ||
| 576 | + return NormalizeRect(b); | ||
| 577 | + } | ||
| 578 | + if (b.IsEmpty()) { | ||
| 579 | + return NormalizeRect(a); | ||
| 580 | + } | ||
| 581 | + SelectionRect na = NormalizeRect(a); | ||
| 582 | + SelectionRect nb = NormalizeRect(b); | ||
| 583 | + int64_t l = std::min(static_cast<int64_t>(na.x), static_cast<int64_t>(nb.x)); | ||
| 584 | + int64_t t = std::min(static_cast<int64_t>(na.y), static_cast<int64_t>(nb.y)); | ||
| 585 | + int64_t r = std::max(RightEdge(na), RightEdge(nb)); | ||
| 586 | + int64_t btm = std::max(BottomEdge(na), BottomEdge(nb)); | ||
| 587 | + return ClampRect(l, t, r - l, btm - t); | ||
| 588 | +} | ||
| 589 | + | ||
| 590 | +std::vector<SelectionRect> SelectionGeometryUtils::SubtractRect(const SelectionRect &a, const SelectionRect &b) | ||
| 591 | +{ | ||
| 592 | + std::vector<SelectionRect> result; | ||
| 593 | + if (a.IsEmpty()) { | ||
| 594 | + return result; | ||
| 595 | + } | ||
| 596 | + SelectionRect na = NormalizeRect(a); | ||
| 597 | + if (b.IsEmpty()) { | ||
| 598 | + result.push_back(na); | ||
| 599 | + return result; | ||
| 600 | + } | ||
| 601 | + SelectionRect inter = IntersectRect(na, b); | ||
| 602 | + if (inter.IsEmpty()) { | ||
| 603 | + result.push_back(na); | ||
| 604 | + return result; | ||
| 605 | + } | ||
| 606 | + int64_t al = na.x; | ||
| 607 | + int64_t at = na.y; | ||
| 608 | + int64_t ar = RightEdge(na); | ||
| 609 | + int64_t ab = BottomEdge(na); | ||
| 610 | + int64_t il = inter.x; | ||
| 611 | + int64_t it = inter.y; | ||
| 612 | + int64_t ir = RightEdge(inter); | ||
| 613 | + int64_t ib = BottomEdge(inter); | ||
| 614 | + if (il > al) { | ||
| 615 | + result.push_back(ClampRect(al, at, il - al, ab - at)); | ||
| 616 | + } | ||
| 617 | + if (ir < ar) { | ||
| 618 | + result.push_back(ClampRect(ir, at, ar - ir, ab - at)); | ||
| 619 | + } | ||
| 620 | + if (it > at) { | ||
| 621 | + result.push_back(ClampRect(il, at, ir - il, it - at)); | ||
| 622 | + } | ||
| 623 | + if (ib < ab) { | ||
| 624 | + result.push_back(ClampRect(il, ib, ir - il, ab - ib)); | ||
| 625 | + } | ||
| 626 | + return result; | ||
| 627 | +} | ||
| 628 | + | ||
| 629 | +std::vector<SelectionRect> SelectionGeometryUtils::IntersectListWithRect(const std::vector<SelectionRect> &list, | ||
| 630 | + const SelectionRect &rect) | ||
| 631 | +{ | ||
| 632 | + std::vector<SelectionRect> result; | ||
| 633 | + for (const auto &item : list) { | ||
| 634 | + SelectionRect inter = IntersectRect(item, rect); | ||
| 635 | + if (!inter.IsEmpty()) { | ||
| 636 | + result.push_back(inter); | ||
| 637 | + } | ||
| 638 | + } | ||
| 639 | + return result; | ||
| 640 | +} | ||
| 641 | + | ||
| 642 | +SelectionRect SelectionGeometryUtils::UnionListBounds(const std::vector<SelectionRect> &list) | ||
| 643 | +{ | ||
| 644 | + if (list.empty()) { | ||
| 645 | + return MakeEmptyRect(); | ||
| 646 | + } | ||
| 647 | + SelectionRect acc = NormalizeRect(list.front()); | ||
| 648 | + for (size_t i = 1; i < list.size(); ++i) { | ||
| 649 | + acc = UnionRect(acc, list[i]); | ||
| 650 | + } | ||
| 651 | + return acc; | ||
| 652 | +} | ||
| 653 | + | ||
| 654 | +double SelectionGeometryUtils::DistancePointRect(const SelectionPoint &point, const SelectionRect &rect) | ||
| 655 | +{ | ||
| 656 | + if (rect.IsEmpty()) { | ||
| 657 | + return 0.0; | ||
| 658 | + } | ||
| 659 | + SelectionRect nr = NormalizeRect(rect); | ||
| 660 | + int64_t px = point.x; | ||
| 661 | + int64_t py = point.y; | ||
| 662 | + int64_t l = nr.x; | ||
| 663 | + int64_t t = nr.y; | ||
| 664 | + int64_t r = RightEdge(nr); | ||
| 665 | + int64_t b = BottomEdge(nr); | ||
| 666 | + int64_t dx = 0; | ||
| 667 | + int64_t dy = 0; | ||
| 668 | + if (px < l) { | ||
| 669 | + dx = l - px; | ||
| 670 | + } else if (px >= r) { | ||
| 671 | + dx = px - r + 1; | ||
| 672 | + } | ||
| 673 | + if (py < t) { | ||
| 674 | + dy = t - py; | ||
| 675 | + } else if (py >= b) { | ||
| 676 | + dy = py - b + 1; | ||
| 677 | + } | ||
| 678 | + double ddx = static_cast<double>(dx); | ||
| 679 | + double ddy = static_cast<double>(dy); | ||
| 680 | + return std::sqrt(ddx * ddx + ddy * ddy); | ||
| 681 | +} | ||
| 682 | + | ||
| 683 | +int32_t SelectionGeometryUtils::SignedDistanceXPointRect(const SelectionPoint &point, const SelectionRect &rect) | ||
| 684 | +{ | ||
| 685 | + if (rect.IsEmpty()) { | ||
| 686 | + return 0; | ||
| 687 | + } | ||
| 688 | + SelectionRect nr = NormalizeRect(rect); | ||
| 689 | + int64_t px = point.x; | ||
| 690 | + int64_t l = nr.x; | ||
| 691 | + int64_t r = RightEdge(nr); | ||
| 692 | + if (px < l) { | ||
| 693 | + return ClampToInt32(px - l); | ||
| 694 | + } | ||
| 695 | + if (px >= r) { | ||
| 696 | + return ClampToInt32(px - r + 1); | ||
| 697 | + } | ||
| 698 | + return 0; | ||
| 699 | +} | ||
| 700 | + | ||
| 701 | +int32_t SelectionGeometryUtils::SignedDistanceYPointRect(const SelectionPoint &point, const SelectionRect &rect) | ||
| 702 | +{ | ||
| 703 | + if (rect.IsEmpty()) { | ||
| 704 | + return 0; | ||
| 705 | + } | ||
| 706 | + SelectionRect nr = NormalizeRect(rect); | ||
| 707 | + int64_t py = point.y; | ||
| 708 | + int64_t t = nr.y; | ||
| 709 | + int64_t b = BottomEdge(nr); | ||
| 710 | + if (py < t) { | ||
| 711 | + return ClampToInt32(py - t); | ||
| 712 | + } | ||
| 713 | + if (py >= b) { | ||
| 714 | + return ClampToInt32(py - b + 1); | ||
| 715 | + } | ||
| 716 | + return 0; | ||
| 717 | +} | ||
| 718 | + | ||
| 719 | +double SelectionGeometryUtils::DistanceRectRect(const SelectionRect &a, const SelectionRect &b) | ||
| 720 | +{ | ||
| 721 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 722 | + return 0.0; | ||
| 723 | + } | ||
| 724 | + int32_t hx = HorizontalGap(a, b); | ||
| 725 | + int32_t vy = VerticalGap(a, b); | ||
| 726 | + if (hx <= 0 && vy <= 0) { | ||
| 727 | + return 0.0; | ||
| 728 | + } | ||
| 729 | + double dx = static_cast<double>(hx > 0 ? hx : 0); | ||
| 730 | + double dy = static_cast<double>(vy > 0 ? vy : 0); | ||
| 731 | + return std::sqrt(dx * dx + dy * dy); | ||
| 732 | +} | ||
| 733 | + | ||
| 734 | +int32_t SelectionGeometryUtils::HorizontalGap(const SelectionRect &a, const SelectionRect &b) | ||
| 735 | +{ | ||
| 736 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 737 | + return 0; | ||
| 738 | + } | ||
| 739 | + SelectionRect na = NormalizeRect(a); | ||
| 740 | + SelectionRect nb = NormalizeRect(b); | ||
| 741 | + int64_t al = na.x; | ||
| 742 | + int64_t ar = RightEdge(na); | ||
| 743 | + int64_t bl = nb.x; | ||
| 744 | + int64_t br = RightEdge(nb); | ||
| 745 | + if (ar <= bl) { | ||
| 746 | + return ClampToInt32(bl - ar); | ||
| 747 | + } | ||
| 748 | + if (br <= al) { | ||
| 749 | + return ClampToInt32(al - br); | ||
| 750 | + } | ||
| 751 | + return 0; | ||
| 752 | +} | ||
| 753 | + | ||
| 754 | +int32_t SelectionGeometryUtils::VerticalGap(const SelectionRect &a, const SelectionRect &b) | ||
| 755 | +{ | ||
| 756 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 757 | + return 0; | ||
| 758 | + } | ||
| 759 | + SelectionRect na = NormalizeRect(a); | ||
| 760 | + SelectionRect nb = NormalizeRect(b); | ||
| 761 | + int64_t at = na.y; | ||
| 762 | + int64_t ab = BottomEdge(na); | ||
| 763 | + int64_t bt = nb.y; | ||
| 764 | + int64_t bb = BottomEdge(nb); | ||
| 765 | + if (ab <= bt) { | ||
| 766 | + return ClampToInt32(bt - ab); | ||
| 767 | + } | ||
| 768 | + if (bb <= at) { | ||
| 769 | + return ClampToInt32(at - bb); | ||
| 770 | + } | ||
| 771 | + return 0; | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +bool SelectionGeometryUtils::IsAdjacent(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 775 | +{ | ||
| 776 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 777 | + return false; | ||
| 778 | + } | ||
| 779 | + SelectionRect na = NormalizeRect(a); | ||
| 780 | + SelectionRect nb = NormalizeRect(b); | ||
| 781 | + int32_t hx = HorizontalGap(na, nb); | ||
| 782 | + int32_t vy = VerticalGap(na, nb); | ||
| 783 | + int32_t absTol = tol < 0 ? -tol : tol; | ||
| 784 | + bool xAdj = (hx >= 0 && hx <= absTol) && Overlap1D(na.y, BottomEdge(na), nb.y, BottomEdge(nb)); | ||
| 785 | + bool yAdj = (vy >= 0 && vy <= absTol) && Overlap1D(na.x, RightEdge(na), nb.x, RightEdge(nb)); | ||
| 786 | + return xAdj || yAdj; | ||
| 787 | +} | ||
| 788 | + | ||
| 789 | +bool SelectionGeometryUtils::IsHorizontallyAligned(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 790 | +{ | ||
| 791 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 792 | + return false; | ||
| 793 | + } | ||
| 794 | + SelectionRect na = NormalizeRect(a); | ||
| 795 | + SelectionRect nb = NormalizeRect(b); | ||
| 796 | + int64_t at = na.y; | ||
| 797 | + int64_t ab = BottomEdge(na); | ||
| 798 | + int64_t bt = nb.y; | ||
| 799 | + int64_t bb = BottomEdge(nb); | ||
| 800 | + int64_t ac = (at + ab) / HALF_FACTOR; | ||
| 801 | + int64_t bc = (bt + bb) / HALF_FACTOR; | ||
| 802 | + int64_t diff = ac - bc; | ||
| 803 | + if (diff < 0) { | ||
| 804 | + diff = -diff; | ||
| 805 | + } | ||
| 806 | + int64_t absTol = tol < 0 ? -static_cast<int64_t>(tol) : static_cast<int64_t>(tol); | ||
| 807 | + return diff <= absTol; | ||
| 808 | +} | ||
| 809 | + | ||
| 810 | +bool SelectionGeometryUtils::IsVerticallyAligned(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 811 | +{ | ||
| 812 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 813 | + return false; | ||
| 814 | + } | ||
| 815 | + SelectionRect na = NormalizeRect(a); | ||
| 816 | + SelectionRect nb = NormalizeRect(b); | ||
| 817 | + int64_t al = na.x; | ||
| 818 | + int64_t ar = RightEdge(na); | ||
| 819 | + int64_t bl = nb.x; | ||
| 820 | + int64_t br = RightEdge(nb); | ||
| 821 | + int64_t ac = (al + ar) / HALF_FACTOR; | ||
| 822 | + int64_t bc = (bl + br) / HALF_FACTOR; | ||
| 823 | + int64_t diff = ac - bc; | ||
| 824 | + if (diff < 0) { | ||
| 825 | + diff = -diff; | ||
| 826 | + } | ||
| 827 | + int64_t absTol = tol < 0 ? -static_cast<int64_t>(tol) : static_cast<int64_t>(tol); | ||
| 828 | + return diff <= absTol; | ||
| 829 | +} | ||
| 830 | + | ||
| 831 | +bool SelectionGeometryUtils::AreOnSameRow(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 832 | +{ | ||
| 833 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 834 | + return false; | ||
| 835 | + } | ||
| 836 | + SelectionRect na = NormalizeRect(a); | ||
| 837 | + SelectionRect nb = NormalizeRect(b); | ||
| 838 | + return OverlapOrAdjacent1D(na.y, BottomEdge(na), nb.y, BottomEdge(nb), tol); | ||
| 839 | +} | ||
| 840 | + | ||
| 841 | +bool SelectionGeometryUtils::AreOnSameColumn(const SelectionRect &a, const SelectionRect &b, int32_t tol) | ||
| 842 | +{ | ||
| 843 | + if (a.IsEmpty() || b.IsEmpty()) { | ||
| 844 | + return false; | ||
| 845 | + } | ||
| 846 | + SelectionRect na = NormalizeRect(a); | ||
| 847 | + SelectionRect nb = NormalizeRect(b); | ||
| 848 | + return OverlapOrAdjacent1D(na.x, RightEdge(na), nb.x, RightEdge(nb), tol); | ||
| 849 | +} | ||
| 850 | + | ||
| 851 | +SelectionRect SelectionGeometryUtils::Inflate(const SelectionRect &rect, int32_t left, int32_t top, | ||
| 852 | + int32_t right, int32_t bottom) | ||
| 853 | +{ | ||
| 854 | + SelectionRect nr = NormalizeRect(rect); | ||
| 855 | + if (nr.IsEmpty()) { | ||
| 856 | + return MakeEmptyRect(); | ||
| 857 | + } | ||
| 858 | + int64_t x = static_cast<int64_t>(nr.x) - static_cast<int64_t>(left); | ||
| 859 | + int64_t y = static_cast<int64_t>(nr.y) - static_cast<int64_t>(top); | ||
| 860 | + int64_t w = static_cast<int64_t>(nr.width) + static_cast<int64_t>(left) + static_cast<int64_t>(right); | ||
| 861 | + int64_t h = static_cast<int64_t>(nr.height) + static_cast<int64_t>(top) + static_cast<int64_t>(bottom); | ||
| 862 | + return ClampRect(x, y, w, h); | ||
| 863 | +} | ||
| 864 | + | ||
| 865 | +SelectionRect SelectionGeometryUtils::InflateUniform(const SelectionRect &rect, int32_t amount) | ||
| 866 | +{ | ||
| 867 | + return Inflate(rect, amount, amount, amount, amount); | ||
| 868 | +} | ||
| 869 | + | ||
| 870 | +SelectionRect SelectionGeometryUtils::Deflate(const SelectionRect &rect, int32_t left, int32_t top, | ||
| 871 | + int32_t right, int32_t bottom) | ||
| 872 | +{ | ||
| 873 | + SelectionRect nr = NormalizeRect(rect); | ||
| 874 | + if (nr.IsEmpty()) { | ||
| 875 | + return MakeEmptyRect(); | ||
| 876 | + } | ||
| 877 | + int64_t x = static_cast<int64_t>(nr.x) + static_cast<int64_t>(left); | ||
| 878 | + int64_t y = static_cast<int64_t>(nr.y) + static_cast<int64_t>(top); | ||
| 879 | + int64_t w = static_cast<int64_t>(nr.width) - static_cast<int64_t>(left) - static_cast<int64_t>(right); | ||
| 880 | + int64_t h = static_cast<int64_t>(nr.height) - static_cast<int64_t>(top) - static_cast<int64_t>(bottom); | ||
| 881 | + return ClampRect(x, y, w, h); | ||
| 882 | +} | ||
| 883 | + | ||
| 884 | +SelectionRect SelectionGeometryUtils::DeflateUniform(const SelectionRect &rect, int32_t amount) | ||
| 885 | +{ | ||
| 886 | + return Deflate(rect, amount, amount, amount, amount); | ||
| 887 | +} | ||
| 888 | + | ||
| 889 | +SelectionRect SelectionGeometryUtils::Offset(const SelectionRect &rect, int32_t dx, int32_t dy) | ||
| 890 | +{ | ||
| 891 | + SelectionRect nr = NormalizeRect(rect); | ||
| 892 | + if (nr.IsEmpty()) { | ||
| 893 | + return MakeEmptyRect(); | ||
| 894 | + } | ||
| 895 | + int64_t x = static_cast<int64_t>(nr.x) + static_cast<int64_t>(dx); | ||
| 896 | + int64_t y = static_cast<int64_t>(nr.y) + static_cast<int64_t>(dy); | ||
| 897 | + return ClampRect(x, y, nr.width, nr.height); | ||
| 898 | +} | ||
| 899 | + | ||
| 900 | +SelectionRect SelectionGeometryUtils::Scale(const SelectionRect &rect, double sx, double sy) | ||
| 901 | +{ | ||
| 902 | + SelectionRect nr = NormalizeRect(rect); | ||
| 903 | + if (nr.IsEmpty()) { | ||
| 904 | + return MakeEmptyRect(); | ||
| 905 | + } | ||
| 906 | + double x = static_cast<double>(nr.x) * sx; | ||
| 907 | + double y = static_cast<double>(nr.y) * sy; | ||
| 908 | + double w = static_cast<double>(nr.width) * sx; | ||
| 909 | + double h = static_cast<double>(nr.height) * sy; | ||
| 910 | + return ClampRect(static_cast<int64_t>(std::floor(x)), static_cast<int64_t>(std::floor(y)), | ||
| 911 | + static_cast<int64_t>(std::floor(w)), static_cast<int64_t>(std::floor(h))); | ||
| 912 | +} | ||
| 913 | + | ||
| 914 | +SelectionRect SelectionGeometryUtils::Transpose(const SelectionRect &rect) | ||
| 915 | +{ | ||
| 916 | + SelectionRect nr = NormalizeRect(rect); | ||
| 917 | + if (nr.IsEmpty()) { | ||
| 918 | + return MakeEmptyRect(); | ||
| 919 | + } | ||
| 920 | + return SelectionRect(nr.x, nr.y, nr.height, nr.width); | ||
| 921 | +} | ||
| 922 | + | ||
| 923 | +SelectionRect SelectionGeometryUtils::MirrorHorizontal(const SelectionRect &rect, int32_t axisX) | ||
| 924 | +{ | ||
| 925 | + SelectionRect nr = NormalizeRect(rect); | ||
| 926 | + if (nr.IsEmpty()) { | ||
| 927 | + return MakeEmptyRect(); | ||
| 928 | + } | ||
| 929 | + int64_t axis = static_cast<int64_t>(axisX); | ||
| 930 | + int64_t l = static_cast<int64_t>(nr.x); | ||
| 931 | + int64_t r = RightEdge(nr); | ||
| 932 | + int64_t newR = MIRROR_FACTOR * axis - l; | ||
| 933 | + int64_t newL = MIRROR_FACTOR * axis - r; | ||
| 934 | + if (newL > newR) { | ||
| 935 | + std::swap(newL, newR); | ||
| 936 | + } | ||
| 937 | + return ClampRect(newL, nr.y, newR - newL, nr.height); | ||
| 938 | +} | ||
| 939 | + | ||
| 940 | +SelectionRect SelectionGeometryUtils::MirrorVertical(const SelectionRect &rect, int32_t axisY) | ||
| 941 | +{ | ||
| 942 | + SelectionRect nr = NormalizeRect(rect); | ||
| 943 | + if (nr.IsEmpty()) { | ||
| 944 | + return MakeEmptyRect(); | ||
| 945 | + } | ||
| 946 | + int64_t axis = static_cast<int64_t>(axisY); | ||
| 947 | + int64_t t = static_cast<int64_t>(nr.y); | ||
| 948 | + int64_t b = BottomEdge(nr); | ||
| 949 | + int64_t newB = MIRROR_FACTOR * axis - t; | ||
| 950 | + int64_t newT = MIRROR_FACTOR * axis - b; | ||
| 951 | + if (newT > newB) { | ||
| 952 | + std::swap(newT, newB); | ||
| 953 | + } | ||
| 954 | + return ClampRect(nr.x, newT, nr.width, newB - newT); | ||
| 955 | +} | ||
| 956 | + | ||
| 957 | +SelectionRect SelectionGeometryUtils::Rotate90(const SelectionRect &rect, const SelectionPoint &pivot) | ||
| 958 | +{ | ||
| 959 | + SelectionRect nr = NormalizeRect(rect); | ||
| 960 | + if (nr.IsEmpty()) { | ||
| 961 | + return MakeEmptyRect(); | ||
| 962 | + } | ||
| 963 | + int64_t px = pivot.x; | ||
| 964 | + int64_t py = pivot.y; | ||
| 965 | + int64_t l = nr.x; | ||
| 966 | + int64_t t = nr.y; | ||
| 967 | + int64_t r = RightEdge(nr); | ||
| 968 | + int64_t b = BottomEdge(nr); | ||
| 969 | + int64_t nx = px - (b - py); | ||
| 970 | + int64_t ny = py + (l - px); | ||
| 971 | + int64_t nw = nr.height; | ||
| 972 | + int64_t nh = nr.width; | ||
| 973 | + int64_t nx2 = px - (t - py); | ||
| 974 | + int64_t ny2 = py + (r - px); | ||
| 975 | + int64_t minX = std::min(nx, nx2 - nw); | ||
| 976 | + int64_t minY = std::min(ny, ny2 - nh); | ||
| 977 | + return ClampRect(minX, minY, nw, nh); | ||
| 978 | +} | ||
| 979 | + | ||
| 980 | +std::vector<SelectionRect> SelectionGeometryUtils::MergeOverlapping(const std::vector<SelectionRect> &list, | ||
| 981 | + int32_t tol) | ||
| 982 | +{ | ||
| 983 | + std::vector<SelectionRect> work; | ||
| 984 | + work.reserve(list.size()); | ||
| 985 | + for (const auto &item : list) { | ||
| 986 | + if (!item.IsEmpty()) { | ||
| 987 | + work.push_back(NormalizeRect(item)); | ||
| 988 | + } | ||
| 989 | + } | ||
| 990 | + bool changed = true; | ||
| 991 | + while (changed) { | ||
| 992 | + changed = false; | ||
| 993 | + for (size_t i = 0; i < work.size(); ++i) { | ||
| 994 | + size_t j = FindMergePartner(work, i, tol); | ||
| 995 | + if (j >= work.size()) { | ||
| 996 | + continue; | ||
| 997 | + } | ||
| 998 | + work[i] = UnionRect(work[i], work[j]); | ||
| 999 | + work.erase(work.begin() + static_cast<ptrdiff_t>(j)); | ||
| 1000 | + changed = true; | ||
| 1001 | + break; | ||
| 1002 | + } | ||
| 1003 | + } | ||
| 1004 | + return work; | ||
| 1005 | +} | ||
| 1006 | + | ||
| 1007 | +int64_t SelectionGeometryUtils::TotalArea(const std::vector<SelectionRect> &list) | ||
| 1008 | +{ | ||
| 1009 | + int64_t total = 0; | ||
| 1010 | + for (const auto &item : list) { | ||
| 1011 | + SelectionRect nr = NormalizeRect(item); | ||
| 1012 | + total += static_cast<int64_t>(AbsInt64(nr.width)) * static_cast<int64_t>(AbsInt64(nr.height)); | ||
| 1013 | + } | ||
| 1014 | + return total; | ||
| 1015 | +} | ||
| 1016 | + | ||
| 1017 | +SelectionRect SelectionGeometryUtils::BoundsOfList(const std::vector<SelectionRect> &list) | ||
| 1018 | +{ | ||
| 1019 | + return UnionListBounds(list); | ||
| 1020 | +} | ||
| 1021 | + | ||
| 1022 | +bool SelectionGeometryUtils::ContainsAny(const std::vector<SelectionRect> &list, const SelectionRect &rect) | ||
| 1023 | +{ | ||
| 1024 | + for (const auto &item : list) { | ||
| 1025 | + if (ContainsRect(item, rect)) { | ||
| 1026 | + return true; | ||
| 1027 | + } | ||
| 1028 | + } | ||
| 1029 | + return false; | ||
| 1030 | +} | ||
| 1031 | + | ||
| 1032 | +bool SelectionGeometryUtils::ContainsAll(const std::vector<SelectionRect> &list, const SelectionRect &rect) | ||
| 1033 | +{ | ||
| 1034 | + if (list.empty()) { | ||
| 1035 | + return false; | ||
| 1036 | + } | ||
| 1037 | + for (const auto &item : list) { | ||
| 1038 | + if (!ContainsRect(item, rect)) { | ||
| 1039 | + return false; | ||
| 1040 | + } | ||
| 1041 | + } | ||
| 1042 | + return true; | ||
| 1043 | +} | ||
| 1044 | + | ||
| 1045 | +std::vector<SelectionRect> SelectionGeometryUtils::RemoveContained(const std::vector<SelectionRect> &list) | ||
| 1046 | +{ | ||
| 1047 | + std::vector<SelectionRect> normed; | ||
| 1048 | + normed.reserve(list.size()); | ||
| 1049 | + for (const auto &item : list) { | ||
| 1050 | + if (!item.IsEmpty()) { | ||
| 1051 | + normed.push_back(NormalizeRect(item)); | ||
| 1052 | + } | ||
| 1053 | + } | ||
| 1054 | + std::vector<SelectionRect> result; | ||
| 1055 | + result.reserve(normed.size()); | ||
| 1056 | + for (size_t i = 0; i < normed.size(); ++i) { | ||
| 1057 | + if (!IsContainedByAny(i, normed)) { | ||
| 1058 | + result.push_back(normed[i]); | ||
| 1059 | + } | ||
| 1060 | + } | ||
| 1061 | + return result; | ||
| 1062 | +} | ||
| 1063 | + | ||
| 1064 | +std::vector<SelectionRect> SelectionGeometryUtils::Deduplicate(const std::vector<SelectionRect> &list, | ||
| 1065 | + int32_t tol) | ||
| 1066 | +{ | ||
| 1067 | + std::vector<SelectionRect> normed; | ||
| 1068 | + normed.reserve(list.size()); | ||
| 1069 | + for (const auto &item : list) { | ||
| 1070 | + if (!item.IsEmpty()) { | ||
| 1071 | + normed.push_back(NormalizeRect(item)); | ||
| 1072 | + } | ||
| 1073 | + } | ||
| 1074 | + std::vector<SelectionRect> result; | ||
| 1075 | + result.reserve(normed.size()); | ||
| 1076 | + for (const auto &item : normed) { | ||
| 1077 | + bool dup = false; | ||
| 1078 | + for (const auto &kept : result) { | ||
| 1079 | + if (EqualsTolerance(item, kept, tol)) { | ||
| 1080 | + dup = true; | ||
| 1081 | + break; | ||
| 1082 | + } | ||
| 1083 | + } | ||
| 1084 | + if (!dup) { | ||
| 1085 | + result.push_back(item); | ||
| 1086 | + } | ||
| 1087 | + } | ||
| 1088 | + return result; | ||
| 1089 | +} | ||
| 1090 | + | ||
| 1091 | +std::vector<std::vector<SelectionRect>> SelectionGeometryUtils::GroupByRows( | ||
| 1092 | + const std::vector<SelectionRect> &list, int32_t rowTol) | ||
| 1093 | +{ | ||
| 1094 | + std::vector<SelectionRect> sorted = SortByRow(list); | ||
| 1095 | + std::vector<std::vector<SelectionRect>> groups; | ||
| 1096 | + for (const auto &rect : sorted) { | ||
| 1097 | + if (rect.IsEmpty()) { | ||
| 1098 | + continue; | ||
| 1099 | + } | ||
| 1100 | + size_t idx = FindRowGroup(rect, groups, rowTol); | ||
| 1101 | + if (idx >= groups.size()) { | ||
| 1102 | + groups.push_back(std::vector<SelectionRect>{rect}); | ||
| 1103 | + } else { | ||
| 1104 | + groups[idx].push_back(rect); | ||
| 1105 | + } | ||
| 1106 | + } | ||
| 1107 | + return groups; | ||
| 1108 | +} | ||
| 1109 | + | ||
| 1110 | +std::vector<SelectionRect> SelectionGeometryUtils::SortByRow(const std::vector<SelectionRect> &list) | ||
| 1111 | +{ | ||
| 1112 | + std::vector<SelectionRect> normed; | ||
| 1113 | + normed.reserve(list.size()); | ||
| 1114 | + for (const auto &item : list) { | ||
| 1115 | + if (!item.IsEmpty()) { | ||
| 1116 | + normed.push_back(NormalizeRect(item)); | ||
| 1117 | + } | ||
| 1118 | + } | ||
| 1119 | + std::sort(normed.begin(), normed.end(), [](const SelectionRect &a, const SelectionRect &b) { | ||
| 1120 | + if (a.y != b.y) { | ||
| 1121 | + return a.y < b.y; | ||
| 1122 | + } | ||
| 1123 | + if (a.x != b.x) { | ||
| 1124 | + return a.x < b.x; | ||
| 1125 | + } | ||
| 1126 | + if (a.height != b.height) { | ||
| 1127 | + return a.height < b.height; | ||
| 1128 | + } | ||
| 1129 | + return a.width < b.width; | ||
| 1130 | + }); | ||
| 1131 | + return normed; | ||
| 1132 | +} | ||
| 1133 | + | ||
| 1134 | +SelectionRect SelectionGeometryUtils::ClipToBounds(const SelectionRect &rect, const SelectionRect &bounds) | ||
| 1135 | +{ | ||
| 1136 | + return IntersectRect(rect, bounds); | ||
| 1137 | +} | ||
| 1138 | + | ||
| 1139 | +std::vector<SelectionRect> SelectionGeometryUtils::ClipRectListToBounds(const std::vector<SelectionRect> &list, | ||
| 1140 | + const SelectionRect &bounds) | ||
| 1141 | +{ | ||
| 1142 | + return IntersectListWithRect(list, bounds); | ||
| 1143 | +} | ||
| 1144 | + | ||
| 1145 | +bool SelectionGeometryUtils::ClipSegmentToRect(SelectionPoint &p1, SelectionPoint &p2, | ||
| 1146 | + const SelectionRect &rect) | ||
| 1147 | +{ | ||
| 1148 | + if (rect.IsEmpty()) { | ||
| 1149 | + return false; | ||
| 1150 | + } | ||
| 1151 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1152 | + double x1 = static_cast<double>(p1.x); | ||
| 1153 | + double y1 = static_cast<double>(p1.y); | ||
| 1154 | + double x2 = static_cast<double>(p2.x); | ||
| 1155 | + double y2 = static_cast<double>(p2.y); | ||
| 1156 | + double dx = x2 - x1; | ||
| 1157 | + double dy = y2 - y1; | ||
| 1158 | + double p[RECT_EDGE_COUNT] = { -dx, dx, -dy, dy }; | ||
| 1159 | + double q[RECT_EDGE_COUNT] = { x1 - static_cast<double>(nr.x), | ||
| 1160 | + static_cast<double>(RightEdge(nr)) - x1, | ||
| 1161 | + y1 - static_cast<double>(nr.y), | ||
| 1162 | + static_cast<double>(BottomEdge(nr)) - y1 }; | ||
| 1163 | + double t0 = CLIP_T_MIN; | ||
| 1164 | + double t1 = CLIP_T_MAX; | ||
| 1165 | + for (int i = 0; i < RECT_EDGE_COUNT; ++i) { | ||
| 1166 | + if (!ApplySegmentClipEdge(p[i], q[i], t0, t1)) { | ||
| 1167 | + return false; | ||
| 1168 | + } | ||
| 1169 | + } | ||
| 1170 | + double nx1 = x1 + t0 * dx; | ||
| 1171 | + double ny1 = y1 + t0 * dy; | ||
| 1172 | + double nx2 = x1 + t1 * dx; | ||
| 1173 | + double ny2 = y1 + t1 * dy; | ||
| 1174 | + p1.x = ClampToInt32(static_cast<int64_t>(std::round(nx1))); | ||
| 1175 | + p1.y = ClampToInt32(static_cast<int64_t>(std::round(ny1))); | ||
| 1176 | + p2.x = ClampToInt32(static_cast<int64_t>(std::round(nx2))); | ||
| 1177 | + p2.y = ClampToInt32(static_cast<int64_t>(std::round(ny2))); | ||
| 1178 | + return true; | ||
| 1179 | +} | ||
| 1180 | + | ||
| 1181 | +bool SelectionGeometryUtils::SegmentIntersectsRect(const SelectionPoint &p1, const SelectionPoint &p2, | ||
| 1182 | + const SelectionRect &rect) | ||
| 1183 | +{ | ||
| 1184 | + if (rect.IsEmpty()) { | ||
| 1185 | + return false; | ||
| 1186 | + } | ||
| 1187 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1188 | + SelectionPoint s1 = p1; | ||
| 1189 | + SelectionPoint s2 = p2; | ||
| 1190 | + if (ClipSegmentToRect(s1, s2, nr)) { | ||
| 1191 | + return true; | ||
| 1192 | + } | ||
| 1193 | + return ContainsPoint(nr, p1) || ContainsPoint(nr, p2); | ||
| 1194 | +} | ||
| 1195 | + | ||
| 1196 | +std::vector<SelectionPoint> SelectionGeometryUtils::ClipPolygonToRect( | ||
| 1197 | + const std::vector<SelectionPoint> &polygon, const SelectionRect &rect) | ||
| 1198 | +{ | ||
| 1199 | + if (polygon.size() < MIN_POLYGON_VERTICES || rect.IsEmpty()) { | ||
| 1200 | + return std::vector<SelectionPoint>(); | ||
| 1201 | + } | ||
| 1202 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1203 | + std::vector<SelectionPoint> output = polygon; | ||
| 1204 | + for (int edge = 0; edge < RECT_EDGE_COUNT; ++edge) { | ||
| 1205 | + output = ClipPolygonSingleEdge(output, edge, nr); | ||
| 1206 | + if (output.empty()) { | ||
| 1207 | + return output; | ||
| 1208 | + } | ||
| 1209 | + } | ||
| 1210 | + return output; | ||
| 1211 | +} | ||
| 1212 | + | ||
| 1213 | +int64_t SelectionGeometryUtils::UnionArea(const std::vector<SelectionRect> &list) | ||
| 1214 | +{ | ||
| 1215 | + if (list.empty()) { | ||
| 1216 | + return 0; | ||
| 1217 | + } | ||
| 1218 | + std::vector<SelectionRect> normed; | ||
| 1219 | + normed.reserve(list.size()); | ||
| 1220 | + for (const auto &item : list) { | ||
| 1221 | + if (!item.IsEmpty()) { | ||
| 1222 | + normed.push_back(NormalizeRect(item)); | ||
| 1223 | + } | ||
| 1224 | + } | ||
| 1225 | + if (normed.empty()) { | ||
| 1226 | + return 0; | ||
| 1227 | + } | ||
| 1228 | + std::vector<int64_t> ys; | ||
| 1229 | + ys.reserve(normed.size() * Y_BOUNDS_PER_RECT); | ||
| 1230 | + for (const auto &rect : normed) { | ||
| 1231 | + ys.push_back(rect.y); | ||
| 1232 | + ys.push_back(BottomEdge(rect)); | ||
| 1233 | + } | ||
| 1234 | + std::sort(ys.begin(), ys.end()); | ||
| 1235 | + ys.erase(std::unique(ys.begin(), ys.end()), ys.end()); | ||
| 1236 | + int64_t total = 0; | ||
| 1237 | + for (size_t i = 0; i + 1 < ys.size(); ++i) { | ||
| 1238 | + int64_t y0 = ys[i]; | ||
| 1239 | + int64_t y1 = ys[i + 1]; | ||
| 1240 | + if (y1 <= y0) { | ||
| 1241 | + continue; | ||
| 1242 | + } | ||
| 1243 | + std::vector<Interval> intervals; | ||
| 1244 | + for (const auto &rect : normed) { | ||
| 1245 | + if (rect.y <= y0 && BottomEdge(rect) >= y1) { | ||
| 1246 | + intervals.push_back({ rect.x, RightEdge(rect) }); | ||
| 1247 | + } | ||
| 1248 | + } | ||
| 1249 | + if (intervals.empty()) { | ||
| 1250 | + continue; | ||
| 1251 | + } | ||
| 1252 | + total += MergeIntervalLengths(intervals) * (y1 - y0); | ||
| 1253 | + } | ||
| 1254 | + return total; | ||
| 1255 | +} | ||
| 1256 | + | ||
| 1257 | +SelectionRect SelectionGeometryUtils::MapFromParent(const SelectionRect &rect, | ||
| 1258 | + const SelectionPoint &parentOrigin) | ||
| 1259 | +{ | ||
| 1260 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1261 | + if (nr.IsEmpty()) { | ||
| 1262 | + return MakeEmptyRect(); | ||
| 1263 | + } | ||
| 1264 | + int64_t x = static_cast<int64_t>(nr.x) - static_cast<int64_t>(parentOrigin.x); | ||
| 1265 | + int64_t y = static_cast<int64_t>(nr.y) - static_cast<int64_t>(parentOrigin.y); | ||
| 1266 | + return ClampRect(x, y, nr.width, nr.height); | ||
| 1267 | +} | ||
| 1268 | + | ||
| 1269 | +SelectionRect SelectionGeometryUtils::MapToParent(const SelectionRect &rect, | ||
| 1270 | + const SelectionPoint &parentOrigin) | ||
| 1271 | +{ | ||
| 1272 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1273 | + if (nr.IsEmpty()) { | ||
| 1274 | + return MakeEmptyRect(); | ||
| 1275 | + } | ||
| 1276 | + int64_t x = static_cast<int64_t>(nr.x) + static_cast<int64_t>(parentOrigin.x); | ||
| 1277 | + int64_t y = static_cast<int64_t>(nr.y) + static_cast<int64_t>(parentOrigin.y); | ||
| 1278 | + return ClampRect(x, y, nr.width, nr.height); | ||
| 1279 | +} | ||
| 1280 | + | ||
| 1281 | +SelectionRect SelectionGeometryUtils::NormalizeForRTL(const SelectionRect &rect, int32_t parentWidth) | ||
| 1282 | +{ | ||
| 1283 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1284 | + if (nr.IsEmpty() || parentWidth <= 0) { | ||
| 1285 | + return nr; | ||
| 1286 | + } | ||
| 1287 | + int64_t pw = static_cast<int64_t>(parentWidth); | ||
| 1288 | + int64_t l = nr.x; | ||
| 1289 | + int64_t r = RightEdge(nr); | ||
| 1290 | + int64_t newR = pw - l; | ||
| 1291 | + int64_t newL = pw - r; | ||
| 1292 | + if (newL > newR) { | ||
| 1293 | + std::swap(newL, newR); | ||
| 1294 | + } | ||
| 1295 | + return ClampRect(newL, nr.y, newR - newL, nr.height); | ||
| 1296 | +} | ||
| 1297 | + | ||
| 1298 | +SelectionRect SelectionGeometryUtils::MinRectByArea(const SelectionRect &a, const SelectionRect &b) | ||
| 1299 | +{ | ||
| 1300 | + if (a.IsEmpty()) { | ||
| 1301 | + return b; | ||
| 1302 | + } | ||
| 1303 | + if (b.IsEmpty()) { | ||
| 1304 | + return a; | ||
| 1305 | + } | ||
| 1306 | + return a.Area() <= b.Area() ? a : b; | ||
| 1307 | +} | ||
| 1308 | + | ||
| 1309 | +SelectionRect SelectionGeometryUtils::MaxRectByArea(const SelectionRect &a, const SelectionRect &b) | ||
| 1310 | +{ | ||
| 1311 | + if (a.IsEmpty()) { | ||
| 1312 | + return b; | ||
| 1313 | + } | ||
| 1314 | + if (b.IsEmpty()) { | ||
| 1315 | + return a; | ||
| 1316 | + } | ||
| 1317 | + return a.Area() >= b.Area() ? a : b; | ||
| 1318 | +} | ||
| 1319 | + | ||
| 1320 | +double SelectionGeometryUtils::PointDistance(const SelectionPoint &a, const SelectionPoint &b) | ||
| 1321 | +{ | ||
| 1322 | + double dx = static_cast<double>(a.x - b.x); | ||
| 1323 | + double dy = static_cast<double>(a.y - b.y); | ||
| 1324 | + return std::sqrt(dx * dx + dy * dy); | ||
| 1325 | +} | ||
| 1326 | + | ||
| 1327 | +bool SelectionGeometryUtils::PointEqualsTolerance(const SelectionPoint &a, const SelectionPoint &b, | ||
| 1328 | + int32_t tol) | ||
| 1329 | +{ | ||
| 1330 | + int32_t absTol = tol < 0 ? -tol : tol; | ||
| 1331 | + int32_t dx = SafeSubInt32(a.x, b.x); | ||
| 1332 | + if (dx < 0) { | ||
| 1333 | + dx = -dx; | ||
| 1334 | + } | ||
| 1335 | + int32_t dy = SafeSubInt32(a.y, b.y); | ||
| 1336 | + if (dy < 0) { | ||
| 1337 | + dy = -dy; | ||
| 1338 | + } | ||
| 1339 | + return dx <= absTol && dy <= absTol; | ||
| 1340 | +} | ||
| 1341 | + | ||
| 1342 | +SelectionPoint SelectionGeometryUtils::PointLerp(const SelectionPoint &a, const SelectionPoint &b, | ||
| 1343 | + double t) | ||
| 1344 | +{ | ||
| 1345 | + if (t < CLIP_T_MIN) { | ||
| 1346 | + t = CLIP_T_MIN; | ||
| 1347 | + } else if (t > CLIP_T_MAX) { | ||
| 1348 | + t = CLIP_T_MAX; | ||
| 1349 | + } | ||
| 1350 | + double x = static_cast<double>(a.x) + t * static_cast<double>(b.x - a.x); | ||
| 1351 | + double y = static_cast<double>(a.y) + t * static_cast<double>(b.y - a.y); | ||
| 1352 | + return SelectionPoint(SnapToPixel(x), SnapToPixel(y)); | ||
| 1353 | +} | ||
| 1354 | + | ||
| 1355 | +SelectionPoint SelectionGeometryUtils::PointTranslate(const SelectionPoint &pt, int32_t dx, int32_t dy) | ||
| 1356 | +{ | ||
| 1357 | + int64_t x = static_cast<int64_t>(pt.x) + static_cast<int64_t>(dx); | ||
| 1358 | + int64_t y = static_cast<int64_t>(pt.y) + static_cast<int64_t>(dy); | ||
| 1359 | + return SelectionPoint(ClampToInt32(x), ClampToInt32(y)); | ||
| 1360 | +} | ||
| 1361 | + | ||
| 1362 | +SelectionPoint SelectionGeometryUtils::PointRotate90(const SelectionPoint &pt, const SelectionPoint &pivot) | ||
| 1363 | +{ | ||
| 1364 | + int64_t dx = static_cast<int64_t>(pt.x) - static_cast<int64_t>(pivot.x); | ||
| 1365 | + int64_t dy = static_cast<int64_t>(pt.y) - static_cast<int64_t>(pivot.y); | ||
| 1366 | + int64_t nx = static_cast<int64_t>(pivot.x) - dy; | ||
| 1367 | + int64_t ny = static_cast<int64_t>(pivot.y) + dx; | ||
| 1368 | + return SelectionPoint(ClampToInt32(nx), ClampToInt32(ny)); | ||
| 1369 | +} | ||
| 1370 | + | ||
| 1371 | +SelectionPoint SelectionGeometryUtils::PointMirrorHorizontal(const SelectionPoint &pt, int32_t axisX) | ||
| 1372 | +{ | ||
| 1373 | + int64_t axis = static_cast<int64_t>(axisX); | ||
| 1374 | + int64_t x = static_cast<int64_t>(pt.x); | ||
| 1375 | + int64_t nx = MIRROR_FACTOR * axis - x; | ||
| 1376 | + return SelectionPoint(ClampToInt32(nx), pt.y); | ||
| 1377 | +} | ||
| 1378 | + | ||
| 1379 | +SelectionPoint SelectionGeometryUtils::PointMirrorVertical(const SelectionPoint &pt, int32_t axisY) | ||
| 1380 | +{ | ||
| 1381 | + int64_t axis = static_cast<int64_t>(axisY); | ||
| 1382 | + int64_t y = static_cast<int64_t>(pt.y); | ||
| 1383 | + int64_t ny = MIRROR_FACTOR * axis - y; | ||
| 1384 | + return SelectionPoint(pt.x, ClampToInt32(ny)); | ||
| 1385 | +} | ||
| 1386 | + | ||
| 1387 | +double SelectionGeometryUtils::SegmentLength(const SelectionPoint &a, const SelectionPoint &b) | ||
| 1388 | +{ | ||
| 1389 | + return PointDistance(a, b); | ||
| 1390 | +} | ||
| 1391 | + | ||
| 1392 | +double SelectionGeometryUtils::PointToSegmentDistance(const SelectionPoint &p, const SelectionPoint &s, | ||
| 1393 | + const SelectionPoint &e) | ||
| 1394 | +{ | ||
| 1395 | + int64_t px = p.x; | ||
| 1396 | + int64_t py = p.y; | ||
| 1397 | + int64_t sx = s.x; | ||
| 1398 | + int64_t sy = s.y; | ||
| 1399 | + int64_t ex = e.x; | ||
| 1400 | + int64_t ey = e.y; | ||
| 1401 | + int64_t dx = ex - sx; | ||
| 1402 | + int64_t dy = ey - sy; | ||
| 1403 | + int64_t lenSq = dx * dx + dy * dy; | ||
| 1404 | + if (lenSq == 0) { | ||
| 1405 | + double ddx = static_cast<double>(px - sx); | ||
| 1406 | + double ddy = static_cast<double>(py - sy); | ||
| 1407 | + return std::sqrt(ddx * ddx + ddy * ddy); | ||
| 1408 | + } | ||
| 1409 | + int64_t crossX = px - sx; | ||
| 1410 | + int64_t crossY = py - sy; | ||
| 1411 | + double t = static_cast<double>(crossX * dx + crossY * dy) / static_cast<double>(lenSq); | ||
| 1412 | + if (t < CLIP_T_MIN) { | ||
| 1413 | + t = CLIP_T_MIN; | ||
| 1414 | + } else if (t > CLIP_T_MAX) { | ||
| 1415 | + t = CLIP_T_MAX; | ||
| 1416 | + } | ||
| 1417 | + double projX = static_cast<double>(sx) + t * static_cast<double>(dx); | ||
| 1418 | + double projY = static_cast<double>(sy) + t * static_cast<double>(dy); | ||
| 1419 | + double ddx = static_cast<double>(px) - projX; | ||
| 1420 | + double ddy = static_cast<double>(py) - projY; | ||
| 1421 | + return std::sqrt(ddx * ddx + ddy * ddy); | ||
| 1422 | +} | ||
| 1423 | + | ||
| 1424 | +SelectionPoint SelectionGeometryUtils::FootOfPerpendicular(const SelectionPoint &p, const SelectionPoint &s, | ||
| 1425 | + const SelectionPoint &e) | ||
| 1426 | +{ | ||
| 1427 | + int64_t sx = s.x; | ||
| 1428 | + int64_t sy = s.y; | ||
| 1429 | + int64_t ex = e.x; | ||
| 1430 | + int64_t ey = e.y; | ||
| 1431 | + int64_t dx = ex - sx; | ||
| 1432 | + int64_t dy = ey - sy; | ||
| 1433 | + int64_t lenSq = dx * dx + dy * dy; | ||
| 1434 | + if (lenSq == 0) { | ||
| 1435 | + return s; | ||
| 1436 | + } | ||
| 1437 | + int64_t crossX = static_cast<int64_t>(p.x) - sx; | ||
| 1438 | + int64_t crossY = static_cast<int64_t>(p.y) - sy; | ||
| 1439 | + double t = static_cast<double>(crossX * dx + crossY * dy) / static_cast<double>(lenSq); | ||
| 1440 | + double projX = static_cast<double>(sx) + t * static_cast<double>(dx); | ||
| 1441 | + double projY = static_cast<double>(sy) + t * static_cast<double>(dy); | ||
| 1442 | + return SelectionPoint(SnapToPixel(projX), SnapToPixel(projY)); | ||
| 1443 | +} | ||
| 1444 | + | ||
| 1445 | +bool SelectionGeometryUtils::IsPointOnSegment(const SelectionPoint &p, const SelectionPoint &s, | ||
| 1446 | + const SelectionPoint &e) | ||
| 1447 | +{ | ||
| 1448 | + int64_t px = p.x; | ||
| 1449 | + int64_t py = p.y; | ||
| 1450 | + int64_t sx = s.x; | ||
| 1451 | + int64_t sy = s.y; | ||
| 1452 | + int64_t ex = e.x; | ||
| 1453 | + int64_t ey = e.y; | ||
| 1454 | + int64_t cross = (ex - sx) * (py - sy) - (ey - sy) * (px - sx); | ||
| 1455 | + if (cross != 0) { | ||
| 1456 | + return false; | ||
| 1457 | + } | ||
| 1458 | + int64_t dot = (px - sx) * (px - ex) + (py - sy) * (py - ey); | ||
| 1459 | + return dot <= 0; | ||
| 1460 | +} | ||
| 1461 | + | ||
| 1462 | +std::vector<SelectionPoint> SelectionGeometryUtils::RectCorners(const SelectionRect &rect) | ||
| 1463 | +{ | ||
| 1464 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1465 | + if (nr.IsEmpty()) { | ||
| 1466 | + return std::vector<SelectionPoint>(); | ||
| 1467 | + } | ||
| 1468 | + int32_t l = nr.x; | ||
| 1469 | + int32_t t = nr.y; | ||
| 1470 | + int32_t r = ClampToInt32(RightEdge(nr)); | ||
| 1471 | + int32_t b = ClampToInt32(BottomEdge(nr)); | ||
| 1472 | + return std::vector<SelectionPoint>{ | ||
| 1473 | + SelectionPoint(l, t), | ||
| 1474 | + SelectionPoint(r, t), | ||
| 1475 | + SelectionPoint(r, b), | ||
| 1476 | + SelectionPoint(l, b), | ||
| 1477 | + }; | ||
| 1478 | +} | ||
| 1479 | + | ||
| 1480 | +std::vector<SelectionPoint> SelectionGeometryUtils::RectEdgeMidpoints(const SelectionRect &rect) | ||
| 1481 | +{ | ||
| 1482 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1483 | + if (nr.IsEmpty()) { | ||
| 1484 | + return std::vector<SelectionPoint>(); | ||
| 1485 | + } | ||
| 1486 | + int64_t cx = CenterX(nr); | ||
| 1487 | + int64_t cy = CenterY(nr); | ||
| 1488 | + int32_t l = nr.x; | ||
| 1489 | + int32_t t = nr.y; | ||
| 1490 | + int32_t r = ClampToInt32(RightEdge(nr)); | ||
| 1491 | + int32_t b = ClampToInt32(BottomEdge(nr)); | ||
| 1492 | + return std::vector<SelectionPoint>{ | ||
| 1493 | + SelectionPoint(static_cast<int32_t>(cx), t), | ||
| 1494 | + SelectionPoint(r, static_cast<int32_t>(cy)), | ||
| 1495 | + SelectionPoint(static_cast<int32_t>(cx), b), | ||
| 1496 | + SelectionPoint(l, static_cast<int32_t>(cy)), | ||
| 1497 | + }; | ||
| 1498 | +} | ||
| 1499 | + | ||
| 1500 | +int32_t SelectionGeometryUtils::ClosestEdgeIndex(const SelectionRect &rect, const SelectionPoint &point) | ||
| 1501 | +{ | ||
| 1502 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1503 | + if (nr.IsEmpty()) { | ||
| 1504 | + return -1; | ||
| 1505 | + } | ||
| 1506 | + int64_t px = point.x; | ||
| 1507 | + int64_t py = point.y; | ||
| 1508 | + int64_t l = nr.x; | ||
| 1509 | + int64_t t = nr.y; | ||
| 1510 | + int64_t r = RightEdge(nr); | ||
| 1511 | + int64_t b = BottomEdge(nr); | ||
| 1512 | + int64_t cx = CenterX(nr); | ||
| 1513 | + int64_t cy = CenterY(nr); | ||
| 1514 | + int64_t dTop = AbsInt64(py - t); | ||
| 1515 | + int64_t dBottom = AbsInt64(py - b); | ||
| 1516 | + int64_t dLeft = AbsInt64(px - l); | ||
| 1517 | + int64_t dRight = AbsInt64(px - r); | ||
| 1518 | + int64_t dists[RECT_EDGE_COUNT] = { dTop, dRight, dBottom, dLeft }; | ||
| 1519 | + int64_t cxToCenter = AbsInt64(px - cx); | ||
| 1520 | + int64_t cyToCenter = AbsInt64(py - cy); | ||
| 1521 | + int64_t extraTop = (py < t) ? cyToCenter : 0; | ||
| 1522 | + int64_t extraBottom = (py >= b) ? cyToCenter : 0; | ||
| 1523 | + int64_t extraLeft = (px < l) ? cxToCenter : 0; | ||
| 1524 | + int64_t extraRight = (px >= r) ? cxToCenter : 0; | ||
| 1525 | + int64_t extras[RECT_EDGE_COUNT] = { extraTop, extraRight, extraBottom, extraLeft }; | ||
| 1526 | + int32_t best = 0; | ||
| 1527 | + for (int i = 1; i < RECT_EDGE_COUNT; ++i) { | ||
| 1528 | + int64_t cur = dists[i] + extras[i]; | ||
| 1529 | + int64_t bestVal = dists[best] + extras[best]; | ||
| 1530 | + if (cur < bestVal) { | ||
| 1531 | + best = i; | ||
| 1532 | + } | ||
| 1533 | + } | ||
| 1534 | + return best; | ||
| 1535 | +} | ||
| 1536 | + | ||
| 1537 | +SelectionRect SelectionGeometryUtils::ExpandToContain(const SelectionRect &rect, const SelectionPoint &point) | ||
| 1538 | +{ | ||
| 1539 | + if (rect.IsEmpty()) { | ||
| 1540 | + return ClampRect(point.x, point.y, 1, 1); | ||
| 1541 | + } | ||
| 1542 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1543 | + int64_t l = std::min(static_cast<int64_t>(nr.x), static_cast<int64_t>(point.x)); | ||
| 1544 | + int64_t t = std::min(static_cast<int64_t>(nr.y), static_cast<int64_t>(point.y)); | ||
| 1545 | + int64_t r = std::max(RightEdge(nr), static_cast<int64_t>(point.x) + 1); | ||
| 1546 | + int64_t b = std::max(BottomEdge(nr), static_cast<int64_t>(point.y) + 1); | ||
| 1547 | + return ClampRect(l, t, r - l, b - t); | ||
| 1548 | +} | ||
| 1549 | + | ||
| 1550 | +SelectionRect SelectionGeometryUtils::ShrinkToAspectRatio(const SelectionRect &rect, double targetRatio) | ||
| 1551 | +{ | ||
| 1552 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1553 | + if (nr.IsEmpty() || targetRatio <= 0.0) { | ||
| 1554 | + return MakeEmptyRect(); | ||
| 1555 | + } | ||
| 1556 | + int64_t w = nr.width; | ||
| 1557 | + int64_t h = nr.height; | ||
| 1558 | + if (h == 0 || targetRatio == 0.0) { | ||
| 1559 | + return MakeEmptyRect(); | ||
| 1560 | + } | ||
| 1561 | + int64_t cx = CenterX(nr); | ||
| 1562 | + int64_t cy = CenterY(nr); | ||
| 1563 | + int64_t newW = w; | ||
| 1564 | + int64_t newH = h; | ||
| 1565 | + double curRatio = static_cast<double>(w) / static_cast<double>(h); | ||
| 1566 | + if (curRatio > targetRatio) { | ||
| 1567 | + newW = static_cast<int64_t>(std::floor(static_cast<double>(h) * targetRatio)); | ||
| 1568 | + } else if (curRatio < targetRatio) { | ||
| 1569 | + newH = static_cast<int64_t>(std::floor(static_cast<double>(w) / targetRatio)); | ||
| 1570 | + } | ||
| 1571 | + if (newW > w) { | ||
| 1572 | + newW = w; | ||
| 1573 | + } | ||
| 1574 | + if (newH > h) { | ||
| 1575 | + newH = h; | ||
| 1576 | + } | ||
| 1577 | + int64_t newL = cx - newW / HALF_FACTOR; | ||
| 1578 | + int64_t newT = cy - newH / HALF_FACTOR; | ||
| 1579 | + return ClampRect(newL, newT, newW, newH); | ||
| 1580 | +} | ||
| 1581 | + | ||
| 1582 | +std::vector<SelectionRect> SelectionGeometryUtils::SplitGrid(const SelectionRect &rect, int32_t rows, | ||
| 1583 | + int32_t cols) | ||
| 1584 | +{ | ||
| 1585 | + std::vector<SelectionRect> result; | ||
| 1586 | + if (rect.IsEmpty()) { | ||
| 1587 | + return result; | ||
| 1588 | + } | ||
| 1589 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1590 | + if (cols <= 0 || rows <= 0) { | ||
| 1591 | + return result; | ||
| 1592 | + } | ||
| 1593 | + int64_t totalW = nr.width; | ||
| 1594 | + int64_t totalH = nr.height; | ||
| 1595 | + int64_t baseW = totalW / static_cast<int64_t>(cols); | ||
| 1596 | + int64_t baseH = totalH / static_cast<int64_t>(rows); | ||
| 1597 | + int64_t remW = totalW - baseW * static_cast<int64_t>(cols); | ||
| 1598 | + int64_t remH = totalH - baseH * static_cast<int64_t>(rows); | ||
| 1599 | + int64_t y = nr.y; | ||
| 1600 | + for (int32_t r = 0; r < rows; ++r) { | ||
| 1601 | + int64_t cellH = baseH + (r < remH ? 1 : 0); | ||
| 1602 | + int64_t x = nr.x; | ||
| 1603 | + for (int32_t c = 0; c < cols; ++c) { | ||
| 1604 | + int64_t cellW = baseW + (c < remW ? 1 : 0); | ||
| 1605 | + result.push_back(ClampRect(x, y, cellW, cellH)); | ||
| 1606 | + x += cellW; | ||
| 1607 | + } | ||
| 1608 | + y += cellH; | ||
| 1609 | + } | ||
| 1610 | + return result; | ||
| 1611 | +} | ||
| 1612 | + | ||
| 1613 | +bool SelectionGeometryUtils::IsSquare(const SelectionRect &rect) | ||
| 1614 | +{ | ||
| 1615 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1616 | + if (nr.IsEmpty()) { | ||
| 1617 | + return false; | ||
| 1618 | + } | ||
| 1619 | + return nr.width == nr.height; | ||
| 1620 | +} | ||
| 1621 | + | ||
| 1622 | +int32_t SelectionGeometryUtils::LargerAxis(const SelectionRect &rect) | ||
| 1623 | +{ | ||
| 1624 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1625 | + if (nr.IsEmpty()) { | ||
| 1626 | + return 0; | ||
| 1627 | + } | ||
| 1628 | + return nr.width >= nr.height ? nr.width : nr.height; | ||
| 1629 | +} | ||
| 1630 | + | ||
| 1631 | +int32_t SelectionGeometryUtils::SmallerAxis(const SelectionRect &rect) | ||
| 1632 | +{ | ||
| 1633 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1634 | + if (nr.IsEmpty()) { | ||
| 1635 | + return 0; | ||
| 1636 | + } | ||
| 1637 | + return nr.width <= nr.height ? nr.width : nr.height; | ||
| 1638 | +} | ||
| 1639 | + | ||
| 1640 | +SelectionRect SelectionGeometryUtils::CenterAt(const SelectionRect &rect, const SelectionPoint ¢er) | ||
| 1641 | +{ | ||
| 1642 | + SelectionRect nr = NormalizeRect(rect); | ||
| 1643 | + if (nr.IsEmpty()) { | ||
| 1644 | + return MakeEmptyRect(); | ||
| 1645 | + } | ||
| 1646 | + int64_t cx = center.x; | ||
| 1647 | + int64_t cy = center.y; | ||
| 1648 | + int64_t newL = cx - static_cast<int64_t>(nr.width) / HALF_FACTOR; | ||
| 1649 | + int64_t newT = cy - static_cast<int64_t>(nr.height) / HALF_FACTOR; | ||
| 1650 | + return ClampRect(newL, newT, nr.width, nr.height); | ||
| 1651 | +} | ||
| 1652 | + | ||
| 1653 | +void SelectionRectCombiner::Add(const SelectionRect &rect) | ||
| 1654 | +{ | ||
| 1655 | + if (rect.IsEmpty()) { | ||
| 1656 | + return; | ||
| 1657 | + } | ||
| 1658 | + rects_.push_back(SelectionGeometryUtils::NormalizeRect(rect)); | ||
| 1659 | + TryMergeAdjacent(); | ||
| 1660 | +} | ||
| 1661 | + | ||
| 1662 | +std::vector<SelectionRect> SelectionRectCombiner::Result() const | ||
| 1663 | +{ | ||
| 1664 | + return rects_; | ||
| 1665 | +} | ||
| 1666 | + | ||
| 1667 | +void SelectionRectCombiner::Clear() | ||
| 1668 | +{ | ||
| 1669 | + rects_.clear(); | ||
| 1670 | +} | ||
| 1671 | + | ||
| 1672 | +size_t SelectionRectCombiner::Size() const | ||
| 1673 | +{ | ||
| 1674 | + return rects_.size(); | ||
| 1675 | +} | ||
| 1676 | + | ||
| 1677 | +bool SelectionRectCombiner::Empty() const | ||
| 1678 | +{ | ||
| 1679 | + return rects_.empty(); | ||
| 1680 | +} | ||
| 1681 | + | ||
| 1682 | +void SelectionRectCombiner::TryMergeAdjacent() | ||
| 1683 | +{ | ||
| 1684 | + bool changed = true; | ||
| 1685 | + while (changed) { | ||
| 1686 | + changed = false; | ||
| 1687 | + for (size_t i = 0; i < rects_.size(); ++i) { | ||
| 1688 | + size_t j = FindMergePartner(rects_, i, 0); | ||
| 1689 | + if (j >= rects_.size()) { | ||
| 1690 | + continue; | ||
| 1691 | + } | ||
| 1692 | + rects_[i] = SelectionGeometryUtils::UnionRect(rects_[i], rects_[j]); | ||
| 1693 | + rects_.erase(rects_.begin() + static_cast<ptrdiff_t>(j)); | ||
| 1694 | + changed = true; | ||
| 1695 | + break; | ||
| 1696 | + } | ||
| 1697 | + } | ||
| 1698 | +} | ||
| 1699 | + | ||
| 1700 | +} // namespace SelectionFwk | ||
| 1701 | +} // namespace OHOS | ||