1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *       http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17@file:Suppress("NOTHING_TO_INLINE")
18
19package androidx.core.graphics
20
21import android.annotation.SuppressLint
22import android.graphics.Matrix
23import android.graphics.Point
24import android.graphics.PointF
25import android.graphics.Rect
26import android.graphics.RectF
27import android.graphics.Region
28
29/**
30 * Returns "left", the first component of the rectangle.
31 *
32 * This method allows to use destructuring declarations when working with rectangles,
33 * for example:
34 * ```
35 * val (left, top, right, bottom) = myRectangle
36 * ```
37 */
38inline operator fun Rect.component1() = this.left
39
40/**
41 * Returns "top", the second component of the rectangle.
42 *
43 * This method allows to use destructuring declarations when working with rectangles,
44 * for example:
45 * ```
46 * val (left, top, right, bottom) = myRectangle
47 * ```
48 */
49inline operator fun Rect.component2() = this.top
50
51/**
52 * Returns "right", the third component of the rectangle.
53 *
54 * This method allows to use destructuring declarations when working with rectangles,
55 * for example:
56 * ```
57 * val (left, top, right, bottom) = myRectangle
58 * ```
59 */
60inline operator fun Rect.component3() = this.right
61
62/**
63 * Returns "bottom", the fourth component of the rectangle.
64 *
65 * This method allows to use destructuring declarations when working with rectangles,
66 * for example:
67 * ```
68 * val (left, top, right, bottom) = myRectangle
69 * ```
70 */
71inline operator fun Rect.component4() = this.bottom
72
73/**
74 * Returns "left", the first component of the rectangle.
75 *
76 * This method allows to use destructuring declarations when working with rectangles,
77 * for example:
78 * ```
79 * val (left, top, right, bottom) = myRectangle
80 * ```
81 */
82inline operator fun RectF.component1() = this.left
83
84/**
85 * Returns "top", the second component of the rectangle.
86 *
87 * This method allows to use destructuring declarations when working with rectangles,
88 * for example:
89 * ```
90 * val (left, top, right, bottom) = myRectangle
91 * ```
92 */
93inline operator fun RectF.component2() = this.top
94
95/**
96 * Returns "right", the third component of the rectangle.
97 *
98 * This method allows to use destructuring declarations when working with rectangles,
99 * for example:
100 * ```
101 * val (left, top, right, bottom) = myRectangle
102 * ```
103 */
104inline operator fun RectF.component3() = this.right
105
106/**
107 * Returns "bottom", the fourth component of the rectangle.
108 *
109 * This method allows to use destructuring declarations when working with rectangles,
110 * for example:
111 * ```
112 * val (left, top, right, bottom) = myRectangle
113 * ```
114 */
115inline operator fun RectF.component4() = this.bottom
116
117/**
118 * Performs the union of this rectangle and the specified rectangle and returns
119 * the result as a new rectangle.
120 */
121inline operator fun Rect.plus(r: Rect): Rect {
122    return Rect(this).apply {
123        union(r)
124    }
125}
126
127/**
128 * Performs the union of this rectangle and the specified rectangle and returns
129 * the result as a new rectangle.
130 */
131inline operator fun RectF.plus(r: RectF): RectF {
132    return RectF(this).apply {
133        union(r)
134    }
135}
136
137/**
138 * Returns a new rectangle representing this rectangle offset by the specified
139 * amount on both X and Y axis.
140 */
141inline operator fun Rect.plus(xy: Int): Rect {
142    return Rect(this).apply {
143        offset(xy, xy)
144    }
145}
146
147/**
148 * Returns a new rectangle representing this rectangle offset by the specified
149 * amount on both X and Y axis.
150 */
151inline operator fun RectF.plus(xy: Float): RectF {
152    return RectF(this).apply {
153        offset(xy, xy)
154    }
155}
156
157/**
158 * Returns a new rectangle representing this rectangle offset by the specified
159 * point.
160 */
161inline operator fun Rect.plus(xy: Point): Rect {
162    return Rect(this).apply {
163        offset(xy.x, xy.y)
164    }
165}
166
167/**
168 * Returns a new rectangle representing this rectangle offset by the specified
169 * point.
170 */
171inline operator fun RectF.plus(xy: PointF): RectF {
172    return RectF(this).apply {
173        offset(xy.x, xy.y)
174    }
175}
176
177/**
178 * Returns the difference of this rectangle and the specified rectangle as a new region.
179 */
180inline operator fun Rect.minus(r: Rect): Region {
181    return Region(this).apply {
182        op(r, Region.Op.DIFFERENCE)
183    }
184}
185
186/**
187 * Returns the difference of this rectangle and the specified rectangle as a new region.
188 * This rectangle is first converted to a [Rect] using [RectF.toRect].
189 */
190inline operator fun RectF.minus(r: RectF): Region {
191    return Region(this.toRect()).apply {
192        op(r.toRect(), Region.Op.DIFFERENCE)
193    }
194}
195
196/**
197 * Returns a new rectangle representing this rectangle offset by the negation
198 * of the specified amount on both X and Y axis.
199 */
200inline operator fun Rect.minus(xy: Int): Rect {
201    return Rect(this).apply {
202        offset(-xy, -xy)
203    }
204}
205
206/**
207 * Returns a new rectangle representing this rectangle offset by the negation
208 * of the specified amount on both X and Y axis.
209 */
210inline operator fun RectF.minus(xy: Float): RectF {
211    return RectF(this).apply {
212        offset(-xy, -xy)
213    }
214}
215
216/**
217 * Returns a new rectangle representing this rectangle offset by the negation of
218 * the specified point.
219 */
220inline operator fun Rect.minus(xy: Point): Rect {
221    return Rect(this).apply {
222        offset(-xy.x, -xy.y)
223    }
224}
225
226/**
227 * Returns a new rectangle representing this rectangle offset by the negation of
228 * the specified point.
229 */
230inline operator fun RectF.minus(xy: PointF): RectF {
231    return RectF(this).apply {
232        offset(-xy.x, -xy.y)
233    }
234}
235
236/**
237 * Returns the union of two rectangles as a new rectangle.
238 */
239inline infix fun Rect.and(r: Rect) = this + r
240
241/**
242 * Returns the union of two rectangles as a new rectangle.
243 */
244inline infix fun RectF.and(r: RectF) = this + r
245
246/**
247 * Returns the intersection of two rectangles as a new rectangle.
248 * If the rectangles do not intersect, returns a copy of the left hand side
249 * rectangle.
250 */
251@SuppressLint("CheckResult")
252inline infix fun Rect.or(r: Rect): Rect {
253    return Rect(this).apply {
254        intersect(r)
255    }
256}
257
258/**
259 * Returns the intersection of two rectangles as a new rectangle.
260 * If the rectangles do not intersect, returns a copy of the left hand side
261 * rectangle.
262 */
263@SuppressLint("CheckResult")
264inline infix fun RectF.or(r: RectF): RectF {
265    return RectF(this).apply {
266        intersect(r)
267    }
268}
269
270/**
271 * Returns the union minus the intersection of two rectangles as a new region.
272 */
273inline infix fun Rect.xor(r: Rect): Region {
274    return Region(this).apply {
275        op(r, Region.Op.XOR)
276    }
277}
278
279/**
280 * Returns the union minus the intersection of two rectangles as a new region.
281 * The two rectangles are first converted to [Rect] using [RectF.toRect].
282 */
283inline infix fun RectF.xor(r: RectF): Region {
284    return Region(this.toRect()).apply {
285        op(r.toRect(), Region.Op.XOR)
286    }
287}
288
289/**
290 * Returns true if the specified point is inside the rectangle.
291 * The left and top are considered to be inside, while the right and bottom are not.
292 * This means that for a point to be contained: left <= x < right and top <= y < bottom.
293 * An empty rectangle never contains any point.
294 */
295inline operator fun Rect.contains(p: Point) = contains(p.x, p.y)
296
297/**
298 * Returns true if the specified point is inside the rectangle.
299 * The left and top are considered to be inside, while the right and bottom are not.
300 * This means that for a point to be contained: left <= x < right and top <= y < bottom.
301 * An empty rectangle never contains any point.
302 */
303inline operator fun RectF.contains(p: PointF) = contains(p.x, p.y)
304
305/**
306 * Returns a [RectF] representation of this rectangle.
307 */
308inline fun Rect.toRectF(): RectF = RectF(this)
309
310/**
311 * Returns a [Rect] representation of this rectangle. The resulting rect will be sized such
312 * that this rect can fit within it.
313 */
314inline fun RectF.toRect(): Rect {
315    val r = Rect()
316    roundOut(r)
317    return r
318}
319
320/**
321 * Returns a [Region] representation of this rectangle.
322 */
323inline fun Rect.toRegion() = Region(this)
324
325/**
326 * Returns a [Region] representation of this rectangle. The resulting rect will be sized such
327 * that this rect can fit within it.
328 */
329inline fun RectF.toRegion() = Region(this.toRect())
330
331/**
332 * Transform this rectangle in place using the supplied [Matrix] and returns
333 * this rectangle.
334 */
335inline fun RectF.transform(m: Matrix) = apply { m.mapRect(this@transform) }
336