worldgen

git clone https://git.godosa.eu/worldgen

master

raw ยท 3549 bytes

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))