Improving pinch/zoom of an image

Hi, everyone!

I have a question about improving zoom in my app, i think it could be helpful for others too.

the code is like this

system.activate("multitouch") -- which environment are we running on? local isDevice = (system.getInfo("environment") == "device") -- returns the distance between points a and b function lengthOf( a, b ) local width, height = b.x-a.x, b.y-a.y return (width\*width + height\*height)^0.5 end -- calculates the average centre of a list of points local function calcAvgCentre( points ) local x, y = 0, 0 for i=1, #points do local pt = points[i] x = x + pt.x y = y + pt.y end return { x = x / #points, y = y / #points } end -- calculate each tracking dot's distance and angle from the midpoint local function updateTracking( centre, points ) for i=1, #points do local point = points[i] point.prevDistance = point.distance point.distance = lengthOf( centre, point ) end end -- calculates scaling amount based on the average change in tracking point distances local function calcAverageScaling( points ) local total = 0 for i=1, #points do local point = points[i] total = total + point.distance / point.prevDistance end return total / #points end local navbar = display.newRect(0,0,100,100) navbar.alpha = 0 -- creates an object to be moved function newTrackDot(e) -- create a user interface object local circle = display.newCircle( e.x, e.y, 50 ) -- make it less imposing circle.alpha = .5 -- keep reference to the rectangle local rect = e.target -- standard multi-touch event listener function circle:touch(e) -- get the object which received the touch event local target = circle -- store the parent object in the event e.parent = rect -- handle each phase of the touch event life cycle... if (e.phase == "began") then -- tell corona that following touches come to this display object display.getCurrentStage():setFocus(target, e.id) -- remember that this object has the focus target.hasFocus = true -- indicate the event was handled return true elseif (target.hasFocus) then -- this object is handling touches if (e.phase == "moved") then -- move the display object with the touch (or whatever) target.x, target.y = e.x, e.y else -- "ended" and "cancelled" phases -- stop being responsible for touches display.getCurrentStage():setFocus(target, nil) -- remember this object no longer has the focus target.hasFocus = false end -- send the event parameter to the rect object rect:touch(e) -- indicate that we handled the touch and not to propagate it return true end -- if the target is not responsible for this touch event return false return false end -- listen for touches starting on the touch layer circle:addEventListener("touch") -- listen for a tap when running in the simulator function circle:tap(e) if (e.numTaps == 2) then -- set the parent e.parent = rect -- call touch to remove the tracking dot rect:tap(e) rect:touch(e) elseif (e.numTaps == 1) then rect:tap(e) end return true end -- only attach tap listener in the simulator if (not isDevice) then circle:addEventListener("tap") end -- pass the began phase to the tracking dot circle:touch(e) -- return the object for use return circle end -- spawning tracking dots -- create object to listen for new touches local rect = display.newImage( "pic.jpg") local h = display.contentHeight; if rect.width \> display.contentWidth or rect.height \> h then if rect.width/display.contentWidth \> rect.height/h then rect.xScale = display.contentWidth/rect.width; rect.yScale = display.contentWidth/rect.width; else rect.xScale = h/rect.height; rect.yScale = h/rect.height; end end rect.x = display.contentWidth\*.5; rect.y = display.contentHeight\*.5; local normXScale, normYScale = rect.xScale, rect.yScale -- keep a list of the tracking dots rect.dots = {} local tapScaler = false -- advanced multi-touch event listener function rect:touch(e) -- get the object which received the touch event local target = e.target -- handle began phase of the touch event life cycle... if (e.phase == "began") then print( e.phase, e.x, e.y ) -- create a tracking dot local dot = newTrackDot(e) -- add the new dot to the list rect.dots[#rect.dots+1] = dot -- pre-store the average centre position of all touch points rect.prevCentre = calcAvgCentre( rect.dots ) -- pre-store the tracking dot scale and rotation values updateTracking( rect.prevCentre, rect.dots ) -- we handled the began phase return true elseif (e.parent == rect) then if (e.phase == "moved") then print( e.phase, e.x, e.y ) -- declare working variables local centre, scale, rotate = {}, 1, 0 -- calculate the average centre position of all touch points centre = calcAvgCentre( rect.dots ) -- refresh tracking dot scale and rotation values updateTracking( rect.prevCentre, rect.dots ) -- if there is more than one tracking dot, calculate the rotation and scaling if (#rect.dots \> 1) then -- calculate the average scaling of the tracking dots scale = calcAverageScaling( rect.dots ) -- apply scaling to rect if rect.xScale \* scale \>= normXScale\*(9/10) and rect.yScale \* scale \>= normYScale\*(9/10) and rect.xScale \* scale \< normXScale\*(5/2) and rect.yScale \* scale \< normYScale\*(5/2) then rect.xScale, rect.yScale = rect.xScale \* scale, rect.yScale \* scale tapScaler = true navbar.alpha=0 end end if rect.xScale \* scale \>= normXScale and rect.yScale \* scale \>= normYScale and rect.xScale \* scale \< normXScale\*(5/2) and rect.yScale \* scale \< normYScale\*(5/2) then -- update the position of rect rect.x = rect.x + (centre.x - rect.prevCentre.x) if rect.height\*rect.yScale \> display.contentHeight then rect.y = rect.y + (centre.y - rect.prevCentre.y) end navbar.alpha=0 end -- store the centre of all touch points rect.prevCentre = centre else -- "ended" and "cancelled" phases print( e.phase, e.x, e.y ) print(rect.height\*rect.yScale ) if rect.yScale \< normYScale and rect.xScale \< normXScale then transition.to( rect, {time=400,yScale = normYScale, xScale = normXScale, x=display.contentWidth/2,y=display.contentHeight/2, transition=easing.outExpo } ) tapScaler = false end if rect.yScale \> normYScale\*2 and rect.xScale \> normXScale\*2 then transition.to( rect, {time=400, yScale = normYScale\*2, xScale = normXScale\*2, transition=easing.outExpo } ) tapScaler = true end -- remove the tracking dot from the list if (isDevice or e.numTaps == 2) then -- get index of dot to be removed local index = table.indexOf( rect.dots, e.target ) -- remove dot from list table.remove( rect.dots, index ) -- remove tracking dot from the screen e.target:removeSelf() -- store the new centre of all touch points rect.prevCentre = calcAvgCentre( rect.dots ) -- refresh tracking dot scale and rotation values updateTracking( rect.prevCentre, rect.dots ) end end return true end -- if the target is not responsible for this touch event return false return false end function rect:tap(e) -- get the object which received the touch event local target = e.target if e.numTaps == 1 then navbar.alpha=math.abs(navbar.alpha-1) elseif e.numTaps == 2 then print("tap occured") if tapScaler == true then transition.to( rect, {time=400,yScale = normYScale, xScale = normXScale, x=display.contentWidth/2,y=display.contentHeight/2, transition=easing.outExpo } ) tapScaler = false navbar.alpha=0 elseif tapScaler == false then transition.to( rect, {time=400, yScale = normYScale\*2, xScale = normXScale\*2, transition=easing.outExpo } ) tapScaler = true navbar.alpha=0 end end end navbar:setFillColor(144) navbar:toFront() -- listen for touches starting on the touch object rect:addEventListener("touch") rect:addEventListener("tap")

I took a sample code from one tutorial http://www.coronalabs.com/blog/2013/01/22/implementing-pinch-zoom-rotate/. and add some little improvements, zoom in and out limits, single tap and douple action, but that no matter.

The main question is how to make an image to stay within the screen, when zooming and moving it. for example:

Thank you!