So I have the script below, where p = Instance.new('Frame', Lines)
and this is all held within a for
loop, so p2
is essentially the previous p
Frame, so that it can be referred to when getting the points to draw between when drawing the line.
if i%2 == 0 then local VD = math.abs(p.Position.Y.Scale - p2.Position.Y.Scale) --Verticle Distance, I tried magnitude but couldn't get it to work local HD = math.abs(p.Position.X.Scale - p2.Position.X.Scale) --Horizontal Distance local DD = math.sqrt(HD^2 + VD^2) --Diagonal Distance local DR = math.atan(VD/HD) --Diagonal Rotation print("VD: ", VD, ", HD: ", HD, ", DD: ", DD, ", DR: ", DR) local l = Instance.new('Frame', Lines) l.Size = UDim2.new(DD, 0, 0, 1) l.Rotation = DR l.Position = UDim2.new(p2.Position.X.Scale, 0, p2.Position.Y.Scale, 0) end p2 = p
Here is what the output looks like: https://gyazo.com/5dbd3237294267dca9aa552050c27a8e https://gyazo.com/e57ea82a2e9e925257cc7f308f6afe9f
I tried following this equation for the rotation of the line:
Slope Angle = Arctan(VD/HD)
I tried following this equation for the length of the line:
?(x2-x1)2+(y2-y1)^2
And I positioned it to p2 (since positioning is based on the top-left corner)
EDIT 1: I now realize that positioning to p2 isn't right, but that doesn't account for the wacky lines.
EDIT 2: So here is the data I'm trying to graph:
http://tcserver.raspctl.com/tc/r/10000/all.txt
It looks like this in case you can't reach the server:
18.189,18.282,18.282,18.285,18.271,18.277,18.275
I already have a function that properly splits it by its commas, so that is not a problem.
The x-axis can just be graphed like 1,2,3,etc.
So here is my full script:
Pixels = script.Parent.Pixels Lines = script.Parent.Lines GuiStorage = game.ServerStorage.GuiStorage function explode(d, p) local t, ll t = {} ll= 0 if(#p == 1) then return {p} end while true do local l = string.find(p, d, ll, true) --find the next d in the string if l ~= nil then --if "not not" found then.. table.insert(t, tonumber(string.sub(p, ll, l - 1))) --Save it in our array. ll = l + 1 --save just after where we found it for searching next time. else table.insert(t, tonumber(string.sub(p, ll))) --Save what's left in our array. break --Break at end, as it should be, according to the lua manual. end end return t end function getLineBetweenPointFrames(p1, p2, lineFrame, parentGui) -- Syntax sugar local p1X, p1Y = p1[1], p1[2] local p2X, p2Y = p2[1], p2[2] -- Get X and Y components of the line length local lenX = math.abs(p2X - p1X) local lenY = math.abs(p2Y - p1Y) -- Get diagonal (true) line length local lenD = math.sqrt(math.pow(lenX,2) + math.pow(lenY,2)) -- Find X and Y coordinates for line's position local nX = (p1X < p2X and p1X or p2X) + lenX / 2 - lenD / 2 local nY = (p1Y < p2Y and p1Y or p2Y) + lenY / 2 -- Find the rotation of the line local rot = math.deg(math.atan2(lenY, lenX)) -- Flip the rotation if needed local X1Start = p1X < p2X local Y1Start = p1Y < p2Y if (X1Start and not Y1Start) or (not X1Start and Y1Start) then -- Some Boolean logic rot = 180 - rot end -- Create a line Frame with calculated properties local line = lineFrame:Clone() line.Parent = parentGui line.Position = UDim2.new(0, nX, 0, nY) line.Size = UDim2.new(0, lenD, 0, 3) line.Rotation = rot return line end function graph(data, type) local datamax = math.max(unpack(data)) local datamin = math.min(unpack(data)) local SpanY = datamax - datamin for i = 1, #data do local PixelY = data[i] local SpanPixelY = datamax - PixelY local FractionY = SpanPixelY / SpanY local PixelX = i local SpanPixelX = #data - PixelX local FractionX = SpanPixelX / #data p = {FractionX*script.Parent.AbsoluteSize.X, FractionY*script.Parent.AbsoluteSize.Y} if i%2 == 0 then if type == "r" then print("Round ",i," P: ",p[1],":",p[2],"P2: ",p2[1],":",p2[2]) getLineBetweenPointFrames(p, p2, GuiStorage.BuxLine, Lines.Bux) elseif type == "t" then print("Round ",i," P: ",p[1],":",p[2],"P2: ",p2[1],":",p2[2]) getLineBetweenPointFrames(p, p2, GuiStorage.TixLine, Lines.Tix) end end p2 = p end end if game.ServerStorage.Local.Value == false then print('Server IP: WAN') bux = game:GetService("HttpService"):GetAsync("http://tcserver.raspctl.com/tc/r/1000/7.txt", true) tix = game:GetService("HttpService"):GetAsync("http://tcserver.raspctl.com/tc/t/1000/7.txt", true) else print('Server IP: LAN') bux = game:GetService("HttpService"):GetAsync("http://10.0.0.21/tc/r/1000/7.txt", true) tix = game:GetService("HttpService"):GetAsync("http://10.0.0.21/tc/t/1000/7.txt", true) end bux = explode(",", bux) tix = explode(",", tix) graph(bux, "r") graph(tix, "t") print("Getting Max/Min...") print("Max Bux: ", math.max(unpack(bux)), "Min Bux: ", math.min(unpack(bux))) print("Max Tix: ", math.max(unpack(tix)), "Min Tix: ", math.min(unpack(tix)))
Intro
Ok, this is too complex to just fix your code, as it would get messy quickly, so I will go through the whole process from scratch.
(TL;DR) Code is on the bottom.
Suggestions
I recommend that you make a table or some kind of storage that stores all the points that need to be connected, so you can get their coordinates easily.
It is also better to use AbsolutePosition
instead of relative, because relative points can sometimes have an offset that you might not want. If you use AbsolutePosition
, you will keep the Scale
, but will get the actual pixel count, Offset
.
What we need to do
Ok, so first, it is a good practice to write down things that you need to do. This is breaking down code into small bits, breaking them further until you get simple steps.
Do mind that I will write those steps in a sort of pseudo code.
1) Get X and Y components of the line length
1.1) Use absolute position components
1.2) Use Lx = ABSOLUTE(X2 - X1)
and Ly = ABSOLUTE(Y2 - Y1)
I will use X for the length of the line, as it will be easier to manipulate later.
2) Get diagonal (true) line length
2.1) Use components calculated in step 1)
2.2) Use L = SQRT(Lx^2 + Ly^2)
Above steps were straightforward, I hope, but this one might be a little tricky to think of by yourself. While I doubt your lines will ever go from right to left, I want to make sure that you do not stumble upon this problem again, asking how to get that. So, to make this code compatible with any orientation of the points, we need to determine the true top and left coordinates of the line. To do that, we use the smallest X and Y values from the 2 points.
When we get those values, we need to - add half of the X component and subtract half of the length of the X value - add half of the Y component to the Y value
We add half of each component because we need to center the line frame in order to make it rotate correctly.
We subtract the length to the X value because I decided to use X as the length, so we get to the first point.
3) Find X and Y coordinates for line's position
3.1) Determine which point has the smallest value for each coordinate
3.2) Add half of X component and subtract half of length if it is X coordinate, otherwise just add half of the Y component
While atan
function is good in some cases, in yours, it is not.
atan
does not account for rotation outside first (and third) quadrant so degrees 90-180 (and 270-360), exclusive, will not be accounted for.
You have to use atan2
, which is more reliable as it also accounts for division with 0, aka. fully vertical lines.
4) Find the rotation of the line
4.1) Use ARCTAN(Y / X)
4.2) Turn it to degrees
There is another problem with rotation. Since we are getting full rotation, 0-360, we need to rotate the line 180 degrees if either X or Y are taken from the second point, but not if both or neither of them are. So, we can use some Boolean logic to get that.
5) Flip the rotation if needed
5.1) Determine if (X1 > X2) or (Y1 > Y2)
, but not both or neither (This is XOR)
5.2) If yes, flip rotation by 180 degrees
Now comes the final step, putting this all into the Properties of the line Frame.
6) Create a line Frame with calculated properties
The code
Once we know what is needed, it is easier to actually code it.
function getLineBetweenPointFrames(p1, p2, lineFrame, parentGui) -- Syntax sugar local p1X, p1Y = p1.AbsolutePosition.X, p1.AbsolutePosition.Y local p2X, p2Y = p2.AbsolutePosition.X, p2.AbsolutePosition.Y -- Get X and Y components of the line length local lenX = math.abs(p2X - p1X) local lenY = math.abs(p2Y - p1Y) -- Get diagonal (true) line length local lenD = math.sqrt(math.pow(lenX,2) + math.pow(lenY,2)) -- Find X and Y coordinates for line's position local nX = (p1X < p2X and p1X or p2X) + lenX / 2 - lenD / 2 local nY = (p1Y < p2Y and p1Y or p2Y) + lenY / 2 -- Find the rotation of the line local rot = math.deg(math.atan2(lenY, lenX)) -- Flip the rotation if needed local X1Start = p1X < p2X local Y1Start = p1Y < p2Y if (X1Start and not Y1Start) or (not X1Start and Y1Start) then -- Some Boolean logic rot = 180 - rot end -- Create a line Frame with calculated properties local line = lineFrame:Clone() line.Parent = parentGui line.Position = UDim2.new(0, nX, 0, nY) line.Size = UDim2.new(0, lenD, 0, 0) line.Rotation = rot return line end
While this could have been done in many ways (some are certainly better methods), this is the one that came to my mind and I hope it helped.
If you need any more help, please leave a comment.