Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Easy "drop-in" pinch handling for drag, rotate and scale — Gideros Forum

Easy "drop-in" pinch handling for drag, rotate and scale

bowerandybowerandy Guru
edited March 2013 in Code snippets
Folks,

I've seen a couple of pinch handlers before but, for whatever reasons, I couldn't get them to do what I wanted so I've created an easy to use extension to Sprite to allow two-finger pinch-dragging-rotation and scaling. At the simplest level, it is just a matter of calling enablePinch() on your sprite.

Here's a quick demo:


This is the code for the demo:
local function constrainXOnly(sprite, x, y)
	return x, sprite:getY()
end
 
local function constrainYOnly(sprite, x, y)
	return sprite:getX(), y
end
 
-- Free drag, rotation and scaling
local cow1=Bitmap.new(Texture.new("cow1.jpg"))
cow1:setAnchorPoint(0.5, 0.5)
cow1:setPosition(240, 160)
stage:addChild(cow1)
cow1:enablePinch()
 
-- Drag in X direction only, and rotation
local cow2=Bitmap.new(Texture.new("cow2.jpg"))
cow2:setScale(0.25)
cow2:setAnchorPoint(0.5, 0.5)
cow2:setPosition(100, 270)
stage:addChild(cow2)
cow2:enablePinch({allowScale=false, dragConstrainFunc=constrainXOnly})
 
-- Drag in Y direction only
local cow3=Bitmap.new(Texture.new("cow3.jpg"))
cow3:setScale(0.1)
cow3:setAnchorPoint(0.5, 0.5)
cow3:setPosition(410, 60)
stage:addChild(cow3)
cow3:enablePinch({allowScale=false, allowRotate=false, dragConstrainFunc=constrainYOnly})
As you can see, it is possible to set whether a pinch should allow dragging/rotation/scaling and, by specifying an optional constrain function for each of the modes, it is possible to do things like implementing a snapping grid or limiting motion to a particular axis.

Note that, if you enable rotation and scaling, then the origin is taken to be the anchor point of the object. This typically means that rotating/zooming anything other than a bitmap with an anchor point of 0.5, 0.5, will probably look weird. Alternatively you can try the setAnchorPoint() extension for Sprite that is found in @ar2rsawseen 's GiderosCodingEasy.

You can grab the code, and a demo project from my GitHub repo. You'll also need to take my BhHelpers library too. Instructions for how to setup and use the Bowerhaus libraries are here.

best regards
+1 -1 (+4 / -0 )Share on Facebook

Comments

  • Thanks a lot,
    it would be useful to have an option to turn on one-finger dragging as well.
    Also some examples or even included functions that constrain zooming so that it never gets outside some rectangular area (e.g. the screen) or what i need more that it never gets "inside" the area (again, i have some help-page filling the screen that i want to make pinch-zoomable while maintaining that the screen is always filled) would be useful.

    Dislikes: matty47

    +1 -1 (+0 / -1 )Share on Facebook
  • @keszegh, I think it is often tempting to over-engineer simple things so that they do too much. I too will probably require one-finger dragging in the near future but I think I'll implement this as a separate library (BhDrag perhaps?) so as to avoid complicating the pinch code.

    It should be fairly straightforward to add your own constrain functions to make them do what you want. In fact the reason why I chose not to add any example constraints to this library was because (like in your case) they are likely to be exclusive to the app in question.

    best regards
  • bowerandybowerandy Guru
    edited March 2013
    @keszegh, okay so I implemented that one-finger dragging. There's a new module, BhDrag, that's included in the pinch library so you'll have to pull the latest version from GitHub. I've updated the example program to demo it too.

    Here's the gist:
    -- Drag in X direction only (with single touch), and rotation (two fingers)
    local cow2=Bitmap.new(Texture.new("cow2.jpg"))
    cow2:setScale(0.25)
    cow2:setAnchorPoint(0.5, 0.5)
    cow2:setPosition(100, 270)
    stage:addChild(cow2)
     
    cow2:enablePinch({allowScale=false, allowDrag=false})
    cow2:enableOneTouchDrag({dragConstrainFunc=constrainXOnly, dragWithOneFingerOnly=true})
    This shows the ability to drag with a single finger, whilst at the same time allowing pinching with two fingers. It's not a good idea to enable pinch-dragging at the same time as one-finger dragging, though, because they will fight each other. If you want to allow one and two finger dragging in the above example then simply remove "dragWithOneFingerOnly=true".

    best regards
  • thanks a lot for adding this. i agree with staying simple.
    maybe from time-to-time some typical constraint functions can be put into this topic by people (e.g. me when i get to continue my other project which will need pinching) who use this class.
  • keszeghkeszegh Member
    edited March 2013
    An example for a drag-constraint to force fill screen (to force the opposite, i.e. that the bitmap is always fully onscreen, one just needs to exchange min and max everywhere):
    local function constrainDragFillScreen(sprite, x, y)
      local outX,outY=x,y
      outX=math.min(outX,sprite:getWidth()/2)
      outX=math.max(outX,application:getContentWidth()-sprite:getWidth()/2)
      outY=math.min(outY,sprite:getHeight()/2)
      outY=math.max(outY,application:getContentHeight()-sprite:getHeight()/2)
      return outX, outY
    end
    On the other hand i tried to do the same for scale but I realized that it is more complicated then just making the proper constraint for sx,sy. The reason is that when zooming out and a screen-edge is reached (e.g. top) it would expected to be able to zoom further, while the top edge of the bitmap sticks to the top edge of the screen. For that, one would also need to move/change anchor point or whatever stuff, so returning only sx,sy won't be enough.

    Likes: bravcm

    +1 -1 (+1 / -0 )Share on Facebook
  • bowerandybowerandy Guru
    edited March 2013
    @keszegh, and others.

    I have had to make some modifications to BhPinch and BhDrag to make them allow scaling/rotation about any object's centre location. Not having a setAnchorPoint() that works correctly for anything other than Bitmap meant that scaling/rotation was almost useless for any other types of objects. It is now much more reasonable.

    The new code can be found on GitHub at:

    https://github.com/bowerhaus/BhPinch

    EDIT: You will need the latest version of BhHelpers too.

    There are a number of other changes that alter the interface to the two classes so you should read through the following and look at the example program to see how to use them. Here's the change list:

    1. Pinch scale and rotate now always act around object centre unless an anchorPoint (in the case of Bitmaps) has been set. This involved replacing all of the move/scale/rotate options with matrices.

    2. Pinch and drag now work correctly when the object in question resides in a parent that has been translated/rotated/scaled itself.

    3. Removed the dubious option to use "oldStyleHysteresis". Now all operations use this style and to avoid any hysteresis "jump" effect you have to reduce the hysteresis value. For this reason, all default hysteresis values are now zero so no jump will occur.

    4. Added moving average smoothing to scaling and rotation to avoid jitter.

    5. Because of the way that pinch dragging is now integral with the matrix calculations for rotate and scale, it is no longer possible for BhDrag to be used for two finger dragging at the same time as BhPinch is doing scale and rotation. Hence if you are using BhPinch you mustn't allow BhDrag to be active at the same time (it turns itself off by default for two fingers). See demo program for an example of how to use BhPinch and BhDrag together.

    best regards
    +1 -1 (+2 / -0 )Share on Facebook
  • A must have! Thank you very much bowerandy! :)>-
  • Sorry, but after trying to resize bitmap an error occured:

    BhPinch.lua:102: attempt to call method 'localToLocal' (a nil value)
    stack traceback:
    BhPinch.lua:102: in function

    Any idea why?
  • bowerandybowerandy Guru
    edited March 2013
    @bravcm, yes sorry, I forgot to check in a new version of BhHelpers. I've done it now so please take it from:

    https://github.com/bowerhaus/BhHelpers

    best regards
  • yes thanks, but another proble occured:

    BhHelpers.lua:38: attempt to index global 'Object' (a nil value)

    ???
  • @bravcm, what version of Gideros are you using? I think the latest BhHelpers requires 2012.9.10.

    Alternatively, you could get away with stealing the Sprite:localToLocal() function from the latest BhHelpers and copying it to BhPinch.lua. Then you can go back to the previous BhHelpers.lua.

    best regards
  • @bowerandy, reinstalling Gideros solved my problems. Now work like a charm!

    best regards
  • piepie Member
    Hi @bowerandy thank you a lot for sharing this! :)
    I was wondering how to enable a couple of features, if it's possible:

    1) I would need it to drag only on multitouch (2 fingers together or more, since I'd like to keep single touch for user interactions) and I found that this is easily achieved using enablePinch and setting its allowScale and allowRotate to false.

    I was wondering if that could be achieved also using bhDrag and some parameter that I didn't find... :)

    2) Is there a constraint parameter to avoid dragging the sprite "outside the screen"?
    I saw the code from @keszegh but I couldn't figure out how to use it, and maybe it was referring to a previous version of bhDrag. Since I am not going to zoom, it should be "easy" to put some constraint but I can't get exactly how your code works.

    Thank you in advance, best regards
    P.
Sign In or Register to comment.