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/utils.h"
27
28 namespace OHOS::Ace::NG {
29 template<typename T>
30 class RectT {
31 public:
32 RectT() = default;
33 ~RectT() = default;
34
RectT(T x,T y,T width,T height)35 RectT(T x, T y, T width, T height)
36 {
37 SetRect(x, y, width, height);
38 }
39
RectT(const OffsetF & offset,const SizeF & size)40 RectT(const OffsetF& offset, const SizeF& size)
41 {
42 SetOffset(offset);
43 SetSize(size);
44 }
45
RectT(const OffsetF & topLeftPoint,const OffsetF & bottomRightPoint)46 RectT(const OffsetF& topLeftPoint, const OffsetF& bottomRightPoint)
47 {
48 SetOffset(topLeftPoint);
49 OffsetF sizeOffset = bottomRightPoint - topLeftPoint;
50 SizeF size(sizeOffset.GetX(), sizeOffset.GetY());
51 SetSize(size);
52 }
53
Reset()54 void Reset()
55 {
56 x_ = 0;
57 y_ = 0;
58 width_ = 0;
59 height_ = 0;
60 }
61
SetRect(T x,T y,T width,T height)62 void SetRect(T x, T y, T width, T height)
63 {
64 x_ = x;
65 y_ = y;
66 width_ = width;
67 height_ = height;
68 }
69
SetRect(const OffsetT<T> & offset,const SizeT<T> & size)70 void SetRect(const OffsetT<T>& offset, const SizeT<T>& size)
71 {
72 SetOffset(offset);
73 SetSize(size);
74 }
75
ApplyScale(T scale)76 void ApplyScale(T scale)
77 {
78 x_ *= scale;
79 y_ *= scale;
80 width_ *= scale;
81 height_ *= scale;
82 }
83
ApplyScaleAndRound(T scale)84 void ApplyScaleAndRound(T scale)
85 {
86 x_ = round(x_ * scale);
87 y_ = round(y_ * scale);
88 width_ = round(width_ * scale);
89 height_ = round(height_ * scale);
90 }
91
Left()92 T Left() const
93 {
94 return GreatNotEqual(width_, 0) ? x_ : x_ + width_;
95 }
96
Top()97 T Top() const
98 {
99 return GreatNotEqual(height_, 0) ? y_ : y_ + height_;
100 }
101
Right()102 T Right() const
103 {
104 return GreatNotEqual(width_, 0) ? x_ + width_ : x_;
105 }
106
Bottom()107 T Bottom() const
108 {
109 return GreatNotEqual(height_, 0) ? y_ + height_ : y_;
110 }
111
GetX()112 T GetX() const
113 {
114 return x_;
115 }
116
GetY()117 T GetY() const
118 {
119 return y_;
120 }
121
Width()122 T Width() const
123 {
124 return width_;
125 }
126
Height()127 T Height() const
128 {
129 return height_;
130 }
131
MainSize(Axis axis)132 T MainSize(Axis axis) const
133 {
134 return axis == Axis::HORIZONTAL ? width_ : height_;
135 }
136
SetSize(const SizeT<T> & size)137 void SetSize(const SizeT<T>& size)
138 {
139 width_ = size.Width();
140 height_ = size.Height();
141 }
142
GetSize()143 SizeT<T> GetSize() const
144 {
145 return SizeT<T>(width_, height_);
146 }
147
SetOffset(const OffsetT<T> & offset)148 void SetOffset(const OffsetT<T>& offset)
149 {
150 x_ = offset.GetX();
151 y_ = offset.GetY();
152 }
153
GetOffset()154 OffsetT<T> GetOffset() const
155 {
156 return OffsetT<T>(x_, y_);
157 }
158
SetLeft(T left)159 void SetLeft(T left)
160 {
161 x_ = left;
162 }
163
SetTop(T top)164 void SetTop(T top)
165 {
166 y_ = top;
167 }
168
SetWidth(T width)169 void SetWidth(T width)
170 {
171 width_ = width;
172 }
173
SetHeight(T height)174 void SetHeight(T height)
175 {
176 height_ = height;
177 }
178
IsInRegion(const PointT<T> & point)179 bool IsInRegion(const PointT<T>& point) const
180 {
181 return GreatOrEqual(point.GetX(), x_) && LessOrEqual(point.GetX(), x_ + width_) &&
182 GreatOrEqual(point.GetY(), y_) && LessOrEqual(point.GetY(), y_ + height_);
183 }
184
IsWrappedBy(const RectT & other)185 bool IsWrappedBy(const RectT& other) const
186 {
187 return GreatOrEqual(Left(), other.Left()) && LessOrEqual(Right(), other.Right()) &&
188 GreatOrEqual(Top(), other.Top()) && LessOrEqual(Bottom(), other.Bottom());
189 }
190
IsValid()191 bool IsValid() const
192 {
193 return NonNegative(width_) && NonNegative(height_);
194 }
195
IsEmpty()196 bool IsEmpty() const
197 {
198 return NonPositive(width_) || NonPositive(height_);
199 }
200
Constrain(const RectT & other)201 RectT Constrain(const RectT& other)
202 {
203 T right = Right();
204 T bottom = Bottom();
205 T left = std::clamp(x_, other.Left(), other.Right());
206 T top = std::clamp(y_, other.Top(), other.Bottom());
207 right = std::clamp(right, other.Left(), other.Right()) - left;
208 bottom = std::clamp(bottom, other.Top(), other.Bottom()) - top;
209 return RectT(left, top, right, bottom);
210 }
211
212 RectT& operator+=(const OffsetT<T>& offset)
213 {
214 x_ += offset.GetX();
215 y_ += offset.GetY();
216 return *this;
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 SizeT<T>& size)
227 {
228 width_ += size.Width();
229 height_ += size.Height();
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 OffsetT<T>& offset) const
241 {
242 return RectT(x_ + offset.GetX(), y_ + offset.GetY(), width_, height_);
243 }
244
245 RectT operator-(const OffsetT<T>& offset) const
246 {
247 return RectT(x_ - offset.GetX(), y_ - offset.GetY(), width_, height_);
248 }
249
250 RectT operator+(const SizeT<T>& size) const
251 {
252 return RectT(x_, y_, width_ + size.Width(), height_ + size.Height());
253 }
254
255 RectT operator-(const SizeT<T>& size) const
256 {
257 return RectT(x_, y_, width_ - size.Width(), height_ - size.Height());
258 }
259
260 RectT operator*(T scale) const
261 {
262 return RectT(x_ * scale, y_ * scale, width_ * scale, height_ * scale);
263 }
264
265 bool operator==(const RectT& RectT) const
266 {
267 return (GetOffset() == RectT.GetOffset()) && (GetSize() == RectT.GetSize());
268 }
269
270 bool operator!=(const RectT& RectT) const
271 {
272 return !operator==(RectT);
273 }
274
IsIntersectWith(const RectT & other)275 bool IsIntersectWith(const RectT& other) const
276 {
277 return !(LessNotEqual(other.Right(), Left()) || GreatNotEqual(other.Left(), Right()) ||
278 LessNotEqual(other.Bottom(), Top()) || GreatNotEqual(other.Top(), Bottom()));
279 }
280
IntersectRectT(const RectT & other)281 RectT IntersectRectT(const RectT& other) const
282 {
283 T left = std::max(Left(), other.Left());
284 T right = std::min(Right(), other.Right());
285 T top = std::max(Top(), other.Top());
286 T bottom = std::min(Bottom(), other.Bottom());
287 return RectT(left, top, right - left, bottom - top);
288 }
289
CombineRectT(const RectT & other)290 RectT CombineRectT(const RectT& other) const
291 {
292 T left = std::min(Left(), other.Left());
293 T right = std::max(Right(), other.Right());
294 T top = std::min(Top(), other.Top());
295 T bottom = std::max(Bottom(), other.Bottom());
296 return RectT(left, top, right - left, bottom - top);
297 }
298
MagneticAttractedBy(const RectT & magnet)299 OffsetT<T> MagneticAttractedBy(const RectT& magnet)
300 {
301 OffsetT<T> offset { 0.0, 0.0 };
302 if (IsWrappedBy(magnet)) {
303 return offset;
304 }
305
306 if (LessNotEqual(Left(), magnet.Left())) {
307 offset.SetX(std::max(0.0, std::min(magnet.Left() - Left(), magnet.Right() - Right())));
308 } else if (GreatNotEqual(Right(), magnet.Right())) {
309 offset.SetX(std::min(0.0, std::max(magnet.Left() - Left(), magnet.Right() - Right())));
310 } else {
311 // No need to offset.
312 }
313
314 if (LessNotEqual(Top(), magnet.Top())) {
315 offset.SetY(std::max(0.0, std::min(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
316 } else if (GreatNotEqual(Bottom(), magnet.Bottom())) {
317 offset.SetY(std::min(0.0, std::max(magnet.Top() - Top(), magnet.Bottom() - Bottom())));
318 } else {
319 // No need to offset.
320 }
321
322 *this += offset;
323
324 return offset;
325 }
326
ToString()327 std::string ToString() const
328 {
329 static const int32_t precision = 2;
330 std::stringstream ss;
331 ss << "RectT (" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << ") - [";
332 ss << width_;
333 ss << " x ";
334 ss << height_;
335 ss << "]";
336 std::string output = ss.str();
337 return output;
338 }
339
ToBounds()340 std::string ToBounds() const
341 {
342 static const int32_t precision = 2;
343 std::stringstream ss;
344 ss << "[" << std::fixed << std::setprecision(precision) << x_ << ", " << y_ << "][";
345 if (NearEqual(width_, Infinity<T>())) {
346 ss << "INFINITE";
347 } else {
348 ss << (x_ + width_);
349 }
350 ss << ",";
351 if (NearEqual(height_, Infinity<T>())) {
352 ss << "INFINITE";
353 } else {
354 ss << (y_ + height_);
355 }
356 ss << "]";
357 std::string output = ss.str();
358 return output;
359 }
360
Center()361 OffsetT<T> Center() const
362 {
363 return OffsetT<T>(width_ / 2.0 + x_, height_ / 2.0 + y_);
364 }
365
366 private:
367 T x_ = 0;
368 T y_ = 0;
369 T width_ = 0;
370 T height_ = 0;
371
372 friend class GeometryProperty;
373 };
374
375 using RectF = RectT<float>;
376
377 struct EdgeF {
378 EdgeF() = default;
EdgeFEdgeF379 EdgeF(float x, float y) : x(x), y(y) {}
SetXEdgeF380 void SetX(float setX)
381 {
382 x = setX;
383 }
SetYEdgeF384 void SetY(float setY)
385 {
386 y = setY;
387 }
388 EdgeF operator+(const EdgeF& add) const
389 {
390 return EdgeF(x + add.x, y + add.y);
391 }
392 float x = 0.0f;
393 float y = 0.0f;
394 };
395
396 struct RadiusF {
RadiusFRadiusF397 RadiusF(const EdgeF& radius) : topLeft(radius), topRight(radius), bottomLeft(radius), bottomRight(radius) {}
RadiusFRadiusF398 RadiusF(const EdgeF& topLeft, const EdgeF& topRight, const EdgeF& bottomLeft, const EdgeF& bottomRight)
399 : topLeft(topLeft), topRight(topRight), bottomLeft(bottomLeft), bottomRight(bottomRight)
400 {}
401
SetCornerRadiusF402 void SetCorner(int32_t pos, const EdgeF& edge)
403 {
404 if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
405 return;
406 }
407 data_[pos] = edge;
408 }
409
GetCornerRadiusF410 EdgeF GetCorner(int32_t pos) const
411 {
412 if (LessNotEqual(pos, 0.0) || GreatOrEqual(pos, data_.size())) {
413 return EdgeF();
414 }
415 return data_[pos];
416 }
417
418 union {
419 struct {
420 EdgeF topLeft;
421 EdgeF topRight;
422 EdgeF bottomLeft;
423 EdgeF bottomRight;
424 };
425 std::array<EdgeF, 4> data_;
426 };
427 };
428
429 class RoundRect {
430 public:
431 enum CornerPos {
432 TOP_LEFT_POS,
433 TOP_RIGHT_POS,
434 BOTTOM_RIGHT_POS,
435 BOTTOM_LEFT_POS,
436 };
437
438 inline RoundRect() noexcept;
439 inline ~RoundRect() = default;
440
441 inline RoundRect(const RoundRect& roundRect) noexcept;
442 inline RoundRect(const RectF& rect, float xRad, float yRad) noexcept;
443 inline RoundRect(const RectF& rect, const RadiusF& radius) noexcept;
444
445 inline void SetCornerRadius(CornerPos pos, float radiusX, float radiusY);
446 inline void SetCornerRadius(float radius);
447 inline EdgeF GetCornerRadius(CornerPos pos) const;
448
449 inline void SetRect(const RectF& rect);
450 inline RectF GetRect() const;
451
452 inline void Offset(float dx, float dy);
453
454 private:
455 RectF rect_;
456 // Four radii are stored: top-left/top-right/bottom-left/bottom-right corner radii.
457 RadiusF radius_;
458 };
459
RoundRect()460 inline RoundRect::RoundRect() noexcept : radius_(EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0), EdgeF(0, 0)) {}
461
RoundRect(const RoundRect & roundRect)462 inline RoundRect::RoundRect(const RoundRect& roundRect) noexcept : RoundRect()
463 {
464 rect_ = roundRect.rect_;
465 radius_ = roundRect.radius_;
466 }
467
RoundRect(const RectF & r,float xRad,float yRad)468 inline RoundRect::RoundRect(const RectF& r, float xRad, float yRad) noexcept : RoundRect()
469 {
470 rect_ = r;
471 for (auto& i : radius_.data_) {
472 i = EdgeF(xRad, yRad);
473 }
474 }
475
RoundRect(const RectF & r,const RadiusF & rad)476 inline RoundRect::RoundRect(const RectF& r, const RadiusF& rad) noexcept : RoundRect()
477 {
478 rect_ = r;
479 radius_ = rad;
480 }
481
SetCornerRadius(CornerPos pos,float radiusX,float radiusY)482 inline void RoundRect::SetCornerRadius(CornerPos pos, float radiusX, float radiusY)
483 {
484 radius_.SetCorner(pos, EdgeF(radiusX, radiusY));
485 }
486
SetCornerRadius(float radius)487 inline void RoundRect::SetCornerRadius(float radius)
488 {
489 radius_.SetCorner(TOP_LEFT_POS, EdgeF(radius, radius));
490 radius_.SetCorner(TOP_RIGHT_POS, EdgeF(radius, radius));
491 radius_.SetCorner(BOTTOM_LEFT_POS, EdgeF(radius, radius));
492 radius_.SetCorner(BOTTOM_RIGHT_POS, EdgeF(radius, radius));
493 }
494
GetCornerRadius(CornerPos pos)495 inline EdgeF RoundRect::GetCornerRadius(CornerPos pos) const
496 {
497 return radius_.GetCorner(pos);
498 }
499
SetRect(const RectF & rect)500 inline void RoundRect::SetRect(const RectF& rect)
501 {
502 rect_ = rect;
503 }
504
GetRect()505 inline RectF RoundRect::GetRect() const
506 {
507 return rect_;
508 }
509 } // namespace OHOS::Ace::NG
510
511 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_NG_RECT_H
512