Getting intersection point between two parts (relative to their front face)

Hello there. I will try to make this as short as possible. The title is probably a bit confusing, so I will try to explain this a little better. In short, I have two parts, Part A and Part B. What I want is to get the intersection point of the two “lines”, which are “created” based on where the part is facing. Let me explain with pictures:

These are Point A (Green) and Point B (Red), their front face on where the black arrow is:
image

Now, lets draw two lines that pass through the two parts:
image

Now that we have the lines, we can finally get the intersection point (Yellow):
image

Now, to make this easier, we could completely disregard the Y-axis as it is not important to our findings. This way, we have two 2-dimentional points consisting of (X, Y). From then onward, we can easily get the intersection point by using (d - b) / (a - c) for the x and a * x + b for y, assuming that one line is y = ax + b and the other is y = cx + d. My only issue is that I have no idea on how I could “create” an equation like that from the part. I can easily get a supposed x and y value and make it a “point”, but I have no idea on how to get the direction of the line. I have never worked with LookVectors, so I have no idea how they work. My question is, how would I be able to get a linear equation from a part, assuming that X is X, Z is Y, and the LookVector (where the front face is facing)? If you could help me, that would be amazing. Thank you for your time and help.

4 Likes

This thread probably answers most of your questions:

To create a line/ray/segment, you need 2 points. Assuming that the parts are always facing parallel to the lines(the direction they face IS the line), you can get these 2 points by offsetting the CFrame of the part in the Z axis.

local point1 = part.CFrame * CFrame.new(0, 0, -10) --10 studs in front of the part
local point2 = part.CFrame * CFrame.new(0, 0, 10) --10 studs behind the part

Then just get the .Position value of the CFrames to turn them into Vector3, and then plug them into the function inside the thread posted above. Note that the function uses segments and not lines, so you have to make sure the 2 segments are long enough to intersect each other.

3 Likes

I think that @Prototrode provided the easiest answer but ig i could share a method you’d use for like a ray tracer lol. What you could do is define a plane for the red thingy

local plane = {
  ["a"] = red.Position;
  ["n"] = red.CFrame.RightVector
}

then define the intersection point as blue.Position + blue.CFrame.LookVector * t

the set of equations we will solve now will be to solve t. If the dot product of our intersection point and a, ie a point on the plane (red) that vector formed to the normal is 0 then thats an intersection. So in implicit form thats like

0=plane["n"]:Dot(plane["a"] - (blue.Position+blue.CFrame.LookVector * t))

and setting t as our solver thingy with simple algebra understanding that dot products are a combination of the multiplication and addition of the vector components we get that t is equal to

local t = plane["n"]:Dot(plane["a"] - blue.Position) / plane["n"]:Dot(blue.LookVector)

print(blue.Position + blue.LookVector * t) -- intersection point

Do note if the directions are like parrallel to each other you will get some weird -nan errors so you might also want to check if t is a positive number too.

4 Likes

Thank you for the reply. This is basically what I want to do. I did take a look at the topic and tried experimenting with it, and got some rather interesting results which can be seen here.

To be fair though, I only needed to be able to get the points in the front and the back in order to make a line, and through inspiration from the topic you linked, I was able to create this code which does exactly what I want it to do:

-- Variables --

local Model = script.Parent
local PartA = Model:WaitForChild("A")
local PartC = Model:WaitForChild("C")
local PointB = Instance.new("Part")

-- Functions --

function GetEndPoints(Part, Distance)
	local Front = Part.CFrame * CFrame.new(0, 0, -Distance)
	local Back = Part.CFrame * CFrame.new(0, 0, Distance)
	return Front, Back
end

function GetLineFromPoints(PartA, PartB)
	local Slope = (PartB.Z - PartA.Z) / (PartB.X - PartA.X)
	local YIntercept = PartA.Z - (Slope * PartA.X)
	return Slope, YIntercept
end

-- Scripting --

while wait(0.02) do

	local AF, AB = GetEndPoints(PartA, 100)
	local BF, BB = GetEndPoints(PartC, 100)
	
	local LineASlope, LineAIntercept = GetLineFromPoints(AF, AB)
	local LineBSlope, LineBIntercept = GetLineFromPoints(BF, BB)

	local X = (LineBIntercept - LineAIntercept) / (LineASlope - LineBSlope)
	local Z = (LineASlope * X) + LineBIntercept
	
	local LineAVertical = ((AF.X / AB.X) % 2) == 1
	local LineBVertical = ((BF.X / BB.X) % 2) == 1
	
	if LineAVertical and LineBVertical then
		print("A")
		continue
	elseif LineAVertical then
		X = AF.X
		Z = (LineBSlope * X) + LineBIntercept
	elseif LineBVertical then
		X = BF.X
		Z = (LineASlope * X) + LineBIntercept
	end
	
	if LineASlope == LineBSlope then
		print("B")
		continue
	end
	
	PointB:Destroy()

	PointB = Instance.new("Part")
	PointB.Anchored = true
	PointB.Name = "PointB"
	PointB.Size = Vector3.new(1,1,1)
	PointB.Position = Vector3.new(X, 0.5, Z)
	PointB.Parent = Model
end

The only issue present is when the two parts have an angular difference of 0 or 180 between each other, or when they are at 90 degrees of the global world axis. Example video:

Do you maybe know how I could fix this? I have no idea why it does that, and although B prints, it only prints when both of the parts on a straight global axis. The code is above, so if you see any issues, please point them out. Again, huge thanks for the topic and for helping, and I hope you have an amazing day.

Figured out the issue. The first step into fixing it is to replace LineAVectical with LineASlope > 99999 instead of ((AF.X / AB.X) % 2) == 1. Same follows for LineBVertical, but just replace A with B.

Secondly, in order to successfully determine if the lines are parallel, it is best if you round up to the 4th decimal place (that was the sweet-spot I found when working with 5 degree increments), as even if the lines are parallel, you will get a different decimal when it comes to the fifth decimal place and down (probably a computer rounding error), so I recommend you round to the 4th decimal place.

Lastly, instead of completely skipping the step if both lines are vertical or they are parallel, you can instead just get the midpoint of the two, which will give you the exact result you need.

Having these aspects in mind, here is the final script:

-- Variables --

local Model = script.Parent
local PartA = Model:WaitForChild("A")
local PartC = Model:WaitForChild("C")
local PointB = Instance.new("Part")

-- Functions --

function GetEndPoints(Part, Distance)
	local Front = Part.CFrame * CFrame.new(0, 0, -Distance)
	local Back = Part.CFrame * CFrame.new(0, 0, Distance)
	return Front, Back
end

function GetLineFromPoints(PartA, PartB)
	local Slope = (PartB.Z - PartA.Z) / (PartB.X - PartA.X)
	local YIntercept = PartA.Z - (Slope * PartA.X)
	return Slope, YIntercept
end

function Round(Number, DecimalPlace)
	local Value = 10 ^ DecimalPlace
	return math.floor(Number * Value) / Value
end

-- Scripting --

while wait(0.02) do

	local AF, AB = GetEndPoints(PartA, 100)
	local BF, BB = GetEndPoints(PartC, 100)
	
	local LineASlope, LineAIntercept = GetLineFromPoints(AF, AB)
	local LineBSlope, LineBIntercept = GetLineFromPoints(BF, BB)

	local X = (LineBIntercept - LineAIntercept) / (LineASlope - LineBSlope)
	local Z = (LineASlope * X) + LineBIntercept
	
	local Y = (PartA.Position.Y + PartC.Position.Y) / 2
	
	local LineAVertical = LineASlope > 99999
	local LineBVertical = LineBSlope > 99999
	
	local LinesParallel = Round(LineASlope, 4) == Round(LineBSlope, 4)
	
	if (LineAVertical and LineBVertical) or LinesParallel then
		local PartAPos = PartA.Position
		local PartCPos = PartC.Position
		X = (PartAPos.X + PartCPos.X) / 2
		Z = (PartAPos.Z + PartCPos.Z) / 2
	elseif LineAVertical then
		X = AF.X
		Z = (LineBSlope * X) + LineBIntercept
	elseif LineBVertical then
		X = BF.X
		Z = (LineASlope * X) + LineAIntercept
	end
	
	PointB:Destroy()

	PointB = Instance.new("Part")
	PointB.Anchored = true
	PointB.Name = "PointB"
	PointB.Size = Vector3.new(1,1,1)
	PointB.Position = Vector3.new(X, Y, Z)
	PointB.Parent = Model
end

I hope this helps anyone in the future who might encounter this issue as well.

4 Likes