Here is a listing to allow you to pinch-zoom images or display groups. Please let me know if you have questions or need it pulling apart…
Matt.
[lua]–[[
These are support functions which I use everywhere. They could easily be in a library file.
]]–
– calculates the hypoteneuse, ie: the distance between two points.
– Input args format: a, b … { x, y }
function getLength( a, b )
local width, height = b.x-a.x, b.y-a.y
return math.sqrt(width*width + height*height)
end
– calculates the angle of a point from the 0,0. (When using atan2 the 0 degrees angle is actually pointing east.)
– Input args: pt … { x, y }
function AngleOfPoint( pt )
local x, y = pt.x, pt.y
local radian = math.atan2(y,x)
local angle = radian*180/math.pi
if angle < 0 then angle = 360 + angle end
return angle
end
– calculates the difference between two angles, but this accepts two points, not their angles
– Input args: pointA, pointB … { x, y }
– Input args: clockwise … true|false
function AngleDiff( pointA, pointB, clockwise )
local angleA, angleB = AngleOfPoint( pointA ), AngleOfPoint( pointB )
if angleA == angleB then
return 0
end
if clockwise then
if angleA > angleB then
return angleA - angleB
else
return 360 - (angleB - angleA)
end
else
if angleA > angleB then
return angleB + (360 - angleA)
else
return angleB - angleA
end
end
end
–[[
This creates a multi-touch tracking object and adds it to a list of multi-touch tracking objects
internal to the image being manipulated by the touches.
]]–
function addTouch( img, event )
–[[Construction]]–
local touch = display.newCircle( event.x, event.y, 50 )
touch:setFillColor( 255,0,0,150 )
– this data is required to be able to calculate the motion of the touches.
– basically, if you don’t use this function to manage your multiple touch points, you need to
– manage this data yourself, so that the objects passed into the ‘makePinchZoom’ functions have it.
– the ‘makePinchZoom’ functions don’t call the other args or functions added by this function.
touch.xStart, touch.yStart = event.x, event.y
touch.xPrev, touch.yPrev = event.x, event.y
touch.x, touch.y = event.x, event.y
touch.parentImg = img
touch.id = event.id
– adds the list in which to keep the multi-touch tracking objects
if (touch.parentImg.touchList == nil) then
local list = {}
function list:indexOf( touch )
for i=1, #list do
if (list[i] == touch) then
return i
end
end
return 0
end
touch.parentImg.touchList = list
end
touch.list = touch.parentImg.touchList
touch.list[#touch.list +1] = touch
–[[External functions]]–
function touch:isFirst()
if (touch.list[1] == touch) then
return true
else
return false
end
end
function touch:indexOf()
return touch.list:indexOf( touch )
end
–[[Internal functions]]–
– basically makes a record of the original event position
function touch:began( event )
– should never be called, this is here for convenience, but should never be called
touch.xStart, touch.yStart = event.x, event.y
end
– moves the touch object and fires the image’s touchMoved event listener
function touch:moved( event )
if (touch.parentImg.touchMoved ~= nil) then
touch.xPrev, touch.yPrev = touch.x, touch.y
touch.x, touch.y = event.x, event.y
touch.parentImg:touchMoved( touch )
end
end
– removes the touch object
function touch:endedCancelled( event )
display.getCurrentStage():setFocus( touch, nil )
table.remove( touch.list, touch:indexOf() )
touch:removeSelf()
end
– just calls other functions to deal with the events, to make the code cleaner
function touch:touch( event )
if (event.phase == “began”) then
touch:began( event )
elseif (event.phase == “moved”) then
touch:moved( event )
elseif (event.phase == “ended” or event.phase == “cancelled”) then
touch:endedCancelled( event )
end
end
–[[Completion]]–
touch:addEventListener( “touch”, touch )
display.getCurrentStage():setFocus( touch, event.id )
return touch – for convenience, but not really used
end
–[[
Adds functions to the image to allow it to be pinch-zoom-able
In short: this contains the functions that do the magic.
If you want to rip the magic out, just take these functions, remove the ‘img:’ from their name,
and make sure you are managing the x, y, and xPrev, yPrev values to be passed to these functions.
]]–
function makePinchZoom( img )
– sets the reference point to be the mid-point between the touches, relative to the img x,y
function img:setReference( touchA, touchB )
img.xPrevReference = img.xReference
img.yPrevReference = img.yReference
– get touch mid-point relative to image
local tax = img.x-(img.x - touchA.x)
local tay = img.y-(img.y - touchA.y)
local tbx = img.x-(img.x - touchB.x)
local tby = img.x-(img.y - touchB.y)
– set the position (relative to the image’s 0,0) around which scaling and rotation is performed
– see: http://developer.anscamobile.com/content/display-objects#object.xReference
img.xReference = (tbx-tax)/2
img.yReference = (tby-tay)/2
end
– sets the reference back to it’s original value, to avoid screwing with other code which may have used it
– this will usually be 0,0
– set: http://developer.anscamobile.com/content/display-objects#object.xReference
function img:unsetReference()
img.xReference = img.xPrevReference
img.yReference = img.yPrevReference
img.xPrevReference = nil
img.yPrevReference = nil
end
– moves the image relative to the amount the mid-point between the touches has moved
function img:doMove( touchA, touchB )
local x = ((touchA.x - touchA.xPrev) + (touchB.x - touchB.xPrev)) / 2
local y = ((touchA.y - touchA.yPrev) + (touchB.y - touchB.yPrev)) / 2
img.x = img.x + x
img.y = img.y + y
end
– rotates the image relative to how much the touch points have moved relative to each other
function img:doRotate( touchA, touchB )
local prev = AngleOfPoint( { x=touchB.xPrev-touchA.xPrev, y=touchB.yPrev-touchA.yPrev } )
local current = AngleOfPoint( { x=touchB.x-touchA.x, y=touchB.y-touchA.y } )
img.rotation = img.rotation + (current - prev)
end
– scales the images relative to the previous and current distance between the two touch points
function img:doScale( touchA, touchB )
local prevLen = getLength( {x=touchA.xPrev, y=touchA.yPrev}, {x=touchB.xPrev, y=touchB.yPrev} )
local currentLen = getLength( {x=touchA.x, y=touchA.y}, {x=touchB.x, y=touchB.y} )
local scale = currentLen / prevLen
img.xScale = img.xScale * scale
img.yScale = img.yScale * scale
end
–[[
This is called by the addTouch functions above.
if you don’t want to have my code managing the multi-touch points, you will have to track those
multi-touch events yourself and make sure you can pass the data objects into these functions.
all that this function really does is make sure there are at least 2 multi-touch points known in
the system and passes them in. If you want to do that yourself, just pass in objects with the data:
{ x, y, xPrev, yPrev }
It is important to call the setReference and unsetReference functions because they manipulate the
x|yReference values. see: http://developer.anscamobile.com/content/display-objects#object.xReference
]]–
function img:touchMoved( touch )
if (#img.touchList == 1) then
img.x, img.y = img.x+(touch.x-touch.xPrev), img.y+(touch.y-touch.yPrev)
elseif (#img.touchList == 2) then
– this is the section of code you need to call yourself if you decide to extract this code
– just make sure you always pass in: { x, y, xPrev, yPrev } for each arg
img:setReference( img.touchList[1], img.touchList[2] )
img:doMove( img.touchList[1], img.touchList[2] )
img:doRotate( img.touchList[1], img.touchList[2] )
img:doScale( img.touchList[1], img.touchList[2] )
img:unsetReference()
end
end
end
– I don’t really have a good image, but my test code always a large crate png!
– My listing (above) adds a lot of functions and internal values to the img object to allow it to be manipulated
– by the pinch zoom code. But this is easily extracted from the ‘makePinchZoom’ function and used in your own way.
local img = display.newImage( “crate.png” )
– handling of the touches on the image is managed by this function until the addTouch objects take over
– if you want to manage the multi-touch events yourself, go ahead and do it - just make sure you pass
– { x, y, xPrev, yPrev } objects into the img:touchMoved event listener when getting this code to do the pinch-zoom.
– if you track multi-touches on an image object but pass a display group into the addTouch function it should work fine.
function img:touch( event )
if (event.phase == “began”) then
addTouch( img, event ) – adds a multitouch tracking object to maintain start, previous and pinchzoom data
end
end
– this is really just here to let me simulate multiple touch events in the simulator
– don’t worry, this listing isn’t really that complicated.
function img:tap( event )
if (event.numTaps == 2) then
addTouch( img, event )
end
end
– just adds a bunch of functions to the display object (in this case an image) to allow easier pinch-zoom manipulation
– but you can just rip that stuff out and put it in more general code or an external library, quite easily
makePinchZoom( img )
– guess what these do
img:addEventListener( “touch”, img )
img:addEventListener( “tap”, img ) – ok, this isn’t even needed in the device, it just helps me add simulated multi-touch points[/lua]
[import]uid: 8271 topic_id: 4184 reply_id: 304184[/import]