Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

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

Asked by
GShocked 150
8 years ago

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

Log in to vote
2
Answered by 8 years ago

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.

0
For some reason, this partly draws the line. Here is a GIF of what the line looks like: https://gyazo.com/c436c323a6cf55b44d2a477efc7562df GShocked 150 — 8y
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. LetThereBeCode 360 — 8y
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. GShocked 150 — 8y
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. GShocked 150 — 8y
Ad

Answer this question