Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

package com.gebauz.bauzoid.graphics.sprite;

import com.gebauz.bauzoid.math.MathUtil;
import com.gebauz.bauzoid.math.Matrix4;
import com.gebauz.bauzoid.math.Vector2;
import com.gebauz.bauzoid.math.collision.AABoundingBox;

/** Transformation, alpha, and color parameters of a sprite. */
public class SpriteTransform
{

        // Constants========================================================================================

        // Embedded Types===================================================================================

        // Fields===========================================================================================

        public float x = 0.0f;
        public float y = 0.0f;
       
        public float w = 0.0f;
        public float h = 0.0f;
       
        /** Angle in degrees. */
        public float angle = 0.0f;
       
        public boolean mirrorX = false;
        public boolean mirrorY = false;
       
        /** Rotation and scaling pivot point in absolute coordinates. */
        public float pivotX = 0.0f;
        public float pivotY = 0.0f;
       
        // Methods==========================================================================================

        public SpriteTransform()
        {
        }
       
        public void centerPivot()
        {
                pivotX = w/2.0f;
                pivotY = h/2.0f;
        }
       
        public void set(SpriteTransform param)
        {
                x = param.x;
                y = param.y;
                w = param.w;
                h = param.h;
                pivotX = param.pivotX;
                pivotY = param.pivotY;
                angle = param.angle;
                mirrorX = param.mirrorX;
                mirrorY = param.mirrorY;
        }
       
        /** Calculate a 4x4 Matrix that transforms points from sprite space to world space. */
        public final Matrix4 getTransform()
        {
                Matrix4 result = Matrix4.createIdentity();

                Matrix4.multiply(result,  result, Matrix4.createTranslation(-w/2, -h/2, 0));
                Matrix4.multiply(result, result, Matrix4.createScale((mirrorX ? -1 : 1), (mirrorY ? -1 : 1), 1));
                Matrix4.multiply(result,  result, Matrix4.createTranslation(w/2, h/2, 0));
                Matrix4.multiply(result,  result, Matrix4.createTranslation(-pivotX, -pivotY, 0));
                Matrix4.multiply(result, result, Matrix4.createRotationZ(angle));
                Matrix4.multiply(result,  result, Matrix4.createTranslation(x, y, 0));
               
                return result;
        }
       
        /** Transform a point that is specified in sprite space into world space.
         * pX/pY is defined in the same space as the pivot point. */

        public Vector2 spriteToWorld(float pX, float pY)
        {
                return spriteToWorld(pY, pY, false);
        }
       
        /** Transform a point that is specified in sprite space into world space.
         * pX/pY is defined in the same space as the pivot point. */

        public Vector2 spriteToWorld(float pX, float pY, boolean ignoreMirror)
        {
                // Step 1. Find out the relative position of (pX/pY) to the pivot point
                // Step 1a. Translate px/pY so that it's coordinates are relative to the pivot point (pivot point becomes origin)
                // Step 1b. Rotate around pivot point
                // Step 2. Add x/y to the relative position
                float resultX;
                float resultY;
               
                if (!ignoreMirror)
                {
                        // mirror in sprite space
                        if (mirrorX)
                                pX = w - pX;
                       
                        if (mirrorY)
                                pY = h - pY;
                }
       
                float relativeToPivotX = pX - pivotX;
                float relativeToPivotY = pY - pivotY;
               
                // rotate relativeToPivotX/Y around pivot (which has become origin now)
                if (angle != 0)
                {
                        final float cos = MathUtil.cos(angle);
                       
                        // Rotate CCW (like Matrix4.CreateRotationZ).
                        final float sin = MathUtil.sin(-angle);
                       
                        resultX = relativeToPivotX * cos - relativeToPivotY * sin;
                        resultY = relativeToPivotX * sin + relativeToPivotY * cos;
                }
                else
                {
                        // no rotation
                        resultX = relativeToPivotX;
                        resultY = relativeToPivotY;
                }                      

                // translate to world space
                resultX += x;
                resultY += y;
               
                return new Vector2(resultX, resultY);
        }
       
        /** Transform a point that is specified in world space into sprite space. */
        public Vector2 worldToSprite(float pX, float pY)
        {
                return worldToSprite(pX, pY, false);
        }

        /** Transform a point that is specified in world space into sprite space. */
        public Vector2 worldToSprite(float pX, float pY, boolean ignoreMirror)
        {
                // Step 1a. Transform into pivot space (pivot becomes origin), translate by x/y (world position of pivot)
                // Step 1b. Rotate back (CW).
                // Step 2a. Translate by Pivot to transform into sprite space.
                // Step 2b. Mirror.
               
                float resultX;
                float resultY;
               
                float relativeToPivotX = pX - x;
                float relativeToPivotY = pY - y;
               
                // rotate relativeToPivotX/Y around pivot (which has become origin now)
                if (angle != 0)
                {
                        // Rotate CW
                        final float cos = MathUtil.cos(-angle);
                        final float sin = MathUtil.sin(angle);
                       
                        resultX = relativeToPivotX * cos - relativeToPivotY * sin;
                        resultY = relativeToPivotX * sin + relativeToPivotY * cos;
                }
                else
                {
                        // no rotation
                        resultX = relativeToPivotX;
                        resultY = relativeToPivotY;
                }
               
                resultX += pivotX;
                resultY += pivotY;
               
                if (!ignoreMirror)
                {
                        // mirror
                        if (mirrorX)
                                resultX = w - resultX;
                        if (mirrorY)
                                resultY = h - resultY;
                }
               
                return new Vector2(resultX, resultY);
        }
       
        /** Get an approximate bounding box of the sprite in world space. */
        public AABoundingBox getApproximateBoundingBox()
        {
                float maxWidth = Math.max(pivotX, w - pivotX)*1.5f;
                float maxHeight = Math.max(pivotY, h - pivotY)*1.5f;
               
                return new AABoundingBox(x - maxWidth, y - maxHeight, x + maxWidth, y + maxHeight);
        }
       
        /** Get an exact bounding box of the sprite in world space. */
        public AABoundingBox getBoundingBox()
        {
                Vector2 p1 = spriteToWorld(0, 0);
                //Vector2 p2 = spriteToWorld(w, 0);
                Vector2 p3 = spriteToWorld(w, h);
                //Vector2 p4 = spriteToWorld(0, h);
               
                Vector2 center = new Vector2((p1.x + p3.x) / 2, (p1.y + p3.y) / 2);
                center.subtractVector(p1);
               
                // Circumcircle radius
                float radius = center.length();
               
                return new AABoundingBox(center.x - radius, center.y - radius, center.x + radius, center.y + radius);
        }
       
        // Getters/Setters==================================================================================

        /** Get the upper left corner as a Vector2 in world space. */
        public Vector2 getTopLeft()
        {
                // return transformed coordinates (without mirroring)
                if (angle != 0)
                        return spriteToWorld(0, 0, true);
               
                return new Vector2(x - pivotX, y - pivotY);
        }
       
        /** Get the upper left corner as a Vector2 in world space. */
        public Vector2 getTopRight()
        {
                // return transformed coordinates (without mirroring)
                if (angle != 0)
                        return spriteToWorld(w, 0, true);
               
                return new Vector2(x - pivotX + w, y - pivotY);
        }
       
        /** Get the upper left corner as a Vector2 in world space. */
        public Vector2 getBottomLeft()
        {
                // return transformed coordinates (without mirroring)
                if (angle != 0)
                        return spriteToWorld(0, h, true);

                return new Vector2(x - pivotX, y - pivotY + h);
        }
       
        /** Get the upper left corner as a Vector2 in world space. */
        public Vector2 getBottomRight()
        {
                // return transformed coordinates (without mirroring)
                if (angle != 0)
                        return spriteToWorld(w, h, true);

                return new Vector2(x - pivotX + w, y - pivotY + h);
        }
       
        /** Check if the point is inside the sprite. */
        public boolean isInside(float x, float y)
        {
                // Check in sprite space. Ignore mirroring since we don't need it for this operation
                Vector2 spriteSpacePt = worldToSprite(x, y, true);
               
                return ((spriteSpacePt.x >= 0) && (spriteSpacePt.y >= 0) &&
                                (spriteSpacePt.x <= w) && (spriteSpacePt.y <= h));
        }

}