15 |
16 |
17 |
Default perturbation - Random perturbation
19 | 20 | ### Sampling in unit circle (with smooth boundary) 21 |
22 |
23 |
24 |
Default perturbation - Random perturbation
26 | 27 | ### Sampling on unit hemisphere 28 |
29 |
30 |
31 |
Default perturbation - Random perturbation
33 | 34 | ### Sampling on unit sphere 35 |
36 |
37 |
38 |
Default perturbation - Random perturbation
40 | 41 | ### Cosine weighted sampling on unit hemisphere 42 |
43 |
44 |
45 |
Default perturbation - Random perturbation
47 | 48 | ```python 49 | # Code 50 | test.test() 51 | ``` 52 | 53 | ## Bibliography 54 | ROBERTS M.: [*Evenly distributing points on a sphere*](http://extremelearning.com.au/evenly-distributing-points-on-a-sphere/), 2018. 55 | -------------------------------------------------------------------------------- /res/circle_mode0_alpha0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/circle_mode0_alpha0.png -------------------------------------------------------------------------------- /res/circle_mode0_alpha2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/circle_mode0_alpha2.png -------------------------------------------------------------------------------- /res/circle_mode1_alpha0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/circle_mode1_alpha0.png -------------------------------------------------------------------------------- /res/circle_mode1_alpha2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/circle_mode1_alpha2.png -------------------------------------------------------------------------------- /res/cosineweightedhemisphere_mode0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/cosineweightedhemisphere_mode0.png -------------------------------------------------------------------------------- /res/cosineweightedhemisphere_mode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/cosineweightedhemisphere_mode1.png -------------------------------------------------------------------------------- /res/hemisphere_mode0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/hemisphere_mode0.png -------------------------------------------------------------------------------- /res/hemisphere_mode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/hemisphere_mode1.png -------------------------------------------------------------------------------- /res/sphere_mode0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/sphere_mode0.png -------------------------------------------------------------------------------- /res/sphere_mode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt77hias/fibpy/6e0380a9fa05e99677a78c06ae123619c520cc05/res/sphere_mode1.png -------------------------------------------------------------------------------- /src/plot_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from mpl_toolkits.mplot3d import Axes3D 4 | 5 | ############################################################################### 6 | ## Plot Utilities 2D 7 | ############################################################################### 8 | 9 | def set_equal_aspect_ratio_2D(ax, xs, ys, alpha=1.5, delta=0.0): 10 | ax.set_aspect('equal') 11 | 12 | mn = np.array([xs.min(), ys.min()]) 13 | mx = np.array([xs.max(), ys.max()]) 14 | d = 0.5 * (mx - mn) 15 | c = mn + d 16 | d = alpha * np.max(d) + delta 17 | 18 | ax.set_xlim(c[0] - d, c[0] + d) 19 | ax.set_ylim(c[1] - d, c[1] + d) 20 | 21 | def vis_samples_2D(ss, fname=None): 22 | plt.figure() 23 | ax = plt.gca() 24 | ax.scatter(ss[:,0], ss[:,1]) 25 | set_equal_aspect_ratio_2D(ax, ss[:,0], ss[:,1]) 26 | ax.set_xlabel("x") 27 | ax.set_ylabel("y") 28 | if fname is None: 29 | plt.show() 30 | else: 31 | plt.savefig(fname) 32 | plt.close() 33 | 34 | ############################################################################### 35 | ## Plot Utilities 3D 36 | ############################################################################### 37 | 38 | def set_equal_aspect_ratio_3D(ax, xs, ys, zs, alpha=1.5, delta=0.0): 39 | ax.set_aspect('equal') 40 | 41 | mn = np.array([xs.min(), ys.min(), zs.min()]) 42 | mx = np.array([xs.max(), ys.max(), zs.max()]) 43 | d = 0.5 * (mx - mn) 44 | c = mn + d 45 | d = alpha * np.max(d) + delta 46 | 47 | ax.set_xlim(c[0] - d, c[0] + d) 48 | ax.set_ylim(c[1] - d, c[1] + d) 49 | ax.set_zlim(c[2] - d, c[2] + d) 50 | 51 | def vis_samples_3D(ss, fname=None): 52 | plt.figure() 53 | ax = plt.gca(projection='3d') 54 | ax.scatter(ss[:,0], ss[:,1], ss[:,2]) 55 | set_equal_aspect_ratio_3D(ax, ss[:,0], ss[:,1], ss[:,2]) 56 | ax.set_xlabel("x") 57 | ax.set_ylabel("y") 58 | ax.set_zlabel("z") 59 | if fname is None: 60 | plt.show() 61 | else: 62 | plt.savefig(fname) 63 | plt.close() 64 | -------------------------------------------------------------------------------- /src/sampling.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def fibonacci_spiral_samples_in_unit_circle(nb_samples, mode=0, alpha=2): 4 | shift = 1.0 if mode == 0 else nb_samples * np.random.random() 5 | 6 | ga = np.pi * (3.0 - np.sqrt(5.0)) 7 | # Boundary points 8 | np_boundary = round(alpha * np.sqrt(nb_samples)) 9 | 10 | ss = np.zeros((nb_samples,2)) 11 | j = 0 12 | for i in range(nb_samples): 13 | if i > nb_samples - (np_boundary + 1): 14 | r = 1.0 15 | else: 16 | r = np.sqrt((i + 0.5) / (nb_samples - 0.5 * (np_boundary + 1))) 17 | phi = ga * (i + shift) 18 | ss[j,:] = np.array([r * np.cos(phi), r * np.sin(phi)]) 19 | j += 1 20 | return ss 21 | 22 | def fibonacci_spiral_samples_on_unit_hemisphere(nb_samples, mode=0, up=True): 23 | n = 2 * nb_samples 24 | rn = range(nb_samples, n) if up else range(nb_samples) 25 | 26 | shift = 1.0 if mode == 0 else n * np.random.random() 27 | 28 | ga = np.pi * (3.0 - np.sqrt(5.0)) 29 | offset = 1.0 / nb_samples 30 | 31 | ss = np.zeros((nb_samples,3)) 32 | j = 0 33 | for i in rn: 34 | phi = ga * ((i + shift) % n) 35 | cos_phi = np.cos(phi) 36 | sin_phi = np.sin(phi) 37 | cos_theta = ((i + 0.5) * offset) - 1.0 38 | sin_theta = np.sqrt(1.0 - cos_theta*cos_theta) 39 | ss[j,:] = np.array([cos_phi * sin_theta, sin_phi * sin_theta, cos_theta]) 40 | j += 1 41 | return ss 42 | 43 | def fibonacci_spiral_samples_on_unit_sphere(nb_samples, mode=0): 44 | shift = 1.0 if mode == 0 else nb_samples * np.random.random() 45 | 46 | ga = np.pi * (3.0 - np.sqrt(5.0)) 47 | offset = 2.0 / nb_samples 48 | 49 | ss = np.zeros((nb_samples,3)) 50 | j = 0 51 | for i in range(nb_samples): 52 | phi = ga * ((i + shift) % nb_samples) 53 | cos_phi = np.cos(phi) 54 | sin_phi = np.sin(phi) 55 | cos_theta = ((i + 0.5) * offset) - 1.0 56 | sin_theta = np.sqrt(1.0 - cos_theta*cos_theta) 57 | ss[j,:] = np.array([cos_phi * sin_theta, sin_phi * sin_theta, cos_theta]) 58 | j += 1 59 | return ss 60 | 61 | def fibonacci_spiral_cosine_weighted_samples_on_unit_hemisphere(nb_samples, mode=0): 62 | shift = 1.0 if mode == 0 else nb_samples * np.random.random() 63 | 64 | ga = np.pi * (3.0 - np.sqrt(5.0)) 65 | 66 | ss = np.zeros((nb_samples,3)) 67 | j = 0 68 | for i in range(nb_samples): 69 | sin_theta = np.sqrt((i + 0.5) / (nb_samples - 0.5)) 70 | cos_theta = np.sqrt(1.0 - sin_theta*sin_theta) 71 | phi = ga * (i + shift) 72 | cos_phi = np.cos(phi) 73 | sin_phi = np.sin(phi) 74 | ss[j,:] = np.array([cos_phi * sin_theta, sin_phi * sin_theta, cos_theta]) 75 | j += 1 76 | return ss -------------------------------------------------------------------------------- /src/test.py: -------------------------------------------------------------------------------- 1 | from plot_utils import vis_samples_2D, vis_samples_3D 2 | from sampling import fibonacci_spiral_samples_in_unit_circle, fibonacci_spiral_samples_on_unit_hemisphere, fibonacci_spiral_samples_on_unit_sphere, fibonacci_spiral_cosine_weighted_samples_on_unit_hemisphere 3 | 4 | def test(nb_samples=256): 5 | # mode 0: default 6 | # mode 1: random perturbation 7 | for mode in [0, 1]: 8 | vis_samples_2D(fibonacci_spiral_samples_in_unit_circle(nb_samples=nb_samples, mode=mode, alpha=0.0)) 9 | vis_samples_2D(fibonacci_spiral_samples_in_unit_circle(nb_samples=nb_samples, mode=mode, alpha=2.0)) 10 | vis_samples_3D(fibonacci_spiral_samples_on_unit_hemisphere(nb_samples=nb_samples, mode=mode)) 11 | vis_samples_3D(fibonacci_spiral_samples_on_unit_sphere(nb_samples=nb_samples, mode=mode)) 12 | vis_samples_3D(fibonacci_spiral_cosine_weighted_samples_on_unit_hemisphere(nb_samples=nb_samples, mode=mode)) --------------------------------------------------------------------------------