1 /*
2  * Copyright (c) 2021-2022 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_RECT_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_RECT_H
18 
19 #include <algorithm>
20 
21 #include "base/geometry/offset.h"
22 #include "base/geometry/point.h"
23 #include "base/geometry/size.h"
24 
25 namespace OHOS::Ace {
26 
27 class Rect {
28 public:
29     Rect() = default;
30     ~Rect() = default;
31 
Rect(double x,double y,double width,double height)32     Rect(double x, double y, double width, double height)
33     {
34         SetRect(x, y, width, height);
35     }
36 
Rect(const Offset & offset,const Size & size)37     Rect(const Offset& offset, const Size& size)
38     {
39         SetOffset(offset);
40         SetSize(size);
41     }
42 
SetRect(double x,double y,double width,double height)43     void SetRect(double x, double y, double width, double height)
44     {
45         x_ = x;
46         y_ = y;
47         width_ = width;
48         height_ = height;
49     }
50 
SetRect(const Offset & offset,const Size & size)51     void SetRect(const Offset& offset, const Size& size)
52     {
53         SetOffset(offset);
54         SetSize(size);
55     }
56 
ApplyScale(double scale)57     void ApplyScale(double scale)
58     {
59         x_ *= scale;
60         y_ *= scale;
61         width_ *= scale;
62         height_ *= scale;
63     }
64 
ApplyScaleAndRound(const Size & scale)65     void ApplyScaleAndRound(const Size& scale)
66     {
67         x_ = round(x_ * scale.Width());
68         y_ = round(y_ * scale.Height());
69         width_ = round(width_ * scale.Width());
70         height_ = round(height_ * scale.Height());
71     }
72 
Left()73     double Left() const
74     {
75         return GreatNotEqual(width_, 0.0) ? x_ : x_ + width_;
76     }
77 
Top()78     double Top() const
79     {
80         return GreatNotEqual(height_, 0.0) ? y_ : y_ + height_;
81     }
82 
Right()83     double Right() const
84     {
85         return GreatNotEqual(width_, 0.0) ? x_ + width_ : x_;
86     }
87 
Bottom()88     double Bottom() const
89     {
90         return GreatNotEqual(height_, 0.0) ? y_ + height_ : y_;
91     }
92 
Width()93     double Width() const
94     {
95         return width_;
96     }
97 
Height()98     double Height() const
99     {
100         return height_;
101     }
102 
SetSize(const Size & size)103     void SetSize(const Size& size)
104     {
105         width_ = size.Width();
106         height_ = size.Height();
107     }
108 
GetSize()109     Size GetSize() const
110     {
111         return Size(width_, height_);
112     }
113 
SetOffset(const Offset & offset)114     void SetOffset(const Offset& offset)
115     {
116         x_ = offset.GetX();
117         y_ = offset.GetY();
118     }
119 
GetOffset()120     Offset GetOffset() const
121     {
122         return Offset(x_, y_);
123     }
124 
SetLeft(double left)125     void SetLeft(double left)
126     {
127         x_ = left;
128     }
129 
SetTop(double top)130     void SetTop(double top)
131     {
132         y_ = top;
133     }
134 
SetWidth(double width)135     void SetWidth(double width)
136     {
137         width_ = width;
138     }
139 
SetHeight(double height)140     void SetHeight(double height)
141     {
142         height_ = height;
143     }
144 
IsInRegion(const Point & point)145     bool IsInRegion(const Point& point) const
146     {
147         return (point.GetX() >= x_) && (point.GetX() < (x_ + width_)) && (point.GetY() >= y_) &&
148                (point.GetY() < (y_ + height_));
149     }
150 
IsWrappedBy(const Rect & other)151     bool IsWrappedBy(const Rect& other) const
152     {
153         return (Left() >= other.Left()) && (Right() <= other.Right()) && (Top() >= other.Top()) &&
154                (Bottom() <= other.Bottom());
155     }
156 
IsValid()157     bool IsValid() const
158     {
159         return width_ > 0.0 && height_ > 0.0;
160     }
161 
Constrain(const Rect & other)162     Rect Constrain(const Rect& other)
163     {
164         double right = Right();
165         double bottom = Bottom();
166         double left = std::clamp(x_, other.Left(), other.Right());
167         double top = std::clamp(y_, other.Top(), other.Bottom());
168         right = std::clamp(right, other.Left(), other.Right()) - left;
169         bottom = std::clamp(bottom, other.Top(), other.Bottom()) - top;
170         return Rect(left, top, right, bottom);
171     }
172 
173     Rect& operator+=(const Offset& offset)
174     {
175         x_ += offset.GetX();
176         y_ += offset.GetY();
177         return *this;
178     }
179 
180     Rect& operator-=(const Offset& offset)
181     {
182         x_ -= offset.GetX();
183         y_ -= offset.GetY();
184         return *this;
185     }
186 
187     Rect& operator+=(const Size& size)
188     {
189         width_ += size.Width();
190         height_ += size.Height();
191         return *this;
192     }
193 
194     Rect& operator-=(const Size& size)
195     {
196         width_ -= size.Width();
197         height_ -= size.Height();
198         return *this;
199     }
200 
201     Rect operator+(const Offset& offset) const
202     {
203         return Rect(x_ + offset.GetX(), y_ + offset.GetY(), width_, height_);
204     }
205 
206     Rect operator-(const Offset& offset) const
207     {
208         return Rect(x_ - offset.GetX(), y_ - offset.GetY(), width_, height_);
209     }
210 
211     Rect operator+(const Size& size) const
212     {
213         return Rect(x_, y_, width_ + size.Width(), height_ + size.Height());
214     }
215 
216     Rect operator-(const Size& size) const
217     {
218         return Rect(x_, y_, width_ - size.Width(), height_ - size.Height());
219     }
220 
221     Rect operator*(double scale) const
222     {
223         return Rect(x_ * scale, y_ * scale, width_ * scale, height_ * scale);
224     }
225 
226     bool operator==(const Rect& rect) const
227     {
228         return (GetOffset() == rect.GetOffset()) && (GetSize() == rect.GetSize());
229     }
230 
231     bool operator!=(const Rect& rect) const
232     {
233         return !operator==(rect);
234     }
235 
IsIntersectWith(const Rect & other)236     bool IsIntersectWith(const Rect& other) const
237     {
238         return !(other.Right() < Left() || other.Left() > Right() || other.Bottom() < Top() || other.Top() > Bottom());
239     }
240 
IsIntersectByCommonSideWith(const Rect & other)241     bool IsIntersectByCommonSideWith(const Rect& other) const
242     {
243         return !(
244             other.Right() <= Left() || other.Left() >= Right() || other.Bottom() <= Top() || other.Top() >= Bottom());
245     }
246 
IntersectRect(const Rect & other)247     Rect IntersectRect(const Rect& other) const
248     {
249         double left = std::max(Left(), other.Left());
250         double right = std::min(Right(), other.Right());
251         double top = std::max(Top(), other.Top());
252         double bottom = std::min(Bottom(), other.Bottom());
253         return Rect(left, top, right - left, bottom - top);
254     }
255 
CombineRect(const Rect & other)256     Rect CombineRect(const Rect& other) const
257     {
258         double left = std::min(Left(), other.Left());
259         double right = std::max(Right(), other.Right());
260         double top = std::min(Top(), other.Top());
261         double bottom = std::max(Bottom(), other.Bottom());
262         return Rect(left, top, right - left, bottom - top);
263     }
264 
265     /**
266      * @brief Magnetically attracted to a "magnetic" rect.
267      *
268      * Let's show some cases to illustrate how this method works:
269      *
270      * Case 1 : Inside. Rect won't be move because it is already attracted by magnet.
271      * Result: Offset(0, 0)
272      *
273      * +-----------------------------+
274      * |                             |
275      * |   Magnetical Rect           |
276      * |                             |
277      * |          +-------+          |
278      * |          |  R    |          |
279      * |          |       |          |
280      * |          +-------+          |
281      * +-----------------------------+
282      *
283      * Case 2: Outside. R will be attracted to position R'
284      * Result: Offset(-12, -3)
285      *
286      * +-----------------------+
287      * |                       |
288      * |   Magnetical Rect  +--+
289      * |                    |  |
290      * |                    |R'|        +--+
291      * +-----------------------+        |R |
292      *                                  |  |
293      *                                  +--+
294      *
295      * Case 3: Half Inside. R will be moved totally into magnet.
296      * Result: Offset(-8, 0)
297      *
298      * +-----------------------------+                  +-----------------------------+
299      * |                             |                  |                             |
300      * |   Magnetical Rect           |   +---------->   |   Magnetical Rect           |
301      * |                             |                  |                             |
302      * |                          +----------+          |                  +----------+
303      * |                          |  |   R   |          |                  |   R'     |
304      * |                          +----------+          |                  +----------+
305      * |                             |                  |                             |
306      * +-----------------------------+                  +-----------------------------+
307      *
308      * Case 4: Outside or Half Outside but space not enough. R will be moved into magnet as more as possible.
309      * Result: Offset(0, 1)
310      *
311      *                    +--+
312      *                    |  |                                     +--+
313      *                    |R |                                     |  |
314      *                    |  |                                     |R'|
315      * +---------------------------+            +---------------------------+
316      * |                  |  |     |            |                  |  |     |
317      * | Magnetical Rect  |  |     |  +----->   | Magnetical Rect  |  |     |
318      * |                  +--+     |            |                  |  |     |
319      * +---------------------------+            +------------------+--+-----+
320      *
321      * Case 5: Totally Across magnet. Nothing should happen.
322      * Result: Offset(0, 0)
323      *
324      *                    +--+
325      *                    |  |
326      *                    |R |
327      *                    |  |
328      * +---------------------------+
329      * |                  |  |     |
330      * | Magnetical Rect  |  |     |
331      * |                  |  |     |
332      * +---------------------------+
333      *                    |  |
334      *                    +--+
335      *
336      * @param[in] magnet The magnetical rectangle.
337      *
338      * @return The offset that this rect need to moving into magnet.
339      */
MagneticAttractedBy(const Rect & magnet)340     Offset MagneticAttractedBy(const Rect& magnet)
341     {
342         Offset offset = Offset::Zero();
343         if (IsWrappedBy(magnet)) {
344             return Offset::Zero();
345         }
346 
347         if (Left() < magnet.Left()) {
348             offset.SetX(std::max(0.0, std::min(magnet.Left() - Left(), magnet.Right() - Right())));
349         } else if (Right() > magnet.Right()) {
350             offset.SetX(std::min(0.0, std::max(magnet.Left() - Left(), magnet.Right() - Right())));
351         }
352 
353         if (Top() < magnet.Top()) {
354             offset.SetY(std::max(0.0, std::min(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
355         } else if (Bottom() > magnet.Bottom()) {
356             offset.SetY(std::min(0.0, std::max(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
357         }
358 
359         *this += offset;
360 
361         return offset;
362     }
363 
ToString()364     std::string ToString() const
365     {
366         std::stringstream ss;
367         ss << "Rect (" << std::fixed << std::setprecision(2) << x_ << ", " << y_ << ") - [";
368         if (NearEqual(width_, Size::INFINITE_SIZE)) {
369             ss << "INFINITE";
370         } else {
371             ss << width_;
372         }
373         ss << " x ";
374         if (NearEqual(height_, Size::INFINITE_SIZE)) {
375             ss << "INFINITE";
376         } else {
377             ss << height_;
378         }
379         ss << "]";
380         std::string output = ss.str();
381         return output;
382     }
383 
ToBounds()384     std::string ToBounds() const
385     {
386         std::stringstream ss;
387         ss << "[" << std::fixed << std::setprecision(2) << x_ << ", " << y_ << "][";
388         if (NearEqual(width_, Size::INFINITE_SIZE)) {
389             ss << "INFINITE";
390         } else {
391             ss << (x_ + width_);
392         }
393         ss << ",";
394         if (NearEqual(height_, Size::INFINITE_SIZE)) {
395             ss << "INFINITE";
396         } else {
397             ss << (y_ + height_);
398         }
399         ss << "]";
400         std::string output = ss.str();
401         return output;
402     }
403 
Center()404     Offset Center() const
405     {
406         return Offset(width_ / 2.0 + x_, height_ / 2.0 + y_);
407     }
408 
409 private:
410     double x_ = 0.0;
411     double y_ = 0.0;
412     double width_ = 0.0;
413     double height_ = 0.0;
414 };
415 
416 } // namespace OHOS::Ace
417 
418 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_RECT_H
419