<p dir="auto">When all of the points are in one hemisphere some of the Voronoi points are incorrect.</p>
<p dir="auto">To be more precise, certain of the points are on the wrong side of the sphere.  An example is to create a tetrahedron with the following generator points:</p>
<p dir="auto">generators (polar)<br>
[[-25.56198973  10.81015471]<br>
[-54.40661695  26.73672285]<br>
[-55.29180524  -6.7928405 ]<br>
[-45.12839961   9.12243389]]<br>
generators (rectangular)<br>
[[ 0.88610999 -0.42383021  0.18755541]<br>
[ 0.51980039 -0.72622668  0.4498915 ]<br>
[ 0.56540011 -0.81629197 -0.11827989]<br>
[ 0.69659682 -0.69972598  0.15854467]]</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2798381/47086102-7b2f5300-d1cd-11e8-9394-405ca519bca6.png"><img src="https://user-images.githubusercontent.com/2798381/47086102-7b2f5300-d1cd-11e8-9394-405ca519bca6.png" alt="p" style="max-width: 100%;"></a></p>
<p dir="auto">The first three Voronoi points and associated Delaunay triangles come out fine:<br>
Voronoi vertex number 1 , coordinates: [ 0.73647759 -0.54764287  0.39709955]<br>
Delaunay vertices<br>
[[ 0.69659682 -0.69972598  0.15854467]<br>
[ 0.88610999 -0.42383021  0.18755541]<br>
[ 0.51980039 -0.72622668  0.4498915 ]]<br>
Voronoi vertex number 2 , coordinates: [ 0.42155897 -0.88977832  0.17487877]<br>
Delaunay vertices<br>
[[ 0.69659682 -0.69972598  0.15854467]<br>
[ 0.56540011 -0.81629197 -0.11827989]<br>
[ 0.51980039 -0.72622668  0.4498915 ]]<br>
Voronoi vertex number 3 , coordinates: [ 0.82153008 -0.54761634 -0.15875979]<br>
Delaunay vertices<br>
[[ 0.69659682 -0.69972598  0.15854467]<br>
[ 0.56540011 -0.81629197 -0.11827989]<br>
[ 0.88610999 -0.42383021  0.18755541]]</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2798381/47086240-ce090a80-d1cd-11e8-83be-95237aff6c81.png"><img src="https://user-images.githubusercontent.com/2798381/47086240-ce090a80-d1cd-11e8-83be-95237aff6c81.png" alt="v1" style="max-width: 100%;"></a><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2798381/47086249-d2352800-d1cd-11e8-8215-32c9c047f499.png"><img src="https://user-images.githubusercontent.com/2798381/47086249-d2352800-d1cd-11e8-8215-32c9c047f499.png" alt="v2" style="max-width: 100%;"></a><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2798381/47086267-de20ea00-d1cd-11e8-945c-1280f73d67a4.png"><img src="https://user-images.githubusercontent.com/2798381/47086267-de20ea00-d1cd-11e8-945c-1280f73d67a4.png" alt="v3" style="max-width: 100%;"></a></p>
<p dir="auto">The fourth point is not correct - it should be on the opposite side of the sphere:<br>
Voronoi vertex number 0 , coordinates: [ 0.69593711 -0.69851441  0.16658079]<br>
Delaunay vertices<br>
[[ 0.56540011 -0.81629197 -0.11827989]<br>
[ 0.88610999 -0.42383021  0.18755541]<br>
[ 0.51980039 -0.72622668  0.4498915 ]]</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2798381/47086358-17f1f080-d1ce-11e8-862c-1902bb002aca.png"><img src="https://user-images.githubusercontent.com/2798381/47086358-17f1f080-d1ce-11e8-862c-1902bb002aca.png" alt="v0" style="max-width: 100%;"></a></p>
<p dir="auto">I have a good understanding of the algorithm and what the problem is but I don't know how to fix it using qhull.</p>
<p dir="auto">As pointed out by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/tylerjereddy/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/tylerjereddy">@tylerjereddy</a> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105392224" data-permission-text="Title is private" data-url="https://github.com/scipy/scipy/issues/5232" data-hovercard-type="pull_request" data-hovercard-url="/scipy/scipy/pull/5232/hovercard" href="https://github.com/scipy/scipy/pull/5232">#5232</a>, the <code class="notranslate">scipy.spatial.SphericalVoronoi</code> algorithm does not cover cases where all generator points are on a single hemisphere. How would an extension of the algorithm look like to include those cases? Are there theoretical treatises tackling these cases?</p>
<h3 dir="auto">Reproducing code example:</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import scipy.spatial
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import proj3d                                         
from matplotlib import colors                                      
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

# calculate points in single hemisphere
phi = np.linspace(0,2*np.pi,10,endpoint=False) # azimuth angle
theta = np.linspace(0.001,np.pi*0.4,5)  # polar angle
theta = theta[np.newaxis, :].T
radius = 1;

phiv, thetav = np.meshgrid(phi, theta)
phiv = np.reshape(phiv,(50,1))
thetav = np.reshape(thetav,(50,1))

x = radius*np.cos(phiv)*np.sin(thetav)
y = radius*np.sin(phiv)*np.sin(thetav)
z = radius*np.cos(thetav)

points = np.concatenate([x,y,z],axis=1)

# Voronoi-Regions
sv = scipy.spatial.SphericalVoronoi(points)
sv.sort_vertices_of_regions()

# plotting
fig = plt.figure()                                                              
ax = fig.add_subplot('111', projection='3d')                                    
ax.scatter(points[...,0], points[...,1], points[...,2])    
                                                                                
for region in sv.regions:                                                       
    random_color = colors.rgb2hex(np.random.rand(3))                            
    polygon = Poly3DCollection([sv.vertices[region]], alpha=1.0)                
    polygon.set_color(random_color)                                             
    ax.add_collection3d(polygon)
    
ax.set_xlim([-1.0,1.0])
ax.set_ylim([-1.0,1.0])
ax.set_zlim([-1.0,1.0])
ax.view_init(45,0)

fig.savefig('hemisphere.png', dpi=300) "><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">scipy</span>.<span class="pl-s1">spatial</span>
<span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-k">import</span> <span class="pl-s1">matplotlib</span>.<span class="pl-s1">pyplot</span> <span class="pl-k">as</span> <span class="pl-s1">plt</span>
<span class="pl-k">from</span> <span class="pl-s1">mpl_toolkits</span>.<span class="pl-s1">mplot3d</span> <span class="pl-k">import</span> <span class="pl-s1">proj3d</span>                                         
<span class="pl-k">from</span> <span class="pl-s1">matplotlib</span> <span class="pl-k">import</span> <span class="pl-s1">colors</span>                                      
<span class="pl-k">from</span> <span class="pl-s1">mpl_toolkits</span>.<span class="pl-s1">mplot3d</span>.<span class="pl-s1">art3d</span> <span class="pl-k">import</span> <span class="pl-v">Poly3DCollection</span>

<span class="pl-c"># calculate points in single hemisphere</span>
<span class="pl-s1">phi</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">linspace</span>(<span class="pl-c1">0</span>,<span class="pl-c1">2</span><span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-s1">pi</span>,<span class="pl-c1">10</span>,<span class="pl-s1">endpoint</span><span class="pl-c1">=</span><span class="pl-c1">False</span>) <span class="pl-c"># azimuth angle</span>
<span class="pl-s1">theta</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">linspace</span>(<span class="pl-c1">0.001</span>,<span class="pl-s1">np</span>.<span class="pl-s1">pi</span><span class="pl-c1">*</span><span class="pl-c1">0.4</span>,<span class="pl-c1">5</span>)  <span class="pl-c"># polar angle</span>
<span class="pl-s1">theta</span> <span class="pl-c1">=</span> <span class="pl-s1">theta</span>[<span class="pl-s1">np</span>.<span class="pl-s1">newaxis</span>, :].<span class="pl-v">T</span>
<span class="pl-s1">radius</span> <span class="pl-c1">=</span> <span class="pl-c1">1</span>;

<span class="pl-s1">phiv</span>, <span class="pl-s1">thetav</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">meshgrid</span>(<span class="pl-s1">phi</span>, <span class="pl-s1">theta</span>)
<span class="pl-s1">phiv</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">reshape</span>(<span class="pl-s1">phiv</span>,(<span class="pl-c1">50</span>,<span class="pl-c1">1</span>))
<span class="pl-s1">thetav</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">reshape</span>(<span class="pl-s1">thetav</span>,(<span class="pl-c1">50</span>,<span class="pl-c1">1</span>))

<span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">radius</span><span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-en">cos</span>(<span class="pl-s1">phiv</span>)<span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-en">sin</span>(<span class="pl-s1">thetav</span>)
<span class="pl-s1">y</span> <span class="pl-c1">=</span> <span class="pl-s1">radius</span><span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-en">sin</span>(<span class="pl-s1">phiv</span>)<span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-en">sin</span>(<span class="pl-s1">thetav</span>)
<span class="pl-s1">z</span> <span class="pl-c1">=</span> <span class="pl-s1">radius</span><span class="pl-c1">*</span><span class="pl-s1">np</span>.<span class="pl-en">cos</span>(<span class="pl-s1">thetav</span>)

<span class="pl-s1">points</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">concatenate</span>([<span class="pl-s1">x</span>,<span class="pl-s1">y</span>,<span class="pl-s1">z</span>],<span class="pl-s1">axis</span><span class="pl-c1">=</span><span class="pl-c1">1</span>)

<span class="pl-c"># Voronoi-Regions</span>
<span class="pl-s1">sv</span> <span class="pl-c1">=</span> <span class="pl-s1">scipy</span>.<span class="pl-s1">spatial</span>.<span class="pl-v">SphericalVoronoi</span>(<span class="pl-s1">points</span>)
<span class="pl-s1">sv</span>.<span class="pl-en">sort_vertices_of_regions</span>()

<span class="pl-c"># plotting</span>
<span class="pl-s1">fig</span> <span class="pl-c1">=</span> <span class="pl-s1">plt</span>.<span class="pl-en">figure</span>()                                                              
<span class="pl-s1">ax</span> <span class="pl-c1">=</span> <span class="pl-s1">fig</span>.<span class="pl-en">add_subplot</span>(<span class="pl-s">'111'</span>, <span class="pl-s1">projection</span><span class="pl-c1">=</span><span class="pl-s">'3d'</span>)                                    
<span class="pl-s1">ax</span>.<span class="pl-en">scatter</span>(<span class="pl-s1">points</span>[...,<span class="pl-c1">0</span>], <span class="pl-s1">points</span>[...,<span class="pl-c1">1</span>], <span class="pl-s1">points</span>[...,<span class="pl-c1">2</span>])    
                                                                                
<span class="pl-k">for</span> <span class="pl-s1">region</span> <span class="pl-c1">in</span> <span class="pl-s1">sv</span>.<span class="pl-s1">regions</span>:                                                       
    <span class="pl-s1">random_color</span> <span class="pl-c1">=</span> <span class="pl-s1">colors</span>.<span class="pl-en">rgb2hex</span>(<span class="pl-s1">np</span>.<span class="pl-s1">random</span>.<span class="pl-en">rand</span>(<span class="pl-c1">3</span>))                            
    <span class="pl-s1">polygon</span> <span class="pl-c1">=</span> <span class="pl-v">Poly3DCollection</span>([<span class="pl-s1">sv</span>.<span class="pl-s1">vertices</span>[<span class="pl-s1">region</span>]], <span class="pl-s1">alpha</span><span class="pl-c1">=</span><span class="pl-c1">1.0</span>)                
    <span class="pl-s1">polygon</span>.<span class="pl-en">set_color</span>(<span class="pl-s1">random_color</span>)                                             
    <span class="pl-s1">ax</span>.<span class="pl-en">add_collection3d</span>(<span class="pl-s1">polygon</span>)
    
<span class="pl-s1">ax</span>.<span class="pl-en">set_xlim</span>([<span class="pl-c1">-</span><span class="pl-c1">1.0</span>,<span class="pl-c1">1.0</span>])
<span class="pl-s1">ax</span>.<span class="pl-en">set_ylim</span>([<span class="pl-c1">-</span><span class="pl-c1">1.0</span>,<span class="pl-c1">1.0</span>])
<span class="pl-s1">ax</span>.<span class="pl-en">set_zlim</span>([<span class="pl-c1">-</span><span class="pl-c1">1.0</span>,<span class="pl-c1">1.0</span>])
<span class="pl-s1">ax</span>.<span class="pl-en">view_init</span>(<span class="pl-c1">45</span>,<span class="pl-c1">0</span>)

<span class="pl-s1">fig</span>.<span class="pl-en">savefig</span>(<span class="pl-s">'hemisphere.png'</span>, <span class="pl-s1">dpi</span><span class="pl-c1">=</span><span class="pl-c1">300</span>) </pre></div>
<p dir="auto">resulting plot<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/7665585/40424201-2e9128a8-5e95-11e8-8ed6-fc13e58c6ded.png"><img src="https://user-images.githubusercontent.com/7665585/40424201-2e9128a8-5e95-11e8-8ed6-fc13e58c6ded.png" alt="hemisphere" style="max-width: 100%;"></a></p>
<h3 dir="auto">Scipy/Numpy/Python version information:</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="1.1.0 1.14.3 sys.version_info(major=3, minor=5, micro=2, releaselevel='final', serial=0)"><pre class="notranslate"><code class="notranslate">1.1.0 1.14.3 sys.version_info(major=3, minor=5, micro=2, releaselevel='final', serial=0)
</code></pre></div>