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