1 /* 2 * Copyright (c) 2021-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_RECT_H 17 #define FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_RECT_H 18 19 #include <algorithm> 20 21 #include "base/geometry/offset.h" 22 #include "base/geometry/point.h" 23 #include "base/geometry/size.h" 24 25 namespace OHOS::Ace { 26 27 class Rect { 28 public: 29 Rect() = default; 30 ~Rect() = default; 31 Rect(double x,double y,double width,double height)32 Rect(double x, double y, double width, double height) 33 { 34 SetRect(x, y, width, height); 35 } 36 Rect(const Offset & offset,const Size & size)37 Rect(const Offset& offset, const Size& size) 38 { 39 SetOffset(offset); 40 SetSize(size); 41 } 42 SetRect(double x,double y,double width,double height)43 void SetRect(double x, double y, double width, double height) 44 { 45 x_ = x; 46 y_ = y; 47 width_ = width; 48 height_ = height; 49 } 50 SetRect(const Offset & offset,const Size & size)51 void SetRect(const Offset& offset, const Size& size) 52 { 53 SetOffset(offset); 54 SetSize(size); 55 } 56 ApplyScale(double scale)57 void ApplyScale(double scale) 58 { 59 x_ *= scale; 60 y_ *= scale; 61 width_ *= scale; 62 height_ *= scale; 63 } 64 ApplyScaleAndRound(const Size & scale)65 void ApplyScaleAndRound(const Size& scale) 66 { 67 x_ = round(x_ * scale.Width()); 68 y_ = round(y_ * scale.Height()); 69 width_ = round(width_ * scale.Width()); 70 height_ = round(height_ * scale.Height()); 71 } 72 Left()73 double Left() const 74 { 75 return GreatNotEqual(width_, 0.0) ? x_ : x_ + width_; 76 } 77 Top()78 double Top() const 79 { 80 return GreatNotEqual(height_, 0.0) ? y_ : y_ + height_; 81 } 82 Right()83 double Right() const 84 { 85 return GreatNotEqual(width_, 0.0) ? x_ + width_ : x_; 86 } 87 Bottom()88 double Bottom() const 89 { 90 return GreatNotEqual(height_, 0.0) ? y_ + height_ : y_; 91 } 92 Width()93 double Width() const 94 { 95 return width_; 96 } 97 Height()98 double Height() const 99 { 100 return height_; 101 } 102 SetSize(const Size & size)103 void SetSize(const Size& size) 104 { 105 width_ = size.Width(); 106 height_ = size.Height(); 107 } 108 GetSize()109 Size GetSize() const 110 { 111 return Size(width_, height_); 112 } 113 SetOffset(const Offset & offset)114 void SetOffset(const Offset& offset) 115 { 116 x_ = offset.GetX(); 117 y_ = offset.GetY(); 118 } 119 GetOffset()120 Offset GetOffset() const 121 { 122 return Offset(x_, y_); 123 } 124 SetLeft(double left)125 void SetLeft(double left) 126 { 127 x_ = left; 128 } 129 SetTop(double top)130 void SetTop(double top) 131 { 132 y_ = top; 133 } 134 SetWidth(double width)135 void SetWidth(double width) 136 { 137 width_ = width; 138 } 139 SetHeight(double height)140 void SetHeight(double height) 141 { 142 height_ = height; 143 } 144 IsInRegion(const Point & point)145 bool IsInRegion(const Point& point) const 146 { 147 return (point.GetX() >= x_) && (point.GetX() < (x_ + width_)) && (point.GetY() >= y_) && 148 (point.GetY() < (y_ + height_)); 149 } 150 IsWrappedBy(const Rect & other)151 bool IsWrappedBy(const Rect& other) const 152 { 153 return (Left() >= other.Left()) && (Right() <= other.Right()) && (Top() >= other.Top()) && 154 (Bottom() <= other.Bottom()); 155 } 156 IsValid()157 bool IsValid() const 158 { 159 return width_ > 0.0 && height_ > 0.0; 160 } 161 Constrain(const Rect & other)162 Rect Constrain(const Rect& other) 163 { 164 double right = Right(); 165 double bottom = Bottom(); 166 double left = std::clamp(x_, other.Left(), other.Right()); 167 double top = std::clamp(y_, other.Top(), other.Bottom()); 168 right = std::clamp(right, other.Left(), other.Right()) - left; 169 bottom = std::clamp(bottom, other.Top(), other.Bottom()) - top; 170 return Rect(left, top, right, bottom); 171 } 172 173 Rect& operator+=(const Offset& offset) 174 { 175 x_ += offset.GetX(); 176 y_ += offset.GetY(); 177 return *this; 178 } 179 180 Rect& operator-=(const Offset& offset) 181 { 182 x_ -= offset.GetX(); 183 y_ -= offset.GetY(); 184 return *this; 185 } 186 187 Rect& operator+=(const Size& size) 188 { 189 width_ += size.Width(); 190 height_ += size.Height(); 191 return *this; 192 } 193 194 Rect& operator-=(const Size& size) 195 { 196 width_ -= size.Width(); 197 height_ -= size.Height(); 198 return *this; 199 } 200 201 Rect operator+(const Offset& offset) const 202 { 203 return Rect(x_ + offset.GetX(), y_ + offset.GetY(), width_, height_); 204 } 205 206 Rect operator-(const Offset& offset) const 207 { 208 return Rect(x_ - offset.GetX(), y_ - offset.GetY(), width_, height_); 209 } 210 211 Rect operator+(const Size& size) const 212 { 213 return Rect(x_, y_, width_ + size.Width(), height_ + size.Height()); 214 } 215 216 Rect operator-(const Size& size) const 217 { 218 return Rect(x_, y_, width_ - size.Width(), height_ - size.Height()); 219 } 220 221 Rect operator*(double scale) const 222 { 223 return Rect(x_ * scale, y_ * scale, width_ * scale, height_ * scale); 224 } 225 226 bool operator==(const Rect& rect) const 227 { 228 return (GetOffset() == rect.GetOffset()) && (GetSize() == rect.GetSize()); 229 } 230 231 bool operator!=(const Rect& rect) const 232 { 233 return !operator==(rect); 234 } 235 IsIntersectWith(const Rect & other)236 bool IsIntersectWith(const Rect& other) const 237 { 238 return !(other.Right() < Left() || other.Left() > Right() || other.Bottom() < Top() || other.Top() > Bottom()); 239 } 240 IsIntersectByCommonSideWith(const Rect & other)241 bool IsIntersectByCommonSideWith(const Rect& other) const 242 { 243 return !( 244 other.Right() <= Left() || other.Left() >= Right() || other.Bottom() <= Top() || other.Top() >= Bottom()); 245 } 246 IntersectRect(const Rect & other)247 Rect IntersectRect(const Rect& other) const 248 { 249 double left = std::max(Left(), other.Left()); 250 double right = std::min(Right(), other.Right()); 251 double top = std::max(Top(), other.Top()); 252 double bottom = std::min(Bottom(), other.Bottom()); 253 return Rect(left, top, right - left, bottom - top); 254 } 255 CombineRect(const Rect & other)256 Rect CombineRect(const Rect& other) const 257 { 258 double left = std::min(Left(), other.Left()); 259 double right = std::max(Right(), other.Right()); 260 double top = std::min(Top(), other.Top()); 261 double bottom = std::max(Bottom(), other.Bottom()); 262 return Rect(left, top, right - left, bottom - top); 263 } 264 265 /** 266 * @brief Magnetically attracted to a "magnetic" rect. 267 * 268 * Let's show some cases to illustrate how this method works: 269 * 270 * Case 1 : Inside. Rect won't be move because it is already attracted by magnet. 271 * Result: Offset(0, 0) 272 * 273 * +-----------------------------+ 274 * | | 275 * | Magnetical Rect | 276 * | | 277 * | +-------+ | 278 * | | R | | 279 * | | | | 280 * | +-------+ | 281 * +-----------------------------+ 282 * 283 * Case 2: Outside. R will be attracted to position R' 284 * Result: Offset(-12, -3) 285 * 286 * +-----------------------+ 287 * | | 288 * | Magnetical Rect +--+ 289 * | | | 290 * | |R'| +--+ 291 * +-----------------------+ |R | 292 * | | 293 * +--+ 294 * 295 * Case 3: Half Inside. R will be moved totally into magnet. 296 * Result: Offset(-8, 0) 297 * 298 * +-----------------------------+ +-----------------------------+ 299 * | | | | 300 * | Magnetical Rect | +----------> | Magnetical Rect | 301 * | | | | 302 * | +----------+ | +----------+ 303 * | | | R | | | R' | 304 * | +----------+ | +----------+ 305 * | | | | 306 * +-----------------------------+ +-----------------------------+ 307 * 308 * Case 4: Outside or Half Outside but space not enough. R will be moved into magnet as more as possible. 309 * Result: Offset(0, 1) 310 * 311 * +--+ 312 * | | +--+ 313 * |R | | | 314 * | | |R'| 315 * +---------------------------+ +---------------------------+ 316 * | | | | | | | | 317 * | Magnetical Rect | | | +-----> | Magnetical Rect | | | 318 * | +--+ | | | | | 319 * +---------------------------+ +------------------+--+-----+ 320 * 321 * Case 5: Totally Across magnet. Nothing should happen. 322 * Result: Offset(0, 0) 323 * 324 * +--+ 325 * | | 326 * |R | 327 * | | 328 * +---------------------------+ 329 * | | | | 330 * | Magnetical Rect | | | 331 * | | | | 332 * +---------------------------+ 333 * | | 334 * +--+ 335 * 336 * @param[in] magnet The magnetical rectangle. 337 * 338 * @return The offset that this rect need to moving into magnet. 339 */ MagneticAttractedBy(const Rect & magnet)340 Offset MagneticAttractedBy(const Rect& magnet) 341 { 342 Offset offset = Offset::Zero(); 343 if (IsWrappedBy(magnet)) { 344 return Offset::Zero(); 345 } 346 347 if (Left() < magnet.Left()) { 348 offset.SetX(std::max(0.0, std::min(magnet.Left() - Left(), magnet.Right() - Right()))); 349 } else if (Right() > magnet.Right()) { 350 offset.SetX(std::min(0.0, std::max(magnet.Left() - Left(), magnet.Right() - Right()))); 351 } 352 353 if (Top() < magnet.Top()) { 354 offset.SetY(std::max(0.0, std::min(magnet.Top() - Top(), magnet.Bottom() - Bottom()))); 355 } else if (Bottom() > magnet.Bottom()) { 356 offset.SetY(std::min(0.0, std::max(magnet.Top() - Top(), magnet.Bottom() - Bottom()))); 357 } 358 359 *this += offset; 360 361 return offset; 362 } 363 ToString()364 std::string ToString() const 365 { 366 std::stringstream ss; 367 ss << "Rect (" << std::fixed << std::setprecision(2) << x_ << ", " << y_ << ") - ["; 368 if (NearEqual(width_, Size::INFINITE_SIZE)) { 369 ss << "INFINITE"; 370 } else { 371 ss << width_; 372 } 373 ss << " x "; 374 if (NearEqual(height_, Size::INFINITE_SIZE)) { 375 ss << "INFINITE"; 376 } else { 377 ss << height_; 378 } 379 ss << "]"; 380 std::string output = ss.str(); 381 return output; 382 } 383 ToBounds()384 std::string ToBounds() const 385 { 386 std::stringstream ss; 387 ss << "[" << std::fixed << std::setprecision(2) << x_ << ", " << y_ << "]["; 388 if (NearEqual(width_, Size::INFINITE_SIZE)) { 389 ss << "INFINITE"; 390 } else { 391 ss << (x_ + width_); 392 } 393 ss << ","; 394 if (NearEqual(height_, Size::INFINITE_SIZE)) { 395 ss << "INFINITE"; 396 } else { 397 ss << (y_ + height_); 398 } 399 ss << "]"; 400 std::string output = ss.str(); 401 return output; 402 } 403 Center()404 Offset Center() const 405 { 406 return Offset(width_ / 2.0 + x_, height_ / 2.0 + y_); 407 } 408 409 private: 410 double x_ = 0.0; 411 double y_ = 0.0; 412 double width_ = 0.0; 413 double height_ = 0.0; 414 }; 415 416 } // namespace OHOS::Ace 417 418 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_GEOMETRY_RECT_H 419