1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits>
6
7#include "base/logging.h"
8#include "base/strings/stringprintf.h"
9#include "ui/gfx/geometry/rect_base.h"
10
11// This file provides the implementation for RectBaese template and
12// used to instantiate the base class for Rect and RectF classes.
13#if !defined(GFX_IMPLEMENTATION)
14#error "This file is intended for UI implementation only"
15#endif
16
17namespace {
18
19template<typename Type>
20void AdjustAlongAxis(Type dst_origin, Type dst_size, Type* origin, Type* size) {
21  *size = std::min(dst_size, *size);
22  if (*origin < dst_origin)
23    *origin = dst_origin;
24  else
25    *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
26}
27
28} // namespace
29
30namespace gfx {
31
32template<typename Class,
33         typename PointClass,
34         typename SizeClass,
35         typename InsetsClass,
36         typename VectorClass,
37         typename Type>
38void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
39    SetRect(Type x, Type y, Type width, Type height) {
40  origin_.SetPoint(x, y);
41  set_width(width);
42  set_height(height);
43}
44
45template<typename Class,
46         typename PointClass,
47         typename SizeClass,
48         typename InsetsClass,
49         typename VectorClass,
50         typename Type>
51void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
52    Inset(const InsetsClass& insets) {
53  Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
54}
55
56template<typename Class,
57         typename PointClass,
58         typename SizeClass,
59         typename InsetsClass,
60         typename VectorClass,
61         typename Type>
62void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
63    Inset(Type left, Type top, Type right, Type bottom) {
64  origin_ += VectorClass(left, top);
65  set_width(std::max(width() - left - right, static_cast<Type>(0)));
66  set_height(std::max(height() - top - bottom, static_cast<Type>(0)));
67}
68
69template<typename Class,
70         typename PointClass,
71         typename SizeClass,
72         typename InsetsClass,
73         typename VectorClass,
74         typename Type>
75void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
76    Offset(Type horizontal, Type vertical) {
77  origin_ += VectorClass(horizontal, vertical);
78}
79
80template<typename Class,
81         typename PointClass,
82         typename SizeClass,
83         typename InsetsClass,
84         typename VectorClass,
85         typename Type>
86void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
87    operator+=(const VectorClass& offset) {
88  origin_ += offset;
89}
90
91template<typename Class,
92         typename PointClass,
93         typename SizeClass,
94         typename InsetsClass,
95         typename VectorClass,
96         typename Type>
97void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
98    operator-=(const VectorClass& offset) {
99  origin_ -= offset;
100}
101
102template<typename Class,
103         typename PointClass,
104         typename SizeClass,
105         typename InsetsClass,
106         typename VectorClass,
107         typename Type>
108bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
109    operator<(const Class& other) const {
110  if (origin_ == other.origin_) {
111    if (width() == other.width()) {
112      return height() < other.height();
113    } else {
114      return width() < other.width();
115    }
116  } else {
117    return origin_ < other.origin_;
118  }
119}
120
121template<typename Class,
122         typename PointClass,
123         typename SizeClass,
124         typename InsetsClass,
125         typename VectorClass,
126         typename Type>
127bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
128    Contains(Type point_x, Type point_y) const {
129  return (point_x >= x()) && (point_x < right()) &&
130         (point_y >= y()) && (point_y < bottom());
131}
132
133template<typename Class,
134         typename PointClass,
135         typename SizeClass,
136         typename InsetsClass,
137         typename VectorClass,
138         typename Type>
139bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
140    Contains(const Class& rect) const {
141  return (rect.x() >= x() && rect.right() <= right() &&
142          rect.y() >= y() && rect.bottom() <= bottom());
143}
144
145template<typename Class,
146         typename PointClass,
147         typename SizeClass,
148         typename InsetsClass,
149         typename VectorClass,
150         typename Type>
151bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
152    Intersects(const Class& rect) const {
153  return !(IsEmpty() || rect.IsEmpty() ||
154           rect.x() >= right() || rect.right() <= x() ||
155           rect.y() >= bottom() || rect.bottom() <= y());
156}
157
158template<typename Class,
159         typename PointClass,
160         typename SizeClass,
161         typename InsetsClass,
162         typename VectorClass,
163         typename Type>
164void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
165    Intersect(const Class& rect) {
166  if (IsEmpty() || rect.IsEmpty()) {
167    SetRect(0, 0, 0, 0);
168    return;
169  }
170
171  Type rx = std::max(x(), rect.x());
172  Type ry = std::max(y(), rect.y());
173  Type rr = std::min(right(), rect.right());
174  Type rb = std::min(bottom(), rect.bottom());
175
176  if (rx >= rr || ry >= rb)
177    rx = ry = rr = rb = 0;  // non-intersecting
178
179  SetRect(rx, ry, rr - rx, rb - ry);
180}
181
182template<typename Class,
183         typename PointClass,
184         typename SizeClass,
185         typename InsetsClass,
186         typename VectorClass,
187         typename Type>
188void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
189    Union(const Class& rect) {
190  if (IsEmpty()) {
191    *this = rect;
192    return;
193  }
194  if (rect.IsEmpty())
195    return;
196
197  Type rx = std::min(x(), rect.x());
198  Type ry = std::min(y(), rect.y());
199  Type rr = std::max(right(), rect.right());
200  Type rb = std::max(bottom(), rect.bottom());
201
202  SetRect(rx, ry, rr - rx, rb - ry);
203}
204
205template<typename Class,
206         typename PointClass,
207         typename SizeClass,
208         typename InsetsClass,
209         typename VectorClass,
210         typename Type>
211void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
212    Subtract(const Class& rect) {
213  if (!Intersects(rect))
214    return;
215  if (rect.Contains(*static_cast<const Class*>(this))) {
216    SetRect(0, 0, 0, 0);
217    return;
218  }
219
220  Type rx = x();
221  Type ry = y();
222  Type rr = right();
223  Type rb = bottom();
224
225  if (rect.y() <= y() && rect.bottom() >= bottom()) {
226    // complete intersection in the y-direction
227    if (rect.x() <= x()) {
228      rx = rect.right();
229    } else if (rect.right() >= right()) {
230      rr = rect.x();
231    }
232  } else if (rect.x() <= x() && rect.right() >= right()) {
233    // complete intersection in the x-direction
234    if (rect.y() <= y()) {
235      ry = rect.bottom();
236    } else if (rect.bottom() >= bottom()) {
237      rb = rect.y();
238    }
239  }
240  SetRect(rx, ry, rr - rx, rb - ry);
241}
242
243template<typename Class,
244         typename PointClass,
245         typename SizeClass,
246         typename InsetsClass,
247         typename VectorClass,
248         typename Type>
249void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
250    AdjustToFit(const Class& rect) {
251  Type new_x = x();
252  Type new_y = y();
253  Type new_width = width();
254  Type new_height = height();
255  AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
256  AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
257  SetRect(new_x, new_y, new_width, new_height);
258}
259
260template<typename Class,
261         typename PointClass,
262         typename SizeClass,
263         typename InsetsClass,
264         typename VectorClass,
265         typename Type>
266PointClass RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass,
267    Type>::CenterPoint() const {
268  return PointClass(x() + width() / 2, y() + height() / 2);
269}
270
271template<typename Class,
272         typename PointClass,
273         typename SizeClass,
274         typename InsetsClass,
275         typename VectorClass,
276         typename Type>
277void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
278    ClampToCenteredSize(const SizeClass& size) {
279  Type new_width = std::min(width(), size.width());
280  Type new_height = std::min(height(), size.height());
281  Type new_x = x() + (width() - new_width) / 2;
282  Type new_y = y() + (height() - new_height) / 2;
283  SetRect(new_x, new_y, new_width, new_height);
284}
285
286template<typename Class,
287         typename PointClass,
288         typename SizeClass,
289         typename InsetsClass,
290         typename VectorClass,
291         typename Type>
292void RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
293    SplitVertically(Class* left_half, Class* right_half) const {
294  DCHECK(left_half);
295  DCHECK(right_half);
296
297  left_half->SetRect(x(), y(), width() / 2, height());
298  right_half->SetRect(left_half->right(),
299                      y(),
300                      width() - left_half->width(),
301                      height());
302}
303
304template<typename Class,
305         typename PointClass,
306         typename SizeClass,
307         typename InsetsClass,
308         typename VectorClass,
309         typename Type>
310bool RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
311    SharesEdgeWith(const Class& rect) const {
312  return (y() == rect.y() && height() == rect.height() &&
313             (x() == rect.right() || right() == rect.x())) ||
314         (x() == rect.x() && width() == rect.width() &&
315             (y() == rect.bottom() || bottom() == rect.y()));
316}
317
318template<typename Class,
319         typename PointClass,
320         typename SizeClass,
321         typename InsetsClass,
322         typename VectorClass,
323         typename Type>
324Type RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
325    ManhattanDistanceToPoint(const PointClass& point) const {
326  Type x_distance = std::max<Type>(0, std::max(
327      x() - point.x(), point.x() - right()));
328  Type y_distance = std::max<Type>(0, std::max(
329      y() - point.y(), point.y() - bottom()));
330
331  return x_distance + y_distance;
332}
333
334template<typename Class,
335         typename PointClass,
336         typename SizeClass,
337         typename InsetsClass,
338         typename VectorClass,
339         typename Type>
340Type RectBase<Class, PointClass, SizeClass, InsetsClass, VectorClass, Type>::
341    ManhattanInternalDistance(const Class& rect) const {
342  Class c(x(), y(), width(), height());
343  c.Union(rect);
344
345  static const Type kEpsilon = std::numeric_limits<Type>::is_integer
346                                   ? 1
347                                   : std::numeric_limits<Type>::epsilon();
348
349  Type x = std::max<Type>(0, c.width() - width() - rect.width() + kEpsilon);
350  Type y = std::max<Type>(0, c.height() - height() - rect.height() + kEpsilon);
351  return x + y;
352}
353
354}  // namespace gfx
355