Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Pinch-to-Zoom — Gideros Forum

Pinch-to-Zoom

ExCxExCx Member
edited December 2012 in Roadmap
So, I'm trying to implement this standard gesture to my program. But I can't find any examples on how to do it. I'm trying to figure it out on my own, but it's taking time and my employer is waiting. Can anyone give me a hand on this?
Tagged:

Comments

  • tkhnomantkhnoman Member
    edited December 2012
    I just put a code that i write to my game, so it might be not really understandable.
    If you have a question, just ask it.

    Also, this example of Pinch-to-zoom only use the center point as zoom point.
    Edit: change a little bit, since the code error on three or more finger...
    zip
    zip
    PinchToZoom.zip
    144K
  • And yeah, lol. I want to copy paste the code here, but i still don't know how to write code properly on this forum..
    What is the syntax...
  • hgvyas123hgvyas123 Guru
    edited December 2012
    @tkhnoman : the syantax :
     
  • @ExCx well from theoretical point of view, to detect if two fingers are touching the screen you can check for count of elements in event.allTouches table. If there are two, you than need to calculate the vector's length between two touches. That's all on touches begin event.

    After that on touches move you need to monitor how vector's length between touches changes and scale whole scene (or any other object/layer that contains your visible elements) accordingly.

    That's actually a pretty interesting thing to implement, that I would want to try myself, problem is I have so many things to do, that this one would make a list probably only next year :(
  • @hgvyas123 yeah I know couple of magic tricks :)

    Likes: hgvyas123

    +1 -1 (+1 / -0 )Share on Facebook
  • tkhnomantkhnoman Member
    edited December 2012
    Ookay. thanks.
     
    local minimumZoom = 0.2
     
    local function touchEVENT(self, event)
    	local touchGet = event.touch
    	local id = touchGet.id
    	local zoomGroup = self.zoomGroup
    	-- id is the finger number
     
    	local touch = self.touch
     
    	-- It just happen that i like to use some checking like this
    	local scalingHappen = false
    	local movingHappen = false
     
    	local otherID = 2
    	if id == 2 then otherID = 1 end
    	--Function to get range between touch/coord
    	local function getDelta(point1,point2)
    		local dx = point1.x - point2.x
    		local dy = point1.y - point2.y
    		return math.sqrt( dx*dx + dy*dy )
    	end
     
     
     
    	if id == 1 or id == 2 then
     
    		if event.type == "touchesBegin" then
     
    			if not self.idTouchExist[otherID] then
     
    				self.x0 = touchGet.x - ( self:getX()* zoomGroup:getScaleX() )
    				self.y0 = touchGet.y - ( self:getY()* zoomGroup:getScaleX() )
     
    				--Get the first Finger
    				touch[id].x = touchGet.x
    				touch[id].y = touchGet.y
     
    			elseif not self.idTouchExist[id] then
     
    				--Get the second Finger
    				touch[id].x = touchGet.x
    				touch[id].y = touchGet.y
     
    				-- get Length between touch and original Zoom
    				local deltaTouch = getDelta(touch[1],touch[2]) 
    				if deltaTouch > 0 then
    					zoomGroup.deltaTouch = deltaTouch
    					zoomGroup.oriScale = zoomGroup:getScaleX()
    				end
     
    				-- TouchC is a Temporary that i use to take center of 1 & 2
    				local touchC = {}
    				touchC.x = (touch[1].x + touch[2].x) * 0.5
    				touchC.y = (touch[1].y + touch[2].y) * 0.5
    				self.x0 = touchC.x - ( self:getX() * zoomGroup:getScaleX() )
    				self.y0 = touchC.y - ( self:getY() * zoomGroup:getScaleX() )
     
     
    			end
     
    			self.idTouchExist[id] = true
     
     
    		elseif event.type == "touchesMove" then
    			-- save Touch Pos
    			touch[id].x = touchGet.x
    			touch[id].y = touchGet.y
     
    			-- If there is one Touch then move
    			if not self.idTouchExist[otherID] then
     
    				self:setX( (touchGet.x  - self.x0) * zoomGroup.scaleRev )
    				self:setY( (touchGet.y  - self.y0) * zoomGroup.scaleRev )
    				movingHappen = true
     
    			-- If there is second finter Then Scale (and move)	
    			else
     
    				local deltaTouch = getDelta(touch[1],touch[2]) 
    				local scaler = deltaTouch / zoomGroup.deltaTouch
     
    				if scaler > 0 then
    					local scaleResult = zoomGroup.oriScale * scaler
    					zoomGroup:setScale( scaleResult,scaleResult )
    					scalingHappen = true
    				end
     
    				local touchC = {}
    				touchC.x = (touch[1].x + touch[2].x) * 0.5
    				touchC.y = (touch[1].y + touch[2].y) * 0.5
    				self:setX( (touchC.x  - self.x0 ) * zoomGroup.scaleRev )
    				self:setY( (touchC.y  - self.y0 ) * zoomGroup.scaleRev )
    			end
     
    			event:stopPropagation()
     
    			-- you can put limitation of X,Y max and min in here
    			if self:getY() < self.downLimit then self:setY( self.downLimit )
    			elseif self:getY() > self.upLimit then self:setY(  self.upLimit )
    			end
    			if self:getX() < self.rightLimit then self:setX( self.rightLimit )
    			elseif self:getX() > self.leftLimit then self:setX(  self.leftLimit )
    			end
     
    			-- and this for limit scaling
    			if scalingHappen then
    				if zoomGroup:getScaleX() > 1.0  then
    					zoomGroup:setScale( 1.0,1.0 )
    				elseif zoomGroup:getScaleX() < minimumZoom then
    					zoomGroup:setScale( minimumZoom,minimumZoom )
    				end	
    				zoomGroup.scaleRev = 1/zoomGroup:getScaleX()
    			end
     
    			-- Using animation also nice...
    			-- So just give some condition to let  ENTER_FRAME event do the scaling / moving after
    			-- And then let them move like this:
    				-- if self.targetX ~= self:getX() then 
    				-- 	local delta = ( self:getX() - self.TargetX ) * 0.1
    				--		if delta > 0 then delta = ceil(delta) else delta = floor(delta) end
    				--		self:setX( self:getX() - delta )
    				-- end 
    			-- But it more complexer than that, i think
     
     
    		elseif event.type == "touchesEnd" then
     
    			if self.idTouchExist[otherID] then
    				self.x0 = touch[otherID].x  -( self:getX()* zoomGroup:getScaleX() )
    				self.y0 = touch[otherID].y  - ( self:getY()* zoomGroup:getScaleX() )
    			end	
     
    			event:stopPropagation()
    			self.idTouchExist[id] = nil
    		end
    	end
     
     
    	return true
     
    end
     
     
    local zoomGroup = Sprite.new()
    zoomGroup:setScale(0.4,0.4)
    zoomGroup:setPosition( application:getContentWidth()/2,application:getContentHeight()/2)
     
    local imageOrGroup = Sprite.new()
    imageOrGroup.bitmap = Bitmap.new(Texture.new("Bunny.jpg"))
    imageOrGroup.bitmap:setAnchorPoint(0.5, 0.5)
    imageOrGroup.zoomGroup = zoomGroup
     
    imageOrGroup:addChild(imageOrGroup.bitmap)
     
    zoomGroup:addChild(imageOrGroup)
    stage:addChild(zoomGroup)
     
    -- Some of initialization that i need for Pinch-Zoom
    zoomGroup.scaleRev = 1/zoomGroup:getScaleX()
     
    imageOrGroup.touch = {}
    imageOrGroup.touch[1] = {}
    imageOrGroup.touch[2] = {}
    imageOrGroup.idTouchExist = {}
     
     
    local halfWidth = imageOrGroup:getWidth()*0.5
    local halfHeight = imageOrGroup:getHeight()*0.5
    imageOrGroup.leftLimit = halfWidth
    imageOrGroup.rightLimit = -halfWidth
    imageOrGroup.upLimit = halfHeight
    imageOrGroup.downLimit = -halfHeight
     
    imageOrGroup:addEventListener(Event.TOUCHES_BEGIN, touchEVENT,imageOrGroup)
    imageOrGroup:addEventListener(Event.TOUCHES_MOVE, touchEVENT,imageOrGroup)
    imageOrGroup:addEventListener(Event.TOUCHES_END, touchEVENT,imageOrGroup)
    Yep, the basic is just like @ar2rsawseen said. But i didn't use event.allTouches

    Dang. My code is really not interesting since i just take it from my game.
    And this code can handle movement & scaling in the same time.

    I used another group called zoomGroup to handle things better.
    And to change the zoom center point, i think we just need to change child's x,y and zoomGroup's x,y.
    Don't know if there is another trick. Anyone?

    Likes: ExCx

    +1 -1 (+1 / -0 )Share on Facebook
  • Well theoretically zoom center would be the same as your anchor point. You can copy anchor point implementation for Sprites and Sprite inherited objects for GiderosCodingEasy:
    https://github.com/ar2rsawseen/GiderosCodingEasy/blob/master/GiderosCodingEasy.lua

    Likes: tkhnoman

    +1 -1 (+1 / -0 )Share on Facebook
  • Oh, thanks. Those code might come in handy sometime.

    And yeah, i wonder why there is no setAnchorPoint for sprite. It will be very handy, rather than setting the child's x and y by ourself.
  • ar2rsawseenar2rsawseen Maintainer
    edited December 2012
    There is no setAnchorPoint for sprite because
    <trollmode>
    @atilim does not now how to implement one :P
    </trollmode>

    But seriously its very hard to implement it, because Sprite may have other indefinite amount of children, etc. And I even doubt that all of scenarios are covered in GiderosCodingEasy
  • Uh, yeah... Is not that easy to implement the code in GiderosCodingEasy for zooming.
    It might because x and y are different after scaling, or might be the width and height that also change during zoom. Might be easier for just change the child's position and zoomGroup's position.

    Tired for trying. I will let it be.
  • There is no setAnchorPoint for sprite because
    <trollmode>
    @atilim does not now how to implement one :P
    </trollmode>

    But seriously its very hard to implement it, because Sprite may have other indefinite amount of children, etc. And I even doubt that all of scenarios are covered in GiderosCodingEasy
    While the complexities are infinite, if each sprite that has children has an upto date bounds when a child is added or moved, then the number of children problem should not be valid, it is then a matter of transversing through the hierarchy.

    Having an anchorPoint for a shape or sprite is very important to be able to rotate them correctly. Not everything looks good rotated from the top left corner. A work around however could be if the Sprite is *renderedToBitmap* and then we can set the anchorPoint.

    If it were easy then who would have asked for it, and would it be a challenge for @Atilim?
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • Hi, thanks for the answers.

    @tkhnoman I implemented your whole code. It's working good but I have 2 serious issues.

    First is that center point issue. It always takes 0,0 as anchor. I honestly couldn't understand your formula about this ("self:setX(((touchC.x - self.x0 ) * zoomGroup.scaleRev))" part) and it doesn't work for me either.

    Second thing is the limiting issue. I want that no blank space to be seen while zooming and/or moving. It would always be limited precisely by the dimensions of the stage. In your formula, the limitations always change.

    For the record, I'm using whole stage. Which means I add every child of the stage to the "imageOrGroup" sprite instead. Remaining of the code is same.

    Lastly I tried @ar2rsawseen's setAnchorPoint feature for sprites too, but it didn't make any change.

    I really can't believe there isn't a clear example/library/plugin about this. Isn't this a standard feature to be found in a mobile software?
  • tkhnomantkhnoman Member
    edited December 2012
    @ExCx Nope, it's not a standard feature for mobile software.

    I used touchC (in the middle of first touch and second touch) to detect the movement while scaling. So if the finger rotated while zooming, it will remain in the same position, since it detect the center.

    That scaleRev (1/scale) is used to maintain speed of movement. It will also needed when you detect touch, to get the real coordinate instead of zoomed coordinate.

    For limiting issue, you need to do some more calculation again. I use that limitation for my game, which might not good for other's. Also for anchor point too.
    Perhaps it depends on what kind of game/app you are making.

    If you give me a hint, i might got a clue of it.
  • OK, so I'm over the limiting issue now. It was actually basic maths, which is my weak side (and yeah I'm calling myself a programmer). But I figured it out somehow. For example I implemented your formula on zooming ratio, which is:

    self:setScale(( current distance between fingers / initial distance between fingers) * initial scale of self)

    In this, I see genius. I'd never figure out that by myself (I was just adding/substracting a constant value to the scale ratio before, which actually works, but it was rough and really uncomfortable).

    Anyway, I'm still studying your code and immersing it into mine. I'll post a working code with an open explanation here when it's finished. This zooming thing ate my whole week and I don't want anyone else to stuck on this issue.
    +1 -1 (+2 / -0 )Share on Facebook
  • I will need this now as well... would you be so kind and share your code? That would be greatly appreciated! ;;)
Sign In or Register to comment.