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