/* * $RCSfile: MotionTraitAnim.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.util.SVGConstants; import org.w3c.dom.DOMException; import java.util.Vector; import com.sun.perseus.j2d.Path; /** * @version $Id: MotionTraitAnim.java,v 1.4 2006/06/29 10:47:33 ln156897 Exp $ */ class MotionTraitAnim extends TransformTraitAnim { /** * Constructs a new TransformTraitAnim for a given ElementNode trait. * * @param targetElement the ElementNode whose trait is animated. * @param targetTrait the name of the animated trait. */ public MotionTraitAnim(final ElementNode targetElement, final String traitName) { super(targetElement, traitName); } /** * Converts the input values set to a RefValues object. For * a MotionTraitAnim, the input values may be the to/from/by/values values * or the path attribute value. * * @param anim the <code>TraitAnimationNode</code> for which the values should be * converted. * @param values a semi-colon seperated list of values which need to be * validated. * @param reqTraitNamespace the namespace of the trait which has the values * value on the requesting element. * @param reqTraitName the name of the trait which has the values value on * the requesting element. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is incompatible with the given trait. * @throws NullPointerException if values is null. */ RefValues toRefValues(final TraitAnimationNode anim, String[] values, final String reqTraitNamespace, final String reqTraitName) throws DOMException { AnimateMotion motion = (AnimateMotion) anim; // The interpretation of values depends on the attribute type. MotionRefValues refValues = new MotionRefValues(motion); if (values.length < 1) { throw new IllegalArgumentException(); } if (!SVGConstants.SVG_PATH_ATTRIBUTE.equals(reqTraitName) && !SVGConstants.SVG_D_ATTRIBUTE.equals(reqTraitName)) { // // We are dealing with a to/from/by or values attribute // if (values.length == 1) { String[] tmpValues = new String[2]; tmpValues[0] = values[0]; tmpValues[1] = values[0]; values = tmpValues; } int nSegments = values.length - 1; refValues.segments = new MotionSegment[nSegments]; // Build the first segment. float[] startPt = new float[2]; float[] endPt = new float[2]; validateCoordinate(anim, reqTraitNamespace, reqTraitName, values[0], startPt); validateCoordinate(anim, reqTraitNamespace, reqTraitName, values[1], endPt); refValues.segments[0] = new LeafMotionSegment(startPt[0], startPt[1], endPt[0], endPt[1], motion); for (int i = 1; i < nSegments; i++) { validateCoordinate(anim, reqTraitNamespace, reqTraitNamespace, values[i + 1], endPt); refValues.segments[i] = new LeafMotionSegment ((LeafMotionSegment) refValues.segments[i - 1], endPt[0], endPt[1], motion); } } else { // // We are dealing with the path attribute on animateMotion // or with the d attribute on a path element referenced from // an mpath element. // // a) convert the path attribute to a Path Path path = anim.parsePathTrait(anim.traitName, values[0]); // b) turn the path into a set of segments after linear // approximation. int type = 0; float[] coords = new float[6]; float[] curPos = new float[2]; float[] endPos = new float[2]; float[][] curve = new float[4][2]; float[] lastMove = new float[2]; Vector segments = new Vector(); Vector curves = new Vector(); LeafMotionSegment prevSegment = null; int nSegments = path.getNumberOfSegments(); for (int i = 0; i < nSegments; i++) { type = path.getSegment(i); switch (type) { case Path.MOVE_TO: curPos[0] = path.getSegmentParam(i, 0); curPos[1] = path.getSegmentParam(i, 1); lastMove[0] = curPos[0]; lastMove[1] = curPos[1]; break; case Path.LINE_TO: if (prevSegment == null) { prevSegment = new LeafMotionSegment( curPos[0], curPos[1], path.getSegmentParam(i, 0), path.getSegmentParam(i, 1), motion); } else { prevSegment = new LeafMotionSegment( prevSegment, path.getSegmentParam(i, 0), path.getSegmentParam(i, 1), motion); } segments.addElement(prevSegment); curPos[0] = path.getSegmentParam(i, 0); curPos[1] = path.getSegmentParam(i, 1); break; case Path.QUAD_TO: // First, linearize the curve. // This is an overkill because we use the same code for // quad curves as for cubic curves. curve[0][0] = curPos[0]; curve[0][1] = curPos[1]; curve[1][0] = path.getSegmentParam(i, 0); curve[1][1] = path.getSegmentParam(i, 1); curve[2][0] = path.getSegmentParam(i, 0); curve[2][1] = path.getSegmentParam(i, 1); curve[3][0] = path.getSegmentParam(i, 2); curve[3][1] = path.getSegmentParam(i, 3); prevSegment = addCubicSegment(motion, curve, segments, curves, prevSegment); curPos[0] = path.getSegmentParam(i, 2); curPos[1] = path.getSegmentParam(i, 3); break; case Path.CURVE_TO: curve[0][0] = curPos[0]; curve[0][1] = curPos[1]; curve[1][0] = path.getSegmentParam(i, 0); curve[1][1] = path.getSegmentParam(i, 1); curve[2][0] = path.getSegmentParam(i, 2); curve[2][1] = path.getSegmentParam(i, 3); curve[3][0] = path.getSegmentParam(i, 4); curve[3][1] = path.getSegmentParam(i, 5); prevSegment = addCubicSegment(motion, curve, segments, curves, prevSegment); curPos[0] = path.getSegmentParam(i, 4); curPos[1] = path.getSegmentParam(i, 5); break; case Path.CLOSE: default: if (prevSegment == null) { prevSegment = new LeafMotionSegment(curPos[0], curPos[1], lastMove[0], lastMove[1], motion); } else { prevSegment = new LeafMotionSegment(prevSegment, lastMove[0], lastMove[1], motion); } segments.addElement(prevSegment); curPos[0] = lastMove[0]; curPos[1] = lastMove[1]; break; } } if (segments.size() == 0) { // This is a degenerate case, for a path with only a moveto or // empty. segments.addElement(new LeafMotionSegment(curPos[0], curPos[1], curPos[0], curPos[1], motion)); } refValues.segments = new MotionSegment[segments.size()]; segments.copyInto(refValues.segments); } return refValues; } /** * Adds the input curve as a CompositeSegment to the segments vector. * * @param motion the AnimateMotion element for which the curve is added. * @param curve a float array with the curve definition. * @param segments the vector holding all the animation segments. * @param motionCoords a working vector where coordinates for the various * linear approximations can be stored. * @param prevSegment the previous LeafMotionSegment. May be null. * @return the last LeafMotionSegment added to into the segments vector. */ LeafMotionSegment addCubicSegment(final AnimateMotion motion, final float[][] curve, final Vector segments, final Vector motionCoords, LeafMotionSegment prevSegment) { motionCoords.removeAllElements(); motionCoords.addElement(new float[] { curve[0][0], curve[0][1] }); // Adds the current position. AbstractAnimate.toRefSpline(curve, motionCoords); int nPoints = motionCoords.size(); float[] curPos = (float[]) motionCoords.elementAt(0); LeafMotionSegment[] subSegments = new LeafMotionSegment[nPoints - 1]; if (nPoints > 1) { float[] endPos = (float[]) motionCoords.elementAt(1); if (prevSegment == null) { subSegments[0] = new LeafMotionSegment(curPos[0], curPos[1], endPos[0], endPos[1], motion); } else { subSegments[0] = new LeafMotionSegment(prevSegment, endPos[0], endPos[1], motion); } curPos = endPos; prevSegment = subSegments[0]; } for (int i = 2; i < nPoints; i++) { float[] endPos = (float[]) motionCoords.elementAt(i); subSegments[i - 1] = new LeafMotionSegment(prevSegment, endPos[0], endPos[1], motion); curPos = endPos; prevSegment = subSegments[i - 1]; } CompositeMotionSegment cSeg = new CompositeMotionSegment(); cSeg.segments = subSegments; segments.addElement(cSeg); return prevSegment; } /** * Parses a coordinate pair. * * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is incompatible with the given trait. */ public void validateCoordinate(final TraitAnimationNode anim, final String traitNamespace, final String traitName, final String value, final float[] pt) throws DOMException { float[] v = anim.parseFloatArrayTrait(traitName, value); if (v.length < 1 || v.length > 2) { throw anim.illegalTraitValue(traitName, value); } // x pt[0] = v[0]; // translate y if (v.length > 1) { pt[1] = v[1]; } else { pt[1] = v[0]; } } }