How can I calculate this?

I am creating a system for my drawing board that you can create a line, how can I calculate the angle, size, position for the line from point a and point b, point a being the starting position, point b being the mouse position

2 Likes

Hi, I make a similar thing recently for calculate orientation of NPCs.

If you use a Frame for draw your line you can use the Lerp function for get the position between two Vector2

For the size of the frame you can use the Magnitude

And for the angle it’s simple Trigonometry calcul.

I willl make a exemple faster with all calcul, just wait a five of minutes.

1 Like

Ok, I made faster a exemple.

That is the code with some comments:

--//Services
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local UserInputService = game:GetService("UserInputService")

--//Elementes
local plr = Players.LocalPlayer
local plrGui = plr:WaitForChild("PlayerGui")

local screen = Instance.new("ScreenGui", plrGui)
screen.IgnoreGuiInset = true

local drawFrame = Instance.new("Frame", screen)
drawFrame.BackgroundColor3 = Color3.new(1, 1, 1)
drawFrame.Size = UDim2.fromOffset(750, 500)
drawFrame.Position = UDim2.fromScale(.5, .5)
drawFrame.AnchorPoint = Vector2.new(.5, .5)

--//Memory
local startPos = Vector2.new()
local drawPos = Vector2.new()
local mouseDown = true

--//Configuration
local LINE_THICKNESS = 5


function PosIsInDrawFrame(pos)
	pos = pos - drawFrame.AbsolutePosition
	
	return pos == ClampPosInDrawFrame(pos) --If the position is not on the frame, the position will be not equal to the clamped position
end

function ClampPosInDrawFrame(pos)
	local frameSize = drawFrame.AbsoluteSize
	local minX, maxX = 0, frameSize.X
	local minY, maxY = 0, frameSize.Y
	local clampedPos = Vector2.new(math.clamp(pos.X, minX, maxX), math.clamp(pos.Y, minY, maxY)) --For lock position to the drawFrame
	
	
	return clampedPos
end


function GetFramePosition(p1, p2)
	local pos = p1:Lerp(p2, .5) --Get the midle between Pos1 and Pos2
	pos = pos / drawFrame.AbsoluteSize --Convert this position(currently in offset) to scale
	return pos.X, pos.Y
end

function GetLineLen(p1, p2)
	local len = (p1 - p2).Magnitude --Calculate Euclidean distance for len, similar to  math.sqrt((p1.X - p2.X)^2 + (p1.Y - p2.Y)^2)
	return len
end

--That it's the most hard part to understand, I will explain that after with shema
function GetOrientation(p1, p2)
	local delta = p1 - p2

	local abjacent = delta.X
	local opposite = delta.Y
	local hypotenuse = math.sqrt(abjacent ^ 2 + opposite ^ 2)

	local cosinusTheta = abjacent / hypotenuse
	local sinusTheta = opposite / hypotenuse

	local sign = sinusTheta > 0 and 1 or -1

	local orientation = math.acos(cosinusTheta) * sign

	return math.deg(orientation)
	
end

function RenderDraw(frame) --Just update the frame posion, size and orientation
	frame.Position = UDim2.fromScale(GetFramePosition(startPos, drawPos))
	frame.Size = UDim2.fromOffset(GetLineLen(startPos, drawPos), LINE_THICKNESS)
	
	frame.Rotation = GetOrientation(startPos, drawPos)
end

function CreateFrame()
	local frame = Instance.new("Frame", drawFrame)
	frame.BackgroundColor3 = Color3.new()
	frame.BorderSizePixel = 0
	frame.AnchorPoint = Vector2.new(.5, .5) --Necessary for after, for have the frame position from the midle of the frame
	
	return frame
end

function StartDrawing()
	mouseDown = true --Set mouseDown to true
	startPos = drawPos --Set the start position to the current position
	
	local frame = CreateFrame()
	
	while mouseDown do
		RenderDraw(frame) --Just update the frame on drawing
		wait()
	end
end


UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local pos = input.Position
		pos = Vector2.new(pos.X, pos.Y) - drawFrame.AbsolutePosition --Convert to vector2 and remove the DrawFrame absolute positions for has the good position
		
		drawPos = ClampPosInDrawFrame(pos) --Clamp the position for don't have a position out of the frame
	end
end)

UserInputService.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		local pos = input.Position
		pos = Vector2.new(pos.X, pos.Y) -- Just convert the input position to vector2
		
		if PosIsInDrawFrame(pos) then --Check if the position is on the draw frame
			StartDrawing()
		end
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		mouseDown = false --Just set mouseDown to false when MouseButton1 up
	end
end)

Explanation of GetOrientation function:

For better explation I will split the function in six part.

function GetOrientation(p1, p2)
	--Phase 1
	local delta = p1 - p2
	
	--Phase 2
	local abjacent = delta.X
	local opposite = delta.Y
	local hypotenuse = math.sqrt(abjacent ^ 2 + opposite ^ 2)
	
	
	--Phase 3
	local cosinusTheta = abjacent / hypotenuse
	local sinusTheta = opposite / hypotenuse
	
	--Phase 4
	local sign = sinusTheta > 0 and 1 or -1
	
	--Phase 5
	local orientation = math.acos(cosinusTheta) * sign
	
	--Phase 6
	return math.deg(orientation)
	
end

Phase1: Get the delta between p1 and p2
Ok, that is a simple part to understand, for determinated orientation I need to sub a vector to the second.

In this exemple p1(1, 1) and p2(4, 2)

p1-p2=p1(1, 1)-p2(4, 2)=delta(-3, -1)

You are currently asking you why?

Phase2: Hypotenus, Adjascent and Opposite

Delta point it’s to make a Right triangle from axis origin for make Trigonometry.

All Right triangle edge as a name Hypotenus, Adjascent and Opposite

This image come from wikipedia

In our case, it corresponds to these edges:

  • Adjacent len it’s the X position, -3 in this case Yes, it’s a negative len, but it’s necessary for has a correct result after
  • Opposite len it’s the Y position, -1 in this case
  • Hypotenus it’s easy to calculate just need to use Pythagorean theorem Hypotenus=math.sqrt(Adjacent^2+Opposite^2) in this case math.sqrt(-3²+-1²)=math.sqrt(10) ≈3.16227766017

Phase3: Cosinus and sinus of theta
Now, a another essencial part for calculate the angle, it’s know cosinus and sinus of theta.

But what is theta? Now I will use the theta symbole → θ
Your question is how to calculate the angle. And θ is this angle.

A exemple, on this image you can see angle θ. The searched angle is θ.

if we know cos(θ) and sin(θ). Find θ will be easy

Why cos(θ) and sin(θ)? For that, I just join a image of Unit circle


(image of wikipedia)

I will use that:
cos(θ) = abjacent / hypotenuse
sin(θ) = opposite / hypotenuse

In this case:
cos(θ) = -3 / 3.16227766017 = -0.94868329805
sin(θ) = -1 / 3.16227766017 = -0.31622776601

Phase 4: Sinus sign?
This is a simple part, just need know if sinus sign is - or +

Why? Because in the Unit circle always two angles as the same cos exept π/2 and -π/2.


In the next step I will use arccosine (an Inverse trigonometric functions! More info about that here: Inverse trigonometric functions - Wikipedia)
And arc cosinus return always the angle whit + sign. But if θ is a negative angle, that will not work. Solution is use sign of sin.

Two case:

  1. sin sign is - , sign of θ is -, it’s -θ
  2. sin sign is +, sing of θ is +, it’s just θ

Is use 1 or -1 for store the code

local sign = sinusTheta > 0 and 1 or -1

It’s just that in one line

local sign
if sinusTheta > 0 then
   sign =1 
else
   sign=-1
end

Phase5: Calculate θ

That it’s simple now.

Get the always positif angle, (I will call this angle θ’)
θ’ = acos(cos(θ))

Aplly the sign of sin

θ = θ’ * singOfSin
It’s because singOfSin is 1 or -1

Fully calcul of this step:
θ = acos(cos(θ)) * singOfSin

In this exemple:
acos(-0.94868329805) * -1 = -2.8198421 rad

WARNING, the angle is in radians, not in degrees, the last part is just the converion.

Now:

Why +2π ?
It’s because angle is represented on counterclockwise

But I can make this representation:

Phase6: Just convert θ to degrees
The calculation was made in radians, last step is just convert radians to degrees. I just use math.deg(θ)

Now:

9 Likes

Holy moly I can not thank you enough for the in depth answer, going to try to understand as much as possible, THANK YOU

4 Likes