You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-users@xmlgraphics.apache.org by ni...@googlemail.com on 2009/08/16 19:43:52 UTC

AnimateMotion with changing path

Hello,
I want to have an object moved along a path using the animateMotion  
element. This works pretty well, but if I extend the path during animation  
the modification is not recognized and the object keeps on moving the  
original path.

Here's the code:
package scripting.java;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

import org.apache.batik.bridge.UpdateManager;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMAnimateMotionElement;
import org.apache.batik.dom.svg.SVGOMAnimatedPathData;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.dom.svg.SVGOMElement;
import org.apache.batik.dom.svg.SVGOMPathElement;
import org.apache.batik.dom.svg.SVGOMSVGElement;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.util.RunnableQueue;
import org.apache.batik.util.XMLConstants;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.svg.SVGPathSeg;
import org.w3c.dom.svg.SVGPathSegList;

/**
*
*/

/**
* @author Nicky Nubbel
*
*/
public class Test2 extends JFrame implements MouseListener {

/**
* serial version uid
*/
private static final long serialVersionUID = 1L;

/**
* @param args
*/
public static void main(String[] args) {
new Test2();
}

private JSVGCanvas canvas;
protected SVGOMDocument doc;
final DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
final String xlinkNS = XMLConstants.XLINK_NAMESPACE_URI;
protected UpdateManager updateManager;
private SVGOMElement plane;
private SVGOMPathElement path;
private SVGOMAnimateMotionElement anim;
private SVGOMSVGElement svgRoot;
protected RunnableQueue queue;

/**
* constructor
*/
public Test2() {
super("Test 2: Animation along an extensible path");
setDefaultCloseOperation(EXIT_ON_CLOSE);

canvas = new JSVGCanvas();
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);

addWindowListener(new WindowAdapter() {
public void windowOpened(WindowEvent e) {
System.out.println("window opened");
createDocument();
canvas.setDocument(doc);
}
});

canvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
System.out.println("renderering completed");

updateManager = canvas.getUpdateManager();
queue = updateManager.getUpdateRunnableQueue();
System.out.println("update manager initialized");

pack();
}
});

canvas.addMouseListener(this);

getContentPane().add(canvas);
setSize(400, 400);
setVisible(true);
}

protected void createDocument() {
System.out.println("creating doc...");

doc = (SVGOMDocument) impl.createDocument(svgNS, "svg", null);

svgRoot = (SVGOMSVGElement) doc.getDocumentElement();

svgRoot.setAttributeNS(null, "viewbox", "0 0 400 400");
// Set the width and height attributes on the root 'svg' element.
svgRoot.setAttributeNS(null, "width", "400");
svgRoot.setAttributeNS(null, "height", "400");

// Create the plane.
plane = (SVGOMElement) doc.createElementNS(svgNS, "circle");
plane.setAttributeNS(null, "id", "plane");
plane.setAttributeNS(null, "cx", "0");
plane.setAttributeNS(null, "cy", "0");
plane.setAttributeNS(null, "r", "5");
plane.setAttributeNS(null, "fill", "red");

// path
path = (SVGOMPathElement) doc.createElementNS(svgNS, "path");
path.setAttributeNS(null, "id", "path");
path.setAttributeNS(null, "fill", "none");
path.setAttributeNS(null, "stroke", "green");
path.setAttributeNS(null, "stroke-width", "3px");
path.setAttributeNS(null, "d", "M50,50 h10 v10 h10 v10 h10 v10");

// animation element
anim = (SVGOMAnimateMotionElement)  
doc.createElementNS(svgNS, "animateMotion");
anim.setAttributeNS(null, "id", "anim");
anim.setAttributeNS(null, "repeatCount", "indefinite");
anim.setAttributeNS(null, "begin", "0s");
anim.setAttributeNS(null, "dur", "3s");
anim.setAttributeNS(null, "rotate", "auto");
anim.setAttributeNS(null, "restart", "always");
anim.setAttributeNS(null, "path", path.getAttributeNS(null, "d"));


svgRoot.appendChild(path);
plane.appendChild(anim);
svgRoot.appendChild(plane);
}

@Override
public void mouseClicked(MouseEvent me) {
System.out.println("clicked");

queue.invokeLater(new Runnable() {
public void run() {
SVGOMAnimatedPathData data = path.getAnimatedPathData();
SVGPathSegList segs = data.getPathSegList();
int length = segs.getNumberOfItems();

SVGPathSeg seg;
if (length % 2 == 0) {
seg = path.createSVGPathSegLinetoVerticalRel(10);
} else {
seg = path.createSVGPathSegLinetoHorizontalRel(10);
}
segs.appendItem(seg);

//anim.putLiveAttributeValue(null, "path", data); no idea what a live attr  
is?!

anim.setAttributeNS(null, "path", path.getAttributeNS(null, "d"));

}
});
}

@Override
public void mouseEntered(MouseEvent me) {
}

@Override
public void mouseExited(MouseEvent me) {
}

@Override
public void mousePressed(MouseEvent me) {
}

@Override
public void mouseReleased(MouseEvent me) {
}
}

I actually got it working by removing the animation element from the doc  
and in turn append a copy of it:
float timeOffset = anim.getCurrentTime();
float dur = path.getTotalLength() / VELOCITY;

SVGOMAnimateMotionElement tmp = (SVGOMAnimateMotionElement)  
anim.cloneNode(true);
tmp.setAttributeNS(null, "dur", String.valueOf(dur) + "s");
plane.replaceChild(tmp, anim);
tmp.beginElementAt(-timeOffset);
anim = tmp;

Problem here is that I don't want the animation to restart, so I started  
the animation at an time offset, but this solution is not satisfactory due  
to lagging.

Another next problem would be to determine the prosition of the moving  
object and to detect a collition with an other element.

Thanks for your help.

Regards Nicky Nubbel

Re: AnimateMotion with changing path

Posted by Dominique d'Argent <ni...@googlemail.com>.
Hello again,

> Hi Nicky,
>
>
> > I want to have an object moved along a path using the animateMotion element.
> > This works pretty well, but if I extend the path during animation the
> > modification is not recognized and the object keeps on moving the original
> > path.
>
> As far as I know, animating paths in SVG requires that the number of
> segments is kept during the animation [1] (at least in SVG Tiny 1.2).
> So I imagine that Batik's animateMotion implementation can be somehow
> tied to this concept...? You may animate the path taking this into
> consideration, though. ;-)
>
> I have tried keeping the number of path segments constant, but with no
success - same result as before.

>
>
> > Here's the code:
>
> I'd suggest only pasting the relevant code snippets inline and
> attaching the full code (or posting a link to it and attach it online
> somewhere) in order to ease on reading/replying. ;-)
>
> Sorry, I'm new to this mailing list mechanism, but makes sense indeed.

>
> > I actually got it working by removing the animation element from the doc and
> > in turn append a copy of it:
>
> Yeah, that would workaround the behavior above, as the new path would
> be then used for the animateMotion.
>
> As I said, it's not the optimal solution but I keep it in mind for the case
I don't find a better one.

>
> > Another next problem would be to determine the prosition of the moving
> > object and to detect a collition with an other element.
>
> For detecting position you have several DOM methods (for computing
> distance along a path [2], getting an element's bounding box - getBBox
> [3], etc.). Also, as far as I know there's no native collision
> detection in SVG, although you may compute it by comparing bounding
> box limits (somehow fuzzy) and/or intersections points [4] (should be
> more precise but also computationally more expensive).
>
> Thanks for that.

>
>
> > Thanks for your help.
>
> For general questions on SVG (not specific to Batik), I'd also
> recommend keeping an eye/posting into SVG Developers [5] mailing list.
> Please avoid cross-posting, though, as many are watching both lists.
> ;-)
>
>
> Hope this helps,
>  Helder
>
>
> [1] http://www.w3.org/TR/SVGTiny12/paths.html#PathDataAnimation
> [2] http://www.w3.org/TR/SVG11/paths.html#DistanceAlongAPath
> [3] http://www.w3.org/TR/SVG11/ecmascript-binding.html
> [4] http://ajaxian.com/archives/intersections-and-svg-objects
> [5] http://groups.yahoo.com/group/svg-developers/
>
> Thank you, you've been a great help sofar.

I came to a new idea using multiple animateMotion elements which are
dynamically appended to the animation target element (the 'plane') and form
a animation sequence using begin="prev_animation.end".
Although I'm not sure if this is supported by batik, I thought this was a
great idea, but it does not work either.
Relevant code here:
    public void mouseClicked(MouseEvent me) {
        System.out.println("clicked");

        queue.invokeLater(new Runnable() {
            public void run() {
                SVGOMPathElement tmpPath = (SVGOMPathElement)
doc.createElementNS(svgNS, "path");

                int id = plane.getChildElementCount() + 1;
                float length = path.getTotalLength();
                SVGPoint point = path.getPointAtLength(length);
                SVGPathSegList segs = tmpPath.getPathSegList();

                segs.appendItem(path.createSVGPathSegMovetoAbs(point.getX(),
point.getY()));

                SVGPathSeg seg;
                if (id % 2 == 0) {
                    seg = path.createSVGPathSegLinetoHorizontalRel(20);
                } else {
                    seg = path.createSVGPathSegLinetoVerticalRel(20);
                }
                path.getPathSegList().appendItem(seg);
                segs.appendItem(seg);

                length = path.getTotalLength() - length;

                anim = (SVGOMAnimateMotionElement)
baseAnim.cloneNode(false);
                anim.setAttributeNS(null, "id", "anim" + id);
                anim.setAttributeNS(null, "begin", "anim" + (id - 1) +
".end");
                anim.setAttributeNS(null, "dur", (length / VELOCITY) + "s");
                anim.setAttributeNS(null, "path",
tmpPath.getAttributeNS(null, "d"));

                plane.appendChild(anim);

                //printDocument();
            }
        });
    }

Full code here: http://gist.github.com/173770

Looks good, doesn't it? But as I said, it doesn't work for some reason: If I
click the mouse button and the new animateMotion element is appended to the
target the animation stops.
I tried to replace the begin="prev_animation.end" with "10s" but the
animation freezes anyway and continues after 10 seconds on the new path
segment (actually the new path).
My function printDocument() gives me something like that:
<svg contentScriptType="text/ecmascript" width="400"
     xmlns:xlink="http://www.w3.org/1999/xlink" zoomAndPan="magnify"
     contentStyleType="text/css" height="400"
     preserveAspectRatio="xMidYMid meet" viewbox="0 0 400 400"
     xmlns="http://www.w3.org/2000/svg" version="1.0">
    <path fill="none" d="M50,50 h20 v20 h20 v20 h20 v20 h20 v20 h 20.0"
          stroke-width="3px" id="path" stroke="green"/>
    <circle fill="red" r="5" id="plane" cx="0" cy="0">
        <animateMotion dur="4.0s" rotate="auto" repeatCount="1" begin="1s"
                       path="M50,50 h20 v20 h20 v20 h20 v20 h20 v20"
                       calcMode="paced" id="anim1"/>
        <animateMotion dur="0.5s" rotate="auto" repeatCount="1"
                       begin="anim1.end" path="M 130.0 130.0 h 20.0"
                       calcMode="paced" id="anim2"/>
    </circle>
</svg>

I tried this code with opera and sqiggle and it worked perfectly. So what am
I doing wrong?

Thanks for your help

Kind regards
Nicky

Re: AnimateMotion with changing path

Posted by Helder Magalhães <he...@gmail.com>.
Hi Nicky,


> I want to have an object moved along a path using the animateMotion element.
> This works pretty well, but if I extend the path during animation the
> modification is not recognized and the object keeps on moving the original
> path.

As far as I know, animating paths in SVG requires that the number of
segments is kept during the animation [1] (at least in SVG Tiny 1.2).
So I imagine that Batik's animateMotion implementation can be somehow
tied to this concept...? You may animate the path taking this into
consideration, though. ;-)


> Here's the code:

I'd suggest only pasting the relevant code snippets inline and
attaching the full code (or posting a link to it and attach it online
somewhere) in order to ease on reading/replying. ;-)


> I actually got it working by removing the animation element from the doc and
> in turn append a copy of it:

Yeah, that would workaround the behavior above, as the new path would
be then used for the animateMotion.


> Another next problem would be to determine the prosition of the moving
> object and to detect a collition with an other element.

For detecting position you have several DOM methods (for computing
distance along a path [2], getting an element's bounding box - getBBox
[3], etc.). Also, as far as I know there's no native collision
detection in SVG, although you may compute it by comparing bounding
box limits (somehow fuzzy) and/or intersections points [4] (should be
more precise but also computationally more expensive).


> Thanks for your help.

For general questions on SVG (not specific to Batik), I'd also
recommend keeping an eye/posting into SVG Developers [5] mailing list.
Please avoid cross-posting, though, as many are watching both lists.
;-)


Hope this helps,
 Helder


[1] http://www.w3.org/TR/SVGTiny12/paths.html#PathDataAnimation
[2] http://www.w3.org/TR/SVG11/paths.html#DistanceAlongAPath
[3] http://www.w3.org/TR/SVG11/ecmascript-binding.html
[4] http://ajaxian.com/archives/intersections-and-svg-objects
[5] http://groups.yahoo.com/group/svg-developers/

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org