[go: nahoru, domu]

blob: 43c6b0bbd56efd55199a860f2dce5adaa9a465c2 [file] [log] [blame]
/*
* 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.
*/
package androidx.compose.ui.geometry
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.util.lerp
import androidx.compose.ui.util.toStringAsFixed
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.
*
* A Rect can be created with one its constructors or from an [Offset] and a
* [Size] using the `&` operator:
*
* ```dart
* Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0);
* ```
*/
@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 {
/** Construct a rectangle from its left, top, right, and bottom edges. */
@Stable
fun fromLTRB(left: Float, top: Float, right: Float, bottom: Float): Rect {
return Rect(left, top, right, bottom)
}
/**
* Construct a rectangle from its left and top edges, its width, and its
* height.
*
* To construct a [Rect] from an [Offset] and a [Size], you can use the
* rectangle constructor operator `&`. See [Offset.&].
*/
@Stable
fun fromLTWH(left: Float, top: Float, width: Float, height: Float): Rect {
return Rect(left, top, left + width, top + height)
}
/**
* Construct a rectangle that bounds the given circle.
*
* The `center` argument is assumed to be an offset from the origin.
*/
@Stable
fun fromCircle(center: Offset, radius: Float): Rect {
return Rect(
center.x - radius,
center.y - radius,
center.x + radius,
center.y + radius
)
}
/**
* Construct the smallest rectangle that encloses the given offsets, treating
* them as vectors from the origin.
*/
@Stable
fun fromPoints(a: Offset, b: Offset): Rect {
return Rect(
min(a.x, b.x),
min(a.y, b.y),
max(a.x, b.x),
max(a.y, b.y)
)
}
/** 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)
val _giantScalar: Float = 1e7f // matches kGiantRect from default_layer_builder.cc
/**
* A rectangle that covers the entire coordinate space.
*
* This covers the space from -1e7,-1e7 to 1e7, 1e7.
* This is the space over which graphics operations are valid.
*/
val largest: Rect =
fromLTRB(
-_giantScalar,
-_giantScalar,
_giantScalar,
_giantScalar
)
}
/** The distance between the left and right edges of this rectangle. */
@Stable
val width = right - left
/** The distance between the top and bottom edges of this rectangle. */
@Stable
val height = bottom - top
// static const int _kDataSize = 4;
// final Float32List _value = new Float32List(_kDataSize);
// double get left => _value[0];
// double get top => _value[1];
//
// double get right => _value[2];
/** The offset of the bottom edge of this rectangle from the y axis. */
// double get bottom => _value[3];
/**
* The distance between the upper-left corner and the lower-right corner of
* this rectangle.
*/
fun getSize() = Size(width, height)
/** Whether any of the coordinates of this rectangle are equal to positive infinity. */
// included for consistency with Offset and Size
@Stable
fun isInfinite(): Boolean {
return left >= Float.POSITIVE_INFINITY ||
top >= Float.POSITIVE_INFINITY ||
right >= Float.POSITIVE_INFINITY ||
bottom >= Float.POSITIVE_INFINITY
}
/** Whether all coordinates of this rectangle are finite. */
@Stable
fun isFinite(): Boolean =
left.isFinite() &&
top.isFinite() &&
right.isFinite() &&
bottom.isFinite()
/**
* Whether this rectangle encloses a non-zero area. Negative areas are
* considered empty.
*/
@Stable
fun isEmpty(): Boolean = left >= right || 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 shift(offset: Offset): Rect {
return fromLTRB(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.
*
* To translate a rectangle by an [Offset] rather than by separate x and y
* components, consider [shift].
*/
@Stable
fun translate(translateX: Float, translateY: Float): Rect {
return fromLTRB(
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 fromLTRB(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 fromLTRB(
max(left, other.left),
max(top, other.top),
min(right, other.right),
min(bottom, other.bottom)
)
}
/**
* Returns a new rectangle which is the bounding box containing this
* rectangle and the given rectangle.
*/
fun expandToInclude(other: Rect): Rect {
return fromLTRB(
min(left, other.left),
min(top, other.top),
max(right, other.right),
max(bottom, other.bottom)
)
}
fun join(other: Rect): Rect {
if (other.isEmpty()) {
// return this if the other params are empty
return this
}
if (isEmpty()) {
// if we are empty, just take other
return other
}
return expandToInclude(other)
}
/** Whether `other` has a nonzero area of overlap with this rectangle. */
fun overlaps(other: Rect): Boolean {
if (right <= other.left || other.right <= left)
return false
if (bottom <= other.top || other.bottom <= top)
return false
return true
}
/**
* 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.
*
* See also [Size.topLeft].
*/
fun getTopLeft(): Offset = Offset(left, top)
/**
* The offset to the center of the top edge of this rectangle.
*
* See also [Size.topCenter].
*/
fun getTopCenter(): Offset = Offset(left + width / 2.0f, top)
/**
* The offset to the intersection of the top and right edges of this rectangle.
*
* See also [Size.topRight].
*/
fun getTopRight(): Offset = Offset(right, top)
/**
* The offset to the center of the left edge of this rectangle.
*
* See also [Size.centerLeft].
*/
fun getCenterLeft(): Offset = 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].
*/
fun getCenter(): Offset = Offset(left + width / 2.0f, top + height / 2.0f)
/**
* The offset to the center of the right edge of this rectangle.
*
* See also [Size.centerLeft].
*/
fun getCenterRight(): Offset = Offset(right, top + height / 2.0f)
/**
* The offset to the intersection of the bottom and left edges of this rectangle.
*
* See also [Size.bottomLeft].
*/
fun getBottomLeft(): Offset = Offset(left, bottom)
/**
* The offset to the center of the bottom edge of this rectangle.
*
* See also [Size.bottomLeft].
*/
fun getBottomCenter(): Offset = Offset(left + width / 2.0f, bottom)
/**
* The offset to the intersection of the bottom and right edges of this rectangle.
*
* See also [Size.bottomRight].
*/
fun getBottomRight(): Offset = 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.
*/
fun contains(offset: Offset): Boolean {
return offset.x >= left && offset.x < right && offset.y >= top && offset.y < bottom
}
override fun toString() = "Rect.fromLTRB(" +
"${left.toStringAsFixed(1)}, " +
"${top.toStringAsFixed(1)}, " +
"${right.toStringAsFixed(1)}, " +
"${bottom.toStringAsFixed(1)})"
}
/**
* 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<Float>], such as
* an `AnimationController`.
*/
@Stable
fun lerp(start: Rect, stop: Rect, fraction: Float): Rect {
return Rect.fromLTRB(
lerp(start.left, stop.left, fraction),
lerp(start.top, stop.top, fraction),
lerp(start.right, stop.right, fraction),
lerp(start.bottom, stop.bottom, fraction)
)
}