aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_sphere_noise.py
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:52:03 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:52:03 +0200
commit346b1c5195bffc71ceaa9262453e3c189656400b (patch)
tree01ac0d31e2724cd6abcc689a5a228e2cbea2f6cf /tests/test_sphere_noise.py
downloadworldgen-346b1c5195bffc71ceaa9262453e3c189656400b.tar.gz
worldgen-346b1c5195bffc71ceaa9262453e3c189656400b.zip
worldgen: initial public history
Diffstat (limited to 'tests/test_sphere_noise.py')
-rw-r--r--tests/test_sphere_noise.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/tests/test_sphere_noise.py b/tests/test_sphere_noise.py
new file mode 100644
index 0000000..a3c325e
--- /dev/null
+++ b/tests/test_sphere_noise.py
@@ -0,0 +1,86 @@
+import unittest
+
+import numpy as np
+
+from mapgen import noise as N
+from mapgen import sphere as S
+
+
+class SphereTest(unittest.TestCase):
+ def test_roundtrip(self):
+ lat = np.array([0.0, 45.0, -60.0, 89.0])
+ lon = np.array([0.0, 120.0, -170.0, 10.0])
+ la, lo = S.xyz_to_latlon(S.latlon_to_xyz(lat, lon))
+ np.testing.assert_allclose(la, lat, atol=1e-9)
+ np.testing.assert_allclose(lo, lon, atol=1e-9)
+
+ def test_east_north_at_pole(self):
+ p = S.latlon_to_xyz(np.array([90.0, -90.0, 0.0]), np.array([0.0, 0.0, 0.0]))
+ e, n = S.east_north(p)
+ self.assertTrue(np.all(np.isfinite(e)) and np.all(np.isfinite(n)))
+ np.testing.assert_allclose(np.linalg.norm(e, axis=1), 1.0)
+ np.testing.assert_allclose(np.sum(e * p, axis=1), 0.0, atol=1e-12)
+ # at the equator/prime meridian east = +y, north = +z
+ np.testing.assert_allclose(e[2], [0, 1, 0], atol=1e-12)
+ np.testing.assert_allclose(n[2], [0, 0, 1], atol=1e-12)
+
+ def test_motion_velocity_matches_request(self):
+ R = 12742.0
+ om = S.motion_to_omega(10.0, 20.0, 90.0, 5.0, R) # due east, 5 cm/yr
+ p = S.latlon_to_xyz(np.array([10.0]), np.array([20.0]))
+ v = S.velocity(p, om, R)[0]
+ e, n = S.east_north(p)
+ self.assertAlmostEqual(float(np.dot(v, e[0])), 0.05, places=6)
+ self.assertAlmostEqual(float(np.dot(v, n[0])), 0.0, places=6)
+
+ def test_zero_speed_is_zero_velocity(self):
+ om = S.motion_to_omega(0.0, 0.0, 45.0, 0.0, 12742.0)
+ self.assertTrue(np.allclose(om, 0.0))
+
+ def test_rotate_and_azimuth(self):
+ p = S.latlon_to_xyz(np.array([0.0]), np.array([0.0]))
+ e, n = S.east_north(p)
+ r = S.rotate_about(p, e, np.pi / 2) # CCW seen from outside: east -> north
+ np.testing.assert_allclose(r, n, atol=1e-12)
+ q = S.latlon_to_xyz(np.array([0.0, 10.0]), np.array([10.0, 0.0]))
+ np.testing.assert_allclose(S.azimuth_deg(p[0], q), [90.0, 0.0], atol=1e-9)
+
+ def test_great_circle_point(self):
+ p0 = S.latlon_to_xyz(np.array([0.0]), np.array([0.0]))[0]
+ e, _ = S.east_north(p0[None])
+ R = 12742.0
+ q = S.great_circle_point(p0, e[0], np.pi * R / 2, R) # quarter turn east
+ np.testing.assert_allclose(q, [0, 1, 0], atol=1e-12)
+
+
+class NoiseTest(unittest.TestCase):
+ def test_deterministic_and_bounded(self):
+ rng = np.random.default_rng(0)
+ p = rng.normal(size=(5000, 3))
+ p /= np.linalg.norm(p, axis=1, keepdims=True)
+ a = N.fbm(p, 42)
+ b = N.fbm(p, 42)
+ c = N.fbm(p, 43)
+ np.testing.assert_array_equal(a, b)
+ self.assertFalse(np.allclose(a, c))
+ self.assertTrue(np.all(np.abs(a) <= 1.0))
+ self.assertGreater(a.std(), 0.05)
+ r = N.ridged(p, 1)
+ self.assertTrue(np.all((r >= 0) & (r <= 1)))
+
+ def test_continuity(self):
+ p = np.array([[0.3, 0.4, 0.5]])
+ d = N.fbm(p + 1e-6, 5) - N.fbm(p, 5)
+ self.assertLess(abs(d[0]), 1e-3)
+
+
+class CompiledNoiseTest(unittest.TestCase):
+ def test_compiled_value_noise_matches_numpy(self):
+ from mapgen import noise as N
+ if N._noise_jit is None:
+ self.skipTest("numba not installed")
+ rng = np.random.default_rng(0)
+ for scale in (1.0, 37.0, 1e4, 1e7):
+ for seed in (0, 4242, 123456789, -5, 2 ** 40):
+ p = rng.normal(0, scale, (2000, 3))
+ self.assertTrue(np.array_equal(N.value_noise(p, seed), N._value_noise(p, seed)), (scale, seed))