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