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

org.robokind.api.animation.compiled.CompiledPath Maven / Gradle / Ivy

/*
 * Copyright 2011 Hanson Robokind LLC.
 *
 * 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 org.robokind.api.animation.compiled;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * An ArrayList of servo positions.
 * The positions are spaced by myStepLength, in milliseconds.
 *
 * @author Matthew Stevenson 
 */
public class CompiledPath extends ArrayList {
    private Long myStepLength;
    private Long myStartTime;

    /**
     * Constructs an empty path list with give start time and step length.
     *
     * @param start offset for each position
     * @param stepLength milliseconds between positions
     */
    public CompiledPath(long start, long stepLength){
        myStartTime = start;
        myStepLength = stepLength;
    }
    /**
     * Returns the time (from 0) the last position is expected to be consumed.
     * This is equal to the startTime + stepLength*positions.
     *
     * @return time path is expected to end
     */
    public Long getEndTime() {
        return myStartTime + size()*myStepLength;
    }
    /**
     * Returns the start offset time.  The time position i in the path should be
     * used is myStartTime + myStepLength*i.
     *
     * @return the start offset time for each position
     */
    public Long getStartTime() {
        return myStartTime;
    }
    /**
     * Returns the time between each position in millisecond.  The time position i in the path should be
     * used is myStartTime + myStepLength*i.
     *
     * @return the time between each position in milliseconds
     */
    public Long getStepLength() {
        return myStepLength;
    }

    /**
     * Returns the position for a given time.  The time position i in the path should be
     * used is myStartTime + myStepLength*i.
     *
     * @param time the time of the position to be retrieved.
     * @return the position for the given time.  If the time is outside the
     * bounds of the path -1 is returned
     */
    public double getStep(long time){
        int i = (int)Math.floor((double)(time-myStartTime)/myStepLength);
        if(i < 0 || i >= size()){
            return -1;
        }
        return get(i);
    }

    /**
     * Returns the estimated path position for the given time.
     * @param time time for which to estimate
     * @return estimated position at time
     */
    public double estimatePosition(long time){
        double t1 = (double)(time-myStartTime)/(double)myStepLength;
        int min = (int)Math.floor(t1); //step on or before time
        int max = min+1; //step after time
        int len = size();
        if(min < 0){
            if(max == 0){
                return get(0);
            }
            return -1;
        }else if(max == len){
            return get(len-1);
        }else if(max == len+1){
            return get(len-1);
        }else if(max > len){
            return -1;
        }
        double p1 = get(min);
        double p2 = get(max);
        if(p1 < 0 || p2 < 0){
            if(p1 > 0){
                return p1;
            }else if(p2 > 0){
                return p2;
            }
            return -1;
        }
        double pX = (t1 - min);
        return pX*(p2 - p1) + p1;
    }

    /**
     * Checks if the given values correspond to this CompiledPath.
     *
     * @param start offset for each position
     * @param end bound on the time of the last position
     * @param step expected position spacing
     * @return true if the given values match this path, otherwise false
     */
    public boolean matches(long start, long end, long step){
        return (start==myStartTime && end==getEndTime() && step==myStepLength);
    }

    /**
     * Creates a deep copy of the CompiledPath.
     * @return a deep copy of the CompiledPath
     */
    @Override
    public CompiledPath clone(){
        CompiledPath cp = new CompiledPath(myStartTime, myStepLength);
        for(double d : this){
            cp.add(d);
        }
        return cp;
    }

    /**
     * Returns a composite of the paths in the given list.
     * If two or more paths overlap for a give time, the path with the lowest
     * index is used.
     *
     * @param paths list of paths to add
     * @return new Compiled path containing positions from given paths
     */
    public static CompiledPath combine(List paths){
        if(paths.isEmpty()){
            return null;
        }
        int len = paths.size();
        if(len == 1){
            return paths.get(0).clone();
        }
        long step = paths.get(0).myStepLength;
        long start = Long.MAX_VALUE;
        long end = Long.MIN_VALUE;
        for(CompiledPath p : paths){
            if(p.myStepLength != step){
                throw new IllegalArgumentException("Cannot add CompiledPaths with different step lengths.");
            }if(p.myStartTime < start){
                start = p.myStartTime;
            }if(p.getEndTime() > end){
                end = p.getEndTime();
            }
        }
        CompiledPath cp = new CompiledPath(start, step);
        for(long t=start; t<=end; t+=step){
            double d = -1;
            for(CompiledPath p : paths){
                if(d != -1){
                    break;
                }
                d = p.getStep(t);
            }
            cp.add(d);
        }
        return cp;
    }

    /**
     * Returns a copy of this path, using a new time window.
     * If there is no change in times it will return itself.
     * Otherwise, a new CompiledPath is created with the given start time.
     * If the new start time is earlier or end time later, then those positions
     * are filled with -1.  If the new start time is later or end time earlier,
     * those positions are truncated.
     *
     * @param start the new start time
     * @param end the new stop time
     * @return if there is no change in times returns itself, otherwise returns
     * a new CompiledPath consisting of values from the existing path using the
     * new times
     */
    public CompiledPath setTimes(long start, long end){
        if(start == myStartTime && end == getEndTime()){
            return this;
        }
        if(start > end){
            return new CompiledPath(start, myStepLength);
        }
        List vals = new ArrayList((int)((end-start)/myStepLength)+1);
        for(long t=start;t<=end; t+=myStepLength){
            double pos = getStep(t);
            vals.add(pos);
        }
        CompiledPath p = new CompiledPath(start, myStepLength);
        p.addAll(vals);
        return p;
    }
    /**
     * Creates a CompiledPath from the interpolated points.
     *
     * @param start path start time
     * @param end path end time
     * @param interpolated points to compile
     * @param stepLength milliseconds between positions multiples of stepLength
     * @return CompiledPath from interpolated points
     */
    public static CompiledPath compilePath(long start, long end, List interpolated, long stepLength) {
        if(interpolated == null || interpolated.isEmpty()){
            return null;
        }
        //end += stepLength;
        Iterator it = interpolated.iterator();
        Point2D prev = it.next(), next = prev;
        CompiledPath path = new CompiledPath(start, stepLength);
        for (long t=start; t <= end; t += stepLength) {
            if(t < prev.getX()){ //fill preceeding play time with -1
                if(t+stepLength > prev.getX()){
                    path.add(prev.getY());
                }else{
                    path.add(-1.0);
                }
                continue;
            }
            while (t >= next.getX()) {
                if (!it.hasNext()) {
                    path.add(next.getY());
                    for(t += stepLength; t <=end; t += stepLength){
                        path.add(-1.0);    //Fill the rest of the play time with -1
                    }
                    return path;
                }
                prev = next;
                next = it.next();
            }
            if(next.getY() == -1.0 || prev.getY() == -1.0){
                if(next.getY() > 0){
                    path.add(next.getY());
                }else if(prev.getY() > 0){
                    path.add(prev.getY());
                }else{
                    path.add(-1.0);
                }
            }else{
                double pX = (t - prev.getX())/(next.getX() - prev.getX());
                path.add(pX*(next.getY() - prev.getY()) + prev.getY());
            }
        }
        return path;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy