1 /*
2  * Copyright (c) 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_NG_RECT_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_NG_RECT_H
18 
19 #include <algorithm>
20 #include <array>
21 
22 #include "base/geometry/axis.h"
23 #include "base/geometry/ng/offset_t.h"
24 #include "base/geometry/ng/point_t.h"
25 #include "base/geometry/ng/size_t.h"
26 #include "base/utils/string_utils.h"
27 #include "base/utils/utils.h"
28 
29 namespace OHOS::Ace::NG {
30 template<typename T>
31 class RectT {
32 public:
33     RectT() = default;
34     ~RectT() = default;
35 
RectT(T x,T y,T width,T height)36     RectT(T x, T y, T width, T height)
37     {
38         SetRect(x, y, width, height);
39     }
40 
RectT(const OffsetF & offset,const SizeF & size)41     RectT(const OffsetF& offset, const SizeF& size)
42     {
43         SetOffset(offset);
44         SetSize(size);
45     }
46 
RectT(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)47     RectT(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
48     {
49         SetOffset(topLeftPoint);
50         OffsetF sizeOffset = bottomRightPoint - topLeftPoint;
51         SizeF size(sizeOffset.GetX(), sizeOffset.GetY());
52         SetSize(size);
53     }
54 
Reset()55     void Reset()
56     {
57         x_ = 0;
58         y_ = 0;
59         width_ = 0;
60         height_ = 0;
61     }
62 
SetRect(T x,T y,T width,T height)63     void SetRect(T x, T y, T width, T height)
64     {
65         x_ = x;
66         y_ = y;
67         width_ = width;
68         height_ = height;
69     }
70 
SetRect(const OffsetT<T> & offset,const SizeT<T> & size)71     void SetRect(const OffsetT<T>& offset, const SizeT<T>& size)
72     {
73         SetOffset(offset);
74         SetSize(size);
75     }
76 
ApplyScale(T scale)77     void ApplyScale(T scale)
78     {
79         x_ *= scale;
80         y_ *= scale;
81         width_ *= scale;
82         height_ *= scale;
83     }
84 
ApplyScaleAndRound(T scale)85     void ApplyScaleAndRound(T scale)
86     {
87         x_ = round(x_ * scale);
88         y_ = round(y_ * scale);
89         width_ = round(width_ * scale);
90         height_ = round(height_ * scale);
91     }
92 
Left()93     T Left() const
94     {
95         return GreatNotEqual(width_, 0) ? x_ : x_ + width_;
96     }
97 
Top()98     T Top() const
99     {
100         return GreatNotEqual(height_, 0) ? y_ : y_ + height_;
101     }
102 
Right()103     T Right() const
104     {
105         return GreatNotEqual(width_, 0) ? x_ + width_ : x_;
106     }
107 
Bottom()108     T Bottom() const
109     {
110         return GreatNotEqual(height_, 0) ? y_ + height_ : y_;
111     }
112 
GetX()113     T GetX() const
114     {
115         return x_;
116     }
117 
GetY()118     T GetY() const
119     {
120         return y_;
121     }
122 
Width()123     T Width() const
124     {
125         return width_;
126     }
127 
Height()128     T Height() const
129     {
130         return height_;
131     }
132 
MainSize(Axis axis)133     T MainSize(Axis axis) const
134     {
135         return axis == Axis::HORIZONTAL ? width_ : height_;
136     }
137 
SetSize(const SizeT<T> & size)138     void SetSize(const SizeT<T>& size)
139     {
140         width_ = size.Width();
141         height_ = size.Height();
142     }
143 
GetSize()144     SizeT<T> GetSize() const
145     {
146         return SizeT<T>(width_, height_);
147     }
148 
SetOffset(const OffsetT<T> & offset)149     void SetOffset(const OffsetT<T>& offset)
150     {
151         x_ = offset.GetX();
152         y_ = offset.GetY();
153     }
154 
GetOffset()155     OffsetT<T> GetOffset() const
156     {
157         return OffsetT<T>(x_, y_);
158     }
159 
SetLeft(T left)160     void SetLeft(T left)
161     {
162         x_ = left;
163     }
164 
SetTop(T top)165     void SetTop(T top)
166     {
167         y_ = top;
168     }
169 
SetWidth(T width)170     void SetWidth(T width)
171     {
172         width_ = width;
173     }
174 
SetHeight(T height)175     void SetHeight(T height)
176     {
177         height_ = height;
178     }
179 
IsInRegion(const PointT<T> & point)180     bool IsInRegion(const PointT<T>& point) const
181     {
182         if (NearZero(width_) || NearZero(height_)) {
183             return false;
184         }
185         return GreatOrEqual(point.GetX(), x_) && LessOrEqual(point.GetX(), x_ + width_) &&
186                GreatOrEqual(point.GetY(), y_) && LessOrEqual(point.GetY(), y_ + height_);
187     }
188 
IsInnerRegion(const PointT<T> & point)189     bool IsInnerRegion(const PointT<T>& point) const
190     {
191         return GreatNotEqual(point.GetX(), x_) && LessNotEqual(point.GetX(), x_ + width_) &&
192                GreatNotEqual(point.GetY(), y_) && LessNotEqual(point.GetY(), y_ + height_);
193     }
194 
IsWrappedBy(const RectT & other)195     bool IsWrappedBy(const RectT& other) const
196     {
197         return GreatOrEqual(Left(), other.Left()) && LessOrEqual(Right(), other.Right()) &&
198                GreatOrEqual(Top(), other.Top()) && LessOrEqual(Bottom(), other.Bottom());
199     }
200 
IsValid()201     bool IsValid() const
202     {
203         return NonNegative(width_) && NonNegative(height_);
204     }
205 
IsEmpty()206     bool IsEmpty() const
207     {
208         return NonPositive(width_) || NonPositive(height_);
209     }
210 
Constrain(const RectT & other)211     RectT Constrain(const RectT& other)
212     {
213         T right = Right();
214         T bottom = Bottom();
215         T left = std::clamp(x_, other.Left(), other.Right());
216         T top = std::clamp(y_, other.Top(), other.Bottom());
217         right = std::clamp(right, other.Left(), other.Right()) - left;
218         bottom = std::clamp(bottom, other.Top(), other.Bottom()) - top;
219         return RectT(left, top, right, bottom);
220     }
221 
222     RectT& operator+=(const OffsetT<T>& offset)
223     {
224         x_ += offset.GetX();
225         y_ += offset.GetY();
226         return *this;
227     }
228 
229     RectT& operator-=(const OffsetT<T>& offset)
230     {
231         x_ -= offset.GetX();
232         y_ -= offset.GetY();
233         return *this;
234     }
235 
236     RectT& operator+=(const SizeT<T>& size)
237     {
238         width_ += size.Width();
239         height_ += size.Height();
240         return *this;
241     }
242 
243     RectT& operator+=(const RectT<T>& rect)
244     {
245         x_ += rect.x_;
246         y_ += rect.y_;
247         width_ += rect.Width();
248         height_ += rect.Height();
249         return *this;
250     }
251 
252     RectT& operator-=(const RectT<T>& rect)
253     {
254         x_ -= rect.x_;
255         y_ -= rect.y_;
256         width_ -= rect.Width();
257         height_ -= rect.Height();
258         return *this;
259     }
260 
261     RectT& operator-=(const SizeT<T>& size)
262     {
263         width_ -= size.Width();
264         height_ -= size.Height();
265         return *this;
266     }
267 
268     RectT operator+(const OffsetT<T>& offset) const
269     {
270         return RectT(x_ + offset.GetX(), y_ + offset.GetY(), width_, height_);
271     }
272 
273     RectT operator-(const OffsetT<T>& offset) const
274     {
275         return RectT(x_ - offset.GetX(), y_ - offset.GetY(), width_, height_);
276     }
277 
278     RectT operator+(const SizeT<T>& size) const
279     {
280         return RectT(x_, y_, width_ + size.Width(), height_ + size.Height());
281     }
282 
283     RectT operator+(const RectT<T>& rect) const
284     {
285         return RectT(x_ + rect.x_, y_ + rect.y_, width_ + rect.Width(), height_ + rect.Height());
286     }
287 
288     RectT operator-(const RectT<T>& rect) const
289     {
290         return RectT(x_ - rect.x_, y_ - rect.y_, width_ - rect.Width(), height_ - rect.Height());
291     }
292 
293     RectT operator-(const SizeT<T>& size) const
294     {
295         return RectT(x_, y_, width_ - size.Width(), height_ - size.Height());
296     }
297 
298     RectT operator*(T scale) const
299     {
300         return RectT(x_ * scale, y_ * scale, width_ * scale, height_ * scale);
301     }
302 
303     bool operator==(const RectT& RectT) const
304     {
305         return (GetOffset() == RectT.GetOffset()) && (GetSize() == RectT.GetSize());
306     }
307 
308     bool operator!=(const RectT& RectT) const
309     {
310         return !operator==(RectT);
311     }
312 
IsIntersectWith(const RectT & other)313     bool IsIntersectWith(const RectT& other) const
314     {
315         return !(LessNotEqual(other.Right(), Left()) || GreatNotEqual(other.Left(), Right()) ||
316                  LessNotEqual(other.Bottom(), Top()) || GreatNotEqual(other.Top(), Bottom()));
317     }
318 
IsInnerIntersectWith(const RectT & other)319     bool IsInnerIntersectWith(const RectT& other) const
320     {
321         return !(LessOrEqual(other.Right(), Left()) || GreatOrEqual(other.Left(), Right()) ||
322                  LessOrEqual(other.Bottom(), Top()) || GreatOrEqual(other.Top(), Bottom()));
323     }
324 
IsInnerIntersectWithRound(const RectT & other)325     bool IsInnerIntersectWithRound(const RectT& other) const
326     {
327         return !(LessOrEqual(other.Right(), Left() + 1.0) || GreatOrEqual(other.Left() + 1.0, Right()) ||
328                  LessOrEqual(other.Bottom(), Top() + 1.0) || GreatOrEqual(other.Top() + 1.0, Bottom()));
329     }
330 
IntersectRectT(const RectT & other)331     RectT IntersectRectT(const RectT& other) const
332     {
333         T left = std::max(Left(), other.Left());
334         T right = std::min(Right(), other.Right());
335         T top = std::max(Top(), other.Top());
336         T bottom = std::min(Bottom(), other.Bottom());
337         return RectT(left, top, right - left, bottom - top);
338     }
339 
CombineRectT(const RectT & other)340     RectT CombineRectT(const RectT& other) const
341     {
342         T left = std::min(Left(), other.Left());
343         T right = std::max(Right(), other.Right());
344         T top = std::min(Top(), other.Top());
345         T bottom = std::max(Bottom(), other.Bottom());
346         return RectT(left, top, right - left, bottom - top);
347     }
348 
MagneticAttractedBy(const RectT & magnet)349     OffsetT<T> MagneticAttractedBy(const RectT& magnet)
350     {
351         OffsetT<T> offset { 0.0, 0.0 };
352         if (IsWrappedBy(magnet)) {
353             return offset;
354         }
355 
356         if (LessNotEqual(Left(), magnet.Left())) {
357             offset.SetX(std::max(0.0, std::min(magnet.Left() - Left(), magnet.Right() - Right())));
358         } else if (GreatNotEqual(Right(), magnet.Right())) {
359             offset.SetX(std::min(0.0, std::max(magnet.Left() - Left(), magnet.Right() - Right())));
360         } else {
361             // No need to offset.
362         }
363 
364         if (LessNotEqual(Top(), magnet.Top())) {
365             offset.SetY(std::max(0.0, std::min(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
366         } else if (GreatNotEqual(Bottom(), magnet.Bottom())) {
367             offset.SetY(std::min(0.0, std::max(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
368         } else {
369             // No need to offset.
370         }
371 
372         *this += offset;
373 
374         return offset;
375     }
376 
ToString()377     std::string ToString() const
378     {
379         static const int32_t precision = 2;
380         std::stringstream ss;
381         ss << "RectT (" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << ") - [";
382         ss << width_;
383         ss << " x ";
384         ss << height_;
385         ss << "]";
386         std::string output = ss.str();
387         return output;
388     }
389 
390     // for example str = RectT (0.00, 0.00) - [0.00 x 0.00]
FromString(const std::string & str)391     static RectT FromString(const std::string& str)
392     {
393         static const int32_t valueSize = 4;
394 
395         std::vector<T> vals;
396         std::string val;
397         for (auto& it : str) {
398             if ((it >= '0' && it <= '9') || (it == '.' && !val.empty())) {
399                 val += it;
400             } else {
401                 if (!val.empty()) {
402                     vals.emplace_back(StringUtils::StringToFloat(val));
403                     val.clear();
404                 }
405             }
406         }
407 
408         if (vals.size() != valueSize) {
409             LOGE("UITree |ERROR| invalid str=%{public}s", str.c_str());
410             return RectT();
411         }
412 
413         int32_t index = 0;
414         T x = vals[index++];
415         T y = vals[index++];
416         T width = vals[index++];
417         T height = vals[index++];
418         return RectT(x, y, width, height);
419     }
420 
ToBounds()421     std::string ToBounds() const
422     {
423         static const int32_t precision = 2;
424         std::stringstream ss;
425         ss << "[" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << "],[";
426         if (NearEqual(width_, Infinity<T>())) {
427             ss << "INFINITE";
428         } else {
429             ss << (x_ + width_);
430         }
431         ss << ",";
432         if (NearEqual(height_, Infinity<T>())) {
433             ss << "INFINITE";
434         } else {
435             ss << (y_ + height_);
436         }
437         ss << "]";
438         std::string output = ss.str();
439         return output;
440     }
441 
Center()442     OffsetT<T> Center() const
443     {
444         return OffsetT<T>(width_ / 2.0 + x_, height_ / 2.0 + y_);
445     }
446 
447 private:
448     T x_ = 0;
449     T y_ = 0;
450     T width_ = 0;
451     T height_ = 0;
452 
453     friend class GeometryProperty;
454 };
455 
456 using RectF = RectT<float>;
457 using RectInt = RectT<int32_t>;
458 
459 struct EdgeF {
460     EdgeF() = default;
EdgeFEdgeF461     EdgeF(float x, float y) : x(x), y(y) {}
SetXEdgeF462     void SetX(float setX)
463     {
464         x = setX;
465     }
SetYEdgeF466     void SetY(float setY)
467     {
468         y = setY;
469     }
470     EdgeF operator+(const EdgeF& add) const
471     {
472         return EdgeF(x + add.x, y + add.y);
473     }
474     float x = 0.0f;
475     float y = 0.0f;
476 };
477 
478 struct RadiusF {
RadiusFRadiusF479     RadiusF(const EdgeF& radius) : topLeft(radius), topRight(radius), bottomLeft(radius), bottomRight(radius) {}
RadiusFRadiusF480     RadiusF(const EdgeF& topLeft, const EdgeF& topRight, const EdgeF& bottomLeft, const EdgeF& bottomRight)
481         : topLeft(topLeft), topRight(topRight), bottomLeft(bottomLeft), bottomRight(bottomRight)
482     {}
483 
SetCornerRadiusF484     void SetCorner(int32_t pos, const EdgeF& edge)
485     {
486         if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
487             return;
488         }
489         data_[pos] = edge;
490     }
491 
GetCornerRadiusF492     EdgeF GetCorner(int32_t pos) const
493     {
494         if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
495             return EdgeF();
496         }
497         return data_[pos];
498     }
499 
500     union {
501         struct {
502             EdgeF topLeft;
503             EdgeF topRight;
504             EdgeF bottomLeft;
505             EdgeF bottomRight;
506         };
507         std::array<EdgeF, 4> data_;
508     };
509 };
510 
511 class RoundRect {
512 public:
513     enum CornerPos {
514         TOP_LEFT_POS,
515         TOP_RIGHT_POS,
516         BOTTOM_RIGHT_POS,
517         BOTTOM_LEFT_POS,
518     };
519 
520     inline RoundRect() noexcept;
521     inline ~RoundRect() = default;
522 
523     inline RoundRect(const RoundRect& roundRect) noexcept;
524     inline RoundRect(const RectF& rect, float xRad, float yRad) noexcept;
525     inline RoundRect(const RectF& rect, const RadiusF& radius) noexcept;
526 
527     inline void SetCornerRadius(CornerPos pos, float radiusX, float radiusY);
528     inline void SetCornerRadius(float radius);
529     inline EdgeF GetCornerRadius(CornerPos pos) const;
530 
531     inline void SetRect(const RectF& rect);
532     inline RectF GetRect() const;
533 
534     inline void Offset(float dx, float dy);
535 
536 private:
537     RectF rect_;
538     // Four radii are stored: top-left/top-right/bottom-left/bottom-right corner radii.
539     RadiusF radius_;
540 };
541 
RoundRect()542 inline RoundRect::RoundRect() noexcept : radius_(EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0)) {}
543 
RoundRect(const RoundRect & roundRect)544 inline RoundRect::RoundRect(const RoundRect& roundRect) noexcept : RoundRect()
545 {
546     rect_ = roundRect.rect_;
547     radius_ = roundRect.radius_;
548 }
549 
RoundRect(const RectF & r,float xRad,float yRad)550 inline RoundRect::RoundRect(const RectF& r, float xRad, float yRad) noexcept : RoundRect()
551 {
552     rect_ = r;
553     for (auto& i : radius_.data_) {
554         i = EdgeF(xRad, yRad);
555     }
556 }
557 
RoundRect(const RectF & r,const RadiusF & rad)558 inline RoundRect::RoundRect(const RectF& r, const RadiusF& rad) noexcept : RoundRect()
559 {
560     rect_ = r;
561     radius_ = rad;
562 }
563 
SetCornerRadius(CornerPos pos,float radiusX,float radiusY)564 inline void RoundRect::SetCornerRadius(CornerPos pos, float radiusX, float radiusY)
565 {
566     radius_.SetCorner(pos, EdgeF(radiusX, radiusY));
567 }
568 
SetCornerRadius(float radius)569 inline void RoundRect::SetCornerRadius(float radius)
570 {
571     radius_.SetCorner(TOP_LEFT_POS, EdgeF(radius, radius));
572     radius_.SetCorner(TOP_RIGHT_POS, EdgeF(radius, radius));
573     radius_.SetCorner(BOTTOM_LEFT_POS, EdgeF(radius, radius));
574     radius_.SetCorner(BOTTOM_RIGHT_POS, EdgeF(radius, radius));
575 }
576 
GetCornerRadius(CornerPos pos)577 inline EdgeF RoundRect::GetCornerRadius(CornerPos pos) const
578 {
579     return radius_.GetCorner(pos);
580 }
581 
SetRect(const RectF & rect)582 inline void RoundRect::SetRect(const RectF& rect)
583 {
584     rect_ = rect;
585 }
586 
GetRect()587 inline RectF RoundRect::GetRect() const
588 {
589     return rect_;
590 }
591 } // namespace OHOS::Ace::NG
592 
593 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_NG_RECT_H
594