Package com.ardor3d.scenegraph.controller.interpolation

Source Code of com.ardor3d.scenegraph.controller.interpolation.CurveInterpolationController

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.scenegraph.controller.interpolation;

import java.util.logging.Logger;

import com.ardor3d.math.Vector3;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.scenegraph.Spatial;
import com.ardor3d.scenegraph.controller.ComplexSpatialController;
import com.ardor3d.spline.ArcLengthTable;
import com.ardor3d.spline.Curve;
import com.ardor3d.spline.Spline;

/**
* CurveInterpolationController class interpolates a {@link Spatial}s vectors using a {@link Curve}.
* <p>
* This class is stateful and can not be used by more than one controller at a time.
* </p>
*/
public class CurveInterpolationController extends Vector3InterpolationController {

    /** Serial UID */
    private static final long serialVersionUID = 1L;

    /** Classes logger */
    private static final Logger LOGGER = Logger.getLogger(CurveInterpolationController.class.getName());

    /** @see #setCurve(Curve) */
    private Curve _curve;

    /** Look up table of arc lengths (used for constant speed) */
    private ArcLengthTable _arcLengths;

    /** Look up table of arc lengths (used for constant speed when travelling in reverse (repeat type = cycle)) */
    private ArcLengthTable _arcLengthsReverse;

    /** Distance travelled between control points (used for constant speed) */
    private double _distance = 0.0;

    /*
     * Overrides to handle constant speed updating
     */
    @Override
    protected double incrementDelta(final double by) {
        double delta;

        /*
         * If constant speed we need to also check we aren't clamped at max index before we call lookup in the arc
         * length table because there would be no point
         */
        if (isConstantSpeed()) {
            _distance += by;

            if (isCycleForward()) {
                assert (null != _arcLengths) : "You need to call generateArcLengths(x, false) to create the required arc length table!";

                delta = _arcLengths.getDelta(getIndex(), _distance);
            } else {
                assert (null != _arcLengthsReverse) : "You need to call generateArcLengths(x, true) to create the required reverse arc length table!";

                delta = _arcLengthsReverse.getDelta(getIndex(), _distance);
            }

            setDelta(delta);
        } else {
            delta = super.incrementDelta(by);
        }

        return delta;
    }

    /*
     * Overrides to handle updating the travelled distance correctly (used during constant speed mode)
     */
    @Override
    protected int decrementIndex() {
        assert (null != _arcLengthsReverse) : "You need to call generateArcLengths() to create the required arc length tables!";

        _distance -= _arcLengthsReverse.getLength(getIndex());

        return super.decrementIndex();
    }

    /*
     * Overrides to handle updating the travelled distance correctly (used during constant speed mode)
     */
    @Override
    protected int incrementIndex() {
        assert (null != _arcLengths) : "You need to call generateArcLengths() to create the required arc length tables!";

        _distance -= _arcLengths.getLength(getIndex());

        return super.incrementIndex();
    }

    @Override
    protected Vector3 interpolateVectors(final ReadOnlyVector3 from, final ReadOnlyVector3 to, final double delta,
            final Vector3 target) {

        assert (null != from) : "parameter 'from' can not be null";
        assert (null != to) : "parameter 'to' can not be null";

        final ReadOnlyVector3 p0 = getControlPointStart();
        final ReadOnlyVector3 p3 = getCotnrolPointEnd();

        final Spline spline = getCurve().getSpline();

        return spline.interpolate(p0, from, to, p3, delta, target);
    }

    /**
     * @return The initial control point, will not be <code>null</code>.
     */
    protected ReadOnlyVector3 getControlPointStart() {
        ReadOnlyVector3 control = null;

        final int fromIndex = getIndex();

        switch (getRepeatType()) {
            case CLAMP:
                control = getControls().get(fromIndex - 1);
                break;

            case CYCLE:
                if (isCycleForward()) {
                    control = getControls().get(fromIndex - 1);
                } else {
                    control = getControls().get(fromIndex + 1);
                }
                break;

            case WRAP:
                control = getControls().get(fromIndex - 1);
                break;
        }

        return control;
    }

    /**
     * @return The final control point, will not be <code>null</code>.
     */
    protected ReadOnlyVector3 getCotnrolPointEnd() {
        ReadOnlyVector3 control = null;

        final int toIndex = getIndex();

        switch (getRepeatType()) {
            case CLAMP:
                control = getControls().get(toIndex + 2);
                break;

            case CYCLE:
                if (isCycleForward()) {
                    control = getControls().get(toIndex + 2);
                } else {
                    control = getControls().get(toIndex - 2);
                }
                break;

            case WRAP:
                control = getControls().get(toIndex + 2);
                break;
        }

        return control;
    }

    /**
     * Setting a new curve will automatically update the control points.
     *
     * @param curve
     *            The new curve to follow, can not be <code>null</code>.
     * @see #getCurve()
     */
    public void setCurve(final Curve curve) {
        if (null == curve) {
            throw new IllegalArgumentException("curve can not be null!");
        }

        _curve = curve;

        setControls(_curve.getControlPoints());

        if (isConstantSpeed()) {
            LOGGER
                    .warning("Constant speed is set to true, you will need to call generateArcLengths() to avoid errors during update.");
        }
    }

    /**
     * @return The curve being followed, will not <code>null</code>.
     * @see #setCurve(Curve)
     */
    public Curve getCurve() {
        assert (null != _curve) : "curve was null, it must be set before use!";

        return _curve;
    }

    /*
     * Overrides to provide a warning about generating arc lengths if constant speed is set to true and they haven't
     * been generated yet.
     */
    @Override
    public void setConstantSpeed(final boolean constantSpeed) {
        super.setConstantSpeed(constantSpeed);

        if (isConstantSpeed() && null == _arcLengths) {
            LOGGER
                    .warning("Constant speed was set to true, you will need to call generateArcLengths() to avoid errors during update.");
        }
    }

    /**
     * Generates the arc lengths, generates the reverse table if the
     * {@link #setRepeatType(com.ardor3d.scenegraph.controller.ComplexSpatialController.RepeatType) repeat type} is set
     * to {@link ComplexSpatialController.RepeatType#CYCLE cycle}
     *
     * @param step
     *            'See Also:' method for more info.
     * @see #generateArcLengths(int, boolean)
     */
    public void generateArcLengths(final int step) {
        generateArcLengths(step, RepeatType.CYCLE.equals(getRepeatType()));
    }

    /**
     * Generates arc lengths which are required if you wish to have {@link #setConstantSpeed(boolean) constant speed}
     * interpolation.
     *
     * @param step
     *            'See Also:' method for more info.
     * @param reverse
     *            <code>true</code> to also generate a reverse look up table. This is only required if you plan to use
     *            the {@link ComplexSpatialController.RepeatType#CYCLE} repeat type.
     * @see ArcLengthTable#generate(int, boolean)
     */
    public void generateArcLengths(final int step, final boolean reverse) {
        _arcLengths = new ArcLengthTable(getCurve());
        _arcLengths.generate(step, false);

        if (reverse) {
            _arcLengthsReverse = new ArcLengthTable(getCurve());
            _arcLengthsReverse.generate(step, true);
        }
    }

    /**
     * Since splines require at least 4 points to interpolate correctly the default maximum value is overridden to 1
     * less than normal.
     */
    @Override
    protected int getMaximumIndex() {
        return super.getMaximumIndex() - 1;
    }

    /**
     * Since splines require at least 4 points to interpolate correctly the default minimum value is overridden to 1
     * more than normal.
     */
    @Override
    protected int getMinimumIndex() {
        return super.getMinimumIndex() + 1;
    }

    /*
     * Overrides to also reset the distance.
     */
    @Override
    public void reset() {
        super.reset();

        _distance = 0.0;
    }

}
TOP

Related Classes of com.ardor3d.scenegraph.controller.interpolation.CurveInterpolationController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.