"""The cone's composition as a point in a 3-simplex: coordinates, camera and the mark. Numpy only; nothing here needs gradio or a model. """ import re import numpy as np import pytest from ulens import simplex, terms def test_the_vertices_are_a_regular_tetrahedron_about_the_origin(): V = simplex.VERT assert V.shape == (4, 3) np.testing.assert_allclose(V.sum(0), 0, atol=1e-6) # barycentre at 0 np.testing.assert_allclose(np.linalg.norm(V, axis=1), 1, atol=1e-6) # circumradius 1 d = [np.linalg.norm(V[i] - V[j]) for i in range(4) for j in range(i + 1, 4)] np.testing.assert_allclose(d, d[0], atol=1e-6) # six equal edges assert len(terms.categories()) == 4 # one vertex per axis def test_shares_sum_to_one_and_a_zero_row_is_undefined(): C = np.array([[1.0, 2.0, 3.0, 4.0], [0.5, 0.5, 0.5, 0.5], [0.0, 0.0, 0.0, 0.0]]) W = simplex.shares(C) np.testing.assert_allclose(W[:2].sum(1), 1.0) np.testing.assert_allclose(W[0], [0.1, 0.2, 0.3, 0.4]) assert np.isnan(W[2]).all() # no composition at all assert (simplex.shares(np.full((5, 4), 2.0)) == 0.25).all() def test_a_pure_one_axis_state_lands_on_its_vertex_and_the_centroid_at_the_origin(): P = simplex.bary(np.eye(4)) np.testing.assert_allclose(P, simplex.VERT, atol=1e-6) np.testing.assert_allclose(simplex.bary(np.full((1, 4), 0.25)), 0, atol=1e-6) # a state between two axes sits on their edge's midpoint np.testing.assert_allclose(simplex.bary(np.array([[0.5, 0.5, 0, 0]]))[0], (simplex.VERT[0] + simplex.VERT[1]) / 2, atol=1e-6) def test_boundary_fraction_is_zero_at_the_centre_and_one_on_the_surface(): V = simplex.VERT assert simplex.boundary_fraction(np.zeros((1, 3)))[0] == pytest.approx(0.0) assert simplex.boundary_fraction(V)[0] == pytest.approx(1.0) # a vertex face = V[:3].mean(0) # a face centre assert simplex.boundary_fraction(face[None])[0] == pytest.approx(1.0) assert simplex.boundary_fraction((0.5 * face)[None])[0] == pytest.approx(0.5) assert simplex.boundary_fraction((1.5 * face)[None])[0] > 1.0 # outside def test_the_camera_sends_the_origin_to_the_screen_centre_and_keeps_the_paper_s_layout(): xy = simplex.project(np.zeros((1, 3))) np.testing.assert_allclose(xy, 0, atol=1e-9) amb, inc, ce, gu = simplex.project(simplex.VERT) assert gu[1] > amb[1] > 0 > inc[1] > ce[1] # general uncertainty on top assert inc[0] < amb[0] < 0 < gu[0] < ce[0] # incompleteness left, conflict right # perspective, not orthographic: the near vertex (ambiguity) is pulled toward the eye orth = simplex.project(simplex.VERT, eye=tuple(1e6 * np.asarray(simplex.EYE))) assert abs(amb[1]) > abs(orth[0][1]) def test_the_mark_is_the_same_tetrahedron_under_the_same_camera(): """`data/mark.svg` is generated from `mark_points`; this keeps it from drifting.""" svg = simplex.mark_svg() assert svg.startswith("") assert 'viewBox="0 0 32 32"' in svg circles = re.findall(r'= 0).all() and (pts <= 32).all() assert simplex.MARK_PATH.name == "mark.svg" and simplex.MARK_PATH.is_file()