PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience

Calculating Dot Products

Calculating Dot Products

Jul 03 2018, 10:26 AM PST 15 min

Introduction

The dot product is an operation performed on two vectors in any dimension that returns a single number. It is also sometimes called the scalar product, the inner product, or rarely the projection product. The dot product is considered a way to multiply two vectors. Performing the dot product operation is simple enough, but in order to use it in code you will need a good understanding of the mathematics behind the operation. This article aims give a better understanding of the dot product so that you can use it in your games!

Prerequisites

  • Articles/Vector3 Math
  • Trigonometric functions

Geometric definition

To start off let’s have a definition for the dot product given vectors A and B.

“The scalar projection of A onto B multiplied by the magnitude of B”

“The scalar projection of B onto A multiplied by the magnitude of A”

This definition may of course leave you wondering what a scalar projection is, and more importantly how to calculate it. A scalar projection is the amount that one vector travels in another vector’s direction. So if we say that we want the projection of A onto B we want to know how much of vector A is going in the same direction as vector B and vice versa for the projection of B onto A.

dot-projection-gif

To calculate the projection we use the trigonometric functions alongside right triangles to solve for the adjacent edge.

dot_product_projection_derive

So that takes care of the projection aspect of our geometric dot product. To finish the dot product calculation we take the magnitude of the vector we’re projecting onto and multiplying it by the projection. With that in mind we can start to define an equation for the dot product:

dotmath1

We can further expand this to:

dotmath2

That’s great, but we aren’t done yet. What about the projection of B onto A multiplied by the magnitude of A? Let’s draw it out and see what we get:

dor_product_projection_derive2

Once again we can take this projection value and plug it into our other dot product definition:

dotmath3

We can further expand this to:

dotmath4

Would you look at that! The dot product is commutative. It doesn’t matter if we dot A against B or B against A, we get the same result! So we know that given both vectors and the angle between them we can geometrically calculate the dot product like so:

function dot(a, b, theta)
	return a.magnitude * b.magnitude * math.cos(theta);
end;

Another important thing this has shown us is that by using simple algebra on the dot product equation we can get both the scalar projection of A onto B and the scalar projection of B onto A.

dotmath5

function projab(b, dot)
    return dot / b.magnitude
end

function projba(a, dot) 
    return dot / a.magnitude
end

Assuming we already have the dot product and the magnitude of both vectors we can also easily solve for the angle between them.

dotmath6

-- this will work with vectors of any dimension
function angle(a, b, dot)
	return math.acos(dot / (a.magnitude * b.magnitude));
end;

Algebraic definition

In the previous section we defined both an equation and a definition for the dot product. We also showed some of the possible/common uses of the dot product. The problem with this however is that the geometric equation for the dot product requires us to know the angle between two vector for us to be able to compute it. Here’s the good news, there’s another way! To figure out what it is let’s review the law of cosines.

law_of_cosines

We can take this definition and bring it over to the triangle created by two vectors:

dot_product_law_of_cosines_2

Our first step will be to substitute in the dot product:

dotmath7

Now we will solve for A dot B:

dotmath8

We know that the magnitude of a vector can be found by using Pythagorean Theorem:

vector_hypotneuses

Since that is the case we know that the magnitude of a vector squared is equal to the sum of its components squared. As such we can rewrite the above as (assuming Vector3):

dotmath9

If we went through the same process with Vector2 we would find that:

dotmath10

If we kept doing this same process with any number of dimensions we would find a pattern for calculating the dot product.

dotmath11

Therefore we can write our two dot product functions for Vector3 and Vector2 as such:

-- dot product for vector2
function dot2d(a, b)
	return a.x * b.x + a.y * b.y;
end;

-- dot product for vector3
-- equivalent to a:Dot(b) method
function dot3d(a, b)
	return a.x * b.x + a.y * b.y + a.z * b.z;
end;

Case study: Reflecting lasers

To finish this article off we’re going to go how we can use the dot product to create a laser beam that reflects off things in our game. To do this calculation we will need two things:

  • The vector we want to reflect
  • The surface normal of what we want to reflect against

Getting the first part is simple enough, but how do we get the surface normal? For the sake of this example we will use the returned normal vector from the Workspace/FindPartOnRay function.

So, let’s take a look at the situation. In the image below we have the vector we want to reflect (initial laser) and the surface normal. Our goal is to calculate the reflected laser’s direction.

dotproductlaser1

The first thing to take note of is that as we have it drawn above the vectors aren’t drawn with their tails attached to the origin. To fix that let’s redraw the above image so we can get a better visualization of our vectors (at least in a mathematical sense). We’ll also draw our reflected laser as a dotted line to signify that we don’t actually know its value, rather it’s something we’re trying to solve for.

dotproduct_laser2

The next step we’ll take is to get the opposite vector to the initial laser and then find its scalar projection on the surface normal:

dotproductlaser4

Finally if we double that scalar projection, convert it into a vector projection, and then finally add the initial laser to it (tail to tip) we’ll find we’re left with the reflected laser!

800px-laser5

So with all this in mind we can easily write a vector reflection function:

function reflect(input, normal)
	return -2 * input:Dot(normal) * normal + input
end

With that being said, this is not a lesson on creating tools. There are other articles on that. As such here is a quick example of a reflecting laser gun.

local tool = script.Parent
local handle = tool:WaitForChild("Handle")
 
function reflect(input, normal)
	-- using dot method b/c vector3
	return -2 * input:Dot(normal) * normal + input
end
 
function drawray(ray, parent)
	local part = Instance.new("Part")
	part.Material = Enum.Material.Neon
	part.Size = Vector3.new(.2, .2, ray.Direction.magnitude)
	part.CFrame = CFrame.new(ray.Origin + ray.Direction/2, ray.Origin + ray.Direction)
	part.Anchored = true
	part.CanCollide = false
	part.BrickColor = BrickColor.new("Bright red")
	part.Parent = parent or game.Workspace.CurrentCamera
	return part
end
 
function fire(from, to, bounce)
	local bounce = bounce or 0
	if bounce <= 5 then -- how many times can it reflect
		-- first ray is to calculate distance and normal
		local ray = Ray.new(from, (to - from).unit * 500)
		local hit, pos, normal = game.Workspace:FindPartOnRay(ray, tool.Parent)
 
		-- this is the actual ray we use and draw
		local ray2 = Ray.new(from, (pos - from))
		local part = drawray(ray2, tool)
 
		-- throw out the laser beam later
		game:GetService("Debris"):AddItem(part, 2)
 
		-- calculate the reflected ray
		local ref = reflect((pos - from), normal)
		if hit then
			local hum = hit.Parent:FindFirstChild("Humanoid")
			if hum then
				hum:TakeDamage(math.random(10, 15))
			end
			-- shoot a ray in the reflected position
			fire(pos, pos + ref, bounce + 1)
		end
	end
end
 
script.Parent.Equipped:connect(function(mouse)
	mouse.TargetFilter = game.Workspace
	script.Parent.Activated:connect(function() 
		fire(handle.CFrame.p, mouse.Hit.p)
	end)
end)
Tags:
  • math
  • coding