views:

52

answers:

1

i'm unsuccessfully trying to rotate a rectangle around an external point while tweening. i'm trying to lock the top of the red rectangle to the line while it tweens from left to right and rotates from 0º to 90º.

alt text

the image above shows 3 states of the tween. state 1 shows the red rectangle at the start of the line with no angle. state 2 shows the red rectangle has tweened half way along the line with an angle of 45º that is also half the total angle of 90º. state 3 shows the final position of the tween where the red rectangle has an angle of 90º and is placed at the edge of the line.

it seems the problem i'm experiencing is that while tweening, the rotation causes the top of the red rectangle to lose sync with the black line.

here is my code that doesn't work, but hopefully will give you a clearer picture of what i'm attempting.

var angle:Number = 90;
var previousAngle:Number = 0;

var distanceObject:Object = new Object();
distanceObject.distance = line.width;

distanceTween = new Tween(distanceObject, "distance", None.easeNone, 0, distanceObject.distance, 5, true);
distanceTween.addEventListener(TweenEvent.MOTION_CHANGE, tweenHandler);

function tweenHandler(evt:TweenEvent):void
    {
    var angleShift:Number = (angle / distance) * distanceObject.distance;

    //1:tween RedBox position
    redBox.x = line.x + line.width * distanceObject.distance;

    //2:tween RedBox angle
    var externalPointMatrix:Matrix = redBox.transform.matrix;
    MatrixTransformer.rotateAroundExternalPoint(externalPointMatrix, 0 + redBox.width * distanceObject.distance, 0, angleShift - previousAngle);
    redBox.transform.matrix = externalPointMatrix;

    previousAngle = angleShift;
    }
+1  A: 

I don't think you have specified the problem well enough for a generic solution. There are 3 things changing here: x, y and rotation. Each of these is calculated as a result of a point on the rectangle (the blue "x" in your diagram) that changes over time. That means the thing you need to focus on first is the point on the rectangle that changes over time. Next you need to know that the x and y can be calculated using that point along with the rotation.

So break it down into steps.

  1. find the location of the "x" point on the line
  2. rotate the object
  3. find the location of the "x" point wrt to the rectangle
  4. based on the angle of rotation and the known location of the "x" point calculate the x and y position of the rectangle (SOHCAHTOA)

Here is some code to illustrate:

package
{

import com.greensock.TweenNano;

import flash.display.Sprite;
import flash.events.Event;

[SWF(width='500', height='300', backgroundColor='#ffffff', frameRate='30')]
public class BoxAnim extends Sprite
{
    private static const LINE_WIDTH:int = 350;
    private static const RECT_WIDTH:int = 150;
    private static const RECT_HEIGHT:int = 100;
    private static const FINAL_ROTATION:Number = Math.PI/2;

    public var point:Number;

    private var line:Sprite;
    private var rect:Sprite;
    private var cross:Sprite;

    public function BoxAnim()
    {
        addEventListener(Event.ADDED_TO_STAGE, addedToStage);
    }

    private function addedToStage(event:Event):void
    {
        line = new Sprite();
        addChild(line);
        line.graphics.lineStyle(10, 0x0);
        line.graphics.lineTo(LINE_WIDTH, 0);
        line.x = 50;
        line.y = 175;

        rect = new Sprite();
        addChild(rect);
        rect.graphics.lineStyle(4, 0xFF0000);
        rect.graphics.beginFill(0xFF0000, 0.5);
        rect.graphics.drawRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
        rect.x = 50;
        rect.y = 175;

        cross = new Sprite();
        addChild(cross);
        cross.graphics.lineStyle(5, 0x41a9f4);
        cross.graphics.moveTo(-5, -5);
        cross.graphics.lineTo(5, 5);
        cross.graphics.moveTo(5, -5);
        cross.graphics.lineTo(-5, 5);
        cross.x = 50;
        cross.y = 175;

        point = 0;
        TweenNano.to(this, 3, {point: 1, onUpdate: tick});
    }

    private function tick():void
    {
        // first calculate where the point should be on the line
        cross.x = (point * LINE_WIDTH) + line.x;

        // calculate the angle of rotation
        var rotationRadians:Number = (point * FINAL_ROTATION);
        rect.rotation = rotationRadians*180/Math.PI;

        // calculate where on the rectangle the point would be
        var rectCrossX:Number = (point * RECT_WIDTH);
        // use trig to find the x & y points
        rect.x = cross.x - Math.cos(rotationRadians)*rectCrossX;
        rect.y = cross.y - Math.sin(rotationRadians)*rectCrossX;
    }
}

}

I'm just using the variable point as a percentage that goes from 0 to 1. I then scale it to find the position of the "x" point on the line. Scale it again to figure out the rotation. Scale it again to find where it lies along the top of the rectangle. Then trig solves the location of the corner of the rectangle wrt the point.

James Fassett
i've been fighting with this for too long. your solution works like magic! thanks so much!
TheDarkInI1978