/* * $RCSfile: TransformSegment.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; import com.sun.perseus.platform.MathSupport; /** * Represents a segment in an animateTransform. * * * @version $Id: TransformSegment.java,v 1.3 2006/06/29 10:47:36 ln156897 Exp $ */ public class TransformSegment implements Segment { /** * Holds the minimal information for the segment's start. * The interpretation depends on the segment type. * TYPE_TRANSLATE: start[0] = tx, start[1] = ty * TYPE_SCALE: start[0] = sx, start[1] = sy * TYPE_ROTATE: start[0] = rotate, start[1] = cx, start[2] = cy; * TYPE_SKEW_X: start[0] = skewX * TYPE_SKEW_Y: start[0] = skewY */ float[] start; /** * Holds the minimal information for the segment's end. * @see #start */ float[] end; /** * The segment type. */ int type; /** * @return the start value. */ public Object[] getStart() { float[][] v = new float[6][1]; compute(0, v); return v; } /** * @return set end value. */ public Object[] getEnd() { float[][] v = new float[6][1]; compute(1, v); return v; } /** * Sets the start value to its notion of 'zero'. * For a FloatSegment, a 'zero' start means zero all all * dimensions for all components. */ public void setZeroStart() { switch (type) { case AnimateTransform.TYPE_TRANSLATE: start[0] = 0; start[1] = 0; break; case AnimateTransform.TYPE_SCALE: start[0] = 1; start[1] = 1; break; case AnimateTransform.TYPE_ROTATE: start[0] = 0; break; case AnimateTransform.TYPE_SKEW_X: case AnimateTransform.TYPE_SKEW_Y: default: start[0] = 0; break; } } /** * Sets the start value. * * @param newStart the new segment start value. */ public void setStart(Object[] newStart) { float[][] ns = (float[][]) newStart; switch (type) { case AnimateTransform.TYPE_TRANSLATE: start[0] = ns[4][0]; start[1] = ns[5][0]; break; case AnimateTransform.TYPE_SCALE: start[0] = ns[0][0]; start[1] = ns[3][0]; break; case AnimateTransform.TYPE_ROTATE: // This assumes that the input matrix is a simple rotate. // We are not trying to extract the rotation from a complex // matrix. start[0] = MathSupport.atan2(ns[1][0], ns[0][0]); break; case AnimateTransform.TYPE_SKEW_X: start[0] = MathSupport.atan(ns[2][0]); break; case AnimateTransform.TYPE_SKEW_Y: default: start[0] = MathSupport.atan(ns[1][0]); break; } } /** * 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) { TransformSegment mseg = (TransformSegment) seg; if (mseg.end.length != end.length) { throw new IllegalArgumentException(); } end = mseg.end; } /** * 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) { float[][] ns = (float[][]) by; switch (type) { case AnimateTransform.TYPE_TRANSLATE: end[0] += ns[4][0]; end[1] += ns[5][0]; break; case AnimateTransform.TYPE_SCALE: end[0] += ns[0][0]; end[1] += ns[3][0]; break; case AnimateTransform.TYPE_ROTATE: // Here, we need to add a value that has the form // [1 0 x] [cos -sin 0] [1 0 -x] [cos -sin x-x.cos+y.sin] // [0 1 y] [sin cos 0] [0 1 -y] = [sin cos y-x.sin-y.cos] // [0 0 1] [0 0 1] [0 0 1] [0 0 1 ] // // We have: // x.(1 - cos) + y.sin = a // y.(1 - cos) - x.sin = b // // So: // I : x.(1 - cos).sin + y.sin.sin = a.sin // II : y.(1 - cos).(1 - cos) - x.sin.(1 - cos) = b.(1 - cos) // // Doing I + II yields: // // y.sin.sin + y.(1 - cos).(1 - cos) = a.sin + b.(1 - cos) // // y.(sin.sin + 1 - 2.cos + cos.cos) = a.sin + b.(1 - cos) // // y.2.(1 - cos) = a.sin + b.(1 - cos) // // First, extract the rotation angle float cos = ns[0][0]; float sin = ns[1][0]; end[0] += MathSupport.atan2(ns[1][0], ns[0][0]); // Now extract the translation components. float a = ns[4][0]; float b = ns[5][0]; if (a != 0 && b != 0) { // There is a translation component float div = 2 * (1 - cos); if (div != 0) { float y = (a * sin + b * (1 - cos)) / div; float x = (a - y * sin) / (1 - cos); end[1] += x; end[2] += y; } // else div = 0, there is no rotation, so we do not further // process the translation component. } break; case AnimateTransform.TYPE_SKEW_X: end[0] += MathSupport.atan(ns[2][0]); break; case AnimateTransform.TYPE_SKEW_Y: default: end[0] += MathSupport.atan(ns[1][0]); break; } } /** * @return true if this segment type supports addition. false * otherwise. */ public boolean isAdditive() { return true; } /** * @return the length of the segment */ public float getLength() { float length = 0; switch (type) { case AnimateTransform.TYPE_TRANSLATE: if (end[0] > start[0]) { length += end[0] - start[0]; } else { length += start[0] - end[0]; } if (end[1] > start[1]) { length += end[1] - start[1]; } else { length += start[1] - end[1]; } return length; case AnimateTransform.TYPE_SCALE: if (end[0] > start[0]) { length += end[0] - start[0]; } else { length += start[0] - end[0]; } if (end[1] > start[1]) { length += end[1] - start[1]; } else { length += start[1] - end[1]; } return length; case AnimateTransform.TYPE_ROTATE: if (end[0] > start[0]) { length += end[0] - start[0]; } else { length += start[0] - end[0]; } if (end[1] > start[1]) { length += end[1] - start[1]; } else { length += start[1] - end[1]; } if (end[2] > start[2]) { length += end[2] - start[2]; } else { length += start[2] - end[2]; } return length; case AnimateTransform.TYPE_SKEW_X: if (end[0] > start[0]) { length += end[0] - start[0]; } else { length += start[0] - end[0]; } return length; case AnimateTransform.TYPE_SKEW_Y: default: if (end[0] > start[0]) { length += end[0] - start[0]; } else { length += start[0] - end[0]; } return length; } } /** * Computes an interpolated value for the given penetration in the * segment. Note that the start and end segment values must be set * <em>before</em> calling this method. Otherwise, a NullPointerException * is thrown. * * @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 Object[] compute(final float p, final float[][] w) { switch (type) { case AnimateTransform.TYPE_TRANSLATE: w[0][0] = 1; w[1][0] = 0; w[2][0] = 0; w[3][0] = 1; w[4][0] = (1 - p) * start[0] + p * end[0]; w[5][0] = (1 - p) * start[1] + p * end[1]; break; case AnimateTransform.TYPE_SCALE: w[0][0] = (1 - p) * start[0] + p * end[0]; w[1][0] = 0; w[2][0] = 0; w[3][0] = (1 - p) * start[1] + p * end[1]; w[4][0] = 0; w[5][0] = 0; break; case AnimateTransform.TYPE_ROTATE: { float x = (1 - p) * start[1] + p * end[1]; float y = (1 - p) * start[2] + p * end[2]; float theta = (1 - p) * start[0] + p * end[0]; float sin = MathSupport.sin(theta); float cos = MathSupport.cos(theta); w[0][0] = cos; w[1][0] = sin; w[2][0] = -sin; w[3][0] = cos; w[4][0] = (x - x * cos + y * sin); w[5][0] = (y - x * sin - y * cos); } break; case AnimateTransform.TYPE_SKEW_X: { float theta = (1 - p) * start[0] + p * end[0]; w[0][0] = 1; w[1][0] = 0; w[2][0] = MathSupport.tan(theta); w[3][0] = 1; w[4][0] = 0; w[5][0] = 0; } break; case AnimateTransform.TYPE_SKEW_Y: default: { float theta = (1 - p) * start[0] + p * end[0]; w[0][0] = 1; w[1][0] = MathSupport.tan(theta); w[2][0] = 0; w[3][0] = 1; w[4][0] = 0; w[5][0] = 0; } break; } return w; } /** * 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() { } }