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 return GreatOrEqual(point.GetX(), x_) && LessOrEqual(point.GetX(), x_ + width_) &&
183 GreatOrEqual(point.GetY(), y_) && LessOrEqual(point.GetY(), y_ + height_);
184 }
185
IsWrappedBy(const RectT & other)186 bool IsWrappedBy(const RectT& other) const
187 {
188 return GreatOrEqual(Left(), other.Left()) && LessOrEqual(Right(), other.Right()) &&
189 GreatOrEqual(Top(), other.Top()) && LessOrEqual(Bottom(), other.Bottom());
190 }
191
IsValid()192 bool IsValid() const
193 {
194 return NonNegative(width_) && NonNegative(height_);
195 }
196
IsEmpty()197 bool IsEmpty() const
198 {
199 return NonPositive(width_) || NonPositive(height_);
200 }
201
Constrain(const RectT & other)202 RectT Constrain(const RectT& other)
203 {
204 T right = Right();
205 T bottom = Bottom();
206 T left = std::clamp(x_, other.Left(), other.Right());
207 T top = std::clamp(y_, other.Top(), other.Bottom());
208 right = std::clamp(right, other.Left(), other.Right()) - left;
209 bottom = std::clamp(bottom, other.Top(), other.Bottom()) - top;
210 return RectT(left, top, right, bottom);
211 }
212
213 RectT& operator+=(const OffsetT<T>& offset)
214 {
215 x_ += offset.GetX();
216 y_ += offset.GetY();
217 return *this;
218 }
219
220 RectT& operator-=(const OffsetT<T>& offset)
221 {
222 x_ -= offset.GetX();
223 y_ -= offset.GetY();
224 return *this;
225 }
226
227 RectT& operator+=(const SizeT<T>& size)
228 {
229 width_ += size.Width();
230 height_ += size.Height();
231 return *this;
232 }
233
234 RectT& operator-=(const SizeT<T>& size)
235 {
236 width_ -= size.Width();
237 height_ -= size.Height();
238 return *this;
239 }
240
241 RectT operator+(const OffsetT<T>& offset) const
242 {
243 return RectT(x_ + offset.GetX(), y_ + offset.GetY(), width_, height_);
244 }
245
246 RectT operator-(const OffsetT<T>& offset) const
247 {
248 return RectT(x_ - offset.GetX(), y_ - offset.GetY(), width_, height_);
249 }
250
251 RectT operator+(const SizeT<T>& size) const
252 {
253 return RectT(x_, y_, width_ + size.Width(), height_ + size.Height());
254 }
255
256 RectT operator-(const SizeT<T>& size) const
257 {
258 return RectT(x_, y_, width_ - size.Width(), height_ - size.Height());
259 }
260
261 RectT operator*(T scale) const
262 {
263 return RectT(x_ * scale, y_ * scale, width_ * scale, height_ * scale);
264 }
265
266 bool operator==(const RectT& RectT) const
267 {
268 return (GetOffset() == RectT.GetOffset()) && (GetSize() == RectT.GetSize());
269 }
270
271 bool operator!=(const RectT& RectT) const
272 {
273 return !operator==(RectT);
274 }
275
IsIntersectWith(const RectT & other)276 bool IsIntersectWith(const RectT& other) const
277 {
278 return !(LessNotEqual(other.Right(), Left()) || GreatNotEqual(other.Left(), Right()) ||
279 LessNotEqual(other.Bottom(), Top()) || GreatNotEqual(other.Top(), Bottom()));
280 }
281
IsInnerIntersectWith(const RectT & other)282 bool IsInnerIntersectWith(const RectT& other) const
283 {
284 return !(LessOrEqual(other.Right(), Left()) || GreatOrEqual(other.Left(), Right()) ||
285 LessOrEqual(other.Bottom(), Top()) || GreatOrEqual(other.Top(), Bottom()));
286 }
287
IntersectRectT(const RectT & other)288 RectT IntersectRectT(const RectT& other) const
289 {
290 T left = std::max(Left(), other.Left());
291 T right = std::min(Right(), other.Right());
292 T top = std::max(Top(), other.Top());
293 T bottom = std::min(Bottom(), other.Bottom());
294 return RectT(left, top, right - left, bottom - top);
295 }
296
CombineRectT(const RectT & other)297 RectT CombineRectT(const RectT& other) const
298 {
299 T left = std::min(Left(), other.Left());
300 T right = std::max(Right(), other.Right());
301 T top = std::min(Top(), other.Top());
302 T bottom = std::max(Bottom(), other.Bottom());
303 return RectT(left, top, right - left, bottom - top);
304 }
305
MagneticAttractedBy(const RectT & magnet)306 OffsetT<T> MagneticAttractedBy(const RectT& magnet)
307 {
308 OffsetT<T> offset { 0.0, 0.0 };
309 if (IsWrappedBy(magnet)) {
310 return offset;
311 }
312
313 if (LessNotEqual(Left(), magnet.Left())) {
314 offset.SetX(std::max(0.0, std::min(magnet.Left() - Left(), magnet.Right() - Right())));
315 } else if (GreatNotEqual(Right(), magnet.Right())) {
316 offset.SetX(std::min(0.0, std::max(magnet.Left() - Left(), magnet.Right() - Right())));
317 } else {
318 // No need to offset.
319 }
320
321 if (LessNotEqual(Top(), magnet.Top())) {
322 offset.SetY(std::max(0.0, std::min(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
323 } else if (GreatNotEqual(Bottom(), magnet.Bottom())) {
324 offset.SetY(std::min(0.0, std::max(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
325 } else {
326 // No need to offset.
327 }
328
329 *this += offset;
330
331 return offset;
332 }
333
ToString()334 std::string ToString() const
335 {
336 static const int32_t precision = 2;
337 std::stringstream ss;
338 ss << "RectT (" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << ") - [";
339 ss << width_;
340 ss << " x ";
341 ss << height_;
342 ss << "]";
343 std::string output = ss.str();
344 return output;
345 }
346
347 // for example str = RectT (0.00, 0.00) - [0.00 x 0.00]
FromString(const std::string & str)348 static RectT FromString(const std::string& str)
349 {
350 static const int32_t valueSize = 4;
351 LOGD("UITree str=%{public}s", str.c_str());
352
353 std::vector<T> vals;
354 std::string val;
355 for (auto& it : str) {
356 if ((it >= '0' && it <= '9') || (it == '.' && !val.empty())) {
357 val += it;
358 } else {
359 if (!val.empty()) {
360 vals.push_back(StringUtils::StringToFloat(val));
361 val.clear();
362 }
363 }
364 }
365
366 if (vals.size() != valueSize) {
367 LOGE("UITree |ERROR| invalid str=%{public}s", str.c_str());
368 return RectT();
369 }
370
371 int32_t index = 0;
372 T x = vals[index++];
373 T y = vals[index++];
374 T width = vals[index++];
375 T height = vals[index++];
376 return RectT(x, y, width, height);
377 }
378
ToBounds()379 std::string ToBounds() const
380 {
381 static const int32_t precision = 2;
382 std::stringstream ss;
383 ss << "[" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << "][";
384 if (NearEqual(width_, Infinity<T>())) {
385 ss << "INFINITE";
386 } else {
387 ss << (x_ + width_);
388 }
389 ss << ",";
390 if (NearEqual(height_, Infinity<T>())) {
391 ss << "INFINITE";
392 } else {
393 ss << (y_ + height_);
394 }
395 ss << "]";
396 std::string output = ss.str();
397 return output;
398 }
399
Center()400 OffsetT<T> Center() const
401 {
402 return OffsetT<T>(width_ / 2.0 + x_, height_ / 2.0 + y_);
403 }
404
405 private:
406 T x_ = 0;
407 T y_ = 0;
408 T width_ = 0;
409 T height_ = 0;
410
411 friend class GeometryProperty;
412 };
413
414 using RectF = RectT<float>;
415
416 struct EdgeF {
417 EdgeF() = default;
EdgeFEdgeF418 EdgeF(float x, float y) : x(x), y(y) {}
SetXEdgeF419 void SetX(float setX)
420 {
421 x = setX;
422 }
SetYEdgeF423 void SetY(float setY)
424 {
425 y = setY;
426 }
427 EdgeF operator+(const EdgeF& add) const
428 {
429 return EdgeF(x + add.x, y + add.y);
430 }
431 float x = 0.0f;
432 float y = 0.0f;
433 };
434
435 struct RadiusF {
RadiusFRadiusF436 RadiusF(const EdgeF& radius) : topLeft(radius), topRight(radius), bottomLeft(radius), bottomRight(radius) {}
RadiusFRadiusF437 RadiusF(const EdgeF& topLeft, const EdgeF& topRight, const EdgeF& bottomLeft, const EdgeF& bottomRight)
438 : topLeft(topLeft), topRight(topRight), bottomLeft(bottomLeft), bottomRight(bottomRight)
439 {}
440
SetCornerRadiusF441 void SetCorner(int32_t pos, const EdgeF& edge)
442 {
443 if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
444 return;
445 }
446 data_[pos] = edge;
447 }
448
GetCornerRadiusF449 EdgeF GetCorner(int32_t pos) const
450 {
451 if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
452 return EdgeF();
453 }
454 return data_[pos];
455 }
456
457 union {
458 struct {
459 EdgeF topLeft;
460 EdgeF topRight;
461 EdgeF bottomLeft;
462 EdgeF bottomRight;
463 };
464 std::array<EdgeF, 4> data_;
465 };
466 };
467
468 class RoundRect {
469 public:
470 enum CornerPos {
471 TOP_LEFT_POS,
472 TOP_RIGHT_POS,
473 BOTTOM_RIGHT_POS,
474 BOTTOM_LEFT_POS,
475 };
476
477 inline RoundRect() noexcept;
478 inline ~RoundRect() = default;
479
480 inline RoundRect(const RoundRect& roundRect) noexcept;
481 inline RoundRect(const RectF& rect, float xRad, float yRad) noexcept;
482 inline RoundRect(const RectF& rect, const RadiusF& radius) noexcept;
483
484 inline void SetCornerRadius(CornerPos pos, float radiusX, float radiusY);
485 inline void SetCornerRadius(float radius);
486 inline EdgeF GetCornerRadius(CornerPos pos) const;
487
488 inline void SetRect(const RectF& rect);
489 inline RectF GetRect() const;
490
491 inline void Offset(float dx, float dy);
492
493 private:
494 RectF rect_;
495 // Four radii are stored: top-left/top-right/bottom-left/bottom-right corner radii.
496 RadiusF radius_;
497 };
498
RoundRect()499 inline RoundRect::RoundRect() noexcept : radius_(EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0)) {}
500
RoundRect(const RoundRect & roundRect)501 inline RoundRect::RoundRect(const RoundRect& roundRect) noexcept : RoundRect()
502 {
503 rect_ = roundRect.rect_;
504 radius_ = roundRect.radius_;
505 }
506
RoundRect(const RectF & r,float xRad,float yRad)507 inline RoundRect::RoundRect(const RectF& r, float xRad, float yRad) noexcept : RoundRect()
508 {
509 rect_ = r;
510 for (auto& i : radius_.data_) {
511 i = EdgeF(xRad, yRad);
512 }
513 }
514
RoundRect(const RectF & r,const RadiusF & rad)515 inline RoundRect::RoundRect(const RectF& r, const RadiusF& rad) noexcept : RoundRect()
516 {
517 rect_ = r;
518 radius_ = rad;
519 }
520
SetCornerRadius(CornerPos pos,float radiusX,float radiusY)521 inline void RoundRect::SetCornerRadius(CornerPos pos, float radiusX, float radiusY)
522 {
523 radius_.SetCorner(pos, EdgeF(radiusX, radiusY));
524 }
525
SetCornerRadius(float radius)526 inline void RoundRect::SetCornerRadius(float radius)
527 {
528 radius_.SetCorner(TOP_LEFT_POS, EdgeF(radius, radius));
529 radius_.SetCorner(TOP_RIGHT_POS, EdgeF(radius, radius));
530 radius_.SetCorner(BOTTOM_LEFT_POS, EdgeF(radius, radius));
531 radius_.SetCorner(BOTTOM_RIGHT_POS, EdgeF(radius, radius));
532 }
533
GetCornerRadius(CornerPos pos)534 inline EdgeF RoundRect::GetCornerRadius(CornerPos pos) const
535 {
536 return radius_.GetCorner(pos);
537 }
538
SetRect(const RectF & rect)539 inline void RoundRect::SetRect(const RectF& rect)
540 {
541 rect_ = rect;
542 }
543
GetRect()544 inline RectF RoundRect::GetRect() const
545 {
546 return rect_;
547 }
548 } // namespace OHOS::Ace::NG
549
550 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_NG_RECT_H
551