commonMain.androidx.compose.ui.geometry.Rect.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui-geometry Show documentation
Show all versions of ui-geometry Show documentation
Compose classes related to dimensions without units
The newest version!
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE")
package androidx.compose.ui.geometry
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.util.lerp
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min
// TODO(mount): Normalize this class. There are many methods that can be extension functions.
/**
* An immutable, 2D, axis-aligned, floating-point rectangle whose coordinates are relative to a
* given origin.
*/
@Immutable
data class Rect(
/** The offset of the left edge of this rectangle from the x axis. */
@Stable val left: Float,
/** The offset of the top edge of this rectangle from the y axis. */
@Stable val top: Float,
/** The offset of the right edge of this rectangle from the x axis. */
@Stable val right: Float,
/** The offset of the bottom edge of this rectangle from the y axis. */
@Stable val bottom: Float
) {
companion object {
/** A rectangle with left, top, right, and bottom edges all at zero. */
@Stable val Zero: Rect = Rect(0.0f, 0.0f, 0.0f, 0.0f)
}
/** The distance between the left and right edges of this rectangle. */
@Stable
inline val width: Float
get() = right - left
/** The distance between the top and bottom edges of this rectangle. */
@Stable
inline val height: Float
get() = bottom - top
/** The distance between the upper-left corner and the lower-right corner of this rectangle. */
@Stable
val size: Size
get() = Size(width, height)
/** Whether any of the coordinates of this rectangle are equal to positive infinity. */
// included for consistency with Offset and Size
@Stable
val isInfinite: Boolean
get() =
(left == Float.POSITIVE_INFINITY) or
(top == Float.POSITIVE_INFINITY) or
(right == Float.POSITIVE_INFINITY) or
(bottom == Float.POSITIVE_INFINITY)
/** Whether all coordinates of this rectangle are finite. */
@Stable
val isFinite: Boolean
get() =
((left.toRawBits() and 0x7fffffff) < FloatInfinityBase) and
((top.toRawBits() and 0x7fffffff) < FloatInfinityBase) and
((right.toRawBits() and 0x7fffffff) < FloatInfinityBase) and
((bottom.toRawBits() and 0x7fffffff) < FloatInfinityBase)
/** Whether this rectangle encloses a non-zero area. Negative areas are considered empty. */
@Stable
val isEmpty: Boolean
get() = (left >= right) or (top >= bottom)
/**
* Returns a new rectangle translated by the given offset.
*
* To translate a rectangle by separate x and y components rather than by an [Offset], consider
* [translate].
*/
@Stable
fun translate(offset: Offset): Rect {
return Rect(left + offset.x, top + offset.y, right + offset.x, bottom + offset.y)
}
/**
* Returns a new rectangle with translateX added to the x components and translateY added to the
* y components.
*/
@Stable
fun translate(translateX: Float, translateY: Float): Rect {
return Rect(left + translateX, top + translateY, right + translateX, bottom + translateY)
}
/** Returns a new rectangle with edges moved outwards by the given delta. */
@Stable
fun inflate(delta: Float): Rect {
return Rect(left - delta, top - delta, right + delta, bottom + delta)
}
/** Returns a new rectangle with edges moved inwards by the given delta. */
@Stable fun deflate(delta: Float): Rect = inflate(-delta)
/**
* Returns a new rectangle that is the intersection of the given rectangle and this rectangle.
* The two rectangles must overlap for this to be meaningful. If the two rectangles do not
* overlap, then the resulting Rect will have a negative width or height.
*/
@Stable
fun intersect(other: Rect): Rect {
return Rect(
max(left, other.left),
max(top, other.top),
min(right, other.right),
min(bottom, other.bottom)
)
}
/**
* Returns a new rectangle that is the intersection of the given rectangle and this rectangle.
* The two rectangles must overlap for this to be meaningful. If the two rectangles do not
* overlap, then the resulting Rect will have a negative width or height.
*/
@Stable
fun intersect(otherLeft: Float, otherTop: Float, otherRight: Float, otherBottom: Float): Rect {
return Rect(
max(left, otherLeft),
max(top, otherTop),
min(right, otherRight),
min(bottom, otherBottom)
)
}
/** Whether `other` has a nonzero area of overlap with this rectangle. */
fun overlaps(other: Rect): Boolean {
return (left < other.right) and
(other.left < right) and
(top < other.bottom) and
(other.top < bottom)
}
/** The lesser of the magnitudes of the [width] and the [height] of this rectangle. */
val minDimension: Float
get() = min(width.absoluteValue, height.absoluteValue)
/** The greater of the magnitudes of the [width] and the [height] of this rectangle. */
val maxDimension: Float
get() = max(width.absoluteValue, height.absoluteValue)
/** The offset to the intersection of the top and left edges of this rectangle. */
val topLeft: Offset
get() = Offset(left, top)
/** The offset to the center of the top edge of this rectangle. */
val topCenter: Offset
get() = Offset(left + width / 2.0f, top)
/** The offset to the intersection of the top and right edges of this rectangle. */
val topRight: Offset
get() = Offset(right, top)
/** The offset to the center of the left edge of this rectangle. */
val centerLeft: Offset
get() = Offset(left, top + height / 2.0f)
/**
* The offset to the point halfway between the left and right and the top and bottom edges of
* this rectangle.
*
* See also [Size.center].
*/
val center: Offset
get() = Offset(left + width / 2.0f, top + height / 2.0f)
/** The offset to the center of the right edge of this rectangle. */
val centerRight: Offset
get() = Offset(right, top + height / 2.0f)
/** The offset to the intersection of the bottom and left edges of this rectangle. */
val bottomLeft: Offset
get() = Offset(left, bottom)
/** The offset to the center of the bottom edge of this rectangle. */
val bottomCenter: Offset
get() {
return Offset(left + width / 2.0f, bottom)
}
/** The offset to the intersection of the bottom and right edges of this rectangle. */
val bottomRight: Offset
get() {
return Offset(right, bottom)
}
/**
* Whether the point specified by the given offset (which is assumed to be relative to the
* origin) lies between the left and right and the top and bottom edges of this rectangle.
*
* Rectangles include their top and left edges but exclude their bottom and right edges.
*/
operator fun contains(offset: Offset): Boolean {
val x = offset.x
val y = offset.y
return (x >= left) and (x < right) and (y >= top) and (y < bottom)
}
override fun toString() =
"Rect.fromLTRB(" +
"${left.toStringAsFixed(1)}, " +
"${top.toStringAsFixed(1)}, " +
"${right.toStringAsFixed(1)}, " +
"${bottom.toStringAsFixed(1)})"
}
/**
* Construct a rectangle from its left and top edges as well as its width and height.
*
* @param offset Offset to represent the top and left parameters of the Rect
* @param size Size to determine the width and height of this [Rect].
* @return Rect with [Rect.left] and [Rect.top] configured to [Offset.x] and [Offset.y] as
* [Rect.right] and [Rect.bottom] to [Offset.x] + [Size.width] and [Offset.y] + [Size.height]
* respectively
*/
@Stable
fun Rect(offset: Offset, size: Size): Rect =
Rect(offset.x, offset.y, offset.x + size.width, offset.y + size.height)
/**
* Construct the smallest rectangle that encloses the given offsets, treating them as vectors from
* the origin.
*
* @param topLeft Offset representing the left and top edges of the rectangle
* @param bottomRight Offset representing the bottom and right edges of the rectangle
*/
@Stable
fun Rect(topLeft: Offset, bottomRight: Offset): Rect =
Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y)
/**
* Construct a rectangle that bounds the given circle
*
* @param center Offset that represents the center of the circle
* @param radius Radius of the circle to enclose
*/
@Stable
fun Rect(center: Offset, radius: Float): Rect =
Rect(center.x - radius, center.y - radius, center.x + radius, center.y + radius)
/**
* Linearly interpolate between two rectangles.
*
* The [fraction] argument represents position on the timeline, with 0.0 meaning that the
* interpolation has not started, returning [start] (or something equivalent to [start]), 1.0
* meaning that the interpolation has finished, returning [stop] (or something equivalent to
* [stop]), and values in between meaning that the interpolation is at the relevant point on the
* timeline between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and 1.0, so
* negative values and values greater than 1.0 are valid (and can easily be generated by curves).
*
* Values for [fraction] are usually obtained from an [Animation], such as an
* `AnimationController`.
*/
@Stable
fun lerp(start: Rect, stop: Rect, fraction: Float): Rect =
Rect(
lerp(start.left, stop.left, fraction),
lerp(start.top, stop.top, fraction),
lerp(start.right, stop.right, fraction),
lerp(start.bottom, stop.bottom, fraction)
)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy