All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threerings.media.util.ArcPath Maven / Gradle / Ivy

The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.media.util;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;

import com.samskivert.util.StringUtil;

import com.threerings.util.DirectionUtil;

/**
 * The line path is used to cause a pathable to go from point A to point B
 * along the arc of an ellipse in a certain number of milliseconds.
 */
public class ArcPath extends TimedPath
{
    /** An orientation constant indicating that the normal (eight)
     * directions should be used to orient the pathable being made to
     * follow this path. */
    public static final int NORMAL = 0;

    /** An orientation constant indicating that the fine (sixteen)
     * directions should be used to orient the pathable being made to
     * follow this path. */
    public static final int FINE = 1;

    /** An orientation indicating that the pathable should not be oriented
     * as it moves along the path.
     */
    public static final int NONE = 2;

    /**
     * Creates an arc path that will animate a pathable from the specified
     * starting position along an ellipse defined by the supplied
     * parameters. The pathable will travel the specified number of
     * radians along the arc of that ellipse. A positive number of radians
     * indicates counter-clockwise travel along the circle, a negative
     * number, clockwise rotation.
     *
     * @param start the starting point for the pathable.
     * @param xradius the length of the x radius.
     * @param yradius the length of the y radius.
     * @param sangle the starting angle.
     * @param delta the angle through which the pathable should be moved.
     * @param duration the number of milliseconds during which to effect
     * the animation.
     * @param orient an orientation code indicating how the pathable
     * should be oriented when following the path, either {@link #NORMAL},
     * or {@link #FINE}.
     */
    public ArcPath (Point start, double xradius, double yradius,
                    double sangle, double delta, long duration,
                    int orient)
    {
        super(duration);
        _xradius = xradius;
        _yradius = yradius;
        _sangle = sangle;
        _delta = delta;
        _orient = orient;

        // compute the center of the ellipse
        _center = new Point(
            (int)(start.x - Math.round(Math.cos(sangle) * xradius)),
            (int)(start.y - Math.round(Math.sin(sangle) * yradius)));
    }

    /**
     * Return a copy of the path, translated by the specified amounts.
     */
    public Path getTranslatedInstance (int x, int y)
    {
        int startx = (int)(_center.x + Math.round(Math.cos(_sangle) * _xradius));
        int starty = (int)(_center.y + Math.round(Math.sin(_sangle) * _yradius));

        return new ArcPath(new Point(startx + x, starty + y),
            _xradius, _yradius, _sangle, _delta, _duration, _orient);
    }

    /**
     * Sets the offset that is applied to the pathable whenever it is oriented. This offset is in
     * clockwise units whose granularity is specified by the {@link #NORMAL} or {@link #FINE}
     * setting supplied to the path at construct time. The intent here is to allow arc paths to be
     * applied that don't orient the pathable in the direction they are traveling but instead in
     * some fixed offset from that direction.
     */
    public void setOrientOffset (int offset)
    {
        _orientOffset = offset;
    }

    /**
     * Returns the position of the end of the path.
     */
    public Point getEndPos ()
    {
        return new Point(
            (int)(_center.x + Math.round(Math.cos(_sangle + _delta) * _xradius)),
            (int)(_center.y + Math.round(Math.sin(_sangle + _delta) * _yradius)));
    }

    // documentation inherited
    public boolean tick (Pathable pable, long timestamp)
    {
        double angle;
        boolean modified = false;

        // if we've blown past our arrival time...
        if (timestamp >= _startStamp + _duration) {
            // ...force the angle to the destination angle
            angle = _sangle + _delta;

        } else {
            // otherwise, compute the angle at which we should place the pathable based on the
            // elapsed time
            long elapsed = timestamp - _startStamp;
            angle = _sangle + _delta * elapsed / _duration;
        }

        // determine where we should be along the path
        computePosition(_center, _xradius, _yradius, angle, _tpos);

        // Skip this if we are not reorienting as we follow the path.
        if (_orient != NONE) {
            // compute the pathable's new orientation
            double theta = angle + ((_delta > 0) ? Math.PI/2 : -Math.PI/2);
            int orient;
            switch (_orient) {
            default:
            case NORMAL:
                orient = DirectionUtil.getDirection(theta);
                // adjust it appropriately
                orient = DirectionUtil.rotateCW(orient, 2*_orientOffset);
                break;

            case FINE:
                orient = DirectionUtil.getFineDirection(theta);
                // adjust it appropriately
                orient = DirectionUtil.rotateCW(orient, _orientOffset);
                break;
            }

            // update the pathable's orientation if it changed
            if (pable.getOrientation() != orient) {
                pable.setOrientation(orient);
                modified = true;
            }
        }

        // update the pathable's location if it moved
        if (pable.getX() != _tpos.x || pable.getY() != _tpos.y) {
            pable.setLocation(_tpos.x, _tpos.y);
            modified = true;
        }

        // if we completed our path, let the sprite know
        if (angle == _sangle + _delta) {
            pable.pathCompleted(timestamp);
        }

        return modified;
    }

    // documentation inherited
    public void paint (Graphics2D gfx)
    {
        int x = (int)(_center.x - _xradius), y = (int)(_center.y - _yradius);
        int width = (int)(2*_xradius), height = (int)(2*_yradius);
        int sangle = (int)(Math.round(180 * _sangle / Math.PI)),
            delta = (int)(Math.round(180 * _delta / Math.PI));

        gfx.setColor(Color.blue);
        gfx.drawRect(x, y, width-1, height-1);

        gfx.setColor(Color.yellow);
        gfx.drawArc(x, y, width-1, height-1, 0, 360);

        gfx.setColor(Color.red);
        gfx.drawArc(x, y, width-1, height-1, 360-sangle, -delta);
    }

    @Override
    protected void toString (StringBuilder buf)
    {
        super.toString(buf);
        buf.append(", center=").append(StringUtil.toString(_center));
        buf.append(", sangle=").append(_sangle);
        buf.append(", delta=").append(_delta);
        buf.append(", radii=").append(_xradius).append("/").append(_yradius);
    }

    /**
     * Computes the position of an entity along the path defined by the supplied parameters
     * assuming that it must finish the path in the specified duration (in millis) and has been
     * traveling the path for the specified number of elapsed milliseconds.
     */
    public static void computePosition (
        Point center, double xradius, double yradius, double angle, Point pos)
    {
        pos.x = (int)Math.round(center.x + xradius * Math.cos(angle));
        pos.y = (int)Math.round(center.y + yradius * Math.sin(angle));
    }

    /** The center of our ellipse. */
    protected Point _center;

    /** Our ellipse radii. */
    protected double _xradius, _yradius;

    /** Our starting and delta angles. */
    protected double _sangle, _delta;

    /** The method to be used to orient the pathable. */
    protected int _orient;

    /** An orientation offset used when orienting our pathable. */
    protected int _orientOffset = 0;

    /** A temporary point used when computing our position along the path. */
    protected Point _tpos = new Point();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy