1 /* 2 * Copyright 2020 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 19 package androidx.compose.ui.geometry 20 21 import kotlin.math.absoluteValue 22 import kotlin.math.max 23 import kotlin.math.min 24 25 /** 26 * An mutable, 2D, axis-aligned, floating-point rectangle whose coordinates are relative to a given 27 * origin. 28 * 29 * @param left The offset of the left edge of this rectangle from the x axis. 30 * @param top The offset of the top edge of this rectangle from the y axis. 31 * @param right The offset of the right edge of this rectangle from the x axis. 32 * @param bottom The offset of the bottom edge of this rectangle from the y axis. 33 */ 34 class MutableRect(var left: Float, var top: Float, var right: Float, var bottom: Float) { 35 /** The distance between the left and right edges of this rectangle. */ 36 inline val width: Float 37 get() = right - left 38 39 /** The distance between the top and bottom edges of this rectangle. */ 40 inline val height: Float 41 get() = bottom - top 42 43 /** The distance between the upper-left corner and the lower-right corner of this rectangle. */ 44 val size: Size 45 get() = Size(width, height) 46 47 /** Whether any of the coordinates of this rectangle are equal to positive infinity. */ 48 // included for consistency with Offset and Size 49 val isInfinite: Boolean 50 get() = 51 (left == Float.POSITIVE_INFINITY) or 52 (top == Float.POSITIVE_INFINITY) or 53 (right == Float.POSITIVE_INFINITY) or 54 (bottom == Float.POSITIVE_INFINITY) 55 56 /** Whether all coordinates of this rectangle are finite. */ 57 val isFinite: Boolean 58 get() = 59 ((left.toRawBits() and 0x7fffffff) < FloatInfinityBase) and 60 ((top.toRawBits() and 0x7fffffff) < FloatInfinityBase) and 61 ((right.toRawBits() and 0x7fffffff) < FloatInfinityBase) and 62 ((bottom.toRawBits() and 0x7fffffff) < FloatInfinityBase) 63 64 /** Whether this rectangle encloses a non-zero area. Negative areas are considered empty. */ 65 val isEmpty: Boolean 66 get() = (left >= right) or (top >= bottom) 67 68 /** Translates the rect by the provided [Offset]. */ translatenull69 fun translate(offset: Offset) = translate(offset.x, offset.y) 70 71 /** 72 * Updates this rectangle with translateX added to the x components and translateY added to the 73 * y components. 74 */ 75 fun translate(translateX: Float, translateY: Float) { 76 left += translateX 77 top += translateY 78 right += translateX 79 bottom += translateY 80 } 81 82 /** Moves edges outwards by the given delta. */ inflatenull83 fun inflate(delta: Float) { 84 left -= delta 85 top -= delta 86 right += delta 87 bottom += delta 88 } 89 90 /** Moves edges inwards by the given delta. */ deflatenull91 fun deflate(delta: Float) = inflate(-delta) 92 93 /** 94 * Modifies `this` to be the intersection of this and the rect formed by [left], [top], [right], 95 * and [bottom]. 96 */ 97 fun intersect(left: Float, top: Float, right: Float, bottom: Float) { 98 this.left = max(left, this.left) 99 this.top = max(top, this.top) 100 this.right = min(right, this.right) 101 this.bottom = min(bottom, this.bottom) 102 } 103 104 /** Whether `other` has a nonzero area of overlap with this rectangle. */ overlapsnull105 fun overlaps(other: Rect): Boolean { 106 return (left < other.right) and 107 (other.left < right) and 108 (top < other.bottom) and 109 (other.top < bottom) 110 } 111 112 /** Whether `other` has a nonzero area of overlap with this rectangle. */ overlapsnull113 fun overlaps(other: MutableRect): Boolean { 114 if (right <= other.left || other.right <= left) return false 115 if (bottom <= other.top || other.bottom <= top) return false 116 return true 117 } 118 119 /** The lesser of the magnitudes of the [width] and the [height] of this rectangle. */ 120 val minDimension: Float 121 get() = min(width.absoluteValue, height.absoluteValue) 122 123 /** The greater of the magnitudes of the [width] and the [height] of this rectangle. */ 124 val maxDimension: Float 125 get() = max(width.absoluteValue, height.absoluteValue) 126 127 /** The offset to the intersection of the top and left edges of this rectangle. */ 128 val topLeft: Offset 129 get() = Offset(left, top) 130 131 /** The offset to the center of the top edge of this rectangle. */ 132 val topCenter: Offset 133 get() = Offset(left + width / 2.0f, top) 134 135 /** The offset to the intersection of the top and right edges of this rectangle. */ 136 val topRight: Offset 137 get() = Offset(right, top) 138 139 /** The offset to the center of the left edge of this rectangle. */ 140 val centerLeft: Offset 141 get() = Offset(left, top + height / 2.0f) 142 143 /** 144 * The offset to the point halfway between the left and right and the top and bottom edges of 145 * this rectangle. 146 * 147 * See also [Size.center]. 148 */ 149 val center: Offset 150 get() = Offset(left + width / 2.0f, top + height / 2.0f) 151 152 /** The offset to the center of the right edge of this rectangle. */ 153 val centerRight: Offset 154 get() = Offset(right, top + height / 2.0f) 155 156 /** The offset to the intersection of the bottom and left edges of this rectangle. */ 157 val bottomLeft: Offset 158 get() = Offset(left, bottom) 159 160 /** The offset to the center of the bottom edge of this rectangle. */ 161 val bottomCenter: Offset 162 get() { 163 return Offset(left + width / 2.0f, bottom) 164 } 165 166 /** The offset to the intersection of the bottom and right edges of this rectangle. */ 167 val bottomRight: Offset 168 get() { 169 return Offset(right, bottom) 170 } 171 172 /** 173 * Whether the point specified by the given offset (which is assumed to be relative to the 174 * origin) lies between the left and right and the top and bottom edges of this rectangle. 175 * 176 * Rectangles include their top and left edges but exclude their bottom and right edges. 177 */ containsnull178 operator fun contains(offset: Offset): Boolean { 179 val x = offset.x 180 val y = offset.y 181 return (x >= left) and (x < right) and (y >= top) and (y < bottom) 182 } 183 184 /** Sets new bounds to ([left], [top], [right], [bottom]) */ setnull185 fun set(left: Float, top: Float, right: Float, bottom: Float) { 186 this.left = left 187 this.top = top 188 this.right = right 189 this.bottom = bottom 190 } 191 toStringnull192 override fun toString() = 193 "MutableRect(" + 194 "${left.toStringAsFixed(1)}, " + 195 "${top.toStringAsFixed(1)}, " + 196 "${right.toStringAsFixed(1)}, " + 197 "${bottom.toStringAsFixed(1)})" 198 } 199 200 fun MutableRect.toRect(): Rect = Rect(left, top, right, bottom) 201 202 /** 203 * Construct a rectangle from its left and top edges as well as its width and height. 204 * 205 * @param offset Offset to represent the top and left parameters of the Rect 206 * @param size Size to determine the width and height of this [Rect]. 207 * @return Rect with [Rect.left] and [Rect.top] configured to [Offset.x] and [Offset.y] as 208 * [Rect.right] and [Rect.bottom] to [Offset.x] + [Size.width] and [Offset.y] + [Size.height] 209 * respectively 210 */ 211 fun MutableRect(offset: Offset, size: Size): MutableRect = 212 MutableRect(offset.x, offset.y, offset.x + size.width, offset.y + size.height) 213 214 /** 215 * Construct the smallest rectangle that encloses the given offsets, treating them as vectors from 216 * the origin. 217 * 218 * @param topLeft Offset representing the left and top edges of the rectangle 219 * @param bottomRight Offset representing the bottom and right edges of the rectangle 220 */ 221 fun MutableRect(topLeft: Offset, bottomRight: Offset): MutableRect = 222 MutableRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y) 223 224 /** 225 * Construct a rectangle that bounds the given circle 226 * 227 * @param center Offset that represents the center of the circle 228 * @param radius Radius of the circle to enclose 229 */ 230 fun MutableRect(center: Offset, radius: Float): MutableRect = 231 MutableRect(center.x - radius, center.y - radius, center.x + radius, center.y + radius) 232