How do you do zooming pinches?

Pinching spreading is what I call zooming with two fingers in iOS

I’ve looked at jmv38’s sensor class and I think I understand it partially, but not entirely

When you have two touches. A zoom event occurs

When either touches moves, calculate a box between the two touches with width and height. Return those width and height values (in the example demo, jmv uses it to set the width and height of a rectangle)

But now I have my question. Go to safari on iOS, and pinch. And translate the touches of your fingers. The page moves along with them

What is going on?

Does it translate like a normal touch, or is there a different logic?

(To me it looks like it is translating like normal, one touch finger, but I’m not sure because of all the zooming in and out)

@xThomas When there’s 2 fingers on the screen and only one of them moves, you get a zoom. When both fingers move, you get a scroll. Also, if you touch the screen with one finger and move it up or down, the screen will scroll up or down but not side to side. If you touch the screen with one finger and move it side to side, the screen will scroll side to side but not up or down. You should be able to code that in Codea.

It is a different logic. Wirh two fingers, let a and b be their original places and c and d their current places. Then there is a unique combination of translation, rotation, and scaling that send a → c and b → d.

It is given by the following:

  1. set b’ = b-a and d’ = d-c
  2. set b^ to be b’ rotated by 90 and d^ similarly
  3. set B to be the 2x2 matrix [b’, b^] and D similarly
  4. set A to be D B^{-1}, this has the property that Ab’ = d’
  5. the transformation is then x |-> A(x-a) + c

If you don’t want rotation but only zoom and pan then the following is what to do. This keeps the centre of the touches fixed.

  1. set a# to be the average of a and b (so a# = (a+b)/2 ) and similarly c#
  2. let r be the distance between a and a#, and s between c and c#
  3. translate by - a#
  4. scale by s/r
  5. translate by c#

@xThomas Here a quick example of using one finger to move an image or using 2 fingers to move and/or zoom an image.

displayMode(FULLSCREEN)

function setup()
    img=readImage("Small World:Icon")
    size=img.width
    dx,dy=0,0
    fill(255)
    tab={}
end

function draw()
    background(0)
    sprite(img,WIDTH/2+dx,HEIGHT/2+dy,size)
end

function touched(t)
    if t.state==BEGAN then
        if #tab<2 then
            table.insert(tab,{id=t.id,x=t.x,y=t.y})
        end
        if #tab==2 then
            v1=vec2(tab[1].x,tab[1].y)
            startDist=v1:dist(vec2(tab[2].x,tab[2].y))
            startSize=size
        end
    elseif t.state==MOVING then
        for a,b in pairs(tab) do
            if b.id==t.id then
                b.x=t.x
                b.y=t.y
            end
        end
        if #tab==2 then
            v1=vec2(tab[1].x,tab[1].y)
            d=v1:dist(vec2(tab[2].x,tab[2].y))
            size=startSize*(d/startDist)
        end
        dx=dx+t.deltaX/#tab
        dy=dy+t.deltaY/#tab
    elseif t.state==ENDED then
        for a,b in pairs(tab) do
            if b.id==t.id then
                table.remove(tab,a)
            end
        end
    end
end

Ooh, nice.

@LoopSpace I wasn’t able to get it working exactly, the scale logic works great but for translation i end up going with deltaXY (when i tried to implement translation earlier i couldnt get it to work)

ill try rotation tomorrow

for now here is the code i have
(note that instead of a# c# i’d put ab or cd

-- zoom pan

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    trans=vec2(0,0)
    scalar=vec2(1,1)

end

-- This function gets called once every frame
tick = 0
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
    pushMatrix()
    scale(scalar.x,scalar.y)
    translate(trans.x,trans.y)

    sprite("Blocks:Brick Grey",0,0)
    popMatrix()
    textMode(CORNER)
    text(trans.x..','..trans.y, 50,700)
    text(scalar.x,50,650)

    
end
abtap = {}
cdtap = {}
-- prevtap = {}
idtap = {}
function began(t)
    if t.state == BEGAN then 
        local a,b = abtap.a,abtap.b
        if not a and not b then 
            -- print"creaate tap a"
            abtap.a = vec2(t.x,t.y)
            cdtap.c = vec2(t.x,t.y)
            -- prevtap.c = vec2(t.x,t.y)
            idtap.a = t.id
        elseif a and not b then 
            -- print"create tap b"
            abtap.b = vec2(t.x,t.y)
            cdtap.d = vec2(t.x,t.y)
            -- prevtap.d = vec2(t.x,t.y)
            idtap.b = t.id
        elseif b and not a then
            -- print"created tap à "
            abtap.a = vec2(t.x,t.y)
            cdtap.c = vec2(t.x,t.y)
            -- prevtap.c = vec2(t.x,t.y)
            idtap.a = t.id
        end
     end
end


function pan(t)
    if t.state == MOVING then
        local a,b = abtap.a,abtap.b
        if not (a and b) then return end
        local c,d = cdtap.c,cdtap.d
        if idtap.a==t.id then
            --abtap.a = vec2(t.prevX,t.prevY)
            -- prevtap.c = vec2(t.prevX,t.prevY)
            cdtap.c = vec2(t.x,t.y)
            dotap(t)

        elseif idtap.b==t.id then
            --abtap.b = vec2(t.prevX,t.prevY)
            -- prevtap.d = vec2(t.prevX,t.prevY)
            cdtap.d = vec2(t.x,t.y)
            dotap(t)
        end
    end
    
end

function dotap(t)
    local a,b = abtap.a,abtap.b
    local c,d = cdtap.c,cdtap.d
    -- local pc, pd = prevtap.c, prevtap.d
    
    local ab = (a+b)/2 --original touch center
    local cd = (c+d)/2 --current touch center
    
    --local pcd = (pc+pd)/2 --prev touch center
    
    local r = a:dist(ab)
    local s = c:dist(cd)
    local rs = s/r
    --trans = trans - ab
    --trans = trans - pcd
    scalar = vec2(rs,rs)
    --print(rs)
    --trans = trans * rs
    --trans = trans + vec2(cd.x,cd.y)
    --trans = trans + pcd
    trans = trans + vec2(t.deltaX,t.deltaY)
end

function touched(t)
    began(t)
    pan(t)
    ended(t)
end

function ended(t)
    if t.state == CANCELLED or t.state == ENDED then
        if idtap.a==t.id then
            -- print"delete tapp a"
            idtap.a = nil
            abtap.a = nil
            cdtap.c = nil
        elseif idtap.b==t.id then
            -- print"dellete tap b"
            idtap.b = nil
            abtap.b = nil
            cdtap.d = nil
        end
    end
end

@xThomas I added rotation to my above code.

displayMode(FULLSCREEN)

function setup()
    img=readImage("Small World:Icon")
    size=img.width
    dx,dy=0,0
    tab={}
    calcAng,endAng=0,0
end

function draw()
    background(0)
    translate(WIDTH/2+dx,HEIGHT/2+dy)
    rotate(calcAng+endAng)  
    sprite(img,0,0,size)
end

function touched(t)
    if t.state==BEGAN then
        if #tab<2 then
            table.insert(tab,{id=t.id,x=t.x,y=t.y})
            if #tab==2 then
                origAng=math.deg(math.atan((tab[2].y-tab[1].y),(tab[2].x-tab[1].x)))
                startDist=vec2(tab[1].x,tab[1].y):dist(vec2(tab[2].x,tab[2].y))
                startSize=size
            end
        end
    elseif t.state==MOVING then
        for a,b in pairs(tab) do
            if b.id==t.id then
                b.x=t.x
                b.y=t.y
            end
        end
        if #tab==2 then
            currAng=math.deg(math.atan((tab[2].y-tab[1].y),(tab[2].x-tab[1].x)))
            if currAng<0 then
                currAng=currAng+360
            end
            calcAng=currAng-origAng
            d=vec2(tab[1].x,tab[1].y):dist(vec2(tab[2].x,tab[2].y))
            size=startSize*(d/startDist)
        end
        dx=dx+t.deltaX/#tab
        dy=dy+t.deltaY/#tab
    elseif t.state==ENDED then
        for a,b in pairs(tab) do
            if b.id==t.id then
                if #tab==2 then
                    endAng=endAng+calcAng
                    calcAng,currAng=0,0
                end
                table.remove(tab,a)
            end
        end
    end
end