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