What’s a way to calculate a point inside a sphere given radius (with uniformity)?
Very similar post from a week ago:
I would go for @buildthomas solution from that post unless you need incredibly high efficiency, in which case you would need to do a lot more maths.
That’s for a circle.
I know but the specific reply I linked to could also be applied to spheres
Simple solution. Here’s some code I threw together. It’ll grab any point within a sphere’s bounds (defined by origin and radius) and can be anywhere from 0.01 studs away from the origin all the way up to resting on the surface of the sphere.
function FindPointInSphere(Origin, Radius)
local CF = CFrame.new(Origin) * CFrame.fromOrientation(math.rad(math.random(0, 359)), math.rad(math.random(0, 359)), 0)
return CF * Vector3.new(0, 0, -math.random(1, Radius * 100) / 100)
end
Working with spherical coordinates, you can easily get a random point in a sphere.
Hope this helps!
local MAX_RADIUS = 10
local RandomRadius = math.random(1, MAX_RADIUS)
local Theta = math.random(0, 359)
local Phi = math.random(-180, 180)
local X = RandomRadius * math.cos(math.rad(Theta)) * math.cos(math.rad(Phi))
local Y = RandomRadius * math.sin(math.rad(Phi))
local Z = RandomRadius * math.sin(math.rad(Theta)) * math.cos(math.rad(Phi))
It isn’t uniform distance.
or
I think you’re on the right track. My code is wrong in that it just makes a random point in the sphere.
My second attempt (which failed and I have yet to have another go at it) was to base the chance of a point being placed based on the area of the sphere if its outer edge were however far out the selected point is. Smaller areas would have smaller chances, larger areas would have larger chances.
I may try this later but if you (or any other reader) wants to crack at it, go on.
local rand = Random.new()
function getUnitSpherePoint()
local vec
local attempts = 10
while attempts > 0 do
vec = Vector3.new(rand:NextNumber(-1,1), rand:NextNumber(-1,1), rand:NextNumber(-1,1))
if vec.magnitude <= 1 then
return vec
end
attempts = attempts - 1
end
-- couldn't find a solution within 10 tries, so let's just normalize the last one:
return vec.unit
end
I wouldn’t rely on random logic to generate points. Sure it may work, but it’s just a messy practice in general.
Here’s an implementation of the algorithm from the StackOverflow post I linked.
function gaussian ()
return math.sqrt(-2 * math.log(math.random())) *
math.cos(2 * math.pi * math.random())
end
-- Generates a random point inside a unit sphere.
local function getUnitSpherePoint()
local u = math.random()
local v = Vector3.new(gaussian(), gaussian(), gaussian())
return v * (u^(1/3)) / v.magnitude
end
BUT! @buildthomas’s non-deterministic implementation is faster than mine on average!
function gaussian ()
return math.sqrt(-2 * math.log(math.random())) *
math.cos(2 * math.pi * math.random())
end
-- Generates a random point inside a unit sphere.
local function getUnitSpherePoint()
local u = math.random()
local v = Vector3.new(gaussian(), gaussian(), gaussian())
return v * (u^(1/3)) / v.magnitude
end
local rand = Random.new()
function getUnitSpherePointBrute()
local vec
local attempts = 1000
while attempts > 0 do
vec = Vector3.new(rand:NextNumber(-1,1), rand:NextNumber(-1,1), rand:NextNumber(-1,1))
if vec.magnitude <= 1 then
return vec
end
attempts = attempts - 1
end
-- couldn't find a solution within 1000 tries, so let's just normalize the last one:
return vec.unit
end
-- Brute force performance test.
local begin = tick()
for i = 1, 100000 do
local x = getUnitSpherePointBrute()
end
print(tick() - begin) -- 0.14083695411682
-- Algorithm from SO
local begin = tick()
for i = 1, 100000 do
local x = getUnitSpherePoint()
end
print(tick() - begin) -- 0.2488055229187
Just go with the non-deterministic approach.
The technique is called rejection sampling and it has its use cases, as visible above (saving performance by giving up determinism and a tiny tiny bit of randomness).
As a little side note, if performance is an issue then the magnitude check (costly square root) could be replaced with checking that vec:Dot(vec) <= r*r.