[help] with joystick precision!?

EDITED post: I decided to make a little drawing app, controlled by a joystick. It works similar to a drawing tablet on a pc.

On the picture you can see my joystick. The more I pull it the more precise I want it to be! But how to do that?
With ‘precise’ I mean it should lock in movement in the direction of the vector, where I pulled the finger to.

@se24vad I’m not sure I understand what you’re trying to do. It sounds like you want to have 2 joystick controllers. The first touch is joystick 1 that controls the x,y movement and the second touch is joystick 2 that controls a different x,y movement. Is that it or am I totally wrong.

I used just a simple joystick to overcome my issue. Updated the thread to a new question…



I decided to make a little drawing app, controlled by a joystick. It works similar to a drawing tablet on a pc.

On the picture you can see my joystick. The more I pull it the more precise I want it to be! But how to do that?
With ‘precise’ I mean it should lock in movement in the direction of the vector, where I pulled the finger to.

@se24vad, you mean that the more you pull, the more it does as you say, and that if your only pull a little, it’ll draw a line instead of what you draw…?

@stevon8ter No. I mean when pulling a little, you should be able to control x,y freely. But the more you pull, the more the joystick gets locked(!) to a certain angle. Meaning at some point you move only in one vector direction.

The idea is… often people draw STRAIGHT lines with a fast motion! My app should be similar. The more you pull, the faster the movement, the more straight the line. I want straight lines by more pulling on the joystick…

Hope it’s clear now :slight_smile:

@se24vad, yes it’s clear now, hmmmm well let me think how you’d do that

@se24vad Would this work. Put you finger on the screen and move it in the direction you want to draw. Try small movements at first.


displayMode(FULLSCREEN)

function setup()
    img=image(WIDTH,HEIGHT)
    js={}
    cx=WIDTH/2
    cy=HEIGHT/2
    cxh=cx
    cyh=cy
    x,y=0,0
    sp=1
end

function draw()
    background(40,40,50)
    if x + y>0 then
        cx=cx+(tx-x)/10*sp
        cy=cy+(ty-y)/10*sp
        noFill()
        ellipse(x,y,2)
        ellipse(x,y,80)
        line(x,y,lx,ly)
    end
    setContext(img)
    stroke(255)
    strokeWidth(2)
    line(cxh,cyh,cx,cy)
    setContext()
    sprite(img,WIDTH/2,HEIGHT/2)
    cxh=cx
    cyh=cy
end

function touched(t)
    if t.state==BEGAN then    -- starting center point
        x=t.x
        y=t.y
        tx=x
        ty=y
        lx=x
        ly=y
    elseif t.state==MOVING then
        lx=t.x
        ly=t.y
        v1=vec2(x,y)
        d1=v1:dist(vec2(t.x,t.y))
        if d1<40 then
            tx=t.x
            ty=t.y
        end
        sp=d1/200+1
    elseif  t.state==ENDED then    -- done moving
        x,y=0,0
    end
end

@dave1707 not quite, no.

try out my code, please! maybe you find then a solution, i’m thinking too)

function setup()
    --displayMode(FULLSCREEN)
    
    canvas = image(WIDTH,HEIGHT)
    setContext(canvas)
    background(147, 177, 180, 255)
    setContext()
    
    pen = vec2(WIDTH/2, HEIGHT/2)
end

function draw()
    background(255, 0, 0, 255)
    noSmooth()
    
    if joystick then
        setContext(canvas)
        blendMode(ZERO, ONE, ZERO, ONE_MINUS_SRC_ALPHA)
        
        fill(0, 0, 0, 200)
        noStroke()
        pen = pen + vec2(joystick.dx, joystick.dy)
        ellipse(pen.x, pen.y, 2)
        
        canvas.premultiplied = false
        blendMode(NORMAL)
        setContext()
    end
    
    spriteMode(CORNER)
    sprite(canvas, 0,0)
    
    if joystick then
        --show HUD
        pushStyle()
        
        fill(216, 216, 216, 255)
        ellipse(pen.x, pen.y, 2)
        
        noFill()
        stroke(30, 99, 122, 255)
        strokeWidth(1)
        ellipse(joystick.cx, joystick.cy, 120)
        
        strokeWidth(2)
        line(joystick.cx, joystick.cy, joystick.x, joystick.y)
        
        popStyle()
    end 
end

-- Map a number to a new range of numbers
-- example: map(255, 0,255, 0,1) would map 255 to 1 in a range of [0-1]
local function map(value, start1, stop1, start2, stop2)
    local n = type(value) == "table" and value or {value}
    local t = {}
    
    for i = 1, #n do
        local norm = (n[i] - start1) / (stop1 - start1)
        norm = norm * (stop2 - start2) + start2
        
        table.insert(t, norm)
    end
    
    return type(value) == "table" and t or unpack(t)
end

function touched(touch)
    if touch.state == BEGAN then
        if not joystick then
            joystick = {
               id = touch.id,
               cx = touch.x,
               cy = touch.y,
               x = touch.x,
               y = touch.y,
               dx = 0,
               dy = 0
           }
       end
   end

   if touch.state == ENDED then
       if joystick and joystick.id == touch.id then
           joystick = nil
       end
   end
    
   if touch.state == MOVING then
       if joystick and joystick.id == touch.id then
           joystick.x, joystick.y = touch.x, touch.y
           joystick.dx, joystick.dy = unpack(map({touch.x - joystick.cx, touch.y - joystick.cy}, -1,1, -.004,.004))
       end
   end
end

@se24vad I changed my code above. If your finger is inside of the joystick circle you can change direction. If you’re beyond the circle you can’t change direction and the farther outside the circle the faster it draws. Is that closer.

@dave1707 yes. this goes in the right direction. BUT the idea was to increase this ‘direction-precision’ BY the length of the pull distance! No after leaving the joystick circle, but constantly…

I have no good idea of how to approach… and don’t know if I’m describing my question clearly…

@se24vad I’m not sure of your “direction precision”. If you’re going in a certain direction, I don’t think you can increase the precision of that direction. To go in a certain direction, you add or subtract values to the x and y values. I don’t see how you can increase the precision of that direction, especially since you’re not trying to end up at a specific destination.

@se24vad if you want it to average all the angles of where the touch point is from the joystick while moving then if the length is past a certain amount or a timer then lock the angle to the averaged and keep going in that direction using the delta:len() of the touch?

How’s this?

function setup()
    --displayMode(FULLSCREEN)

    canvas = image(WIDTH,HEIGHT)
    setContext(canvas)
    background(147, 177, 180, 255)
    setContext()

    pen = vec2(WIDTH/2, HEIGHT/2)
end

function draw()
    background(255, 0, 0, 255)
    noSmooth()

    if joystick then
        setContext(canvas)
        blendMode(ZERO, ONE, ZERO, ONE_MINUS_SRC_ALPHA)

        fill(0, 0, 0, 200)
        noStroke()
        pen = pen + vec2(joystick.dx, joystick.dy)
        ellipse(pen.x, pen.y, 2)

        canvas.premultiplied = false
        blendMode(NORMAL)
        setContext()
    end

    spriteMode(CORNER)
    sprite(canvas, 0,0)

    if joystick then
        --show HUD
        pushStyle()

        fill(216, 216, 216, 255)
        ellipse(pen.x, pen.y, 2)

        noFill()
        stroke(30, 99, 122, 255)
        strokeWidth(1)
        ellipse(joystick.cx, joystick.cy, 120)

        strokeWidth(2)
        line(joystick.cx, joystick.cy, joystick.x, joystick.y)

        popStyle()
    end 
end

-- Map a number to a new range of numbers
-- example: map(255, 0,255, 0,1) would map 255 to 1 in a range of [0-1]
local function map(value, start1, stop1, start2, stop2)
    local n = type(value) == "table" and value or {value}
    local t = {}

    for i = 1, #n do
        local norm = (n[i] - start1) / (stop1 - start1)
        norm = norm * (stop2 - start2) + start2

        table.insert(t, norm)
    end

    return type(value) == "table" and t or unpack(t)
end

function touched(touch)
    if touch.state == BEGAN then
        if not joystick then
            joystick = {
                id = touch.id,
                cx = touch.x,
                cy = touch.y,
                x = touch.x,
                y = touch.y,
                dx = 0,
                dy = 0
            }
        end
    end
    
    if touch.state == ENDED or touch.state == CANCELLED then
        if joystick and joystick.id == touch.id then
            joystick = nil
        end
    end
    
    if touch.state == MOVING then
        if joystick and joystick.id == touch.id then
            local dx, dy = joystick.x - joystick.cx, joystick.y - joystick.cy
            local pos = {0, 0}
            if vec2(joystick.cx, joystick.cy):dist(vec2(joystick.x, joystick.y)) >= 150 then
                local rad = -(math.floor(math.atan2(dy, dx) / math.pi * 2 + 0.5) / 2 * math.pi) + math.pi / 2
                local sin, cos = math.sin(rad), math.cos(rad)
                sin, cos = math.abs(touch.x - joystick.cx) * sin, math.abs(touch.y - joystick.cy) * cos
                pos[1], pos[2] = sin, cos
            elseif vec2(joystick.cx, joystick.cy):dist(vec2(joystick.x, joystick.y)) >= 125 then
                local rad = -(math.floor(math.atan2(dy, dx) / math.pi * 4 + 0.5) / 4 * math.pi) + math.pi / 2
                local sin, cos = math.sin(rad), math.cos(rad)
                sin, cos = math.abs(touch.x - joystick.cx) * sin, math.abs(touch.y - joystick.cy) * cos
                pos[1], pos[2] = sin, cos
            elseif vec2(joystick.cx, joystick.cy):dist(vec2(joystick.x, joystick.y)) >= 100 then
                local rad = -(math.floor(math.atan2(dy, dx) / math.pi * 8 + 0.5) / 8 * math.pi) + math.pi / 2
                local sin, cos = math.sin(rad), math.cos(rad)
                sin, cos = math.abs(touch.x - joystick.cx) * sin, math.abs(touch.y - joystick.cy) * cos
                pos[1], pos[2] = sin, cos
            elseif vec2(joystick.cx, joystick.cy):dist(vec2(joystick.x, joystick.y)) >= 75 then
                local rad = -(math.floor(math.atan2(dy, dx) / math.pi * 16 + 0.5) / 16 * math.pi) + math.pi / 2
                local sin, cos = math.sin(rad), math.cos(rad)
                sin, cos = math.abs(touch.x - joystick.cx) * sin, math.abs(touch.y - joystick.cy) * cos
                pos[1], pos[2] = sin, cos
            else
                pos[1], pos[2] = touch.x - joystick.cx, touch.y - joystick.cy
            end
            joystick.x, joystick.y = touch.x, touch.y
            joystick.dx, joystick.dy = unpack(map(pos, -1,1, -.004,.004))
        end
    end
end

The precision increases pretty quickly, you can change it how you want. Only edit is at the bottom, in touch.state == MOVING.

Of course, to my mind, the simplest answer is trigonometry. (Though, it does work pretty well…)

this are very useful suggestion all together. but one more question: how can I predict a point on a vector in a certain distance? (I know my math sux, I really have to learn…)

say i have a vec2(100, 400) and in a distance of 300 pixel (in the same direction as the vector goes), I want to know the coordinate of the point.

Trigonometry?

distance = 300
coordinate = vec2(100, 400)
angle = math.atan2(coordinate.y, coordinate.x)
result = vec2(distance * math.sin(angle), distance * math.cos(angle))

Not tested, but it should work. Although, sometimes I get funny outputs from math.atan2…

@SkyTheCoder thanks very much! I have to learn (at least) basic math or leave the coding beside… :frowning:

result = coordinate:normalize()*distance

Never, ever, calculate an actual angle.

@Andrew_Stacey, why not? :s it helped me in my color chooser class… there i calculated angles, to make sure the touches are right… :s

@Andrew_Stacey Why not? (It’s required in my precision example, and sometimes I use it for rotating the screen.)

I forgot about the normalize() functions… I’m not too sure what they do, though. I looked on wikipedia and it didn’t make too much sense, but I believe it converts the number to UV somehow? If so, that code is perfect.

Also, I didn’t know you could multiply a vector using standard Lua syntax…