Skip to content

Commit

Permalink
Update to new units
Browse files Browse the repository at this point in the history
  • Loading branch information
Seggan committed Feb 29, 2024
1 parent 8fcc4b0 commit 5605bee
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.addoncommunity.galactifun

import io.github.addoncommunity.galactifun.units.Acceleration.Companion.metersPerSecondSquared
import org.bukkit.Location
import org.bukkit.World

Expand All @@ -10,7 +11,7 @@ object Constants {
const val KM_PER_AU = 1.495978707e8

const val GRAVITATIONAL_CONSTANT = 6.674e-11
const val EARTH_GRAVITY = 9.81
val EARTH_GRAVITY = 9.81.metersPerSecondSquared

/**
* The maximum radix for the [Int.toString] and [String.toInt] functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.addoncommunity.galactifun.api.objects.properties.Orbit
import io.github.addoncommunity.galactifun.units.Angle.Companion.degrees
import io.github.addoncommunity.galactifun.units.Distance
import io.github.addoncommunity.galactifun.units.Mass
import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond
import io.github.addoncommunity.galactifun.util.LazyDouble
import io.github.thebusybiscuit.slimefun4.libraries.dough.items.CustomItemStack
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils
Expand All @@ -22,18 +23,17 @@ sealed class CelestialObject(name: String, baseItem: ItemStack) {
abstract fun distanceTo(other: CelestialObject, time: Instant): Distance



abstract val mass: Mass
abstract val radius: Distance

val gravitationalParameter by LazyDouble { Constants.GRAVITATIONAL_CONSTANT * mass.kilograms }
val escapeVelocity by LazyDouble { sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.meters) }
val escapeVelocity by lazy { sqrt(2 * Constants.GRAVITATIONAL_CONSTANT * mass.kilograms / radius.meters).metersPerSecond }
val parkingOrbit: Orbit by lazy {
Orbit(
parent = this,
semimajorAxis = radius * 1.1,
eccentricity = Orbit.TINY_ECCENTRICITY,
longitudeOfPeriapsis = 0.0.degrees,
longitudeOfPeriapsis = 0.degrees,
timeOfPeriapsis = Instant.fromEpochMilliseconds(0)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import io.github.addoncommunity.galactifun.api.objects.properties.visVivaEquatio
import io.github.addoncommunity.galactifun.core.managers.PlanetManager
import io.github.addoncommunity.galactifun.units.Distance
import io.github.addoncommunity.galactifun.units.Distance.Companion.meters
import io.github.addoncommunity.galactifun.units.Velocity
import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond
import io.github.addoncommunity.galactifun.units.abs
import io.github.addoncommunity.galactifun.units.cos
import io.github.seggan.kfun.location.plus
import kotlinx.datetime.Instant
import org.bukkit.Location
import org.bukkit.inventory.ItemStack
import kotlin.math.abs
import kotlin.math.sqrt

abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObject(name, baseItem) {
Expand All @@ -35,7 +37,7 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj
}

override fun distanceTo(other: CelestialObject, time: Instant): Distance {
if (other == this) return 0.0.meters
if (other == this) return 0.meters
if (other is Star) {
if (star == other) {
var dist = orbit.radius(time)
Expand All @@ -62,8 +64,8 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj
}
}

fun getDeltaVForTransferTo(other: PlanetaryObject, time: Instant): Double {
if (this == other) return 0.0
fun getDeltaVForTransferTo(other: PlanetaryObject, time: Instant): Velocity {
if (this == other) return 0.metersPerSecond
val thisParents = generateSequence(this as CelestialObject) {
if (it is PlanetaryObject) it.orbit.parent else null
}.toList()
Expand All @@ -75,7 +77,7 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj
}.toList()
if (this in otherParents) {
var height = other.parkingOrbit
var dV = 0.0
var dV = 0.metersPerSecond
for (obj in otherParents) {
if (obj == this) break
dV += abs(
Expand All @@ -90,7 +92,8 @@ abstract class PlanetaryObject(name: String, baseItem: ItemStack) : CelestialObj
dV += height.hohmannTransfer(parkingOrbit, time)
return dV
} else {
val commonParent = thisParents.firstOrNull { it in otherParents } ?: return Double.POSITIVE_INFINITY
val commonParent = thisParents.firstOrNull { it in otherParents }
?: return Double.POSITIVE_INFINITY.metersPerSecond
val thisSibling = thisParents[thisParents.indexOf(commonParent) - 1] as PlanetaryObject
val otherSibling = otherParents[otherParents.indexOf(commonParent) - 1] as PlanetaryObject
val thisDeltaV = abs(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Star(

override fun distanceTo(other: CelestialObject, time: Instant): Distance {
return when (other) {
this -> 0.0.meters
this -> 0.meters
is Star -> position.distanceTo(other.position)
else -> other.distanceTo(this, time)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import io.github.addoncommunity.galactifun.pluginInstance
import io.github.addoncommunity.galactifun.units.*
import io.github.addoncommunity.galactifun.units.Angle.Companion.radians
import io.github.addoncommunity.galactifun.units.Distance.Companion.meters
import io.github.addoncommunity.galactifun.units.Velocity.Companion.metersPerSecond
import io.github.addoncommunity.galactifun.units.coordiantes.CartesianVector
import io.github.addoncommunity.galactifun.units.coordiantes.PolarVector
import io.github.addoncommunity.galactifun.util.Either
import io.github.addoncommunity.galactifun.util.LazyDouble
import kotlinx.datetime.Instant
import java.util.*
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.sqrt
import kotlin.time.Duration
Expand Down Expand Up @@ -98,18 +98,23 @@ data class Orbit(
return ((meanAnomalyB - meanAnomalyA).radians / meanMotion).seconds
}

fun hohmannTransfer(target: Orbit, time: Instant): Double {
fun hohmannTransfer(target: Orbit, time: Instant): Velocity {
val parkingR = radius(time)
val targetR = target.radius(time)
val transferA = (parkingR + targetR) / 2.0
val mu = parent.gravitationalParameter
val firstManeuver = abs(visVivaEquation(mu, parkingR, transferA) - visVivaEquation(mu, parkingR, semimajorAxis))
val secondManeuver =
abs(visVivaEquation(mu, targetR, target.semimajorAxis) - visVivaEquation(mu, targetR, transferA))
val firstManeuver = abs(
visVivaEquation(mu, parkingR, transferA)
- visVivaEquation(mu, parkingR, semimajorAxis)
)
val secondManeuver = abs(
visVivaEquation(mu, targetR, target.semimajorAxis) -
visVivaEquation(mu, targetR, transferA)
)
return firstManeuver + secondManeuver
}

fun arbitraryTransfer(targetOrbit: Orbit, time: Instant): Double {
fun arbitraryTransfer(targetOrbit: Orbit, time: Instant): Velocity {
require(parent == targetOrbit.parent) { "Both orbits must have the same parent" }

val parking = position(time)
Expand Down Expand Up @@ -146,7 +151,7 @@ data class Orbit(
val transferOrbit = transfer.value
val transferVel = transferOrbit.velocity(targetTime)
val targetVel = targetOrbit.velocity(targetTime)
val burn2 = transferVel.distanceTo(targetVel).meters
val burn2 = transferVel.distanceTo(targetVel).meters.metersPerSecond

val burn1 = abs(
visVivaEquation(parent.gravitationalParameter, parking.radius, transferOrbit.semimajorAxis) -
Expand Down Expand Up @@ -177,7 +182,7 @@ data class Orbit(
)
val intersectTrueAnomaly = intersectLongitude - transferOrbit.longitudeOfPeriapsis
val mean = meanAnomalyFromTrueAnomaly(transferOrbit.eccentricity, intersectTrueAnomaly)
return Transfer(Either.Left(transferOrbit), transferOrbit.timeOfFlight(0.0.radians, mean))
return Transfer(Either.Left(transferOrbit), transferOrbit.timeOfFlight(0.radians, mean))
} else {
val distance = parking.distanceTo(target)
val transferBrachistochrone = brachistochroneTransfer(distance)
Expand All @@ -188,7 +193,12 @@ data class Orbit(

private const val MAX_ITERATIONS = 512

private fun getAnomalyDifference(transfer: Transfer, time: Instant, targetOrbit: Orbit, intersectLongitude: Angle): Angle {
private fun getAnomalyDifference(
transfer: Transfer,
time: Instant,
targetOrbit: Orbit,
intersectLongitude: Angle
): Angle {
val targetTime = time + transfer.tof
val target = targetOrbit.position(targetTime)
return abs(target.angle + targetOrbit.longitudeOfPeriapsis - intersectLongitude).standardForm
Expand All @@ -199,8 +209,8 @@ private tailrec fun kelpersEquation(m: Angle, e: Double, guessE: Angle = m): Ang
return if (abs(nextGuess - guessE).radians < 1e-6) nextGuess else kelpersEquation(m, e, nextGuess)
}

fun visVivaEquation(mu: Double, r: Distance, a: Distance): Double =
sqrt(mu * (2 / r.meters - 1 / a.meters))
fun visVivaEquation(mu: Double, r: Distance, a: Distance): Velocity =
sqrt(mu * (2 / r.meters - 1 / a.meters)).metersPerSecond

// https://math.stackexchange.com/a/407425/1291722
// https://www.desmos.com/calculator/mdifr3y167
Expand All @@ -225,14 +235,14 @@ private fun oneTangentTransferOrbit(

private fun brachistochroneTransfer(
distance: Distance,
acceleration: Double = Constants.EARTH_GRAVITY / 16
acceleration: Acceleration = Constants.EARTH_GRAVITY / 16
): BrachistochroneTransfer {
val time = 2 * sqrt(distance.meters / acceleration)
return BrachistochroneTransfer(acceleration * time, time.seconds)
val time = 2.seconds * sqrt(distance.meters / acceleration.metersPerSecondSquared)
return BrachistochroneTransfer(acceleration * time, time)
}

private data class BrachistochroneTransfer(
val deltaV: Double,
val deltaV: Velocity,
val time: Duration
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ object BaseUniverse {
val sun = Star(
name = "Sun",
mass = 1.989e30.kilograms,
radius = 695700.0.kilometers,
radius = 695700.kilometers,
// The sun is at the center of the universe, yay!
position = SphericalVector(0.0.radians, 0.0.radians, 0.0.meters)
position = SphericalVector(0.radians, 0.radians, 0.meters)
)

val earth = Earth()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class Earth : PlanetaryWorld("Earth", ItemStack(Material.GRASS_BLOCK)) {

override val orbit = Orbit(
parent = BaseUniverse.sun,
semimajorAxis = 1.0.au,
semimajorAxis = 1.au,
eccentricity = 0.0167,
longitudeOfPeriapsis = 288.1.degrees,
timeOfPeriapsis = Instant.parse("2024-01-01T00:00:00Z")
)
override val mass = 5.972e24.kilograms
override val radius = 6371.0.kilometers
override val radius = 6371.kilometers
override val dayCycle = DayCycle.EARTH_LIKE
override val atmosphere = Atmosphere.EARTH_LIKE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ class Moon : AlienWorld("Moon", ItemStack(Material.END_STONE)) {
override val dayCycle = DayCycle(29.days + 12.hours)
override val orbit = Orbit(
parent = BaseUniverse.earth,
semimajorAxis = 384399.0.kilometers,
semimajorAxis = 384399.kilometers,
eccentricity = 0.0549,
longitudeOfPeriapsis = 0.0.degrees, // the argument changes over the course of the year
longitudeOfPeriapsis = 0.degrees, // the argument changes over the course of the year
timeOfPeriapsis = Instant.parse("2024-01-13T10:36:00Z")
)
override val mass = 7.346e22.kilograms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.github.seggan.uom.Measure
import kotlin.math.PI

@Measure(base = "radians")
@AlternateUnit(name = "degrees", ratio = 180.0 / Math.PI)
@AlternateUnit(unit = "degrees", ratio = 180.0 / Math.PI)
private class AAngle

val Angle.standardForm: Angle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import io.github.seggan.uom.MultipliesTo
import kotlin.time.Duration

@Measure(base = "meters")
@AlternateUnit(name = "lightYears", ratio = Constants.KM_PER_LY * 1000)
@AlternateUnit(name = "kilometers", ratio = 1000.0)
@AlternateUnit(name = "au", ratio = Constants.KM_PER_AU * 1000)
@AlternateUnit(unit = "lightYears", ratio = Constants.KM_PER_LY * 1000)
@AlternateUnit(unit = "kilometers", ratio = 1000.0)
@AlternateUnit(unit = "au", ratio = Constants.KM_PER_AU * 1000)
private class ADistance

@Measure(base = "kilograms")
@AlternateUnit(name = "pounds", ratio = 2.20462)
@AlternateUnit(unit = "pounds", ratio = 2.20462)
private class AMass

@Measure(base = "metersPerSecond")
Expand All @@ -34,5 +34,7 @@ operator fun Velocity.div(time: Duration): Acceleration = (metersPerSecond / tim
operator fun Acceleration.times(time: Duration): Velocity = (metersPerSecondSquared * time.doubleSeconds).metersPerSecond

@Measure(base = "newtons")
@AlternateUnit(unit = "kilonewtons", ratio = 1000.0)
@AlternateUnit(unit = "meganewtons", ratio = 1_000_000.0)
@DividesTo(other = AAcceleration::class, quotient = AMass::class)
private class AForce
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class OrbitTest : CommonTest() {
fun setUpOrbit() {
orbit = Orbit(
parent = BaseUniverse.sun,
semimajorAxis = 1.0.au,
semimajorAxis = 1.au,
eccentricity = Orbit.TINY_ECCENTRICITY,
longitudeOfPeriapsis = 0.0.degrees,
longitudeOfPeriapsis = 0.degrees,
timeOfPeriapsis = EPOCH
)
Orbit.TIME_SCALE = 1.0
Expand All @@ -53,21 +53,21 @@ class OrbitTest : CommonTest() {
orbit.parent.gravitationalParameter,
orbit.radius(orbit.timeOfPeriapsis),
orbit.semimajorAxis
)
).metersPerSecond
vel.polar.angle.standardForm.degrees shouldBeRoughly expectedAngle.degrees
}

testVelocityAt(orbit.timeOfPeriapsis, 90.0.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period, 90.0.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 2, 270.0.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 4, 180.0.degrees)
testVelocityAt(orbit.timeOfPeriapsis, 90.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period, 90.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 2, 270.degrees)
testVelocityAt(orbit.timeOfPeriapsis + orbit.period / 4, 180.degrees)
}

@Test
fun testTimeOfFlight() {
val time = orbit.timeOfPeriapsis
val timeOfFlight = orbit.timeOfFlight(
0.0.radians,
0.radians,
orbit.meanAnomaly(time + orbit.period / 4)
)
timeOfFlight.inWholeDays shouldBe 91
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,15 @@ private fun TypeSpec.Builder.addScalarOperator(
baseUnit: String,
className: ClassName
): TypeSpec.Builder {
val operatorFun = FunSpec.builder(operator)
.addModifiers(KModifier.OPERATOR)
.addParameter("scalar", Double::class)
.returns(className)
.addStatement("return %T(%L %L scalar)", className, baseUnit, symbol)
return addFunction(operatorFun.build())
for (type in listOf(Double::class, Int::class)) {
val operatorFun = FunSpec.builder(operator)
.addModifiers(KModifier.OPERATOR)
.addParameter("scalar", type)
.returns(className)
.addStatement("return %T(%L %L scalar)", className, baseUnit, symbol)
addFunction(operatorFun.build())
}
return this
}

fun KSDeclaration.getBaseUnit(): String? {
Expand Down
33 changes: 28 additions & 5 deletions uom/src/main/kotlin/io/github/seggan/uom/UoM.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,40 @@ package io.github.seggan.uom

import kotlin.reflect.KClass

@Repeatable
/**
* A measure.
*
* @param base The base unit for this measure.
*/
@Target(AnnotationTarget.CLASS)
annotation class MultipliesTo(val other: KClass<*>, val product: KClass<*>)
annotation class Measure(val base: String)

/**
* An alternate unit for a measure.
*
* @param unit The alternate unit.
* @param ratio How many alternate units are in the base unit.
*/
@Repeatable
@Target(AnnotationTarget.CLASS)
annotation class DividesTo(val other: KClass<*>, val quotient: KClass<*>)
annotation class AlternateUnit(val unit: String, val ratio: Double)

/**
* A measure that multiplies to another measure.
*
* @param other The other measure.
* @param product The product measure.
*/
@Repeatable
@Target(AnnotationTarget.CLASS)
annotation class Measure(val base: String)
annotation class MultipliesTo(val other: KClass<*>, val product: KClass<*>)

/**
* A measure that divides to another measure.
*
* @param other The other measure.
* @param quotient The quotient measure.
*/
@Repeatable
@Target(AnnotationTarget.CLASS)
annotation class AlternateUnit(val name: String, val ratio: Double)
annotation class DividesTo(val other: KClass<*>, val quotient: KClass<*>)

0 comments on commit 5605bee

Please sign in to comment.