/* * $RCSfile: CompositeMotionSegment.java,v $ * * Copyright 1990-2009 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 TraitAnimationNode this segment is part of. */ public void collapse(final Segment seg, final TraitAnimationNode 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; } }