Scott Whitman
09/30/2021, 1:47 PMclass CompassHeading(degrees: Double) {
private var _degrees by Delegates.notNull<Double>()
var degrees: Double
get() = _degrees
set(value) {
var d = value % 360.0
if (d < 0.0) d += 360
_degrees = d
}
init {
this.degrees = degrees
}
}
William Reed
09/30/2021, 1:48 PMhho
09/30/2021, 2:17 PMRuckus
09/30/2021, 2:27 PMclass CompassHeading(degrees: Double) {
var degrees: Double = degrees.mod(360.0)
set(value) {
field = value.mod(360.0)
}
}
Klitos Kyriacou
09/30/2021, 3:04 PM@JvmInline
value class CompassHeading private constructor(val degrees: Double) {
companion object {
operator fun invoke(degrees: Double) = CompassHeading(degrees.mod(360.0))
}
}
or a little more complete:
@JvmInline
value class CompassHeading private constructor(val degrees: Double) : Comparable<CompassHeading> {
val radians: Double
get() = degrees / 180 * PI
companion object {
fun ofDegrees(degrees: Double) = CompassHeading(degrees.mod(360.0))
fun ofDegrees(degrees: Int) = CompassHeading(degrees.mod(360).toDouble())
fun ofRadians(radians: Double) = ofDegrees(radians / PI * 180)
}
override fun compareTo(other: CompassHeading) = degrees.compareTo(other.degrees)
}
Ruckus
09/30/2021, 4:10 PM%
? That's exactly what mod
is for.Klitos Kyriacou
09/30/2021, 4:15 PM%
was a shortcut for mod
. In fact, it's a shortcut for rem
. Thanks for pointing this out. I will edit my answer.Scott Whitman
09/30/2021, 4:29 PMpublic class CompassHeading private constructor(
override val internalValue: Double
) : AppUnit<CompassHeading>() {
public val degrees: Double get() = internalValue
public val radians: Double get() = internalValue / 180 * PI
public val cardinal: CardinalDirection
get() = run {
require(degrees in 0.0..360.0) { "degrees must be between 0 and 360" }
return@run when {
degrees >= 0.0 && degrees < 11.25 -> CardinalDirection.N
degrees >= 11.25 && degrees < 33.75 -> CardinalDirection.NNE
degrees >= 33.75 && degrees < 56.25 -> <http://CardinalDirection.NE|CardinalDirection.NE>
degrees >= 56.25 && degrees < 78.75 -> CardinalDirection.ENE
degrees >= 78.75 && degrees < 101.25 -> CardinalDirection.E
degrees >= 101.25 && degrees < 123.75 -> CardinalDirection.ESE
degrees >= 123.75 && degrees < 146.25 -> <http://CardinalDirection.SE|CardinalDirection.SE>
degrees >= 146.25 && degrees < 168.75 -> CardinalDirection.SSE
degrees >= 168.75 && degrees < 191.25 -> CardinalDirection.S
degrees >= 191.25 && degrees < 213.75 -> CardinalDirection.SSW
degrees >= 213.75 && degrees < 236.25 -> CardinalDirection.SW
degrees >= 236.25 && degrees < 258.75 -> CardinalDirection.WSW
degrees >= 258.75 && degrees < 281.25 -> CardinalDirection.W
degrees >= 281.25 && degrees < 303.75 -> CardinalDirection.WNW
degrees >= 303.75 && degrees < 326.25 -> CardinalDirection.NW
degrees >= 326.25 && degrees < 348.75 -> CardinalDirection.NNW
degrees >= 348.75 && degrees < 360.0 -> CardinalDirection.N
else -> throw IllegalStateException("Error converting $degrees to ordinal direction")
}
}
override fun toString(): String {
return "($cardinal, $degrees)"
}
public companion object {
public operator fun invoke(degrees: Double): CompassHeading = degrees(degrees)
public fun degrees(degrees: Double): CompassHeading = CompassHeading(degrees.mod(360.toDouble()))
public fun radians(radians: Double): CompassHeading = degrees(radians / PI * 180)
}
}
public val Number.compassHeading: CompassHeading get() = CompassHeading(this.toDouble())
public val String.compassHeading: CompassHeading get() = CompassHeading(this.toDouble())
Klitos Kyriacou
09/30/2021, 4:44 PMcardinal
to just a few (untested):
val numPoints = CardinalDirection.values().length
val pointWidth = 360.0 / numPoints
val directionIndex = (degrees / pointWidth + 0.5).toInt() % numPoints
CardinalDirection.values()[directionIndex]
Scott Whitman
09/30/2021, 6:13 PMpublic class CompassHeading private constructor(
override val internalValue: Double
) : AppUnit<CompassHeading>() {
public val degrees: Double get() = internalValue
public val radians: Double get() = internalValue / 180 * PI
public val cardinal: CardinalDirection get() = CardinalDirection.fromDegrees(degrees)
override fun toString(): String {
return "($cardinal, $degrees)"
}
public companion object {
public fun degrees(degrees: Double): CompassHeading = CompassHeading(degrees.mod(360.toDouble()))
public fun radians(radians: Double): CompassHeading = degrees(radians / PI * 180)
}
}
public val Number.degrees: CompassHeading get() = CompassHeading.degrees(this.toDouble())
public val Number.radians: CompassHeading get() = CompassHeading.degrees(this.toDouble())
public val String.degrees: CompassHeading get() = CompassHeading.radians(this.toDouble())
public val String.radians: CompassHeading get() = CompassHeading.radians(this.toDouble())
public enum class CardinalDirection {
N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW;
public companion object {
public fun fromDegrees(degrees: Number): CardinalDirection {
val numPoints = values().size
val pointWidth = 360.0 / numPoints
val directionIndex = (degrees.toDouble().mod(360.0) / pointWidth + 0.5).toInt().mod(numPoints)
return values()[directionIndex]
}
}
}