
commonMain.korlibs.math.geom.MMatrix.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
The newest version!
package korlibs.math.geom
import korlibs.datastructure.ConcurrentPool
import korlibs.math.annotations.KormaMutableApi
import korlibs.math.interpolation.Interpolable
import korlibs.math.interpolation.MutableInterpolable
import korlibs.math.interpolation.Ratio
import korlibs.math.interpolation.interpolate
import korlibs.math.isAlmostEquals
import kotlin.math.*
val MMatrix?.immutable: Matrix get() = if (this == null) Matrix.NIL else Matrix(a, b, c, d, tx, ty)
@KormaMutableApi
@Deprecated("Use Matrix")
data class MMatrix(
var a: Double = 1.0,
var b: Double = 0.0,
var c: Double = 0.0,
var d: Double = 1.0,
var tx: Double = 0.0,
var ty: Double = 0.0
) : MutableInterpolable, Interpolable {
val immutable: Matrix get() = Matrix(a, b, c, d, tx, ty)
companion object {
val POOL: ConcurrentPool = ConcurrentPool({ it.identity() }) { MMatrix() }
inline operator fun invoke(a: Float, b: Float = 0f, c: Float = 0f, d: Float = 1f, tx: Float = 0f, ty: Float = 0f) =
MMatrix(a.toDouble(), b.toDouble(), c.toDouble(), d.toDouble(), tx.toDouble(), ty.toDouble())
inline operator fun invoke(a: Int, b: Int = 0, c: Int = 0, d: Int = 1, tx: Int = 0, ty: Int = 0) =
MMatrix(a.toDouble(), b.toDouble(), c.toDouble(), d.toDouble(), tx.toDouble(), ty.toDouble())
operator fun invoke(m: MMatrix, out: MMatrix = MMatrix()): MMatrix = out.copyFrom(m)
@Deprecated("Use transform instead")
fun transformXf(a: Float, b: Float, c: Float, d: Float, tx: Float, ty: Float, px: Float, py: Float): Float = a * px + c * py + tx
@Deprecated("Use transform instead")
fun transformYf(a: Float, b: Float, c: Float, d: Float, tx: Float, ty: Float, px: Float, py: Float): Float = d * py + b * px + ty
fun isAlmostEquals(a: MMatrix, b: MMatrix, epsilon: Double = 0.000001): Boolean =
a.tx.isAlmostEquals(b.tx, epsilon)
&& a.ty.isAlmostEquals(b.ty, epsilon)
&& a.a.isAlmostEquals(b.a, epsilon)
&& a.b.isAlmostEquals(b.b, epsilon)
&& a.c.isAlmostEquals(b.c, epsilon)
&& a.d.isAlmostEquals(b.d, epsilon)
}
fun isAlmostEquals(other: MMatrix, epsilon: Double = 0.000001): Boolean = isAlmostEquals(this, other, epsilon)
var af: Float
get() = a.toFloat()
set(value) { a = value.toDouble() }
var bf: Float
get() = b.toFloat()
set(value) { b = value.toDouble() }
var cf: Float
get() = c.toFloat()
set(value) { c = value.toDouble() }
var df: Float
get() = d.toFloat()
set(value) { d = value.toDouble() }
var txf: Float
get() = tx.toFloat()
set(value) { tx = value.toDouble() }
var tyf: Float
get() = ty.toFloat()
set(value) { ty = value.toDouble() }
fun getType(): MatrixType {
val hasRotation = b != 0.0 || c != 0.0
val hasScale = a != 1.0 || d != 1.0
val hasTranslation = tx != 0.0 || ty != 0.0
return when {
hasRotation -> MatrixType.COMPLEX
hasScale && hasTranslation -> MatrixType.SCALE_TRANSLATE
hasScale -> MatrixType.SCALE
hasTranslation -> MatrixType.TRANSLATE
else -> MatrixType.IDENTITY
}
}
fun setTo(a: Double, b: Double, c: Double, d: Double, tx: Double, ty: Double): MMatrix {
this.a = a
this.b = b
this.c = c
this.d = d
this.tx = tx
this.ty = ty
return this
}
fun setTo(a: Float, b: Float, c: Float, d: Float, tx: Float, ty: Float): MMatrix = setTo(a.toDouble(), b.toDouble(), c.toDouble(), d.toDouble(), tx.toDouble(), ty.toDouble())
fun setTo(a: Int, b: Int, c: Int, d: Int, tx: Int, ty: Int): MMatrix = setTo(a.toDouble(), b.toDouble(), c.toDouble(), d.toDouble(), tx.toDouble(), ty.toDouble())
fun copyTo(that: MMatrix = MMatrix()): MMatrix {
that.copyFrom(this)
return that
}
fun copyFromInverted(that: MMatrix): MMatrix {
return invert(that)
}
fun copyFrom(that: Matrix): MMatrix = setTo(that.a, that.b, that.c, that.d, that.tx, that.ty)
fun copyFrom(that: MMatrix?): MMatrix {
if (that != null) {
setTo(that.a, that.b, that.c, that.d, that.tx, that.ty)
} else {
identity()
}
return this
}
fun rotate(angle: Angle) = this.apply {
val theta = angle.radians
val cos = cos(theta)
val sin = sin(theta)
val a1 = a * cos - b * sin
b = (a * sin + b * cos)
a = a1
val c1 = c * cos - d * sin
d = (c * sin + d * cos)
c = c1
val tx1 = tx * cos - ty * sin
ty = (tx * sin + ty * cos)
tx = tx1
}
fun skew(skewX: Angle, skewY: Angle): MMatrix {
val sinX = sind(skewX)
val cosX = cosd(skewX)
val sinY = sind(skewY)
val cosY = cosd(skewY)
return this.setTo(
a * cosY - b * sinX,
a * sinY + b * cosX,
c * cosY - d * sinX,
c * sinY + d * cosX,
tx * cosY - ty * sinX,
tx * sinY + ty * cosX
)
}
fun setToMultiply(l: MMatrix?, r: MMatrix?): MMatrix {
when {
l != null && r != null -> multiply(l, r)
l != null -> copyFrom(l)
r != null -> copyFrom(r)
else -> identity()
}
return this
}
fun scale(sx: Double, sy: Double = sx) = setTo(a * sx, b * sx, c * sy, d * sy, tx * sx, ty * sy)
fun scale(sx: Float, sy: Float = sx) = scale(sx.toDouble(), sy.toDouble())
fun scale(sx: Int, sy: Int = sx) = scale(sx.toDouble(), sy.toDouble())
fun prescale(sx: Double, sy: Double = sx) = setTo(a * sx, b * sx, c * sy, d * sy, tx, ty)
fun prescale(sx: Float, sy: Float = sx) = prescale(sx.toDouble(), sy.toDouble())
fun prescale(sx: Int, sy: Int = sx) = prescale(sx.toDouble(), sy.toDouble())
fun translate(dx: Double, dy: Double) = this.apply { this.tx += dx; this.ty += dy }
fun translate(dx: Float, dy: Float) = translate(dx.toDouble(), dy.toDouble())
fun translate(dx: Int, dy: Int) = translate(dx.toDouble(), dy.toDouble())
fun pretranslate(dx: Double, dy: Double) = this.apply { tx += a * dx + c * dy; ty += b * dx + d * dy }
fun pretranslate(dx: Float, dy: Float) = pretranslate(dx.toDouble(), dy.toDouble())
fun pretranslate(dx: Int, dy: Int) = pretranslate(dx.toDouble(), dy.toDouble())
fun prerotate(angle: Angle) = this.apply {
val m = MMatrix()
m.rotate(angle)
this.premultiply(m)
}
fun preskew(skewX: Angle, skewY: Angle) = this.apply {
val m = MMatrix()
m.skew(skewX, skewY)
this.premultiply(m)
}
fun premultiply(m: Matrix) = this.premultiply(m.a, m.b, m.c, m.d, m.tx, m.ty)
fun premultiply(m: MMatrix) = this.premultiply(m.a, m.b, m.c, m.d, m.tx, m.ty)
fun postmultiply(m: MMatrix) = multiply(this, m)
fun premultiply(la: Double, lb: Double, lc: Double, ld: Double, ltx: Double, lty: Double): MMatrix = setTo(
la * a + lb * c,
la * b + lb * d,
lc * a + ld * c,
lc * b + ld * d,
ltx * a + lty * c + tx,
ltx * b + lty * d + ty
)
fun premultiply(la: Float, lb: Float, lc: Float, ld: Float, ltx: Float, lty: Float): MMatrix = premultiply(la.toDouble(), lb.toDouble(), lc.toDouble(), ld.toDouble(), ltx.toDouble(), lty.toDouble())
fun premultiply(la: Int, lb: Int, lc: Int, ld: Int, ltx: Int, lty: Int): MMatrix = premultiply(la.toDouble(), lb.toDouble(), lc.toDouble(), ld.toDouble(), ltx.toDouble(), lty.toDouble())
fun multiply(l: MMatrix, r: MMatrix): MMatrix = setTo(
l.a * r.a + l.b * r.c,
l.a * r.b + l.b * r.d,
l.c * r.a + l.d * r.c,
l.c * r.b + l.d * r.d,
l.tx * r.a + l.ty * r.c + r.tx,
l.tx * r.b + l.ty * r.d + r.ty
)
/** Transform point without translation */
fun deltaTransformPoint(point: MPoint, out: MPoint = MPoint()) = deltaTransformPoint(point.x, point.y, out)
fun deltaTransformPoint(x: Float, y: Float, out: MPoint = MPoint()): MPoint = deltaTransformPoint(x.toDouble(), y.toDouble(), out)
fun deltaTransformPoint(x: Double, y: Double, out: MPoint = MPoint()): MPoint {
out.x = deltaTransformX(x, y)
out.y = deltaTransformY(x, y)
return out
}
fun deltaTransformX(x: Double, y: Double): Double = (x * a) + (y * c)
fun deltaTransformY(x: Double, y: Double): Double = (x * b) + (y * d)
fun identity() = setTo(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
fun setToNan() = setTo(Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN)
fun isIdentity() = getType() == MatrixType.IDENTITY
fun invert(matrixToInvert: MMatrix = this): MMatrix {
val src = matrixToInvert
val dst = this
val norm = src.a * src.d - src.b * src.c
if (norm == 0.0) {
dst.setTo(0.0, 0.0, 0.0, 0.0, -src.tx, -src.ty)
} else {
val inorm = 1.0 / norm
val d = src.a * inorm
val a = src.d * inorm
val b = src.b * -inorm
val c = src.c * -inorm
dst.setTo(a, b, c, d, -a * src.tx - c * src.ty, -b * src.tx - d * src.ty)
}
return this
}
fun concat(value: MMatrix): MMatrix = this.multiply(this, value)
fun preconcat(value: MMatrix): MMatrix = this.multiply(this, value)
fun postconcat(value: MMatrix): MMatrix = this.multiply(value, this)
fun inverted(out: MMatrix = MMatrix()) = out.invert(this)
fun setTransform(
transform: Transform,
pivotX: Double = 0.0,
pivotY: Double = 0.0,
): MMatrix {
return setTransform(
transform.x, transform.y,
transform.scaleX, transform.scaleY,
transform.rotation, transform.skewX, transform.skewY,
pivotX, pivotY
)
}
fun setTransform(
x: Double = 0.0,
y: Double = 0.0,
scaleX: Double = 1.0,
scaleY: Double = 1.0,
rotation: Angle = Angle.ZERO,
skewX: Angle = Angle.ZERO,
skewY: Angle = Angle.ZERO,
pivotX: Double = 0.0,
pivotY: Double = 0.0,
): MMatrix {
// +0.0 drops the negative -0.0
this.a = cosd(rotation + skewY) * scaleX + 0.0
this.b = sind(rotation + skewY) * scaleX + 0.0
this.c = -sind(rotation - skewX) * scaleY + 0.0
this.d = cosd(rotation - skewX) * scaleY + 0.0
if (pivotX == 0.0 && pivotY == 0.0) {
this.tx = x
this.ty = y
} else {
this.tx = x - ((pivotX * this.a) + (pivotY * this.c))
this.ty = y - ((pivotX * this.b) + (pivotY * this.d))
}
return this
}
fun setTransform(x: Float = 0f, y: Float = 0f, scaleX: Float = 1f, scaleY: Float = 1f, rotation: Angle = Angle.ZERO, skewX: Angle = Angle.ZERO, skewY: Angle = Angle.ZERO): MMatrix =
setTransform(x.toDouble(), y.toDouble(), scaleX.toDouble(), scaleY.toDouble(), rotation, skewX, skewY)
fun clone(): MMatrix = MMatrix(a, b, c, d, tx, ty)
operator fun times(that: MMatrix): MMatrix = MMatrix().multiply(this, that)
operator fun times(scale: Double): MMatrix = MMatrix().copyFrom(this).scale(scale)
fun toTransform(out: Transform = Transform()): Transform {
out.setMatrixNoReturn(this)
return out
}
@Suppress("DuplicatedCode")
fun transformRectangle(rectangle: MRectangle, delta: Boolean = false) {
val a = this.af
val b = this.bf
val c = this.cf
val d = this.df
val tx = if (delta) 0f else this.txf
val ty = if (delta) 0f else this.tyf
val x = rectangle.x
val y = rectangle.y
val xMax = x + rectangle.width
val yMax = y + rectangle.height
var x0 = a * x + c * y + tx
var y0 = b * x + d * y + ty
var x1 = a * xMax + c * y + tx
var y1 = b * xMax + d * y + ty
var x2 = a * xMax + c * yMax + tx
var y2 = b * xMax + d * yMax + ty
var x3 = a * x + c * yMax + tx
var y3 = b * x + d * yMax + ty
var tmp = 0.0
if (x0 > x1) {
tmp = x0
x0 = x1
x1 = tmp
}
if (x2 > x3) {
tmp = x2
x2 = x3
x3 = tmp
}
rectangle.x = floor(if (x0 < x2) x0 else x2)
rectangle.width = ceil((if (x1 > x3) x1 else x3) - rectangle.x)
if (y0 > y1) {
tmp = y0
y0 = y1
y1 = tmp
}
if (y2 > y3) {
tmp = y2
y2 = y3
y3 = tmp
}
rectangle.y = floor(if (y0 < y2) y0 else y2)
rectangle.height = ceil((if (y1 > y3) y1 else y3) - rectangle.y)
}
fun copyFromArray(value: FloatArray, offset: Int = 0): MMatrix = setTo(
value[offset + 0], value[offset + 1], value[offset + 2],
value[offset + 3], value[offset + 4], value[offset + 5]
)
fun copyFromArray(value: DoubleArray, offset: Int = 0): MMatrix = setTo(
value[offset + 0].toFloat(), value[offset + 1].toFloat(), value[offset + 2].toFloat(),
value[offset + 3].toFloat(), value[offset + 4].toFloat(), value[offset + 5].toFloat()
)
fun decompose(out: Transform = Transform()): Transform {
return out.setMatrix(this)
}
// Transform points
fun transform(p: Point): Point = Point(transformX(p.x, p.y), transformY(p.x, p.y))
@Deprecated("")
fun transform(p: MPoint, out: MPoint = MPoint()): MPoint = transform(p.x, p.y, out)
@Deprecated("")
fun transform(px: Double, py: Double, out: MPoint = MPoint()): MPoint = out.setTo(transformX(px, py), transformY(px, py))
@Deprecated("")
fun transform(px: Float, py: Float, out: MPoint = MPoint()): MPoint = out.setTo(transformX(px, py), transformY(px, py))
@Deprecated("")
fun transform(px: Int, py: Int, out: MPoint = MPoint()): MPoint = out.setTo(transformX(px, py), transformY(px, py))
@Deprecated("")
fun transformX(p: MPoint): Double = transformX(p.x, p.y)
@Deprecated("")
fun transformX(px: Double, py: Double): Double = this.a * px + this.c * py + this.tx
@Deprecated("")
fun transformX(px: Float, py: Float): Double = this.a * px + this.c * py + this.tx
@Deprecated("")
fun transformX(px: Int, py: Int): Double = this.a * px + this.c * py + this.tx
@Deprecated("")
fun transformY(p: MPoint): Double = transformY(p.x, p.y)
@Deprecated("")
fun transformY(px: Double, py: Double): Double = this.d * py + this.b * px + this.ty
@Deprecated("")
fun transformY(px: Float, py: Float): Double = this.d * py + this.b * px + this.ty
@Deprecated("")
fun transformY(px: Int, py: Int): Double = this.d * py + this.b * px + this.ty
@Deprecated("")
fun transformXf(p: MPoint): Float = transformX(p.x, p.y).toFloat()
@Deprecated("")
fun transformXf(px: Double, py: Double): Float = transformX(px, py).toFloat()
@Deprecated("")
fun transformXf(px: Float, py: Float): Float = transformX(px.toDouble(), py.toDouble()).toFloat()
@Deprecated("")
fun transformXf(px: Int, py: Int): Float = transformX(px.toDouble(), py.toDouble()).toFloat()
@Deprecated("")
fun transformYf(p: MPoint): Float = transformY(p.x, p.y).toFloat()
@Deprecated("")
fun transformYf(px: Double, py: Double): Float = transformY(px, py).toFloat()
@Deprecated("")
fun transformYf(px: Float, py: Float): Float = transformY(px.toDouble(), py.toDouble()).toFloat()
@Deprecated("")
fun transformYf(px: Int, py: Int): Float = transformY(px.toDouble(), py.toDouble()).toFloat()
@Deprecated("Use MatrixTransform")
data class Transform(
var x: Double = 0.0, var y: Double = 0.0,
var scaleX: Double = 1.0, var scaleY: Double = 1.0,
var skewX: Angle = 0.radians, var skewY: Angle = 0.radians,
var rotation: Angle = 0.radians
) : MutableInterpolable, Interpolable {
val immutable: MatrixTransform get() = MatrixTransform(x, y, scaleX, scaleY, skewX, skewY, rotation)
val scale: Scale get() = Scale(scaleX, scaleY)
var scaleAvg: Double
get() = (scaleX + scaleY) * 0.5
set(value) {
scaleX = value
scaleY = value
}
override fun interpolateWith(ratio: Ratio, other: Transform): Transform = Transform().setToInterpolated(ratio, this, other)
override fun setToInterpolated(ratio: Ratio, l: Transform, r: Transform): Transform = this.setTo(
ratio.interpolate(l.x, r.x),
ratio.interpolate(l.y, r.y),
ratio.interpolate(l.scaleX, r.scaleX),
ratio.interpolate(l.scaleY, r.scaleY),
ratio.interpolateAngleDenormalized(l.rotation, r.rotation),
ratio.interpolateAngleDenormalized(l.skewX, r.skewX),
ratio.interpolateAngleDenormalized(l.skewY, r.skewY)
)
fun identity() {
x = 0.0
y = 0.0
scaleX = 1.0
scaleY = 1.0
skewX = 0.0.radians
skewY = 0.0.radians
rotation = 0.0.radians
}
fun setMatrixNoReturn(matrix: MMatrix, pivotX: Double = 0.0, pivotY: Double = 0.0) {
val a = matrix.a
val b = matrix.b
val c = matrix.c
val d = matrix.d
val skewX = -atan2(-c, d)
val skewY = atan2(b, a)
val delta = abs(skewX + skewY)
if (delta < 0.00001 || abs((PI * 2) - delta) < 0.00001) {
this.rotation = skewY.radians
this.skewX = 0.0.radians
this.skewY = 0.0.radians
} else {
this.rotation = 0.radians
this.skewX = skewX.radians
this.skewY = skewY.radians
}
this.scaleX = hypot(a, b)
this.scaleY = hypot(c, d)
if (pivotX == 0.0 && pivotY == 0.0) {
this.x = matrix.tx
this.y = matrix.ty
} else {
this.x = matrix.tx + ((pivotX * a) + (pivotY * c));
this.y = matrix.ty + ((pivotX * b) + (pivotY * d));
}
}
fun setMatrix(matrix: MMatrix, pivotX: Double = 0.0, pivotY: Double = 0.0): Transform {
setMatrixNoReturn(matrix, pivotX, pivotY)
return this
}
fun toMatrix(out: MMatrix = MMatrix()): MMatrix = out.setTransform(x, y, scaleX, scaleY, rotation, skewX, skewY)
fun copyFrom(that: Transform) = setTo(that.x, that.y, that.scaleX, that.scaleY, that.rotation, that.skewX, that.skewY)
fun setTo(x: Double, y: Double, scaleX: Double, scaleY: Double, rotation: Angle, skewX: Angle, skewY: Angle): Transform {
this.x = x
this.y = y
this.scaleX = scaleX
this.scaleY = scaleY
this.rotation = rotation
this.skewX = skewX
this.skewY = skewY
return this
}
fun setTo(x: Float, y: Float, scaleX: Float, scaleY: Float, rotation: Angle, skewX: Angle, skewY: Angle): Transform =
setTo(x.toDouble(), y.toDouble(), scaleX.toDouble(), scaleY.toDouble(), rotation, skewX, skewY)
fun add(value: Transform): Transform = setTo(
x + value.x,
y + value.y,
scaleX * value.scaleX,
scaleY * value.scaleY,
skewX + value.skewX,
skewY + value.skewY,
rotation + value.rotation,
)
fun minus(value: Transform): Transform = setTo(
x - value.x,
y - value.y,
scaleX / value.scaleX,
scaleY / value.scaleY,
skewX - value.skewX,
skewY - value.skewY,
rotation - value.rotation,
)
fun clone() = Transform().copyFrom(this)
fun isAlmostEquals(other: Transform, epsilon: Double = 0.000001): Boolean = isAlmostEquals(this, other, epsilon)
companion object {
fun isAlmostEquals(a: Transform, b: Transform, epsilon: Double = 0.000001): Boolean =
a.x.isAlmostEquals(b.x, epsilon)
&& a.y.isAlmostEquals(b.y, epsilon)
&& a.scaleX.isAlmostEquals(b.scaleX, epsilon)
&& a.scaleY.isAlmostEquals(b.scaleY, epsilon)
&& a.skewX.isAlmostEquals(b.skewX, epsilon)
&& a.skewY.isAlmostEquals(b.skewY, epsilon)
&& a.rotation.isAlmostEquals(b.rotation, epsilon)
}
}
class Computed(val matrix: MMatrix, val transform: Transform) {
companion object;
constructor(matrix: MMatrix) : this(matrix, Transform().also { it.setMatrixNoReturn(matrix) })
constructor(transform: Transform) : this(transform.toMatrix(), transform)
}
override fun setToInterpolated(ratio: Ratio, l: MMatrix, r: MMatrix) = this.setTo(
a = ratio.interpolate(l.a, r.a),
b = ratio.interpolate(l.b, r.b),
c = ratio.interpolate(l.c, r.c),
d = ratio.interpolate(l.d, r.d),
tx = ratio.interpolate(l.tx, r.tx),
ty = ratio.interpolate(l.ty, r.ty)
)
override fun interpolateWith(ratio: Ratio, other: MMatrix): MMatrix =
MMatrix().setToInterpolated(ratio, this, other)
inline fun keepMatrix(callback: (MMatrix) -> T): T {
val a = this.a
val b = this.b
val c = this.c
val d = this.d
val tx = this.tx
val ty = this.ty
try {
return callback(this)
} finally {
this.a = a
this.b = b
this.c = c
this.d = d
this.tx = tx
this.ty = ty
}
}
override fun toString(): String = "Matrix(a=$a, b=$b, c=$c, d=$d, tx=$tx, ty=$ty)"
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy