def test_order_handling(self, start, end, t_func): # geometric_slerp() should handle scenarios with # ascending and descending t value arrays gracefully; # results should simply be reversed # for scrambled / unsorted parameters, the same values # should be returned, just in scrambled order num_t_vals = 20 np.random.seed(789) forward_t_vals = t_func(0, 10, num_t_vals) # normalize to max of 1 forward_t_vals /= forward_t_vals.max() reverse_t_vals = np.flipud(forward_t_vals) shuffled_indices = np.arange(num_t_vals) np.random.shuffle(shuffled_indices) scramble_t_vals = forward_t_vals.copy()[shuffled_indices] forward_results = geometric_slerp(start=start, end=end, t=forward_t_vals) reverse_results = geometric_slerp(start=start, end=end, t=reverse_t_vals) scrambled_results = geometric_slerp(start=start, end=end, t=scramble_t_vals) # check fidelity to input order assert_allclose(forward_results, np.flipud(reverse_results)) assert_allclose(forward_results[shuffled_indices], scrambled_results)
def test_input_shape_flat(self, start, end): # geometric_slerp should handle input arrays that are # not flat appropriately with pytest.raises(ValueError, match='one-dimensional'): geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 10))
def test_unit_sphere_enforcement(self, start, end): # geometric_slerp() should raise on input that clearly # cannot be on an n-sphere of radius 1 with pytest.raises(ValueError, match='unit n-sphere'): geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 5))
def test_input_at_least1d(self, start, end): # empty inputs to geometric_slerp must # be handled appropriately when not detected # by mismatch with pytest.raises(ValueError, match='at least two-dim'): geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 10))
def test_input_dim_mismatch(self, start, end): # geometric_slerp must appropriately handle cases where # an interpolation is attempted across two different # dimensionalities with pytest.raises(ValueError, match='dimensions'): geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 10))
def test_degenerate_input(self, start, t): shape = (t.size,) + start.shape expected = np.full(shape, start) actual = geometric_slerp(start=start, end=start, t=t) assert_allclose(actual, expected) # Check that degenerate and non-degenerate inputs yield the same size non_degenerate = geometric_slerp(start=start, end=start[::-1], t=t) assert actual.size == non_degenerate.size
def test_interpolation_param_ndim(self, t): # regression test for gh-14465 arr1 = np.array([0, 1]) arr2 = np.array([1, 0]) with pytest.raises(ValueError): geometric_slerp(start=arr1, end=arr2, t=t) with pytest.raises(ValueError): geometric_slerp(start=arr1, end=arr1, t=t)
def test_t_values_limits(self, t): # geometric_slerp() should appropriately handle # interpolation parameters < 0 and > 1 with pytest.raises(ValueError, match='interpolation parameter'): _ = geometric_slerp(start=np.array([1, 0]), end=np.array([0, 1]), t=t)
def test_0_sphere_handling(self, start, end): # it does not make sense to interpolate the set of # two points that is the 0-sphere with pytest.raises(ValueError, match='at least two-dim'): _ = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 4))
def test_degenerate_input(self, start, t): if np.asarray(t).ndim > 1: with pytest.raises(ValueError): geometric_slerp(start=start, end=start, t=t) else: shape = (t.size,) + start.shape expected = np.full(shape, start) actual = geometric_slerp(start=start, end=start, t=t) assert_allclose(actual, expected) # Check that degenerate and non-degenerate # inputs yield the same size non_degenerate = geometric_slerp(start=start, end=start[::-1], t=t) assert actual.size == non_degenerate.size
def test_tol_sign(self, tol): # geometric_slerp() currently handles negative # tol values, as long as they are floats _ = geometric_slerp(start=np.array([1, 0]), end=np.array([0, 1]), t=np.linspace(0, 1, 5), tol=tol)
def test_straightforward_examples(self, start, end, expected): # some straightforward interpolation tests, sufficiently # simple to use the unit circle to deduce expected values; # for larger dimensions, pad with constants so that the # data is N-D but simpler to reason about actual = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 4)) assert_allclose(actual, expected, atol=1e-16)
def test_tol_type(self, tol): # geometric_slerp() should raise if tol is not # a suitable float type with pytest.raises(ValueError, match='must be a float'): _ = geometric_slerp(start=np.array([1, 0]), end=np.array([0, 1]), t=np.linspace(0, 1, 5), tol=tol)
def test_scalar_t(self): # when t is a scalar, return value is a single # interpolated point of the appropriate dimensionality # requested by reviewer in gh-10380 actual = geometric_slerp([1, 0], [0, 1], 0.5) expected = np.array([np.sqrt(2) / 2, np.sqrt(2) / 2], dtype=np.float64) assert actual.shape == (2, ) assert_allclose(actual, expected)
def test_degenerate_input(self, start): # handle start == end with repeated value # like np.linspace expected = [start] * 5 actual = geometric_slerp(start=start, end=start, t=np.linspace(0, 1, 5)) assert_allclose(actual, expected)
def test_handle_antipodes(self, start, end, expected): # antipodal points must be handled appropriately; # there are an infinite number of possible geodesic # interpolations between them in higher dims if expected == "warning": with pytest.warns(UserWarning, match='antipodes'): res = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 10)) else: res = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, 10)) # antipodes or near-antipodes should still produce # slerp paths on the surface of the sphere (but they # may be ambiguous): assert_allclose(np.linalg.norm(res, axis=1), 1.0)
def test_ultra_close_gens(self): # use geometric_slerp to produce generators that # are close together, to push the limits # of the area (angle) calculations # also, limit generators to a single hemisphere path = geometric_slerp([0, 0, 1], [1, 0, 0], t=np.linspace(0, 1, 1000)) sv = SphericalVoronoi(path) areas = sv.calculate_areas() assert_almost_equal(areas.sum(), 4 * np.pi)
def test_shape_property(self, n_dims, n_pts): # geometric_slerp output shape should match # input dimensionality & requested number # of interpolation points start, end = _generate_spherical_points(n_dims, 2) actual = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, n_pts)) assert actual.shape == (n_pts, n_dims)
def addCamerafield(points,delthe,delphi): """add the FOV of the camera""" radius = RADIUS # center = np.array([0, 0, 0]) t_vals = np.linspace(0, 1, 2000) # set the num of points on the line result = np.random.rand(0, 2000, 3) n = len(points) for i in range(n-2): for j in range(4): sphepoints=cart2spher(points[i]) start = spher2cart(sphepoints+[sqrt(2)*cos(pi/4+j*pi/2)*delthe,sqrt(2)*sin(pi/4+j*pi/2)*delphi]) end = spher2cart(sphepoints+[sqrt(2)*cos(pi/4+(j+1)*pi/2)*delthe,sqrt(2)*sin(pi/4+(j+1)*pi/2)*delphi]) temp = geometric_slerp(start, end, t_vals) result = np.concatenate((result, temp[None]), axis=0) return radius*result
def test_numerical_stability_pi(self, k): # geometric_slerp should have excellent numerical # stability for angles approaching pi between # the start and end points angle = np.pi - k ts = np.linspace(0, 1, 100) P = np.array([1, 0, 0, 0]) Q = np.array([np.cos(angle), np.sin(angle), 0, 0]) # the test should only be enforced for cases where # geometric_slerp determines that the input is actually # on the unit sphere with np.testing.suppress_warnings() as sup: sup.filter(UserWarning) result = geometric_slerp(P, Q, ts, 1e-18) norms = np.linalg.norm(result, axis=1) error = np.max(np.abs(norms - 1)) assert error < 4e-15
def test_accept_arraylike(self): # array-like support requested by reviewer # in gh-10380 actual = geometric_slerp([1, 0], [0, 1], [0, 1 / 3, 0.5, 2 / 3, 1]) # expected values are based on visual inspection # of the unit circle for the progressions along # the circumference provided in t expected = np.array( [[1, 0], [np.sqrt(3) / 2, 0.5], [np.sqrt(2) / 2, np.sqrt(2) / 2], [0.5, np.sqrt(3) / 2], [0, 1]], dtype=np.float64) # Tyler's original Cython implementation of geometric_slerp # can pass at atol=0 here, but on balance we will accept # 1e-16 for an implementation that avoids Cython and # makes up accuracy ground elsewhere assert_allclose(actual, expected, atol=1e-16)
def test_include_ends(self, n_dims, n_pts): # geometric_slerp should return a data structure # that includes the start and end coordinates # when t includes 0 and 1 ends # this is convenient for plotting surfaces represented # by interpolations for example # the generator doesn't work so well for the unit # sphere (it always produces antipodes), so use # custom values there start, end = _generate_spherical_points(n_dims, 2) actual = geometric_slerp(start=start, end=end, t=np.linspace(0, 1, n_pts)) assert_allclose(actual[0], start) assert_allclose(actual[-1], end)
def addVoronoi(points): """add voronoi on the surface. Note that before using this function, modify the parameters(radius, center) first""" radius = RADIUS points = points/radius center = np.array([0, 0, 0]) sv = SphericalVoronoi(points, 1, center) sv.sort_vertices_of_regions() t_vals = np.linspace(0, 1, 2000)#set the num of points on the line result = np.random.rand(0, 2000, 3) for region in sv.regions: n = len(region) for i in range(n): start = sv.vertices[region][i] end = sv.vertices[region][(i + 1) % n] if not (start == end).all(): temp=radius*geometric_slerp(start, end, t_vals) result = np.concatenate((result, temp[None]), axis=0) return result
def time_geometric_slerp_3d(self, num_points): # time geometric_slerp() for 3D interpolation geometric_slerp(start=self.start, end=self.end, t=self.t)
def test_t_values_conversion(self, t): with pytest.raises(ValueError): _ = geometric_slerp(start=np.array([1]), end=np.array([0]), t=t)
# try applying spherical linear interpolation to improve plot N = spherical_polyon.shape[0] n_int = 900 interpolated_polygon = np.zeros((N * n_int, 3), dtype=np.float64) t_values = np.float64(np.linspace(0, 1, n_int)) counter = 0 for i in range(N): if i == (N-1): next_index = 0 else: next_index = i + 1 interpolated_polygon[counter:(counter + n_int), ...] = geometric_slerp(spherical_polyon[i], spherical_polyon[next_index], t_values) counter += n_int results = lib.cast_subgrids(spherical_polyon=spherical_polyon, MAXD=4) (edge_count_array_L1, cartesian_coords_cells_L1, edge_count_array_L2, cartesian_coords_cells_L2, edge_count_array_L3, cartesian_coords_cells_L3, edge_count_array_L4, cartesian_coords_cells_L4) = results