What is "epsilon radius" in Vector3::isClose?

There’s no explanation of what the epsilon argument is for Vector3::isClose and all the mentions of it on the forum use it without an explanation. What does it mean and how do I use it?

2 Likes

The epsilon argument specifies the radius, isClose is used to check if another Vector3 is within the radius of the first Vector3.
i.e

local vec1 = Vector3.new()
local vec2 = Vector3.new(0,10,0)
print(vec1:isClose(vec2,5)) -- false
print(vec1:isClose(vec2,10)) -- true

edit: i was wrong about this

This is not how :isClose works iirc. It doesn’t check the radius, but instead the distance on each axis. The behavior also differs if the first vector isn’t (0, 0, 0).

Not sure why this behavior hasn’t been documented.

Some examples:

thisVector:isClose(partPosition) was true for green parts and false for red parts. The center text shows current thisVector values.

Repro code
local thisVector = Vector3.new(1,0,0)

local function createPart(pos, text)
	local part = Instance.new("Part")
	part.Anchored = true
	part.CanCollide = false
	part.Size = Vector3.new(.5,.5,.5)
	part.TopSurface = "Smooth"
	part.BottomSurface = "Smooth"
	part.CFrame = CFrame.new(pos)
	part.Parent = workspace
	
	if(text)then
		local billboardGUI = Instance.new("BillboardGui",part)
		billboardGUI.Size = UDim2.new(0,100,0,50)
		billboardGUI.AlwaysOnTop = true
		billboardGUI.ExtentsOffset = Vector3.new(0,1,0)
		
		local textLabel = Instance.new("TextLabel",billboardGUI)
		textLabel.Text = text
		textLabel.Size = UDim2.new(1,0,1,0)
		textLabel.BackgroundTransparency = 1
		textLabel.TextStrokeTransparency = 0
		textLabel.TextColor3 = Color3.new(1,1,1)
		textLabel.TextSize = 18
	end
	
	return part
end

createPart(thisVector, tostring(thisVector)).BrickColor = BrickColor.Black()

for i = 1, 100 do
	local otherVector = thisVector + Vector3.new(math.random(-10,10), 0, math.random(-10,10))
	
	createPart(otherVector).BrickColor = (thisVector:isClose(otherVector,5) and BrickColor.Green() or BrickColor.Red())
end

local edgeVec = thisVector + Vector3.new(10, 0, 10)
createPart(edgeVec, tostring(edgeVec)).BrickColor = (thisVector:isClose(edgeVec,5) and BrickColor.Green() or BrickColor.Red())

workspace.CurrentCamera.CameraType = "Scriptable"
workspace.CurrentCamera.CFrame = CFrame.new(0,20,0)*CFrame.Angles(-math.pi*0.5,0,0)

11 Likes

isClose (or the undocumented PascalCase alias Vector3::FuzzyEq) essentially subtracts the two vectors from one another and compares the resulting vector’s magnitude to epsilon. It’s to make it possible to perform comparisons on Vector3s, which use floats and thus run into floating point issues when comparing them.

Epsilon’s default value is something like 0.000001 iirc.

2 Likes

It seems like you described exactly what Halalaluyafail3 said which Coeptus said was wrong. Are you saying it’s specifically to check if two vectors are very close in order to avoid floating point errors?

Yes. It’s essentially a, well, fuzzy equality operation (thus the name of the alias). You can use it with larger epsilon values like how Coeptus was saying, but that’s not the intended use.

Was looking into this out of curiosity and expanded on Coeptus’ code to help me visualize what kind of inclusion occurred when using this. Helped me understand what was happening better so I thought I would share it.

Feel free to change/add vectors/epsilon values to test yourself.

Code
--[[modification of Coeptus' code]]--

local testVectors = {
	Vector3.new(0,1,0),
	Vector3.new(1,1,0),
	Vector3.new(2,2,0),
	Vector3.new(7,7,0)
}
local testEpsilons = {0, 0.5, 1, 1.5, 2, 3}
local testTime = 2  	--seconds delay before running next test
local origin = Vector3.new(0,0,0)
local size = 10   



game.Lighting.ClockTime = 12
game.Lighting.GlobalShadows = false
local sky = Instance.new("Sky")
sky.Parent = game.Lighting
sky.CelestialBodiesShown = false

local gui = game:GetService("StarterGui")
local screen = Instance.new("ScreenGui")
screen.Parent=gui
local list = Instance.new("UIListLayout")
list.Parent = screen
local vectorLabel = Instance.new("TextLabel")
vectorLabel.TextSize=20
vectorLabel.Size = UDim2.new(0.2,0,0.1,0)
vectorLabel.Parent = screen
local epsilonLabel = Instance.new("TextLabel")
epsilonLabel.Size = vectorLabel.Size
epsilonLabel.TextSize=20
epsilonLabel.Parent = screen

--holds the bricks
local bricks = {}

--makes a brick
local function createPart(pos)
	local part = Instance.new("Part")
	part.Transparency = .95
	part.Anchored = true
	part.CanCollide = false
	part.Size = Vector3.new(.8,.8,.8)
	part.TopSurface = "Smooth"
	part.BottomSurface = "Smooth"
	part.CFrame = CFrame.new(pos)
	part.Parent = workspace
	return part
end


--makes bricks
local function generate()
	size = 2*size+1
	for u = 1, size do
		for i = 1, size do
			for o = 1, size do
				bricks[((u-1)*size*size)+((i-1)*size)+(o)] = createPart(
						Vector3.new(o-(math.floor(size/2))-1,u-(math.floor(size/2))-1,i-(math.floor(size/2))-1))
			end		
		end
		wait()
	end
end

local function runTest()
	for v in pairs(testVectors) do
		local vector = testVectors[v]
		vectorLabel.Text = ("Vector: ("..vector.X..","..vector.Y..","..vector.Z..")")
		for e in pairs(testEpsilons) do
			local epsilon = testEpsilons[e]
			epsilonLabel.Text = ("Epsilon: "..epsilon)
			for i in pairs(bricks) do
				local pos = bricks[i].Position		
				if pos == origin then
					bricks[i].Transparency = 0
					bricks[i].BrickColor = BrickColor.Green()
				else
					bricks[i].Transparency = (vector:isClose(pos, epsilon) and .05 or 0.95)
				end
				if i%200 ==1 then
					wait()
				end
			end
			wait(testTime)
		end
	end
end


generate()
while true do
	runTest()
end

green part is your origin (0,0,0)
blocks rendered visible are ‘close’

2 Likes