commonMain.korlibs.math.geom.MRectangle.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of korma Show documentation
Show all versions of korma Show documentation
Mathematic library for Multiplatform Kotlin 1.3
package korlibs.math.geom
import korlibs.datastructure.*
import korlibs.math.annotations.*
import korlibs.math.internal.*
import korlibs.math.interpolation.*
import korlibs.math.isAlmostEquals
import korlibs.math.roundDecimalPlaces
@KormaMutableApi
@Deprecated("Use Rectangle")
data class MRectangle(
var x: Double, var y: Double,
var width: Double, var height: Double
) : MutableInterpolable, Interpolable, Sizeable, MSizeable {
operator fun contains(that: Point) = contains(that.x, that.y)
operator fun contains(that: MPoint) = contains(that.x, that.y)
operator fun contains(that: MPointInt) = contains(that.x, that.y)
fun contains(x: Double, y: Double) = (x >= left && x < right) && (y >= top && y < bottom)
fun contains(x: Float, y: Float) = contains(x.toDouble(), y.toDouble())
fun contains(x: Int, y: Int) = contains(x.toDouble(), y.toDouble())
val area: Double get() = width * height
val isEmpty: Boolean get() = width == 0.0 && height == 0.0
val isNotEmpty: Boolean get() = width != 0.0 || height != 0.0
val mutable: MRectangle get() = MRectangle(x, y, width, height)
val topLeft: Point get() = Point(left, top)
val topRight: Point get() = Point(right, top)
val bottomLeft: Point get() = Point(left, bottom)
val bottomRight: Point get() = Point(right, bottom)
val center: Point get() = Point((right + left) * 0.5, (bottom + top) * 0.5)
fun isAlmostEquals(other: MRectangle, epsilon: Double = 0.001): Boolean =
this.x.isAlmostEquals(other.x, epsilon) &&
this.y.isAlmostEquals(other.y, epsilon) &&
this.width.isAlmostEquals(other.width, epsilon) &&
this.height.isAlmostEquals(other.height, epsilon)
/**
* Circle that touches or contains all the corners ([topLeft], [topRight], [bottomLeft], [bottomRight]) of the rectangle.
*/
fun outerCircle(): Circle {
return Circle(center, Point.distance(center, topRight))
}
fun without(padding: Margin): MRectangle = MRectangle.fromBounds(
left + padding.left,
top + padding.top,
right - padding.right,
bottom - padding.bottom
)
fun with(margin: Margin): MRectangle = MRectangle.fromBounds(
left - margin.left,
top - margin.top,
right + margin.right,
bottom + margin.bottom
)
infix fun intersects(that: MRectangle): Boolean = intersectsX(that) && intersectsY(that)
infix fun intersectsX(that: MRectangle): Boolean =
that.left <= this.right && that.right >= this.left
infix fun intersectsY(that: MRectangle): Boolean =
that.top <= this.bottom && that.bottom >= this.top
fun intersection(that: MRectangle, target: MRectangle = MRectangle()) =
if (this intersects that) target.setBounds(
kotlin.math.max(this.left, that.left), kotlin.math.max(this.top, that.top),
kotlin.math.min(this.right, that.right), kotlin.math.min(this.bottom, that.bottom)
) else null
companion object {
val POOL: ConcurrentPool = ConcurrentPool({ it.clear() }) { MRectangle() }
// Creates a rectangle from 2 points where the (x,y) is the top left point
// with the same width and height as the point. The 2 points provided can be
// in any arbitrary order, the rectangle will be created from the projected
// rectangle of the 2 points.
//
// Here is one example
// Rect XY point1
// │ │
// ▼ ▼
// ┌────────┐
// │ │
// │ │
// └────────┘
// ▲
// │
// point2
//
// Here is another example
// point1 (Rect XY)
// │
// ▼
// ┌────────┐
// │ │
// │ │
// └────────┘
// ▲
// │
// point2
operator fun invoke(point1: MPoint, point2: MPoint): MRectangle {
val left = minOf(point1.x, point2.x)
val top = minOf(point1.y, point2.y)
val right = maxOf(point1.x, point2.x)
val bottom = maxOf(point1.y, point2.y)
return MRectangle(left, top, right - left, bottom - top)
}
operator fun invoke(): MRectangle = MRectangle(0.0, 0.0, 0.0, 0.0)
operator fun invoke(x: Int, y: Int, width: Int, height: Int): MRectangle = MRectangle(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
operator fun invoke(x: Float, y: Float, width: Float, height: Float): MRectangle = MRectangle(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
operator fun invoke(topLeft: MPoint, size: MSize): MRectangle = MRectangle(topLeft.x, topLeft.y, size.width, size.height)
operator fun invoke(topLeft: Point, size: Size): MRectangle = MRectangle(topLeft.x, topLeft.y, size.width, size.height)
fun fromBounds(left: Double, top: Double, right: Double, bottom: Double): MRectangle = MRectangle().setBounds(left, top, right, bottom)
fun fromBounds(left: Int, top: Int, right: Int, bottom: Int): MRectangle = MRectangle().setBounds(left, top, right, bottom)
fun fromBounds(left: Float, top: Float, right: Float, bottom: Float): MRectangle = MRectangle().setBounds(left, top, right, bottom)
fun fromBounds(point1: MPoint, point2: MPoint): MRectangle = MRectangle(point1, point2)
fun isContainedIn(a: MRectangle, b: MRectangle): Boolean = a.x >= b.x && a.y >= b.y && a.x + a.width <= b.x + b.width && a.y + a.height <= b.y + b.height
}
fun setXY(x: Double, y: Double) {
this.x = x
this.y = y
}
var left: Double ; get() = x; set(value) { width += (x - value); x = value }
var top: Double ; get() = y; set(value) { height += (y - value); y = value }
var right: Double ; get() = x + width ; set(value) { width = value - x }
var bottom: Double ; get() = y + height ; set(value) { height = value - y }
val pos: Point get() = Point(x, y)
val mPosition: MPoint get() = MPoint(x, y)
override val size: Size get() = Size(width, height)
override val mSize: MSize get() = MSize(width, height)
fun setToBounds(left: Double, top: Double, right: Double, bottom: Double): MRectangle = setTo(left, top, right - left, bottom - top)
fun setTo(x: Double, y: Double, width: Double, height: Double): MRectangle {
this.x = x
this.y = y
this.width = width
this.height = height
return this
}
fun setTo(x: Int, y: Int, width: Int, height: Int): MRectangle = setTo(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
fun setTo(x: Float, y: Float, width: Float, height: Float): MRectangle = setTo(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble())
fun copyFrom(that: Rectangle): MRectangle = setTo(that.x, that.y, that.width, that.height)
fun copyFrom(that: MRectangle): MRectangle = setTo(that.x, that.y, that.width, that.height)
fun setBounds(left: Double, top: Double, right: Double, bottom: Double): MRectangle = setTo(left, top, right - left, bottom - top)
fun setBounds(left: Int, top: Int, right: Int, bottom: Int): MRectangle = setBounds(left.toDouble(), top.toDouble(), right.toDouble(), bottom.toDouble())
fun setBounds(left: Float, top: Float, right: Float, bottom: Float): MRectangle = setBounds(left.toDouble(), top.toDouble(), right.toDouble(), bottom.toDouble())
operator fun times(scale: Double): MRectangle = MRectangle(x * scale, y * scale, width * scale, height * scale)
operator fun times(scale: Float): MRectangle = this * scale.toDouble()
operator fun times(scale: Int): MRectangle = this * scale.toDouble()
operator fun div(scale: Double): MRectangle = MRectangle(x / scale, y / scale, width / scale, height / scale)
operator fun div(scale: Float): MRectangle = this / scale.toDouble()
operator fun div(scale: Int): MRectangle = this / scale.toDouble()
operator fun contains(that: MRectangle) = isContainedIn(that, this)
fun setToIntersection(a: MRectangle, b: MRectangle): MRectangle? =
if (a.intersection(b, this) != null) this else null
fun setToUnion(a: MRectangle, b: MRectangle): MRectangle = setToBounds(
kotlin.math.min(a.left, b.left),
kotlin.math.min(a.top, b.top),
kotlin.math.max(a.right, b.right),
kotlin.math.max(a.bottom, b.bottom)
)
infix fun intersection(that: MRectangle) = intersection(that, MRectangle())
fun displaced(dx: Double, dy: Double) = MRectangle(this.x + dx, this.y + dy, width, height)
fun displaced(dx: Float, dy: Float) = displaced(dx.toDouble(), dy.toDouble())
fun displaced(dx: Int, dy: Int) = displaced(dx.toDouble(), dy.toDouble())
fun displace(dx: Double, dy: Double) = setTo(this.x + dx, this.y + dy, this.width, this.height)
fun displace(dx: Float, dy: Float) = displace(dx.toDouble(), dy.toDouble())
fun displace(dx: Int, dy: Int) = displace(dx.toDouble(), dy.toDouble())
fun place(item: MSize, anchor: Anchor, scale: ScaleMode, out: MRectangle = MRectangle()): MRectangle =
place(item.width, item.height, anchor, scale, out)
fun place(
width: Double,
height: Double,
anchor: Anchor,
scale: ScaleMode,
out: MRectangle = MRectangle()
): MRectangle {
val (ow, oh) = scale.transform(Size(width, height), Size(this.width, this.height))
val x = (this.width - ow) * anchor.doubleX
val y = (this.height - oh) * anchor.doubleY
return out.setTo(x, y, ow.toDouble(), oh.toDouble())
}
fun inflate(
left: Double,
top: Double = left,
right: Double = left,
bottom: Double = top
): MRectangle = setBounds(this.left - left, this.top - top, this.right + right, this.bottom + bottom)
fun clear(): MRectangle = setTo(0.0, 0.0, 0.0, 0.0)
fun clone(): MRectangle = MRectangle(x, y, width, height)
fun setToAnchoredRectangle(item: MRectangle, anchor: Anchor, container: MRectangle): MRectangle =
setToAnchoredRectangle(item.mSize, anchor, container)
fun setToAnchoredRectangle(item: MSize, anchor: Anchor, container: MRectangle): MRectangle =
setToAnchoredRectangle(item.immutable, anchor, container)
fun setToAnchoredRectangle(item: Size, anchor: Anchor, container: MRectangle): MRectangle = setTo(
(container.x + anchor.doubleX * (container.width - item.width)).toFloat(),
(container.y + anchor.doubleY * (container.height - item.height)).toFloat(),
item.width,
item.height
)
fun applyTransform(m: MMatrix): MRectangle {
val tl = m.transform(left, top)
val tr = m.transform(right, top)
val bl = m.transform(left, bottom)
val br = m.transform(right, bottom)
val minX = korlibs.math.min(tl.x, tr.x, bl.x, br.x)
val minY = korlibs.math.min(tl.y, tr.y, bl.y, br.y)
val maxX = korlibs.math.max(tl.x, tr.x, bl.x, br.x)
val maxY = korlibs.math.max(tl.y, tr.y, bl.y, br.y)
//val l = m.transformX(left, top)
//val t = m.transformY(left, top)
//val r = m.transformX(right, bottom)
//val b = m.transformY(right, bottom)
return setBounds(minX, minY, maxX, maxY)
}
//override fun toString(): String = "Rectangle([${left.niceStr}, ${top.niceStr}]-[${right.niceStr}, ${bottom.niceStr}])"
override fun toString(): String = "Rectangle(x=${x.niceStr}, y=${y.niceStr}, width=${width.niceStr}, height=${height.niceStr})"
fun toStringBounds(): String = "Rectangle([${left.niceStr},${top.niceStr}]-[${right.niceStr},${bottom.niceStr}])"
fun toStringSize(): String = "Rectangle([${left.niceStr},${top.niceStr}],[${width.niceStr},${height.niceStr}])"
fun toStringCompat(): String = "Rectangle(x=${left.niceStr}, y=${top.niceStr}, w=${width.niceStr}, h=${height.niceStr})"
override fun equals(other: Any?): Boolean = other is MRectangle
&& x.isAlmostEquals(other.x)
&& y.isAlmostEquals(other.y)
&& width.isAlmostEquals(other.width)
&& height.isAlmostEquals(other.height)
override fun interpolateWith(ratio: Ratio, other: MRectangle): MRectangle =
MRectangle().setToInterpolated(ratio, this, other)
override fun setToInterpolated(ratio: Ratio, l: MRectangle, r: MRectangle): MRectangle =
this.setTo(
ratio.interpolate(l.x, r.x),
ratio.interpolate(l.y, r.y),
ratio.interpolate(l.width, r.width),
ratio.interpolate(l.height, r.height)
)
val immutable: Rectangle get() = Rectangle(x, y, width, height)
fun toInt(): MRectangleInt = MRectangleInt(x.toInt(), y.toInt(), width.toInt(), height.toInt())
fun floor(): MRectangle = setTo(
kotlin.math.floor(x),
kotlin.math.floor(y),
kotlin.math.floor(width),
kotlin.math.floor(height)
)
fun round(): MRectangle = setTo(
kotlin.math.round(x),
kotlin.math.round(y),
kotlin.math.round(width),
kotlin.math.round(height)
)
fun roundDecimalPlaces(places: Int): MRectangle = setTo(
x.roundDecimalPlaces(places),
y.roundDecimalPlaces(places),
width.roundDecimalPlaces(places),
height.roundDecimalPlaces(places)
)
fun ceil(): MRectangle = setTo(
kotlin.math.ceil(x),
kotlin.math.ceil(y),
kotlin.math.ceil(width),
kotlin.math.ceil(height)
)
fun normalize() {
if (width < 0.0) {
x += width
width = -width
}
if (height < 0.0) {
y += height
height = -height
}
}
fun expand(left: Float, top: Float, right: Float, bottom: Float): MRectangle =
this.setToBounds(this.left - left, this.top - top, this.right + right, this.bottom + bottom)
fun expand(left: Int, top: Int, right: Int, bottom: Int): MRectangle =
expand(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
fun expand(margin: Margin): MRectangle =
expand(margin.left, margin.top, margin.right, margin.bottom)
fun expand(margin: MarginInt): MRectangle =
expand(margin.left, margin.top, margin.right, margin.bottom)
fun toRectangle(): Rectangle = Rectangle(x, y, width, height)
@KormaMutableApi fun asInt(): MRectangleInt = MRectangleInt(this)
@KormaMutableApi val int: MRectangleInt get() = MRectangleInt(x, y, width, height)
val value: Rectangle get() = Rectangle(x, y, width, height)
}
fun Rectangle.copyTo(out: MRectangle = MRectangle()): MRectangle = out.copyFrom(this)
@KormaMutableApi
fun Iterable.bounds(target: MRectangle = MRectangle()): MRectangle {
var first = true
var left = 0.0
var right = 0.0
var top = 0.0
var bottom = 0.0
for (r in this) {
if (first) {
left = r.left
right = r.right
top = r.top
bottom = r.bottom
first = false
} else {
left = kotlin.math.min(left, r.left)
right = kotlin.math.max(right, r.right)
top = kotlin.math.min(top, r.top)
bottom = kotlin.math.max(bottom, r.bottom)
}
}
return target.setBounds(left, top, right, bottom)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy