Imports

import java.net.URL
import java.util.Date

import scala.Vector

import com.jogamp.opengl.GL2
import edu.uci.ics.jung.graph.DirectedSparseGraph

import cats.implicits._

import spire.implicits.FloatAlgebra
import spire.implicits.additiveGroupOps
import spire.implicits.moduleOps

import axle.algebra.GeoCoordinates
import axle.algebra.SphericalVector
import axle.algebra.modules.floatDoubleModule
import axle.algebra.modules.floatRationalModule
import axle.jung.directedGraphJung
import axle.quanta.Angle
import axle.quanta.Distance
import axle.quanta.UnittedQuantity

import axle.jogl._
import Color._

Distance and Angle unit conversions

implicit val ddc = {
  import axle.algebra.modules.doubleRationalModule
  import spire.implicits.DoubleAlgebra
  Distance.converterGraphK2[Double, DirectedSparseGraph]
}

implicit val distanceConverter = Distance.converterGraphK2[Float, DirectedSparseGraph]
import distanceConverter._

implicit val angleConverter = Angle.converterGraphK2[Float, DirectedSparseGraph]
import angleConverter._

val zeroDegrees = 0f *: °

Constants and scalars

val millisPerDay = 1000f * 60 * 60 * 24
// millisPerDay: Float = 8.64E7

val startTimeMillis = new Date().getTime
// startTimeMillis: Long = 1496619985276

val simulatedStartTime = new Date()
// simulatedStartTime: java.util.Date = Sun Jun 04 16:46:25 PDT 2017

val simulatedStartTimeMillis = simulatedStartTime.getTime
// simulatedStartTimeMillis: Long = 1496619985456

val timeCoefficient = 3600f // simulate one hour each second
// timeCoefficient: Float = 3600.0

Camera position

val cameraDistance = 13000f *: km
// cameraDistance: axle.quanta.UnittedQuantity[axle.quanta.Distance,Float] = UnittedQuantity(13000.0,UnitOfMeasurement(kilometer,km,None))

// http://www.sjsu.edu/faculty/watkins/elevsun.htm
val cameraCoordinates = GeoCoordinates(39.828328f *: °, -98.579416f *: °)
// cameraCoordinates: axle.algebra.GeoCoordinates[Float] = GeoCoordinates(UnittedQuantity(39.828327,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))),UnittedQuantity(-98.579414,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))))

Earth and Moon spheres

val earthRadius = 6371f *: km
// earthRadius: axle.quanta.UnittedQuantity[axle.quanta.Distance,Float] = UnittedQuantity(6371.0,UnitOfMeasurement(kilometer,km,None))

val earth = Sphere(earthRadius, 96, 64, blue)
// earth: axle.jogl.Sphere[Float] = Sphere(UnittedQuantity(6371.0,UnitOfMeasurement(kilometer,km,None)),96,64,Color(0.0,0.0,0.1))

def earthOrienter(t: Long)(gl: GL2): Unit = {
  translate(gl, km, 0f *: km, 0f *: km, -1f *: cameraDistance)
  rotate(gl, cameraCoordinates.latitude, 1f, 0f, 0f)
  rotate(gl, cameraCoordinates.longitude, 0f, -1f, 0f)
  rotate(gl, 90f *: °, -1f, 0f, 0f)
}
// earthOrienter: (t: Long)(gl: com.jogamp.opengl.GL2)Unit

// Note: Moon measurements are not accurate

val moonSphere = Sphere(1000f *: km, 48, 16, white)
// moonSphere: axle.jogl.Sphere[Float] = Sphere(UnittedQuantity(1000.0,UnitOfMeasurement(kilometer,km,None)),48,16,Color(1.0,1.0,1.0))

def moonOrienter(t: Long)(gl: GL2): Unit = {
  translate(gl, km, 7500f *: km, 3500f *: km, -13000f *: km)
  rotate(gl, (-360f * (t / millisPerDay)) *: °, 0f, 1f, 0f)
  rotate(gl, 90f *: °, -1f, 0f, 0f)
}
// moonOrienter: (t: Long)(gl: com.jogamp.opengl.GL2)Unit

Sun (the light source)

val sunDistance = 1f *: au
// sunDistance: axle.quanta.UnittedQuantity[axle.quanta.Distance,Float] = UnittedQuantity(1.0,UnitOfMeasurement(Astronomical Unit,AU,Some(http://en.wikipedia.org/wiki/Astronomical_unit)))

Airports

case class Airport(icaoCode: String, coords: GeoCoordinates[Float], altitude: UnittedQuantity[Distance, Float])
// defined class Airport

val airportMark = Sphere(100f *: km, 10, 10, red)
// airportMark: axle.jogl.Sphere[Float] = Sphere(UnittedQuantity(100.0,UnitOfMeasurement(kilometer,km,None)),10,10,Color(1.0,0.0,0.0))

val sfo = Airport("SFO", GeoCoordinates(37.6189f *: °, -122.3750f *: °), 13f *: ft)
// sfo: Airport = Airport(SFO,GeoCoordinates(UnittedQuantity(37.6189,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))),UnittedQuantity(-122.375,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),UnittedQuantity(13.0,UnitOfMeasurement(foot,ft,None)))

val jfk = Airport("JFK", GeoCoordinates(40.6413f *: °, -73.7781f *: °), 13.12f *: ft)
// jfk: Airport = Airport(JFK,GeoCoordinates(UnittedQuantity(40.6413,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))),UnittedQuantity(-73.7781,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),UnittedQuantity(13.12,UnitOfMeasurement(foot,ft,None)))

val airports = sfo :: jfk :: Nil
// airports: List[Airport] = List(Airport(SFO,GeoCoordinates(UnittedQuantity(37.6189,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))),UnittedQuantity(-122.375,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),UnittedQuantity(13.0,UnitOfMeasurement(foot,ft,None))), Airport(JFK,GeoCoordinates(UnittedQuantity(40.6413,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle)))),UnittedQuantity(-73.7781,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),UnittedQuantity(13.12,UnitOfMeasurement(foot,ft,None))))

def airportOrienter(airport: Airport, t: Long)(gl: GL2): Unit = {
  translate(gl, km, 0f *: km, 0f *: km, -1f *: cameraDistance)
  rotate(gl, cameraCoordinates.latitude - airport.coords.latitude, 1f, 0f, 0f)
  rotate(gl, airport.coords.longitude - cameraCoordinates.longitude, 0f, 1f, 0f)
  translate(gl, km, 0f *: km, 0f *: km, earth.radius)
}
// airportOrienter: (airport: Airport, t: Long)(gl: com.jogamp.opengl.GL2)Unit

Scene renderer

def renderAll(gl: GL2, rc: RenderContext, t: Long): Unit = {

  val sunVector = SphericalVector[Float](sunDistance, (-360f * (t / millisPerDay)) *: °, zeroDegrees)
  gl.glLoadIdentity()
  positionLight(sunVector.toPosition, km, gl)

  render(moonSphere, moonOrienter(t) _, gl, rc)

  render(earth, earthOrienter(t) _, gl, rc)

  airports foreach { airport =>
    render(airportMark, airportOrienter(airport, t) _, gl, rc)
  }

}
// renderAll: (gl: com.jogamp.opengl.GL2, rc: axle.jogl.RenderContext, t: Long)Unit

State-changing function

def tic(previous: Long): Long = {
  val actualMillisElapsed = new Date().getTime - startTimeMillis
  simulatedStartTimeMillis + (actualMillisElapsed * timeCoefficient).toLong
}
// tic: (previous: Long)Long

SceneFrame

val sceneFrame = SceneFrame(
  renderAll,
  startTimeMillis,
  tic,
  "Axle Scene Demo",
  Vector.empty,  // texture (URL, String) pairs (second half is file extension)
  km,
  640,           // width
  480,           // height
  700f *: km,    // zNear
  700000f *: km, // zFar
  30)            // fps (frames per second)
// sceneFrame: axle.jogl.SceneFrame[Long] = SceneFrame(<function3>,1496619985276,<function1>,Axle Scene Demo,Vector(),UnitOfMeasurement(kilometer,km,None),640,480,UnittedQuantity(700.0,UnitOfMeasurement(kilometer,km,None)),UnittedQuantity(700000.0,UnitOfMeasurement(kilometer,km,None)),30)

Run the scene

sceneFrame.run()

earth scene