hm9
hm9

Reputation: 1

how to scale using canvas

i am using this code to scale my canvas around a focus point

 private class MyScaleDetector
            extends ScaleGestureDetector.SimpleOnScaleGestureListener {


        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            float focusx=detector.getFocusX();
            float focusy=detector.getFocusY();
             return super.onScaleBegin(detector);

        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float factor=detector.getScaleFactor();

              scale *= factor;


            scale = Math.max(0.4f, Math.min(scale, 20.0f));


            return true;
        }
    }

and this is the code i use to scale inside the ondraw method

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

       canvas.scale(scale,scale,focusx,focusy);
}

my problem is when i start scaling around the focus point (200,200) for example ,at first it start going there but than while increasing the scale it start going away from the point . so my problem is how i scale toward a specific point so that that this point becomes the center of the viewport while scaling .

I think i should use canvas.translate() with it but i don't know how much i should move the x and y position of canvas while scaling .

Edit:the image below summarize what i am trying to say

image

Upvotes: 0

Views: 1641

Answers (1)

RabidMutant
RabidMutant

Reputation: 611

There are several things to bear in mind here:

  1. The ScaleDetector only detects scaling events; this sounds good but is not sufficient by itself to handle pinch/zoom in 2D (eg. in a picture).
  2. The 'focus' returned by the detector is the mid point between the two pointers (fingers); this is less useful since the natural assumption when you start scaling is that the image pixel under your finger will remain under your finger as you zoom. People often move fingers at different speeds (or deliberately keep one finger stationary), which invalidates the use of the mid-point.
  3. You (probably) want to handle rotation too.

The broad approach I have used in the past is:

  • manually detect first and second touch events; record the coordinate of the two pointers when the second pointer touch event occurs. These points are to logically 'follow' the pointers from now on.
  • when a motion event occurs that continues to have two pointers down (ie. a two fingered rotation, zoom, or drag), determine the translation, scale and rotation as follows:
    • the scale is based on the distance between the new points compared to the original points (recorded at the start)
    • the rotation is based on the difference in the angle between the first two points and the angle between the most recent points
    • the translation is based arbitrarily on one of the points (usually the first) - just translate based on the pixels between the original point and the latest point
    • these values can be calculated and stored during the motion event, then used in onDraw() method

Note that in onDraw():

  • It's important to perform the translation first since you are working in screen pixels for the two reference points.
  • you can then rotate & scale in any order since the scale is always equal in the X & Y direction

Edit to address more specific request:

If all you really need to do is simply scale around a specific redefined point p (px, py) by an amount s, and keep p visible, then you can do the following:

canvas.translate(px, py);
canvas.scale(s,s);
canvas.translate(-px*s, py*s);

This should keep p in the same pixel-position it was in prior to scaling. This will suffer from all the problems alluded to above as well as the risks of drift due to successive scale events around a moving focus (based on your original code). You are far better off doing what I described above.

Upvotes: 1

Related Questions