Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Maths query

in General Posts: 1,600

Hi All - anyone know of a conversion of atan() to atan2() ? Apparently atan2() of an angle calculation returns -pi < 0 < pi.

Tagged:

Comments

  • dave1707dave1707 Mod
    Posts: 8,187

    @Bri_G Does one of there work for you.

    function setup()
        ang=25
    
        print(math.atan(ang))
        print(math.atan(math.deg(ang)))
        print(math.atan(math.rad(ang)))
    
        print(math.atan2(ang))
        print(math.atan2(math.deg(ang)))
        print(math.atan2(math.rad(ang)))
    end
    
  • dave1707dave1707 Mod
    Posts: 8,187

    @Bri_G Or something like this.

    function setup()
        print(math.tan(math.rad(45)))
        print(math.atan2(1)*180/math.pi)
    end
    
  • edited January 26 Posts: 480
    • math.atan(y/x) returns the angle from 0 to pi of the line with gradient y/x, so (x,y) and (-x,-y) return the same angle.
    • math.atan2(y,x) returns the angle from -pi to pi of the ray through the point (x,y) so (x,y) and (-x,-y) return angles that differ by pi. (Note the order of the arguments is y,x.)
  • edited January 26 Posts: 1,600

    @dave1707 - thanks for the feedback, tried using them and slight improvement with the following line of code :


    angle = math.deg(math.atan(dy,dx))

    Didn't produce the effect I wanted - now beginning to think it is language problem javascript to Lua. Just trying to duplicate some demo code and my Javascript is a little rusty - but I think it represents angles in a different form to Lua. Now investigating this. Will most what I am doing once I resolve it.

    @LoopSpace - thanks for the feedback that's the best definition I've seen so far and fits what I am trying to do.

    p.s. Is atan2() in the reference section for Lua in the Codea reference? Didn't see it.

  • Posts: 480

    @Bri_G If you describe what you're trying to do, I can advise better on which to use (though the answer is most likely to be atan2). I know a little javascript so if you post the original code, I may be able to help translate it. The atan/atan2 issue is different to radians vs degrees, but it may be that the latter is muddying the waters a bit (Codea isn't consistent on when it uses degrees and when radians).

  • dave1707dave1707 Mod
    Posts: 8,187

    @Bri_G Run this. I don’t see a difference between atan and atan2.

    function setup()
        print("atan functions")
        print(math.atan(5,5)*180/math.pi)       -- 1st quadrant
        print(math.atan(-5,5)*180/math.pi)      -- 2nd quadrant
        print(math.atan(-5,-5)*180/math.pi)      -- 3rd quadrant
        print(math.atan(5,-5)*180/math.pi)      -- 4th quadrant
    
        print("atan2 functions")    
        print(math.atan2(5,5)*180/math.pi)
        print(math.atan2(-5,5)*180/math.pi)
        print(math.atan2(-5,-5)*180/math.pi)
        print(math.atan2(5,-5)*180/math.pi)
    end
    
  • Posts: 1,600

    @dave1707 - would you expect that?

    OK, I'll post my code - it's trying to duplicate an example which I think is Javascript as it runs in a `PC window. My Codea post is below and the link to the demo is below it. I was hoping to use this to enable drawing of random strings on the screen - like scribbling across the screen, so I was interested in the curve and linking code to see if I could reproduce and modify to my needs:


    displayMode(FULLSCREEN) function setup() -- parameter.watch("xpos[1]") parameter.watch("ypos[1]") parameter.watch("xpos[19]") parameter.watch("ypos[19]") parameter.watch("angle") xpos = {} -- 20 ypos = {} -- 20 for loop = 1, 20, 1 do xpos[loop] = 0 ypos[loop] = 0 end segLength = 32 angle = 0 xin, yin = 0,0 skip = false end function draw() background(0) dragSegment(1,xin,yin) if not skip then for i = 1, #xpos-1, 1 do dragSegment(i, xpos[i], ypos[i]) end end end function touched(t) -- if t.state == BEGAN then xin,yin = t.x, t.y skip = true elseif t.state == MOVING then xin,yin = t.x, t.y skip = true elseif t.state == ENDED then xin,yin = 0,0 skip = true end end function dragSegment(sn, xin, yin) -- dx = xin - xpos[sn] dy = yin - ypos[sn] angle = math.atan2(dy, dx) xpos[sn] = xin - math.cos(angle) * segLength ypos[sn] = yin - math.sin(angle) * segLength segment(xpos[sn], ypos[sn], angle) end function segment( x, y, a) -- pushMatrix() strokeWidth(9) stroke(140, 143, 160, 100) translate(x, y) rotate(a) line(0, 0, segLength, 0) popMatrix() end

    and here is the link -

    follow3

  • Posts: 480

    @dave1707 In lua5.3 it seems that they've made a very sensible decision regarding math.atan and math.atan2:

    math.atan (y [, x])

    Returns the arc tangent of y/x (in radians), but uses the signs of both arguments to find the quadrant of the result. (It also handles correctly the case of x being zero.)

    The default value for x is 1, so that the call math.atan(y) returns the arc tangent of y.

    (source https://www.lua.org/manual/5.3/manual.html#pdf-math.atan )

    I'm presuming that @Simeon has made math.atan2 an alias of math.atan for backwards compatibility.

    So math.atan() with a single argument computes the inverse tangent of its argument, while math.atan() with two arguments takes into account the quadrant.

    This means that the crucial difference between the two is in the number of arguments that are supplied.

  • Posts: 1,600

    @LoopSpace - no problem with that but an entry in the reference reflecting this would be useful and highlight the importance of the number of parameters.

  • Posts: 480

    @Bri_G Point taken on the reference. Tagging @Simeon on this.

    With regard to your code, I recommand that you replace:

        dx = xin - xpos[sn]
        dy = yin - ypos[sn]
        angle = math.atan2(dy, dx)
        xpos[sn] = xin - math.cos(angle) * segLength
        ypos[sn] = yin - math.sin(angle) * segLength
    

    with

        dir = vec2(xin - xpos[sn], yin - ypos[sn])
        dir = dir:normalize() * segLength
        xpos[sn] = xin - dir.x
        ypos[sn] = yin - dir.y
    

    Calculating the inverse tangent is expensive, computationally speaking, and if you're only going to take its cosine and sine afterwards then there's no need. Normalising a vector is much cheaper.

  • Posts: 1,600

    @dave1707 @LoopSpace - my code above tries to imitate Follow3.html - I have a number of parameters set up to view a few of the variables involved some seem to change rapidly whilst others don't appear to change suggesting I haven't got the correct logic. Also each segment is either overwriting the previous one or is not drawn at all and the vertical y properties seem to be ignored with the exception of the touch position.

    The answer is probably very simple and obvious - but at the moment I can not see it.

  • dave1707dave1707 Mod
    edited January 27 Posts: 8,187

    @Bri_G Try this. The simple answer was that angle expects to be in degrees but all the atan2 values are in radians. Multiplying the angle by 180/pi converts radians to degrees.

    displayMode(FULLSCREEN)
    
    function setup()
        x,y={},{}
        for z=1,20 do
            x[z]=0
            y[z]=0
        end
        segLength = 18
        strokeWidth(9);
        stroke(255, 100);
        tx,ty=0,0
    end
    
    function draw()
        background(0);
        dragSegment(1, tx, ty);
        for i=1,#x-1 do
            dragSegment(i+1, x[i], y[i]);
        end
    end
    
    function touched(t)
        if t.state==BEGAN or t.state==MOVING then
            tx=t.x
            ty=t.y
        end
    end
    
    function dragSegment(i,xin,yin)
        dx = xin - x[i]
        dy = yin - y[i];
        angle = math.atan2(dy, dx)
        x[i] = xin - math.cos(angle) * segLength;
        y[i] = yin - math.sin(angle) * segLength;
        segment(x[i], y[i], angle);
    end
    
    function segment(x,y,a)
        pushMatrix()
        translate(x,y)
        rotate(a*180/math.pi)
        line(0,0,segLength,0)
        popMatrix()
    end
    
  • dave1707dave1707 Mod
    edited January 27 Posts: 8,187

    @Bri_G After I got your example working and I was playing with it, I thought it looked familar. I looked thru my code and found this. I’ll see if I can find when I first posted this. It must have been early in my coding years because I have displayMode inside of setup. I did that in my earlier years of Codea. Now I put it before setup.

    --snake1
    
    function setup()
        displayMode(FULLSCREEN)
        tab={}
        x=WIDTH/2
        y=HEIGHT/2
        sx=0
        sy=0
        length=150
    end
    
    function draw()
        background(40,40,50)     
        fill(255,0,0)
        text("Drag your finger around the screen",WIDTH/2,HEIGHT-50)
        x = x + sx
        y = y + sy
        if #tab<length then
            table.insert(tab,1,vec2(x,y))
        else
            table.insert(tab,1,vec2(x,y))
            table.remove(tab,#tab)
        end
        for a,b in pairs(tab) do
            if a==1 then
                ellipse(b.x,b.y,20,20)
            else
                ellipse(b.x,b.y,10,10)
            end
        end
    end
    
    function getDirection()
        dx=CurrentTouch.x-x
        dy=CurrentTouch.y-y
        h=math.sqrt(dx*dx+dy*dy)
        sx=dx/100
        sy=dy/100
    end
    
    function touched(t)
        getDirection()
    end
    
  • Posts: 1,600

    @dave1707 - thanks for the correction, interesting how such a subtle change can make or break it. Thought it was down to format initially but struggled to resolve.

    Also interesting that code with a terminal ; on lines is accepted?

    Thanks again

Sign In or Register to comment.