Search code examples
actionscript-3animationmatriximage-rotationimage-scaling

Strange stability issues with Matrix scale/rotation in Actionscript


I have a Flash app where I am performing a scale and rotation operation about the center of _background:MovieClip (representing a page of a book). I have simple event listeners on the GESTURE_ROTATE and GESTURE_SCALE events of this MC which update some variables currentRotation and currentScaleX, currentScaleY. I then have the following code trigger on the ENTER_FRAME event of the app.

The problem I am encountering is upon rotating the MC beyond the limits of roughly 60 or -60 degrees, or scaling slightly and rotating, the MC begins to oscillate and finally spin wildly out of control and off the screen. I've tried several things to debug it, and even tried Math.flooring the currentRotationValue and rounding the values of currentScaleX/Y to the tenths place (Math.floor(currentScale * 10) / 10), but neither of these seems to remedy it. I'm a little stuck at this point and have tried researching as much as I can, but couldn't find anything. Any suggestions? Is there an issue with doing this operation on each frame perhaps?

private function renderPage(e:Event) {
    var matrix:Matrix = new Matrix();

    // Get dimension of current rectangle.
    var rect:Rectangle = _background.getBounds(_background.parent); 

    // Calculate the center.
    var centerX = rect.left + (rect.width/2);
    var centerY = rect.top + (rect.height/2);

    // Translating to the desired reference point.
    matrix.translate(-centerX, -centerY); 

    matrix.rotate(currentRotation / 180) * Math.PI);
    matrix.scale(currentScaleX, currentScaleY);
    matrix.translate(centerX, centerY); 

    _background.transform.matrix = matrix;
}

Solution

  • I'm not certain what behaviour you're trying to produce, but I think the problem is that centerX and centerY define the middle of _background in _background.parent's coordinate space. You're then translating the matrix so that _background is rotated around the values centerX, centerY, but in _background's coordinate space.

    Assuming you want _background to rotate around a point which remains static on screen, what you actually need to do is use two different Points:

    matrix.translate(-_rotateAroundPoint.x, -_rotateAroundPoint.y); 
    
    matrix.rotate(currentRotation / 180) * Math.PI);
    matrix.scale(currentScaleX, currentScaleY);
    
    matrix.translate(_centerOnPoint.x, _centerOnPoint.y); 
    

    Where _rotateAroundPoint is the point around which _background should turn in it's own coordinate space, and _centerOnPoint is the point around which it should turn in its parent's coordinate space.

    Both of those values only need to be recalculated when you want to pan _background, rather than every frame. For example:

    private var _rotateAroundPoint:Point = new Point(_background.width * 0.5, _background.height * 0.5);
    private var _centerOnPoint:Point = new Point(50, 50);
    
    private function renderPage(e:Event) {
        var matrix:Matrix = new Matrix();
    
        matrix.translate(-_rotateAroundPoint.x, -_rotateAroundPoint.y); 
    
        matrix.rotate((currentRotation / 180) * Math.PI);
        matrix.scale(currentScaleX, currentScaleY);
        matrix.translate(_centerOnPoint.x, _centerOnPoint.y); 
    
        _background.transform.matrix = matrix;
    }