# geo_maths.gd
# geospatial mathematics utilities for earth globe
# translated from geographical-adventures geomaths.cs

class_name GeoMaths

const EARTH_RADIUS_KM: float = 6371.0
const EARTH_CIRCUMFERENCE_KM: float = EARTH_RADIUS_KM * PI * 2.0

# convert point on unit sphere to coordinate (longitude, latitude in radians)
static func point_to_coordinate(point_on_unit_sphere: Vector3) -> Vector2:
    var latitude = asin(point_on_unit_sphere.y)
    var a = point_on_unit_sphere.x
    var b = -point_on_unit_sphere.z
    var longitude = atan2(a, b)
    return Vector2(longitude, latitude)

# calculate point on sphere given longitude and latitude (in radians) and radius
static func coordinate_to_point(longitude: float, latitude: float, radius: float = 1.0) -> Vector3:
    var y = sin(latitude)
    var r = cos(latitude) # radius of 2d circle cut through sphere at y
    var x = sin(longitude) * r
    var z = -cos(longitude) * r
    return Vector3(x, y, z) * radius

# convert coordinate (lon, lat in radians) to uv texture coordinates [0,1]
static func coordinate_to_uv(longitude: float, latitude: float) -> Vector2:
    var u = (longitude + PI) / (2.0 * PI)
    var v = (latitude + PI / 2.0) / PI
    return Vector2(u, v)

# convert point on unit sphere to uv coordinates
static func point_to_uv(point_on_unit_sphere: Vector3) -> Vector2:
    var coord = point_to_coordinate(point_on_unit_sphere)
    return coordinate_to_uv(coord.x, coord.y)

# convert uv coordinates to coordinate (lon, lat in radians)
static func uv_to_coordinate(uv: Vector2) -> Vector2:
    var longitude = (uv.x - 0.5) * 2.0 * PI
    var latitude = (uv.y - 0.5) * PI
    return Vector2(longitude, latitude)

# convert uv coordinates to point on unit sphere
static func uv_to_point_on_sphere(uv: Vector2) -> Vector3:
    var coord = uv_to_coordinate(uv)
    return coordinate_to_point(coord.x, coord.y)

# calculate distance between two points on unit sphere (result in radians)
static func distance_between_points_on_unit_sphere(a: Vector3, b: Vector3) -> float:
    # more precise for small distances than acos(dot(a,b))
    return atan2(a.cross(b).length(), a.dot(b))
