1 /*
2 * Copyright (C) 2024 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 package android.tools.datatypes
18
19 import android.graphics.Color
20 import android.graphics.Rect
21 import android.graphics.RectF
22 import android.graphics.Region
23 import androidx.core.graphics.toRect
24 import androidx.core.graphics.toRectF
25 import kotlin.math.abs
26
emptyColornull27 fun emptyColor(): Color = Color.valueOf(/*r */ -1f, /*g */ -1f, /*b */ -1f, /*a */ 0f)
28
29 fun defaultColor(): Color = Color.valueOf(/*r */ 0f, /* g */ 0f, /* b */ 0f, /* a */ 1f)
30
31 fun Color.isEmpty(): Boolean =
32 this.red() == -1f && this.green() == -1f && this.blue() == -1f && this.alpha() == 0f
33
34 fun Color.isNotEmpty(): Boolean = !isEmpty()
35
36 fun Rect.crop(crop: Rect): Rect = this.toRectF().crop(crop.toRectF()).toRect()
37
38 fun RectF.crop(crop: RectF): RectF {
39 val newLeft = maxOf(left, crop.left)
40 val newTop = maxOf(top, crop.top)
41 val newRight = minOf(right, crop.right)
42 val newBottom = minOf(bottom, crop.bottom)
43 return RectF(newLeft, newTop, newRight, newBottom)
44 }
45
RectFnull46 fun RectF.containsWithThreshold(r: RectF, threshold: Float = 0.01f): Boolean {
47 // check for empty first
48 return this.left < this.right &&
49 this.top < this.bottom && // now check for containment
50 (left <= r.left || abs(left - r.left) < threshold) &&
51 (top <= r.top || abs(top - r.top) < threshold) &&
52 (right >= r.right || abs(right - r.right) < threshold) &&
53 (bottom >= r.bottom || abs(bottom - r.bottom) < threshold)
54 }
55
Rectnull56 fun Rect.intersection(r: Rect): Rect = intersection(r.left, r.top, r.right, r.bottom)
57
58 /**
59 * If the rectangle specified by left,top,right,bottom intersects this rectangle, return true and
60 * set this rectangle to that intersection, otherwise return false and do not change this rectangle.
61 * No check is performed to see if either rectangle is empty. Note: To just test for intersection,
62 * use intersects()
63 *
64 * @param left The left side of the rectangle being intersected with this rectangle
65 * @param top The top of the rectangle being intersected with this rectangle
66 * @param right The right side of the rectangle being intersected with this rectangle.
67 * @param bottom The bottom of the rectangle being intersected with this rectangle.
68 * @return A rectangle with the intersection coordinates
69 */
70 fun Rect.intersection(left: Int, top: Int, right: Int, bottom: Int): Rect {
71 if (this.left < right && left < this.right && this.top <= bottom && top <= this.bottom) {
72 var intersectionLeft = this.left
73 var intersectionTop = this.top
74 var intersectionRight = this.right
75 var intersectionBottom = this.bottom
76
77 if (this.left < left) {
78 intersectionLeft = left
79 }
80 if (this.top < top) {
81 intersectionTop = top
82 }
83 if (this.right > right) {
84 intersectionRight = right
85 }
86 if (this.bottom > bottom) {
87 intersectionBottom = bottom
88 }
89 return Rect(intersectionLeft, intersectionTop, intersectionRight, intersectionBottom)
90 }
91 return Rect()
92 }
93
Regionnull94 fun Region.outOfBoundsRegion(testRegion: Region): Region {
95 val testRect = testRegion.bounds
96 val outOfBoundsRegion = Region(this)
97 outOfBoundsRegion.op(testRect, Region.Op.INTERSECT) && outOfBoundsRegion.op(this, Region.Op.XOR)
98 return outOfBoundsRegion
99 }
100
uncoveredRegionnull101 fun Region.uncoveredRegion(testRegion: Region): Region {
102 val uncoveredRegion = Region(this)
103 uncoveredRegion.op(testRegion, Region.Op.INTERSECT) &&
104 uncoveredRegion.op(testRegion, Region.Op.XOR)
105 return uncoveredRegion
106 }
107
coversAtLeastnull108 fun Region.coversAtLeast(testRegion: Region): Boolean {
109 val intersection = Region(this)
110 return intersection.op(testRegion, Region.Op.INTERSECT) &&
111 !intersection.op(testRegion, Region.Op.XOR)
112 }
113
Regionnull114 fun Region.coversAtMost(testRegion: Region): Boolean {
115 if (this.isEmpty) {
116 return true
117 }
118 val testRect = testRegion.bounds
119 val intersection = Region(this)
120 return intersection.op(testRect, Region.Op.INTERSECT) && !intersection.op(this, Region.Op.XOR)
121 }
122
coversMoreThannull123 fun Region.coversMoreThan(testRegion: Region): Boolean {
124 return coversAtLeast(testRegion) && !Region(this).minus(testRegion).isEmpty
125 }
126
minusnull127 fun Region.minus(other: Region): Region {
128 val thisRegion = Region(this)
129 thisRegion.op(other, Region.Op.XOR)
130 return thisRegion
131 }
132