view game/Piece.java @ 138:9eb791e2fa17

Optimize board updating logic, so that the old placed tiles need not to be redrawn from scratch on each screen update, as they do not change usually.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 25 Nov 2011 11:04:09 +0200
parents 4c0dec72e2f0
children 35374be74fdc
line wrap: on
line source

/*
 * Ristipolku
 * (C) Copyright 2011 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
 */
package game;

import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.math.*;


public class Piece
{
    public enum RotateDir { LEFT, RIGHT }

    static final int numConnections = 8;
    static final float maxTime = 50.0f;

    int currRotation;
    int[] connections;
    boolean[] states;
    PieceType type, prevType;

    boolean rotationChanged, rotationActive,
            typeChanged, typeActive,
            stateChanged, stateActive,
            active;

    float   currAngle, rotationTime, rotationSpeed,
            typeTime, typeValue, throbTime;

    Interpolate lerpRotation,
                lerpType;


    public Piece(PieceType ptype)
    {
        // Initialize
        connections = new int[numConnections];
        states = new boolean[numConnections];
        type = ptype;

        rotationChanged = false;
        rotationActive = false;
        currRotation = 4 * 5000;
        currAngle = getAngle(currRotation);

        typeChanged = false;
        typeActive = false;

        throbTime = 0;

        lerpType = new Interpolate(1.0f, 0.0f, maxTime);

        // Initialize connections between endpoints of the paths inside the piece
        for (int i = 0; i < numConnections; i++)
            connections[i] = -1;


        // Randomize connections in the piece
        Random rnd = new Random();
        for (int i = 0; i < numConnections; i++)
        {
            while (connections[i] < 0)
            {
                int tmp = rnd.nextInt(numConnections);
                if (tmp != i && connections[tmp] < 0)
                {
                    connections[i] = tmp;
                    connections[tmp] = i;
                }
            }
        }
    }

    public Piece()
    {
        this(PieceType.NONE);
    }


    public void changed()
    {
        typeChanged = true;
    }

    public void setType(PieceType ptype)
    {
//        typeChanged = (prevType != ptype) && (ptype == PieceType.LOCKED);
        prevType = type;
        type = ptype;
    }

    public void clearStates()
    {
        for (int i = 0; i < numConnections; i++)
            states[i] = false;
        stateChanged = true;
    }

    public int getRotation()
    {
        return currRotation % 4;
    }

    public int getRotatedPoint(int in)
    {
        int point = (in - (getRotation() * 2)) % 8;
        if (point < 0) point = 8 + point;
        return point;
    }

    public int getAntiRotatedPoint(int in)
    {
        int point = (in + (getRotation() * 2)) % 8;
        if (point < 0) point = 8 + point;
        return point;
    }

    public int getMatchingPoint(int point)
    {
        switch (point)
        {
            case 0: return 5;
            case 1: return 4;

            case 2: return 7;
            case 3: return 6;

            case 4: return 1;
            case 5: return 0;

            case 6: return 3;
            case 7: return 2;
        }
        return -1;
    }

    public void setConnectionState(int point, boolean state)
    {
        states[point] = state;
        states[connections[point]] = state;
        stateChanged = true;
    }

    public int getConnection(int point)
    {
        return connections[point];
    }

    private float getAngle(float rotation)
    {
        return (float) ((rotation * Math.PI) / 2.0f);
    }

    public void rotate(RotateDir dir)
    {
        // Only normal
        if (type != PieceType.LOCKED && type != PieceType.ACTIVE)
            return;

        currRotation = (currRotation + (dir == RotateDir.RIGHT ? 1 : -1));
        lerpRotation = new Interpolate(getAngle(currRotation), currAngle, maxTime);
        rotationChanged = true;
    }

    public Point2D getPointCoords(float x, float y, float dim, int index)
    {
        float ox = 0, oy = 0;
        float step = dim / 10;

        switch (index) {
            // Normal line starting and ending points
            case  0: ox = 3.0f; oy = 0.4f; break;
            case  1: ox = 7.0f; oy = 0.4f; break;

            case  2: ox = 9.6f; oy = 3.0f; break;
            case  3: ox = 9.6f; oy = 7.0f; break;

            case  4: ox = 7.0f; oy = 9.6f; break;
            case  5: ox = 3.0f; oy = 9.6f; break;

            case  6: ox = 0.4f; oy = 7.0f; break;
            case  7: ox = 0.4f; oy = 3.0f; break;


            // Matching control points for each point above (+8)
            case  8: ox = 3.0f; oy = 2.5f; break;
            case  9: ox = 7.0f; oy = 2.5f; break;

            case 10: ox = 7.5f; oy = 3.0f; break;
            case 11: ox = 7.5f; oy = 7.0f; break;

            case 12: ox = 7.0f; oy = 7.5f; break;
            case 13: ox = 3.0f; oy = 7.5f; break;

            case 14: ox = 2.5f; oy = 7.0f; break;
            case 15: ox = 2.5f; oy = 3.0f; break;
        }

        return new Point2D.Float(x + ox * step, y + oy * step);
    }

    public PieceType getType()
    {
        return type;
    }

    public void animate(float time)
    {
        active = false;

        if (rotationChanged)
        {
            rotationTime = time;
            rotationActive = true;
            rotationChanged = false;
            rotationSpeed = 1.0f;
        }

        if (typeChanged && type == PieceType.LOCKED)
        {
            rotationSpeed = 1.5f;
        }

        if (rotationActive)
        {
            float t = (time - rotationTime) * rotationSpeed;

            if (t < maxTime)
                currAngle = lerpRotation.getValue(t);
            else
            {
                currAngle = lerpRotation.start;
                rotationActive = false;
            }

            active = true;
        }

        if (typeChanged)
        {
            typeTime = time;
            typeActive = true;
            typeChanged = false;
        }

        if (typeActive)
        {
            float t = (time - typeTime) * 2.0f;

            if (t < maxTime)
                typeValue = lerpType.getValue(t);
            else
            {
                typeValue = lerpType.start;
                typeActive = false;
            }


            active = true;
        }

        if (stateChanged)
        {
        }

        throbTime = (time % 100) / 100.0f;
    }

    public void paint(Graphics2D g, float x, float y, float dim)
    {
        AffineTransform save = g.getTransform();
        Composite csave = g.getComposite();

        // Change compositing alpha for the whole piece drawing
        // when the piece is being "introduced".
        if (typeActive)
        {
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, typeValue));
        }


        // Transform drawing by current angle
        g.rotate(currAngle, x + dim / 2.0f, y + dim / 2.0f);

        // Color piece by type
        switch (type) {
            case LOCKED:  g.setPaint(new Color(0.3f, 0.8f, 0.3f, 0.35f)); break;
            case ACTIVE:  g.setPaint(new Color(0.9f, 0.3f, 0.3f, 0.35f)); break;
            case START:   g.setPaint(new Color(1.0f, 0.6f, 0.0f, 0.95f)); break;
        }

        g.fill(new RoundRectangle2D.Float(x, y, dim, dim, dim / 10, dim / 10));

        // Start pieces (center piece) have a different kind of border
        // and no connections drawn inside
        if (type == PieceType.START)
        {
            // Draw piece border
            g.setPaint(Color.black);
            g.setStroke(new BasicStroke(2.0f));
            g.draw(new RoundRectangle2D.Float(x, y, dim, dim, dim / 10, dim / 10));
        }
        else
        {
            // Active piece has a throbbing "ghost" border
            if (type == PieceType.ACTIVE)
            {
                float offs1 = throbTime * 10.0f,
                      offs2 = throbTime * 20.0f;

                g.setPaint(new Color(0.0f, 0.0f, 0.0f, (float) (1.0f - throbTime) ));
                g.setStroke(new BasicStroke(2.0f + throbTime * 2.0f));
                g.draw(new RoundRectangle2D.Float(
                    x - offs1, y - offs1,
                    dim + offs2, dim + offs2,
                    dim / 10, dim / 10));
            }

            // Draw piece border
            g.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.6f));
            g.setStroke(new BasicStroke(5.0f));
            g.draw(new RoundRectangle2D.Float(x, y, dim, dim, dim / 10, dim / 10));

            // Draw the connections
            g.setStroke(new BasicStroke(5.5f));
            CubicCurve2D curve = new CubicCurve2D.Float();
            boolean[] drawn = new boolean[numConnections];
            for (int i = 0; i < numConnections; i++)
            if (!drawn[i])
            {
                Point2D start, cp1, cp2, end;
                boolean isActive = states[i] || states[connections[i]];

                g.setPaint(isActive ? Color.white : Color.black);

                start = getPointCoords(x, y, dim, i);
                end   = getPointCoords(x, y, dim, connections[i]);
                cp1   = getPointCoords(x, y, dim, i + 8);
                cp2   = getPointCoords(x, y, dim, connections[i] + 8);

                curve.setCurve(start, cp1, cp2, end);
                g.draw(curve);

                // Mark connection drawn, so we don't overdraw
                drawn[i] = true;
                drawn[connections[i]] = true;
            }

        } // !PieceType.START

        g.setTransform(save);
        g.setComposite(csave);
    }
}