Search code examples
pythoncoordinatespolar-coordinates

Python - spherical coordinates have Z axis bias


Long story short I need to get a bunch of balls moving in random directions in space. I am using a python script with VRED (3D rendering software) to display the balls on screen.

I am trying to use spherical coordinates but somehow the distribution of the balls in space is biased to the Z axis. I really cannot figure out where I got it wrong.

I am proceeding like this:

  1. I generate a random yaw starting direction (-180, 180) and a random starting pitch (0, 180)

  2. At each new frame I change the yaw and pitch by a small amount and move the ball in that new direction.

Here is my Python code (I hope it's not too hard to read; vrAEBase is a class related to VRED which allows the loop() to be updated each frame):

import random
import math

populationsize = 1000
balllist = []

#________________________________________BALL CLASS______________________________________________

class Ball(vrAEBase):

    def __init__(self):
        vrAEBase.__init__(self)
        self.addLoop()
        self.body = createSphere(2,100,1,1,1)  # create sphere
        self.isplaying = false 
        self.steplength = 20      #step length between each frame
        self.yaw = random.uniform(-180, 180)     #set starting yaw
        self.pitch = random.uniform(0, 180)      #set starting pitch
        self.maxsteering = 1 # max angular change for yaw/pitch for each frame
        self.x = 0  #startting X location
        self.y = 0  #startting Y location
        self.z = 0  #startting Z location

    def loop(self):          #loop is executed every frame
        if self.isplaying:

            self.yaw = self.yaw + random.uniform(-1*self.maxsteering, self.maxsteering)       #set new yaw
            self.pitch = self.pitch + random.uniform(-1*self.maxsteering, self.maxsteering)   #set new pitch

            localX = self.steplength * (math.sin(self.pitch)) * (math.cos(self.yaw))  #calculate X step length
            localY = self.steplength * (math.sin(self.pitch)) * (math.sin(self.yaw))  #calculate Y step length
            localZ = self.steplength * (math.cos(self.pitch))                         #calculate Z step length


            self.x += localX
            self.y += localY
            self.z += localZ
            setTransformNodeTranslation(self.body, self.x,self.y,self.z,true)



    def rewind(self):
        self.isplaying = false
        self.x = 0
        self.y = 0
        self.z = 0
        setTransformNodeTranslation(self.body, self.x,self.y,self.z,true)


#__________________________________PLAY__________________________________

def play():
    global balllist
    for ball in balllist:
        if ball.isplaying == false:
            ball.isplaying = true
        else:
            ball.isplaying = false

#__________________________________REWIND_________________________________

def rewind():
    global balllist
    for ball in balllist:
        ball.rewind()


#_______________________________SPAWN BALLS________________________________


for x in range(0, populationsize):
    newball =  Ball()         #create ball
    balllist.append(newball)   #add ball to list

play()

print("end")

Here is an image of the final distribution:

enter image description here


Solution

  • The problem is that in order to generate a uniform distribution of points around a sphere you cannot do phi = [0,pi] and theta=[-pi,pi] as this would lead to a surface elment dA= dphi*dtheta instead of the correct one dA= sin(phi)*dphi*dtheta.

    In order to achieve the correct volume elment change

    def __init__( self):
        ...
        self.yaw = random.uniform(-180, 180)     #set starting yaw
        self.pitch = random.uniform(0, 180)      #set starting pitch
        ...
    

    Non-uniform distribution around sphere

    to

    def __init__( self):
        ...
        u = random.uniform(0,1)
        v = random.uniform(0,1)
        self.yaw = 2 * math.pi * u     #set starting yaw
        self.pitch = math.acos( 2*v -1)      #set starting pitch
        ...
    

    Uniform distribution around sphere

    For more documentation see http://mathworld.wolfram.com/SpherePointPicking.html.

    Also be careful of the behavior of your timestep routine, as of now it seems that the dots will tend to collapse more toward this distribution. I don't know if this is your intended behavior

    enter image description here