FileDocCategorySizeDatePackage
CompositeMotionSegment.javaAPI DocphoneME MR2 API (J2ME)6619Wed May 02 18:00:36 BST 2007com.sun.perseus.model

CompositeMotionSegment.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program 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
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.perseus.model;

/**
 * A composite implementation of the <code>Segment</code> interface.
 * This is used in AnimateMotion to handle linearization of 
 * quadratic and cubic segments.
 *
 * @version $Id: CompositeMotionSegment.java,v 1.3 2006/04/21 06:36:43 st125089 Exp $
 */
class CompositeMotionSegment implements MotionSegment {
    /**
     * The list of segment components.
     */
    MotionSegment[] segments;

    /**
     * The cached segment length.
     */
    float length;

    /**
     * The cached, normalized segment length
     */
    float[] nSegLength;

    /**
     * @return the start value.
     */
    public Object[] getStart() {
        return segments[0].getStart();
    }

    /**
     * @return set end value.
     */
    public Object[] getEnd() {
        return segments[segments.length - 1].getEnd();
    }

    /**
     * Computes an interpolated value for the given penetration in the 
     * segment.
     *
     * @param p the segment penetration. Should be in the [0, 1] range.
     * @param w array where the computed value should be stored.
     * @return the interpolated value.
     */
    public void compute(float p, final float[][] w) {
        // First, identify the child MotionSegment at the desired 
        // normalized distance.
        int si = segments.length - 1;
        float prevSegLength = 0;

        if (p < 1) {
            for (si = 0; si < segments.length; si++) {
                if (p < nSegLength[si]) {
                    break;
                }
                prevSegLength = nSegLength[si];
            }
        } else {
            if (si > 0) {
                prevSegLength = nSegLength[si - 1];
            }
        }

        // The sub-segment is at index si. Now, we need to 
        // compute the penetration in that segment.
        if (nSegLength[si] > prevSegLength) {
            p = (p - prevSegLength) / (nSegLength[si] - prevSegLength);
        } else {
            p = 1;
        }

        segments[si].compute(p, w);
    }

    /**
     * Computes this segment's length
     */
    public float getLength() {
        return length;
    }

    /**
     * Collapses this segment with the one passed as a parameter.
     * Note that if the input segment is not of the same class
     * as this one, an IllegalArgumentException is thrown. The 
     * method also throws an exception if the input segment's
     * end does not have the same number of components as this 
     * segment's end.
     *
     * After this method is called, this segment's end value
     * is the one of the input <code>seg</code> parameter.
     *
     * @param seg the Segment to collapse with this one.
     * @param anim the Animation this segment is part of.
     */
    public void collapse(final Segment seg, final Animation anim) {
        CompositeMotionSegment cseg = (CompositeMotionSegment) seg;
        MotionSegment[] newSegments = 
            new MotionSegment[segments.length + cseg.segments.length];

        System.arraycopy(segments, 
                         0, 
                         newSegments, 
                         0, 
                         segments.length);

        System.arraycopy(cseg.segments, 
                         0, 
                         newSegments, 
                         segments.length, 
                         cseg.segments.length);

        segments = newSegments;
    }

    /**
     * Adds the input value to this Segment's end value.
     * 
     * @param by the value to add. Throws IllegalArgumentException if this
     * Segment type is not additive or if the input value is incompatible (e.g.,
     * different number of components or different number of dimensions on a
     * component).
     */
    public void addToEnd(Object[] by) {
        segments[segments.length - 1].addToEnd(by);
    }

    /**
     * @return true if this segment type supports addition. false
     * otherwise.
     */
    public boolean isAdditive() {
        return segments[0].isAdditive();
    }

    /**
     * Sets the start value to its notion of 'zero'
     */
    public void setZeroStart() {
        segments[0].setZeroStart();
    }

    /**
     * Sets the start value. 
     *
     * @param newStart the new segment start value.
     */
    public void setStart(Object[] newStart) {
        segments[0].setStart(newStart);
    }

    /**
     * Should be called after the segment's configuration is complete
     * to give the segment's implementation a chance to initialize 
     * internal data and cache values.
     */
    public void initialize() {
        // Initialize the component segments.
        final int ns = segments.length;
        for (int si = 0; si < ns; si++) {
            segments[si].initialize();
        }

        // Now, initialize the length cache.
        length = 0;
        nSegLength = new float[ns];        
        for (int si = 0; si < segments.length; si++) {
            nSegLength[si] = segments[si].getLength();
            length += nSegLength[si];
        }
        
        // Now, initialize the normalized segment lengths array
        if (length > 0) {
            float curLength = 0;
            for (int si = 0; si < ns - 1; si++) {
                curLength += nSegLength[si];
                nSegLength[si] = curLength / length;
            }
        } else {
            for (int si = 0; si < ns - 1; si++) {
                nSegLength[si] = 0;
            }
        }
        
        // Make sure that, in all cases, the last value is 1.
        nSegLength[ns - 1] = 1;
    }

}