Pinch Zoom

I wanted to figure out a way to implement pinch zoom, and the easiest way was to build off of the multi-touch example.

function setup()
    print("This example tracks multiple touches and colors them based on their ID")
    -- keep track of our touches in this table
    touches = {}
    a = nil
    b = nil
    av = vec2(0,0)
    bv = vec2(0,0)
    initialDistance = 0
    ratio = 1    
end

function countTouches()
    i = 0
    for k,v in pairs(touches) do
        i = i+1
    end
    return i
end

function gatherTouches()
    a = {}
    for k,t in pairs(touches) do
        table.insert(a,t)
    end
    return a[1],a[2]
end

function between( val, low, high )
    return (low <= val and val <= high)
end

function touched(touch)
    if touch.state == ENDED then
        -- When any touch ends, remove it from
        --  our table
        touches[touch.id] = nil
        if( twoFingerTouch == true ) then
            twoFingerTouch = false 
            initialDistance = 0
            ratio = 1
        end
    else
        -- If the touch is in any other state 
        --  (such as BEGAN) we add it to our
        --  table
        touches[touch.id] = touch
    end
    --print( "num touches", countTouches())
    if( countTouches() == 2 ) then
        --calc distance between the touches
        a,b = gatherTouches()
        av = vec2(a.x, a.y)
        bv = vec2(b.x, b.y)
        dist = av:dist(bv)
        --print("distance between touches", dist)
        if( touch.state == BEGAN ) then
            --we started a 2-finger touch so mark the distance
            initialDistance = dist
            --when we stop touching with two fingers, we need to reset ratio and initialDistance
            twoFingerTouch = true
        elseif( touch.state == MOVING and dist ~= initialDistance ) then
            --figure out how much it's changed
            if( between(dist, initialDistance*0.5, initialDistance * 2) ) then
                ratio = dist / initialDistance
            end
        end
    else
        a,b = nil,nil 
        initialDistance = 0
        ratio = 1
    end
end

function draw()
    background(0, 0, 0, 255)
    spriteMode(CENTER)
    w,h = spriteSize("SpaceCute:Background")
    sprite("SpaceCute:Background",WIDTH/2, HEIGHT/2, w*0.5*ratio, h*0.5*ratio)
    
    for k,touch in pairs(touches) do
        -- Use the touch id as the random seed
        math.randomseed(touch.id)
        -- This ensures the same fill color is used for the same id
        fill(math.random(255),math.random(255),math.random(255))
        -- Draw ellipse at touch position
        ellipse(touch.x, touch.y, 100 * ratio, 100 * ratio)
    end
    if( a ~= nil and b ~= nil ) then
        fill(255,0,0,255)
        strokeWidth(1)
        line(av.x,av.y, bv.x, bv.y)
    end
end

Can anyone see any means of improving this such that the gatherTouches() and countTouches() methods aren’t needed?

Have you tried a forum search, there are a couple of implementations of pinch to zoom on here. One of them I thought worked pretty well. If you add a touch of velocity to it, it can smooth out the motion.

@matkatmusic Here’s a version I had that doesn’t use a table.

function setup()
    t1,t2=vec3(0,0,0),vec3(0,0,0)
    img=readImage("Planet Cute:Character Horn Girl")
    scale=img.width
end

function draw()
    background(40, 40, 50)
    sprite(img,WIDTH/2,HEIGHT/2,scale)
end

function touched(t)
    if t.state==BEGAN then
        hdist=0
        if t1.z==0 then
            t1=vec3(t.x,t.y,t.id)
        elseif t2.z==0 then
            t2=vec3(t.x,t.y,t.id)
        end
    end
    if t.state==MOVING then
        if t1.z==t.id then
            t1=vec3(t.x,t.y,t.id)
        elseif t2.z==t.id then
            t2=vec3(t.x,t.y,t.id)
        end
        if t1.z~=0 and t2.z~=0 then
            dist=vec2(t1.x,t1.y):dist(vec2(t2.x,t2.y))
            if dist-hdist>0 then
                scale=scale*1.02
            else
                scale=scale*.98
            end
            hdist=dist
        end
    end
    if t.state==ENDED then
        if t1.z==t.id then
            t1=vec3(0,0,0)
        elseif t2.z==t.id then
            t2=vec3(0,0,0)
        end
    end
end

I saw @Herwig’s version after following your suggestion and searching for it. I’m at least on the right track, as we both did the same thing (gather the touches, see if there are 2, calc the distance between the two, monitor the change in distance)

Based on the version above I coded my own version which supports pinch to zoom and moving at the same time: https://gist.github.com/dmitrii-eremin/3bfe12835cc4b8a7c6041dde0b654e24

@NeonMercury Works great. Need to add background(0) to the draw function in your example or else you’ll get multiple images as you zoom in/out.

@matkatmusic - neat demo, having a play with it to see where I might apply it. Thanks.