diff --git a/CMakeLists.txt b/CMakeLists.txt
index c7a360c..d4aa351 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,6 @@ option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)
-
# Find dependency packages
find_package(dr_wav REQUIRED CONFIG)
find_package(stb REQUIRED CONFIG)
diff --git a/src/astro/coordinates.cpp b/src/astro/coordinates.cpp
deleted file mode 100644
index 10e85f5..0000000
--- a/src/astro/coordinates.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2021 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see .
- */
-
-#include "coordinates.hpp"
-#include
-
-namespace astro
-{
-
-double3 rectangular_to_spherical(const double3& rectangular)
-{
- const double xx_yy = rectangular.x * rectangular.x + rectangular.y * rectangular.y;
-
- return double3
- {
- std::sqrt(xx_yy + rectangular.z * rectangular.z),
- std::atan2(rectangular.z, std::sqrt(xx_yy)),
- std::atan2(rectangular.y, rectangular.x)
- };
-}
-
-double3 spherical_to_rectangular(const double3& spherical)
-{
- return double3
- {
- spherical[0] * std::cos(spherical[1]) * std::cos(spherical[2]),
- spherical[0] * std::cos(spherical[1]) * std::sin(spherical[2]),
- spherical[0] * std::sin(spherical[1]),
- };
-}
-
-double3x3 ecliptic_to_equatorial(double ecl)
-{
- const double c_ecl = std::cos(ecl);
- const double s_ecl = std::sin(ecl);
-
- return double3x3
- {
- 1.0, 0.0, 0.0,
- 0.0, c_ecl, s_ecl,
- 0.0, -s_ecl, c_ecl
- };
-}
-
-double3x3 ecliptic_to_horizontal(double ecl, double lat, double lst)
-{
- const double c_ecl = std::cos(ecl);
- const double s_ecl = std::sin(ecl);
- const double c_lat = std::cos(lat);
- const double s_lat = std::sin(lat);
- const double c_lst = std::cos(lst);
- const double s_lst = std::sin(lst);
-
- return double3x3
- {
- s_lat * c_lst, s_lst, c_lat * c_lst,
- s_lat * s_lst * c_ecl - c_lat * s_ecl, -c_lst * c_ecl, c_lat * s_lst * c_ecl + s_lat * s_ecl,
- s_lat * s_lst * -s_ecl - c_lat * c_ecl, c_lst * s_ecl, c_lat * s_lst * -s_ecl + s_lat * c_ecl
- };
-}
-
-double3x3 equatorial_to_ecliptic(double ecl)
-{
- const double c_ecl = std::cos(ecl);
- const double s_ecl = std::sin(ecl);
-
- return double3x3
- {
- 1.0, 0.0, 0.0,
- 0.0, c_ecl, -s_ecl,
- 0.0, s_ecl, c_ecl
- };
-}
-
-double3x3 equatorial_to_horizontal(double lat, double lst)
-{
- const double c_lat = std::cos(lat);
- const double s_lat = std::sin(lat);
- const double c_lst = std::cos(lst);
- const double s_lst = std::sin(lst);
-
- return double3x3
- {
- s_lat * c_lst, s_lst, c_lat * c_lst,
- s_lat * s_lst, -c_lst, c_lat * s_lst,
- -c_lat, 0.0, s_lat
- };
-}
-
-double3x3 horizontal_to_equatorial(double lat, double lst)
-{
- const double c_lat = std::cos(lat);
- const double s_lat = std::sin(lat);
- const double c_lst = std::cos(lst);
- const double s_lst = std::sin(lst);
-
- return double3x3
- {
- c_lst * s_lat, s_lst * s_lat, -c_lat,
- s_lst, c_lst, 0.0,
- c_lst * c_lat, s_lst * c_lat, s_lat
- };
-}
-
-double3x3 horizontal_to_ecliptic(double ecl, double lat, double lst)
-{
- const double c_ecl = std::cos(ecl);
- const double s_ecl = std::sin(ecl);
- const double c_lat = std::cos(lat);
- const double s_lat = std::sin(lat);
- const double c_lst = std::cos(lst);
- const double s_lst = std::sin(lst);
-
- return double3x3
- {
- s_lat * c_lst, s_lat * s_lst * c_ecl - c_lat * s_ecl, s_lat * s_lst * -s_ecl - c_lat * c_ecl,
- s_lst, -c_lst * c_ecl, c_lst * s_ecl,
- c_lat * c_lst, c_lat * s_lst * c_ecl + s_lat * s_ecl, c_lat * s_lst * -s_ecl + s_lat * c_ecl
- };
-}
-
-} // namespace astro
diff --git a/src/astro/coordinates.hpp b/src/astro/coordinates.hpp
deleted file mode 100644
index 5e76468..0000000
--- a/src/astro/coordinates.hpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2021 Christopher J. Howard
- *
- * This file is part of Antkeeper source code.
- *
- * Antkeeper source code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Antkeeper source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Antkeeper source code. If not, see .
- */
-
-#ifndef ANTKEEPER_ASTRO_COORDINATES_HPP
-#define ANTKEEPER_ASTRO_COORDINATES_HPP
-
-#include "utility/fundamental-types.hpp"
-
-namespace astro
-{
-
-/**
- * Converts rectangular coordinates to spherical coordinates.
- *
- * @param rectangular Rectangular coordinates.
- * @return Equivalent spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
- */
-double3 rectangular_to_spherical(const double3& rectangular);
-
-/**
- * Convert spherical coordinates to rectangular coordinates.
- *
- * @param spherical Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
- * @return Equivalent rectangular coordinates.
- */
-double3 spherical_to_rectangular(const double3& spherical);
-
-/**
- * Produces a matrix which transforms rectangular coordinates from ecliptic space to equatorial space.
- *
- * @param ecl Obliquity of the ecliptic, in radians.
- * @return Transformation matrix.
- */
-double3x3 ecliptic_to_equatorial(double ecl);
-
-/**
- * Produces a matrix which transforms coordinates from ecliptic space to horizontal space.
- *
- * @param ecl Obliquity of the ecliptic, in radians.
- * @param lat Observer's latitude, in radians.
- * @param lst Local sidereal time, in radians.
- * @return Transformation matrix.
- */
-double3x3 ecliptic_to_horizontal(double ecl, double lat, double lst);
-
-/**
- * Produces a matrix which transforms rectangular coordinates from equatorial space to ecliptic space.
- *
- * @param ecl Obliquity of the ecliptic, in radians.
- * @return Transformation matrix.
- */
-double3x3 equatorial_to_ecliptic(double ecl);
-
-/**
- * Produces a matrix which transforms rectangular coordinates from equatorial space to horizontal space.
- *
- * @param lat Observer's latitude, in radians.
- * @param lst Local sidereal time, in radians.
- * @return Transformation matrix.
- */
-double3x3 equatorial_to_horizontal(double lat, double lst);
-
-/**
- * Produces a matrix which transforms rectangular coordinates from horizontal space to equatorial space.
- *
- * @param lat Observer's latitude, in radians.
- * @param lst Local sidereal time, in radians.
- * @return Transformation matrix.
- */
-double3x3 horizontal_to_equatorial(double lat, double lst);
-
-/**
- * Produces a matrix which transforms rectangular coordinates from horizontal space to ecliptic space.
- *
- * @param ecl Obliquity of the ecliptic, in radians.
- * @param lat Observer's latitude, in radians.
- * @param lst Local sidereal time, in radians.
- * @return Transformation matrix.
- */
-double3x3 horizontal_to_ecliptic(double ecl, double lat, double lst);
-
-/// Matrix which transforms coordinates from horizontal space to a right-handed coordinate system.
-constexpr double3x3 horizontal_to_right_handed =
-{
- 0.0, 0.0, 1.0,
- 1.0, 0.0, 0.0,
- 0.0, -1.0, 0.0
-};
-
-} // namespace astro
-
-#endif // ANTKEEPER_ASTRO_COORDINATES_HPP
diff --git a/src/coordinates/coordinates.hpp b/src/coordinates/coordinates.hpp
new file mode 100644
index 0000000..4751438
--- /dev/null
+++ b/src/coordinates/coordinates.hpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_HPP
+#define ANTKEEPER_COORDINATES_HPP
+
+#include "utility/fundamental-types.hpp"
+
+/// Functions for converting between coordinate systems.
+namespace coordinates {}
+
+#include "ecliptic.hpp"
+#include "equatorial.hpp"
+#include "horizontal.hpp"
+#include "rectangular.hpp"
+#include "spherical.hpp"
+
+#endif // ANTKEEPER_COORDINATES_HPP
diff --git a/src/coordinates/ecliptic.hpp b/src/coordinates/ecliptic.hpp
new file mode 100644
index 0000000..2277f61
--- /dev/null
+++ b/src/coordinates/ecliptic.hpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_ECLIPTIC_HPP
+#define ANTKEEPER_COORDINATES_ECLIPTIC_HPP
+
+#include "coordinates/rectangular.hpp"
+#include "coordinates/spherical.hpp"
+#include "utility/fundamental-types.hpp"
+#include
+
+namespace coordinates {
+
+namespace rectangular {
+
+ /// Rectangular coordinate system with the plane of the Earth's orbit as the fundamental plane. This is a right-handed coordinate system with the x-axis pointing to the vernal equinox, the y-axis pointing east, and the z-axis is the north orbital pole.
+ namespace ecliptic {
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from ecliptic space into equatorial space.
+ *
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::equatorial
+ */
+ template
+ math::matrix3 to_equatorial(T ecl);
+
+ /**
+ * Rotates rectangular coordinates from ecliptic space into equatorial space.
+ *
+ * @param v Rectangular coordinates in ecliptic space.
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Rectangular coordinates in equatorial space.
+ *
+ * @note If more than one point is being rotated, it is recommended to use the rotation matrix directly.
+ *
+ * @see coordinates::ecliptic::rectangular::to_equatorial(T)
+ * @see coordinates::equatorial
+ */
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T ecl);
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from ecliptic space into local horizontal space.
+ *
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::horizontal
+ */
+ template
+ math::matrix3 to_horizontal(T ecl, T lat, T lst);
+
+ /**
+ * Rotates rectangular coordinates from ecliptic space into local horizontal space.
+ *
+ * @param v Rectangular coordinates in ecliptic space.
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rectangular coordinates in local horizontal space.
+ *
+ * @note If more than one point is being rotated, it is recommended to use the rotation matrix directly.
+ *
+ * @see coordinates::ecliptic::rectangular::to_horizontal(T, T, T)
+ * @see coordinates::horizontal
+ */
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T ecl, T lat, T lst);
+
+ } // namespace ecliptic
+
+} // namespace rectangular
+
+namespace spherical {
+
+ /// Spherical equatorial coordinate system.
+ namespace ecliptic {
+
+ /**
+ * Rotates spherical coordinates from ecliptic space into equatorial space.
+ *
+ * @param v Spherical coordinates in ecliptic space, in the ISO order of radial distance, ecliptic latitude (radians), and ecliptic longitude (radians).
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Spherical coordinates in equatorial space, in the ISO order of radial distance, declination (radians), and right ascension (radians).
+ *
+ * @see coordinates::equatorial
+ */
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T ecl);
+
+ /**
+ * Rotates spherical coordinates from ecliptic space into local horizontal space.
+ *
+ * @param v Spherical coordinates in ecliptic space, in the ISO order of radial distance, ecliptic latitude (radians), and ecliptic longitude (radians).
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Spherical coordinates in local horizontal space, in the ISO order of radial distance, altitude (radians), and azimuth (radians).
+ *
+ * @see coordinates::horizontal
+ */
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T ecl, T lat, T lst);
+
+ } // namespace ecliptic
+
+} // namespace spherical
+
+namespace rectangular {
+
+ namespace ecliptic {
+
+ template
+ math::matrix3 to_equatorial(T ecl)
+ {
+ const T c_ecl = std::cos(ecl);
+ const T s_ecl = std::sin(ecl);
+
+ return math::matrix3
+ {
+ T(1.0), T(0.0), T(0.0),
+ T(0.0), c_ecl, s_ecl,
+ T(0.0), -s_ecl, c_ecl
+ };
+ }
+
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T ecl)
+ {
+ return to_equatorial(ecl) * v;
+ }
+
+ template
+ math::matrix3 to_horizontal(T ecl, T lat, T lst)
+ {
+ const T c_ecl = std::cos(ecl);
+ const T s_ecl = std::sin(ecl);
+ const T c_lat = std::cos(lat);
+ const T s_lat = std::sin(lat);
+ const T c_lst = std::cos(lst);
+ const T s_lst = std::sin(lst);
+
+ return math::matrix3
+ {
+ s_lat * c_lst, s_lst, c_lat * c_lst,
+ s_lat * s_lst * c_ecl - c_lat * s_ecl, -c_lst * c_ecl, c_lat * s_lst * c_ecl + s_lat * s_ecl,
+ s_lat * s_lst * -s_ecl - c_lat * c_ecl, c_lst * s_ecl, c_lat * s_lst * -s_ecl + s_lat * c_ecl
+ };
+ }
+
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T ecl, T lat, T lst)
+ {
+ return to_horizontal(ecl, lat, lst) * v;
+ }
+
+ } // namespace ecliptic
+
+} // namespace rectangular
+
+namespace spherical {
+
+ namespace ecliptic {
+
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T ecl)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::ecliptic::to_equatorial
+ (
+ coordinates::spherical::to_rectangular(v),
+ ecl
+ )
+ );
+ }
+
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T ecl, T lat, T lst)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::ecliptic::to_horizontal
+ (
+ coordinates::spherical::to_rectangular(v),
+ ecl,
+ lat,
+ lst
+ )
+ );
+ }
+
+ } // namespace ecliptic
+
+} // namespace spherical
+
+} // namespace coordinates
+
+#endif // ANTKEEPER_COORDINATES_ECLIPTIC_HPP
diff --git a/src/coordinates/equatorial.hpp b/src/coordinates/equatorial.hpp
new file mode 100644
index 0000000..30f86e3
--- /dev/null
+++ b/src/coordinates/equatorial.hpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_EQUATORIAL_HPP
+#define ANTKEEPER_COORDINATES_EQUATORIAL_HPP
+
+#include "coordinates/rectangular.hpp"
+#include "coordinates/spherical.hpp"
+#include "utility/fundamental-types.hpp"
+#include
+
+namespace coordinates {
+
+namespace rectangular {
+
+ /// Rectangular coordinate system with the Earth's equator as the fundamental plane. This is a right-handed coordinate system with the x-axis pointing to the vernal equinox, the y-axis pointing east, and the z-axis is the north celestial pole.
+ namespace equatorial {
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from equatorial space into ecliptic space.
+ *
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::matrix3 to_ecliptic(T ecl);
+
+ /**
+ * Rotates rectangular coordinates from equatorial space into ecliptic space.
+ *
+ * @param v Rectangular coordinates in equatorial space.
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Rectangular coordinates in ecliptic space.
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl);
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from equatorial space into local horizontal space.
+ *
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::horizontal
+ */
+ template
+ math::matrix3 to_horizontal(T lat, T lst);
+
+ /**
+ * Rotates rectangular coordinates from equatorial space into local horizontal space.
+ *
+ * @param v Rectangular coordinates in equatorial space.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rectangular coordinates in local horizontal space.
+ *
+ * @see coordinates::horizontal
+ */
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T lat, T lst);
+
+ } // namespace equatorial
+
+} // namespace rectangular
+
+namespace spherical {
+
+ /// Spherical equatorial coordinate system.
+ namespace equatorial {
+
+ /**
+ * Rotates spherical coordinates from equatorial space into ecliptic space.
+ *
+ * @param v Spherical coordinates in equatorial space, in the ISO order of radial distance, declination (radians), and right ascension (radians).
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @return Spherical coordinates in ecliptic space, in the ISO order of radial distance, ecliptic latitude (radians), and ecliptic longitude (radians).
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl);
+
+ /**
+ * Rotates spherical coordinates from equatorial space into local horizontal space.
+ *
+ * @param v Spherical coordinates in equatorial space, in the ISO order of radial distance, declination (radians), and right ascension (radians).
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Spherical coordinates in local horizontal space, in the ISO order of radial distance, altitude (radians), and azimuth (radians).
+ *
+ * @see coordinates::horizontal
+ */
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T lat, T lst);
+
+ } // namespace equatorial
+
+} // namespace spherical
+
+namespace rectangular {
+
+ namespace equatorial {
+
+ template
+ math::matrix3 to_ecliptic(T ecl)
+ {
+ const T c_ecl = std::cos(ecl);
+ const T s_ecl = std::sin(ecl);
+
+ return math::matrix3
+ {
+ T(1.0), T(0.0), T(0.0),
+ T(0.0), c_ecl, -s_ecl,
+ T(0.0), s_ecl, c_ecl
+ };
+ }
+
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl)
+ {
+ return to_ecliptic(ecl) * v;
+ }
+
+ template
+ math::matrix3 to_horizontal(T lat, T lst)
+ {
+ const T c_lat = std::cos(lat);
+ const T s_lat = std::sin(lat);
+ const T c_lst = std::cos(lst);
+ const T s_lst = std::sin(lst);
+
+ return math::matrix3
+ {
+ s_lat * c_lst, s_lst, c_lat * c_lst,
+ s_lat * s_lst, -c_lst, c_lat * s_lst,
+ -c_lat, T(0.0), s_lat
+ };
+ }
+
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T lat, T lst)
+ {
+ return to_horizontal(lat, lst) * v;
+ }
+
+ } // namespace equatorial
+
+} // namespace rectangular
+
+namespace spherical {
+
+ namespace equatorial {
+
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::equatorial::to_ecliptic
+ (
+ coordinates::spherical::to_rectangular(v),
+ ecl
+ )
+ );
+ }
+
+ template
+ math::vector3 to_horizontal(const math::vector3& v, T lat, T lst)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::equatorial::to_horizontal
+ (
+ coordinates::spherical::to_rectangular(v),
+ lat,
+ lst
+ )
+ );
+ }
+
+ } // namepace equatorial
+
+} // namespace spherical
+
+} // namespace coordinates
+
+#endif // ANTKEEPER_COORDINATES_EQUATORIAL_HPP
diff --git a/src/coordinates/horizontal.hpp b/src/coordinates/horizontal.hpp
new file mode 100644
index 0000000..23f08ec
--- /dev/null
+++ b/src/coordinates/horizontal.hpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_HORIZONTAL_HPP
+#define ANTKEEPER_COORDINATES_HORIZONTAL_HPP
+
+#include "coordinates/rectangular.hpp"
+#include "coordinates/spherical.hpp"
+#include "utility/fundamental-types.hpp"
+#include
+
+namespace coordinates {
+
+namespace rectangular {
+
+ /// Rectangular local horizontal coordinate system in which the x-axis points north, the y-axis points east, and the z-axis points to the vertical.
+ namespace horizontal {
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from local horizontal space into equatorial space.
+ *
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::equatorial
+ */
+ template
+ math::matrix3 to_equatorial(T lat, T lst);
+
+ /**
+ * Rotates rectangular coordinates from local horizontal space into equatorial space.
+ *
+ * @param v Rectangular coordinates in local horizontal space.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rectangular coordinates in equatorial space.
+ *
+ * @see coordinates::equatorial
+ */
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T lat, T lst);
+
+ /**
+ * Produces a matrix which rotates rectangular coordinates from local horizontal space into ecliptic space.
+ *
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rotation matrix.
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::matrix3 to_ecliptic(T ecl, T lat, T lst);
+
+ /**
+ * Rotates rectangular coordinates from local horizontal space into ecliptic space.
+ *
+ * @param v Rectangular coordinates in local horizontal space.
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Rectangular coordinates in ecliptic space.
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl, T lat, T lst);
+
+ } // namespace horizontal
+
+} // namespace rectangular
+
+namespace spherical {
+
+ /// Spherical local horizontal coordinate system.
+ namespace horizontal {
+
+ /**
+ * Rotates spherical coordinates from local horizontal space into equatorial space.
+ *
+ * @param v Spherical coordinates in local horizontal space, in the ISO order of radial distance, altitude (radians), and azimuth (radians).
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Spherical coordinates in equatorial space, in the ISO order of radial distance, declination (radians), and right ascension (radians).
+ *
+ * @see coordinates::equatorial
+ */
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T lat, T lst);
+
+ /**
+ * Rotates spherical coordinates from local horizontal space into ecliptic space.
+ *
+ * @param v Spherical coordinates in local horizontal space, in the ISO order of radial distance, altitude (radians), and azimuth (radians).
+ * @param ecl Obliquity of the ecliptic, in radians.
+ * @param lat Observer's latitude, in radians.
+ * @param lst Local sidereal time, in radians.
+ * @return Spherical coordinates in ecliptic space, in the ISO order of radial distance, ecliptic latitude (radians), and ecliptic longitude (radians).
+ *
+ * @see coordinates::ecliptic
+ */
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl, T lat, T lst);
+
+ } // namespace horizontal
+
+} // namespace spherical
+
+namespace rectangular {
+
+ namespace horizontal {
+
+ template
+ math::matrix3 to_equatorial(T lat, T lst)
+ {
+ const T c_lat = std::cos(lat);
+ const T s_lat = std::sin(lat);
+ const T c_lst = std::cos(lst);
+ const T s_lst = std::sin(lst);
+
+ return math::matrix3
+ {
+ c_lst * s_lat, s_lst * s_lat, -c_lat,
+ s_lst, c_lst, T(0.0),
+ c_lst * c_lat, s_lst * c_lat, s_lat
+ };
+ }
+
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T lat, T lst)
+ {
+ return to_equatorial(lat, lst) * v;
+ }
+
+ template
+ math::matrix3 to_ecliptic(T ecl, T lat, T lst)
+ {
+ const T c_ecl = std::cos(ecl);
+ const T s_ecl = std::sin(ecl);
+ const T c_lat = std::cos(lat);
+ const T s_lat = std::sin(lat);
+ const T c_lst = std::cos(lst);
+ const T s_lst = std::sin(lst);
+
+ return math::matrix3
+ {
+ s_lat * c_lst, s_lat * s_lst * c_ecl - c_lat * s_ecl, s_lat * s_lst * -s_ecl - c_lat * c_ecl,
+ s_lst, -c_lst * c_ecl, c_lst * s_ecl,
+ c_lat * c_lst, c_lat * s_lst * c_ecl + s_lat * s_ecl, c_lat * s_lst * -s_ecl + s_lat * c_ecl
+ };
+ }
+
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl, T lat, T lst)
+ {
+ return to_ecliptic(ecl, lat, lst) * v;
+ }
+
+ } // namespace horizontal
+
+} // namespace rectangular
+
+namespace spherical {
+
+ namespace horizontal {
+
+ template
+ math::vector3 to_equatorial(const math::vector3& v, T lat, T lst)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::horizontal::to_equatorial
+ (
+ coordinates::spherical::to_rectangular(v),
+ lat,
+ lst
+ )
+ );
+ }
+
+ template
+ math::vector3 to_ecliptic(const math::vector3& v, T ecl, T lat, T lst)
+ {
+ return coordinates::rectangular::to_spherical
+ (
+ coordinates::rectangular::horizontal::to_ecliptic
+ (
+ coordinates::spherical::to_rectangular(v),
+ ecl,
+ lat,
+ lst
+ )
+ );
+ }
+
+ } // namespace horizontal
+
+} // namespace spherical
+
+} // namespace coordinates
+
+#endif // ANTKEEPER_COORDINATES_HORIZONTAL_HPP
diff --git a/src/coordinates/rectangular.hpp b/src/coordinates/rectangular.hpp
new file mode 100644
index 0000000..382e537
--- /dev/null
+++ b/src/coordinates/rectangular.hpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_RECTANGULAR_HPP
+#define ANTKEEPER_COORDINATES_RECTANGULAR_HPP
+
+#include "utility/fundamental-types.hpp"
+#include
+
+namespace coordinates {
+
+/// Rectangular (Cartesian) coordinate systems.
+namespace rectangular {
+
+/**
+ * Produces a matrix which rotates rectangular coordinates about the x-axis by a given angle.
+ *
+ * @param angle Angle of rotation, in radians.
+ * @return Rotation matrix.
+ */
+template
+math::matrix3 rotate_x(T angle);
+
+/**
+ * Rotates rectangular coordinates about the x-axis.
+ *
+ * @param v Rectangular coordinates to rotate.
+ * @param angle Angle of rotation, in radians.
+ * @return Rotated rectangular coordinates.
+ */
+template
+math::vector3 rotate_x(const math::vector3& v, T angle);
+
+/**
+ * Produces a matrix which rotates rectangular coordinates about the y-axis by a given angle.
+ *
+ * @param angle Angle of rotation, in radians.
+ * @return Rotation matrix.
+ */
+template
+math::matrix3 rotate_y(T angle);
+
+/**
+ * Rotates rectangular coordinates about the y-axis.
+ *
+ * @param v Rectangular coordinates to rotate.
+ * @param angle Angle of rotation, in radians.
+ * @return Rotated rectangular coordinates.
+ */
+template
+math::vector3 rotate_y(const math::vector3& v, T angle);
+
+/**
+ * Produces a matrix which rotates rectangular coordinates about the z-axis by a given angle.
+ *
+ * @param angle Angle of rotation, in radians.
+ * @return Rotation matrix.
+ */
+template
+math::matrix3 rotate_z(T angle);
+
+/**
+ * Rotates rectangular coordinates about the z-axis.
+ *
+ * @param v Rectangular coordinates to rotate.
+ * @param angle Angle of rotation, in radians.
+ * @return Rotated rectangular coordinates.
+ */
+template
+math::vector3 rotate_z(const math::vector3& v, T angle);
+
+/**
+ * Converts rectangular coordinates to spherical coordinates.
+ *
+ * @param v Rectangular coordinates.
+ * @return Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
+ *
+ * @see coordinates::spherical
+ */
+template
+math::vector3 to_spherical(const math::vector3& v);
+
+template
+math::vector3 to_spherical(const math::vector3& v)
+{
+ const T xx_yy = v.x * v.x + v.y * v.y;
+
+ return math::vector3
+ {
+ std::sqrt(xx_yy + v.z * v.z),
+ std::atan2(v.z, std::sqrt(xx_yy)),
+ std::atan2(v.y, v.x)
+ };
+}
+
+template
+math::matrix3 rotate_x(T angle)
+{
+ const T c = std::cos(angle);
+ const T s = std::sin(angle);
+
+ return math::matrix3
+ {
+ T(1), T(0), T(0),
+ T(0), c, -s,
+ T(0), s, c
+ };
+}
+
+template
+math::vector3 rotate_x(const math::vector3& v, T angle)
+{
+ return rotate_x(angle) * v;
+}
+
+template
+math::matrix3 rotate_y(T angle)
+{
+ const T c = std::cos(angle);
+ const T s = std::sin(angle);
+
+ return math::matrix3
+ {
+ c, T(0), s,
+ T(0), T(1), T(0),
+ -s, T(0), c
+ };
+}
+
+template
+math::vector3 rotate_y(const math::vector3& v, T angle)
+{
+ return rotate_y(angle) * v;
+}
+
+template
+math::matrix3 rotate_z(T angle)
+{
+ const T c = std::cos(angle);
+ const T s = std::sin(angle);
+
+ return math::matrix3
+ {
+ c, -s, T(0),
+ s, c, T(0),
+ T(0), T(0), T(1)
+ };
+}
+
+template
+math::vector3 rotate_z(const math::vector3& v, T angle)
+{
+ return rotate_z(angle) * v;
+}
+
+} // namespace rectangular
+} // namespace coordinates
+
+#endif // ANTKEEPER_COORDINATES_RECTANGULAR_HPP
diff --git a/src/coordinates/spherical.hpp b/src/coordinates/spherical.hpp
new file mode 100644
index 0000000..649439c
--- /dev/null
+++ b/src/coordinates/spherical.hpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 Christopher J. Howard
+ *
+ * This file is part of Antkeeper source code.
+ *
+ * Antkeeper source code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Antkeeper source code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Antkeeper source code. If not, see .
+ */
+
+#ifndef ANTKEEPER_COORDINATES_SPHERICAL_HPP
+#define ANTKEEPER_COORDINATES_SPHERICAL_HPP
+
+#include "utility/fundamental-types.hpp"
+#include
+
+namespace coordinates {
+
+/// Spherical coordinate systems.
+namespace spherical {
+
+/**
+ * Converts spherical coordinates to rectangular coordinates.
+ *
+ * @param v Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians).
+ * @return Rectangular coordinates.
+ *
+ * @see coordinates::rectangular
+ */
+template
+math::vector3 to_rectangular(const math::vector3& v);
+
+template
+math::vector3 to_rectangular(const math::vector3& v)
+{
+ const T x = v[0] * std::cos(v[1]);
+
+ return math::vector3
+ {
+ x * std::cos(v[2]),
+ x * std::sin(v[2]),
+ v[0] * std::sin(v[1]),
+ };
+}
+
+} // namespace spherical
+} // namespace coordinates
+
+#endif // ANTKEEPER_COORDINATES_SPHERICAL_HPP
diff --git a/src/ecs/systems/astronomy-system.cpp b/src/ecs/systems/astronomy-system.cpp
index d74cb68..a6470e5 100644
--- a/src/ecs/systems/astronomy-system.cpp
+++ b/src/ecs/systems/astronomy-system.cpp
@@ -18,7 +18,7 @@
*/
#include "ecs/systems/astronomy-system.hpp"
-#include "astro/coordinates.hpp"
+#include "coordinates/coordinates.hpp"
#include "astro/apparent-size.hpp"
#include "ecs/components/celestial-body-component.hpp"
#include "ecs/components/transform-component.hpp"
@@ -60,15 +60,21 @@ void astronomy_system::update(double t, double dt)
horizontal.z -= observer_location[0];
// Convert rectangular horizontal coordinates to spherical
- double3 spherical = astro::rectangular_to_spherical(horizontal);
- spherical.z -= math::pi;
+ double3 spherical = coordinates::rectangular::to_spherical(horizontal);
// Find angular radius
- double angular_radius = astro::find_angular_radius(body.radius, spherical.x);
+ double angular_radius = astro::find_angular_radius(body.radius, spherical[0]);
- // Transform into local right-handed coordinates
- double3 translation = astro::horizontal_to_right_handed * horizontal;
- double3x3 rotation = astro::horizontal_to_right_handed * ecliptic_to_horizontal;
+ // Transform into local coordinates
+ const double3x3 horizontal_to_local =
+ {
+ 0.0, 0.0, -1.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ };
+
+ double3 translation = horizontal_to_local * horizontal;
+ double3x3 rotation = horizontal_to_local * ecliptic_to_horizontal;
// Set local transform of transform component
transform.local.translation = math::type_cast(translation);
@@ -180,7 +186,7 @@ void astronomy_system::update_sidereal_time()
void astronomy_system::update_ecliptic_to_horizontal()
{
- ecliptic_to_horizontal = astro::ecliptic_to_horizontal(obliquity, observer_location[1], lst);
+ ecliptic_to_horizontal = coordinates::rectangular::ecliptic::to_horizontal(obliquity, observer_location[1], lst);
}
} // namespace ecs
diff --git a/src/ecs/systems/solar-system.cpp b/src/ecs/systems/solar-system.cpp
index fd4e3c4..322ade7 100644
--- a/src/ecs/systems/solar-system.cpp
+++ b/src/ecs/systems/solar-system.cpp
@@ -18,7 +18,7 @@
*/
#include "ecs/systems/solar-system.hpp"
-#include "astro/coordinates.hpp"
+#include "coordinates/coordinates.hpp"
#include "astro/orbit.hpp"
#include "astro/constants.hpp"
#include "ecs/components/celestial-body-component.hpp"
diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp
index 77d3a9f..d9e7eac 100644
--- a/src/game/bootloader.cpp
+++ b/src/game/bootloader.cpp
@@ -1180,9 +1180,9 @@ void setup_controls(game_context* ctx)
(
[ctx, time_scale]()
{
- ctx->weather_system->set_time_scale(time_scale * 500.0f);
- ctx->solar_system->set_time_scale(time_scale * 500.0f);
- ctx->astronomy_system->set_time_scale(time_scale * 500.0f);
+ ctx->weather_system->set_time_scale(time_scale * 100.0f);
+ ctx->solar_system->set_time_scale(time_scale * 100.0f);
+ ctx->astronomy_system->set_time_scale(time_scale * 100.0f);
}
);
ctx->control_system->get_fast_forward_control()->set_deactivated_callback
@@ -1198,9 +1198,9 @@ void setup_controls(game_context* ctx)
(
[ctx, time_scale]()
{
- ctx->weather_system->set_time_scale(time_scale * -500.0f);
- ctx->solar_system->set_time_scale(time_scale * -500.0f);
- ctx->astronomy_system->set_time_scale(time_scale * -500.0f);
+ ctx->weather_system->set_time_scale(time_scale * -100.0f);
+ ctx->solar_system->set_time_scale(time_scale * -100.0f);
+ ctx->astronomy_system->set_time_scale(time_scale * -100.0f);
}
);
ctx->control_system->get_rewind_control()->set_deactivated_callback
diff --git a/src/game/states/play-state.cpp b/src/game/states/play-state.cpp
index c2c9a19..321cb7b 100644
--- a/src/game/states/play-state.cpp
+++ b/src/game/states/play-state.cpp
@@ -234,7 +234,7 @@ void play_state_enter(game_context* ctx)
auto nest_entity = nest_archetype->create(ecs_registry);
// Create terrain
- int terrain_radius = 6;
+ int terrain_radius = 0;//6;
for (int x = -terrain_radius; x <= terrain_radius; ++x)
{
for (int z = -terrain_radius; z <= terrain_radius; ++z)
@@ -249,7 +249,7 @@ void play_state_enter(game_context* ctx)
}
// Create trees
- for (int i = 0; i < 30; ++i)
+ for (int i = 0; i < 0; ++i)
{
auto redwood = redwood_archetype->create(ecs_registry);
diff --git a/src/renderer/passes/sky-pass.cpp b/src/renderer/passes/sky-pass.cpp
index e127084..c15c033 100644
--- a/src/renderer/passes/sky-pass.cpp
+++ b/src/renderer/passes/sky-pass.cpp
@@ -38,8 +38,9 @@
#include "scene/camera.hpp"
#include "utility/fundamental-types.hpp"
#include "color/color.hpp"
+#include "astro/illuminance.hpp"
#include "math/interpolation.hpp"
-#include "astro/astro.hpp"
+#include "coordinates/coordinates.hpp"
#include
#include
#include
@@ -107,9 +108,8 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
ra = math::wrap_radians(math::radians(ra));
dec = math::wrap_radians(math::radians(dec));
- // Transform spherical coordinates to rectangular (Cartesian) coordinates
- double3 spherical = {1.0, dec, ra};
- double3 rectangular = astro::spherical_to_rectangular(spherical);
+ // Transform spherical equatorial coordinates to rectangular equatorial coordinates
+ double3 position = coordinates::spherical::to_rectangular(double3{1.0, dec, ra});
// Convert color index to color temperature
double cct = color::index::bv_to_cct(bv_color);
@@ -130,9 +130,9 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
double3 scaled_color = color_acescg * vmag_lux;
// Build vertex
- *(star_vertex++) = static_cast(rectangular.x);
- *(star_vertex++) = static_cast(rectangular.y);
- *(star_vertex++) = static_cast(rectangular.z);
+ *(star_vertex++) = static_cast(position.x);
+ *(star_vertex++) = static_cast(position.y);
+ *(star_vertex++) = static_cast(position.z);
*(star_vertex++) = static_cast(scaled_color.x);
*(star_vertex++) = static_cast(scaled_color.y);
*(star_vertex++) = static_cast(scaled_color.z);
@@ -287,19 +287,35 @@ void sky_pass::render(render_context* context) const
// Draw stars
{
- float star_distance = (clip_near + clip_far) * 0.5f;
+ float star_distance = (clip_near + clip_far) * 0.5f;
- math::quaternion rotation_y = math::angle_axis(time_of_day / 24.0f * -math::two_pi, {0, 1, 0});
- math::quaternion rotation_x = math::angle_axis(-math::half_pi + math::radians(30.0f), {1, 0, 0});
+ double lat = math::radians(30.0);
+ double lst = time_of_day / 24.0f * math::two_pi;
+ //std::cout << "lst: " << lst << std::endl;
- math::transform star_transform;
- star_transform.translation = {0.0, 0.0, 0.0};
+ double3x3 equatorial_to_horizontal = coordinates::rectangular::equatorial::to_horizontal(lat, lst);
+
+ const double3x3 horizontal_to_local =
+ {
+ 0.0, 0.0, -1.0,
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0
+ };
+
+ double3x3 rotation = horizontal_to_local * equatorial_to_horizontal;
+ //math::quaternion rotation_y = math::angle_axis(time_of_day / 24.0f * -math::two_pi, {0, 1, 0});
+ //math::quaternion rotation_x = math::angle_axis(-math::half_pi + math::radians(30.0f), {1, 0, 0});
+
+ model = math::type_cast(math::scale(math::resize<4, 4>(rotation), double3{star_distance, star_distance, star_distance}));;
+
+ //math::transform star_transform;
+ //star_transform.translation = {0.0, 0.0, 0.0};
//star_transform.rotation = math::normalize(rotation_x * rotation_y);
- star_transform.rotation = math::normalize(rotation_x * rotation_y * math::angle_axis(math::radians(-90.0f), {1, 0, 0}));
+ //star_transform.rotation = math::normalize(math::type_cast(math::quaternion_cast(rotation)));
//star_transform.rotation = math::identity_quaternion;
- star_transform.scale = {star_distance, star_distance, star_distance};
+ //star_transform.scale = {star_distance, star_distance, star_distance};
+ //model = math::matrix_cast(star_transform);
- model = math::matrix_cast(star_transform);
model_view = view * model;
rasterizer->use_program(*star_shader_program);