commonMain.ru.casperix.math.angle.float32.DegreeFloat.kt Maven / Gradle / Ivy
package ru.casperix.math.angle.float32
import ru.casperix.math.angle.Angle
import ru.casperix.math.angle.AngleBuilder
import ru.casperix.math.angle.float64.DegreeDouble
import ru.casperix.math.geometry.*
import ru.casperix.math.interpolation.float32.InterpolateFloatFunction
import ru.casperix.math.interpolation.float32.linearInterpolatef
import ru.casperix.math.vector.float32.Vector2f
import ru.casperix.misc.toPrecision
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
import kotlin.math.absoluteValue
@JvmInline
@Serializable
value class DegreeFloat(val value: Float) : ru.casperix.math.angle.Angle {
/**
* @return angle in [0, 360) interval
*/
override fun normalize(): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(ru.casperix.math.angle.float32.DegreeFloat.Companion.absMod(value, 360f))
}
override fun isFinite(): Boolean {
return value.isFinite()
}
fun toRadian(): ru.casperix.math.angle.float32.RadianFloat {
return ru.casperix.math.angle.float32.RadianFloat(value * fDEGREE_TO_RADIAN)
}
fun toDegreeDouble(): DegreeDouble {
return DegreeDouble(value.toDouble())
}
override operator fun plus(other: ru.casperix.math.angle.float32.DegreeFloat): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value + other.value)
}
override operator fun minus(other: ru.casperix.math.angle.float32.DegreeFloat): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value - other.value)
}
override operator fun plus(other: Float): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value + other)
}
override operator fun minus(other: Float): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value - other)
}
override operator fun unaryMinus(): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(-value)
}
override operator fun times(factor: Float): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value * factor)
}
override operator fun div(factor: Float): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(value / factor)
}
override operator fun compareTo(other: Float): Int {
return value.compareTo(other)
}
override operator fun compareTo(other: ru.casperix.math.angle.float32.DegreeFloat): Int {
return value.compareTo(other.value)
}
override fun format(precision: Int): String {
return value.toPrecision(precision) + '°'
}
override fun toString(): String {
return format(1)
}
fun toDirection():Vector2f {
return toRadian().toDirection()
}
fun distTo(other: ru.casperix.math.angle.float32.DegreeFloat): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat.Companion.betweenAngles(this, other)
}
companion object : ru.casperix.math.angle.AngleBuilder {
override val ZERO = ru.casperix.math.angle.float32.DegreeFloat(0f)
override val MAX = ru.casperix.math.angle.float32.DegreeFloat(360f)
val HALF = ru.casperix.math.angle.float32.DegreeFloat(180f)
val QUARTER = ru.casperix.math.angle.float32.DegreeFloat(90f)
private fun absMod(value: Float, mod: Float): Float {
return if (value < 0f) {
(mod - (-value) % mod) % mod
} else {
value % mod
}
}
/**
* @return angle from X-axis to vector in [-180, 180) interval
*/
override fun byDirection(x: Float, y: Float): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat(
ru.casperix.math.angle.float32.RadianFloat.Companion.byDirectionRadian(
x,
y
) * fRADIAN_TO_DEGREE
)
}
override fun byDirection(value: Vector2f): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat.Companion.byDirection(value.x, value.y)
}
fun interpolateAngular(
a: ru.casperix.math.angle.float32.DegreeFloat,
b: ru.casperix.math.angle.float32.DegreeFloat,
weightB: Float,
interpolator: InterpolateFloatFunction = linearInterpolatef
): ru.casperix.math.angle.float32.DegreeFloat {
val startNormalized = a.normalize().value
val finishNormalized = b.normalize().value
val finishAdapted = if ((startNormalized - finishNormalized).absoluteValue <= 180f) {
finishNormalized
} else if (startNormalized > finishNormalized) {
finishNormalized + 360f
} else {
finishNormalized - 360f
}
return ru.casperix.math.angle.float32.DegreeFloat(interpolator(startNormalized, finishAdapted, weightB)).normalize()
}
fun betweenDirections(directionA: Vector2f, directionB: Vector2f): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat.Companion.betweenAngles(
ru.casperix.math.angle.float32.DegreeFloat.Companion.byDirection(
directionA
), ru.casperix.math.angle.float32.DegreeFloat.Companion.byDirection(directionB)
)
}
fun betweenAngles(angleA: ru.casperix.math.angle.float32.DegreeFloat, angleB: ru.casperix.math.angle.float32.DegreeFloat): ru.casperix.math.angle.float32.DegreeFloat {
val a = (angleB - angleA).normalize()
val b = (angleA - angleB).normalize()
return if (a.value <= b.value) a else b
}
fun betweenDirectionsDirected(directionA: Vector2f, directionB: Vector2f): ru.casperix.math.angle.float32.DegreeFloat {
return ru.casperix.math.angle.float32.DegreeFloat.Companion.betweenAnglesDirected(
ru.casperix.math.angle.float32.DegreeFloat.Companion.byDirection(
directionA
), ru.casperix.math.angle.float32.DegreeFloat.Companion.byDirection(directionB)
)
}
/**
* Radial distance from angle-a to angle-b in counter-clock-wise direction
*/
fun betweenAnglesDirected(angleA: ru.casperix.math.angle.float32.DegreeFloat, angleB: ru.casperix.math.angle.float32.DegreeFloat): ru.casperix.math.angle.float32.DegreeFloat {
val adaptedA = angleA.normalize()
var adaptedB = angleB.normalize()
if (adaptedB < adaptedA) {
adaptedB += ru.casperix.math.angle.float32.DegreeFloat.Companion.MAX
}
return adaptedB - adaptedA
}
}
}