com.ridiculousRPG.movement.auto.MoveArcAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.ridiculousRPG.movement.auto.MoveArcAdapter.java

Source

/*
 * Copyright 2011 Alexander Baumgartner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ridiculousRPG.movement.auto;

import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.ridiculousRPG.event.EventObject;
import com.ridiculousRPG.event.EventTrigger;
import com.ridiculousRPG.movement.Movable;
import com.ridiculousRPG.movement.MovementHandler;

/**
 * This {@link MovementHandler} tries to move an event by the given origin and
 * angle. The move waits if a blocking event exists on it's way.<br>
 * It also supports rotating the texture of the event automatically.<br>
 * Furthermore it allows stretching the arc either in x or in y direction. This
 * allows you to move elliptic curves.<br>
 * After succeeding the switch finished is set to true.
 * 
 * @author Alexander Baumgartner
 */
public class MoveArcAdapter extends MovementHandler {
    private static final long serialVersionUID = 1L;

    private Vector2 originReset;
    private Vector2 origin;
    private float angle;
    private boolean rotateTexture;
    private Vector2 stretch;
    private boolean fixedSpeed;
    private Vector2 drift;
    // initialized by the first call of tryMove(...)
    private float radius = Float.NaN;
    private float arcCorrection = Float.NaN;
    private float startArc = Float.NaN;
    private float correctX, correctY;

    private float lastMoveArc = 0f;
    private float entireMoveArc = 0f;

    private static final float PI = MathUtils.PI;
    private static final float PI2 = PI * 2;

    /**
     * This {@link MovementHandler} tries to move an event by the given origin
     * and angle. The move waits if a blocking event exists on it's way.<br>
     * It also supports rotating the texture of the event automatically.<br>
     * After succeeding the switch finished is set to true.
     * 
     * @param origin
     *            The origin of the cycle
     * @param angle
     *            The angle in degrees or +-0 if you want to loop forever. If
     *            the angle is negative, the move will be clockwise.
     * @return
     */
    public MoveArcAdapter(Vector2 origin, float angle) {
        this(origin, angle, false, false);
    }

    /**
     * This {@link MovementHandler} tries to move an event by the given origin
     * and angle. The move waits if a blocking event exists on it's way.<br>
     * It also supports rotating the texture of the event automatically.<br>
     * After succeeding the switch finished is set to true.
     * 
     * @param origin
     *            The origin of the cycle
     * @param angle
     *            The angle in degrees or +-0 if you want to loop forever. If
     *            the angle is negative, the move will be clockwise.
     * @param rotateTexture
     *            Should the texture be rotated automatically?
     * @param fixedSpeed
     *            If this is set to true, the speed will be fixed. (Default
     *            value = false = dynamic speed)
     * @return
     */
    public MoveArcAdapter(Vector2 origin, float angle, boolean rotateTexture, boolean variableSpeed) {
        this(origin, angle, rotateTexture, variableSpeed, new Vector2(1f, 1f), new Vector2());
    }

    /**
     * This {@link MovementHandler} tries to move an event by the given origin
     * and angle. The move waits if a blocking event exists on it's way.<br>
     * It also supports rotating the texture of the event automatically.<br>
     * After succeeding the switch finished is set to true.
     * 
     * @param origin
     *            The origin of the cycle
     * @param angle
     *            The angle in degrees or +-0 if you want to loop forever. If
     *            the angle is negative, the move will be clockwise.
     * @param rotateTexture
     *            Should the texture be rotated automatically?
     * @param fixedSpeed
     *            If this is set to true, the speed will be fixed. (Default
     *            value = false = dynamic speed)
     * @param stretch
     *            Stretch the curve in x,y direction by the given relative
     *            amount. (Default value = 1f = no stretching)
     * @param drift
     *            Specifies a drift into direction x,y (positive/negative
     *            allowed).<br>
     *            x-drift will produce a vertical helix. y-drift will produce a
     *            horizontal helix. (Default value = 0f = no drift)
     * @return
     */
    public MoveArcAdapter(Vector2 origin, float angle, boolean rotateTexture, boolean variableSpeed,
            Vector2 stretch, Vector2 drift) {
        this.originReset = origin;
        this.origin = origin.cpy();
        this.angle = angle * MathUtils.degreesToRadians;
        this.rotateTexture = rotateTexture;
        this.stretch = stretch;
        this.fixedSpeed = variableSpeed;
        this.drift = drift == null ? new Vector2() : drift;
    }

    @Override
    public void tryMove(Movable event, float deltaTime, EventTrigger eventTrigger) {
        float absAngle = Math.abs(angle);
        if ((absAngle > 0f && entireMoveArc >= absAngle) || finished) {
            if (entireMoveArc == 0f)
                event.stop();
            finished = true;
        } else {
            float radius = this.radius;
            // computes distance between origin and event only once.
            if (Float.isNaN(radius)) {
                float evX = event.getX() - origin.x;
                float evY = event.getY() - origin.y;
                if ((radius = (float) Math.sqrt(Math.pow(evX / stretch.x, 2) + Math.pow(evY / stretch.y, 2))) == 0f)
                    return;
                this.radius = radius;
                double stretchCorrection = Math.sqrt(stretch.x * stretch.x + stretch.y * stretch.y);
                this.arcCorrection = (float) (1. / (radius * stretchCorrection));
                this.correctX = (float) (stretch.x / stretchCorrection);
                this.correctY = (float) (stretch.y / stretchCorrection);
                this.startArc = (float) Math.asin(evY / radius);
                if (evX < 0f)
                    startArc = PI - startArc;
                else if (startArc < 0f)
                    startArc = PI2 + startArc;
            }
            float arc = event.getMoveSpeed().computeStretch(deltaTime) * arcCorrection;
            lastMoveArc = arc;
            // greater zero or positive zero
            if (angle > 0f || Float.floatToRawIntBits(angle) == 0f) {
                arc += startArc + entireMoveArc;
                arc %= PI2;
            } else {
                arc = startArc - entireMoveArc - arc;
                arc %= PI2;
                if (arc < 0f)
                    arc += PI2;
            }
            float x = MathUtils.cos(arc);
            float y = MathUtils.sin(arc);
            event.offerMoveTo(origin.x + x * radius * stretch.x, origin.y + y * radius * stretch.y);
            if (fixedSpeed) {
                // it's only an approximation but it should be good enough
                // for most cases
                lastMoveArc *= Math.cbrt(x * x * correctX + y * y * correctY);
            }
            entireMoveArc += lastMoveArc;
            if (angle == 0f)
                entireMoveArc %= PI2;
            origin.x += drift.x * deltaTime;
            origin.y += drift.y * deltaTime;
            if (rotateTexture && event instanceof EventObject) {
                EventObject ev = (EventObject) event;
                ev.rotation = MathUtils.atan2(y * stretch.x, x * stretch.y) * MathUtils.radiansToDegrees;
                // lower zero or negative zero
                if (angle < 0f || (angle == 0f && Float.floatToRawIntBits(angle) != 0f)) {
                    ev.rotation = ev.rotation - 180f;
                }
            }
        }
    }

    @Override
    public void moveBlocked(Movable event) {
        entireMoveArc -= lastMoveArc;
        event.stop();
    }

    @Override
    public void reset() {
        super.reset();
        origin.set(originReset);
        radius = Float.NaN;
        startArc = Float.NaN;
        entireMoveArc = 0f;
        lastMoveArc = 0f;
    }
}