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
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.
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:
- sin sign is - , sign of θ is -, it’s -θ
- 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:
Holy moly I can not thank you enough for the in depth answer, going to try to understand as much as possible, THANK YOU









