# Finding an orthogonal vector to any vector

Given an arbitrary vector, how can I find one of the infinitely many orthogonal vectors? One method would be to cross it with a pure up vector <0, 1, 0> but that wouldn’t work for a purely up or down vector. This could be fixed by implementing edge cases for purely up/down vectors, but I’m curious if there’s another method altogether?

6 Likes

You could probably use `CFrame` mechanics to turn this vector into a `LookVector` and rotate the CFrame 90 degrees around the `X` or `Y` axis:

``````--generates a random vector orthogonal to `SomeVector`
function CreateOrthoVector(SomeVector)
local CF = CFrame.new(Vector3.new(),SomeVector)
local newCF = CF
* CFrame.Angles(0,0,math.random() * 2 * math.pi)
* CFrame.Angles(math.pi/2,0,0)

return newCF.LookVector
end
``````
1 Like

Say you have a vector <x,y,z> and you want to find an orthogonal vector to it <a,b,c>
You know that the dot product of the two vectors has to be 0
So then you have `ax+by+cz=0`
I think you can just let `a` and `b` be anything and solve for `c`?

If it needs to be normalized you have the additional constraint that `a^2+b^2+c^2=1`, so you can let `a` be any value in the interval `[-1,1]` and you have two equations with two unknowns

But I am guessing it suffices to do the first approach and take the unit vector of it

also I really like your visuals xd

4 Likes

Your first method seems to work, but it’s suspiciously convenient lol. I’m gonna try to see if there are any edge cases.

EDIT: I see no reason why it wouldn’t work. Set two components to 1 and solve for the third, but you have to ensure the component you’re solving for isn’t multiplied by a 0. Otherwise, you get an indeterminate. So much for the nice visuals >.<

3 Likes

# Solutions

@goldenstein64’s solution:

• Consistent floating-point error on order of 10-8
• Creates 4 CFrames
``````local function GetOrthogonalVector(vec)
if vec.Magnitude == 0 then error("Vector has no magnitude") end

local newCF = CFrame.new(Vector3.new(), vec)
* CFrame.Angles(0, 0, math.random() * 2 * math.pi)
* CFrame.Angles(math.pi / 2, 0, 0)

return newCF.LookVector
end
``````

@Acreol’s solution:

• Sometimes has a floating-point error on order of 10-8
• Creates 1 Vector3
``````local function GetOrthogonalVector(vec)
if vec.Magnitude == 0 then error("Vector has no magnitude") end

if vec.X ~= 0 then
return Vector3.new((-vec.Y - vec.Z) / vec.X, 1, 1).Unit
elseif vec.Y ~= 0 then
return Vector3.new(1, (-vec.X - vec.Z) / vec.Y, 1).Unit
else
return Vector3.new(1, 1, (-vec.X - vec.Y) / vec.Z).Unit
end
end
``````

@AstroCode’s solution:

• Rarely has a floating-point error on order of 10-13
• Creates 3 Vector3s
``````local function GetOrthogonalVector(vec)
if vec.Magnitude == 0 then error("Vector has no magnitude") end

if vec == Vector3.new(0, 1, 0) or vec == Vector3.new(0, -1, 0) then
return Vector3.new(1, 0, 0)
else
return Vector3.new(vec.Z, 0, -vec.X) -- <0, 1, 0> cross vec
end
end
``````

The floating-point errors come from dotting the orthogonal vector with the original. I tested them on randomly oriented surfaces and surfaces in pure directions.

8 Likes

How are you making the initial random vectors?

``````for i=1,1e2 do
local pitch=(math.random()-.5)*math.pi
local yaw=math.pi*2
local co=math.cos(pitch)
local si=math.sin(pitch)
local vec=Vector3.new(math.cos(yaw)*co,si,math.sin(yaw)*co)

print(vec.Magnitude)--and if you don't trust .Magnitude, vec:Dot(vec) is also imperfect
end
``````
My output
``````1 (x2)
0.99999994039536 (x2)
1 (x3)
0.99999994039536
1 (x2)
0.99999994039536
1 (x11)
0.99999994039536
1 (x7)
0.99999994039536
1 (x3)
0.99999994039536
1 (x6)
0.99999994039536
1
0.99999994039536
1
0.99999994039536
1 (x2)
0.99999994039536
1
0.99999994039536 (x2)
1 (x8)
0.99999994039536
1
0.99999994039536
1 (x5)
0.99999994039536
1 (x9)
0.99999994039536
1 (x19)
0.99999994039536 (x2)
``````

As you can see, just generating the random unit vectors has floating point error on the order of `10^-8`

edit: I guess this isn’t necessarily too relevant to the dot product floating point but I’m still curious

edit2: how did you get 10^-13 for cross product?

I get 10^-23
``````local function GetOrthogonalVector(vec)
if vec.Magnitude == 0 then error("Vector has no magnitude") end

if vec == Vector3.new(0, 1, 0) or vec == Vector3.new(0, -1, 0) then
return Vector3.new(1, 0, 0)
else
return Vector3.new(0, 1, 0):Cross(vec).Unit
end
end

local worst=0
for i=1,1e5 do
local pitch=(math.random()-.5)*math.pi
local yaw=math.pi*2
local co=math.cos(pitch)
local si=math.sin(pitch)
local vec=Vector3.new(math.cos(yaw)*co,si,math.sin(yaw)*co)
worst=math.max(worst,math.abs(GetOrthogonalVector(vec):Dot(vec)))
end
print(worst)
``````

edit 3:

``````local function GetOrthogonalVector(vec)
local x=vec.x
local y=vec.y
local z=vec.z

if (y==1 or y==-1)and x==0 and z==0 then
return Vector3.new(1,0,0)
end
return Vector3.new(z,0,-x)
end

local worst=0
for i=1,1e5 do
local pitch=(math.random()-.5)*math.pi
local yaw=math.pi*2
local co=math.cos(pitch)
local si=math.sin(pitch)
local vec=Vector3.new(math.cos(yaw)*co,si,math.sin(yaw)*co)
worst=math.max(worst,math.abs(GetOrthogonalVector(vec):Dot(vec)))
end
print(worst)
``````

@AstroCode this has a 0 precision error but the orthogonal vector is not necessarily unit
When its unit it has 10^-23 precision (I just expanded out the cross product)

Also I never knew Vector3s overloaded the equal operator, thats pretty cool `print(Vector3.new(0,1,0)==Vector3.new(0,1,0))--true`

edit:: oh no it turns out I was doing `local yaw=math.pi*2` instead of `local yaw=math.pi*2*math.random()` doing random yaw makes me get `10^-8` error for cross product (still `10^-8` for dot)

So still very curious about how you make random unit vecs:P

I have a raycast in my game that gets the normal vector of a surface. I’m not making them. Their magnitudes can sometimes have floating-point errors, too.

1 Like

dang if its for a game why are you concerned about such small floating point errors xd

Haha I’m not. I just figured I’d put the information there. But even if it needed to be accurate, those errors are so negligible that it wouldn’t matter.

2 Likes