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