Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Why doesn't this draw a line between two points?

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)))

1 answer

✓ Accepted answer

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.

4 comments

0

For some reason, this partly draws the line. Here is a GIF of what the line looks like: https://gyazo.com/c436c323a6cf55b44d2a477efc7562df

1

Are you sure you have the point Frames set up correctly? They have to be a gui object with size 1,1, btw. I tested the function and it worked fine for me.

0

I've tried using your function alone in a script, and it worked fine. But when I run it in my loop, passing in p and p2, it does that. I'll try to find what is wrong.

0

I tried using the raw data instead of adjusting for the scale of the graph, but that gave worse results. I've updated my question with the data I'm trying to graph, so if you would have any idea on how to do this.

This discussion is preserved as it was. New answers, comments, and votes are closed.