dalboris / vpaint

Experimental vector graphics and 2D animation editor
http://www.vpaint.org
Apache License 2.0
731 stars 54 forks source link

Improve SVG import/export #9

Open Emasoft opened 9 years ago

Emasoft commented 9 years ago

Wow, this is really going to change the way vector animations are done! But please support the SVG file format instead of limiting yourself to the VEC format. You need to make your application able to communicate with the world of graphic design. All you need is to save frames as SVG files or as SVG SMIL animations and to import vector images as keyframes. The SVG file format is an open standard widely used, and the accepted standard for vector images.

We need four options:

  1. IMPORT SVG FILES: I want to create the animation keyframes in Inkscape or Adobe Illustrator, for example. And then importing them in VPaint to create the interpolation frames.
  2. EXPORT ALL FRAMES AS SVG FILES: I want to be able to use the resulting animation everywere. The file format of vpaint VEC is not supported. I need the option to save the SVG frames generated by vpaint, using a framerate and a duration of my choice.
  3. EXPORT ANIMATION AS SINGLE SVG SMIL FILE: You can also use the SMIL language included in the SVG 1.1 specifications to save the entire animation in a single SVG file, interpolating keyframe by keyframe, as you can learn from here: http://fettblog.eu/blog/2013/07/16/basic-svg-path-tweening-with-smil/
  4. LOAD BACK THE SVG SMIL FILE : I want to be able to reload and modify the SVG SMIL animation I just saved. You can also playback the SMIL file inside the editor to check if it is fine, using some SMIL player open source like this: http://ambulantplayer.org

Please add support for SVG if you want your app to become popular and used in real world applications (from websites icon animations to cartoon movies).

P.S: You can also consider the option to integrate vpaint in Inkscape as a Plugin. It would be awesome. There are already some morphing plugins, but they are so primitive to be useless. Yours, instead, will make a true revolution, and people using Inkscape will start making SVG animations immediately and making your app very popular. Here is a proposal I made some time ago for a SVG animation plugin for Inkscape: inkscape onion skin multipage feature proposal

scribblemaniac commented 8 years ago

@Emasoft You seem to be very knowledgeable with SVGs and SMIL, so I hope you'll also give me some feedback on my work as I get closer to completion :wink:

@dalboris A hierarchy of layers is much the same to me, sounds good. Would multiple parents be a possibility with this system? I am familiar with the issues of multiple inheritance, and it could be difficult to display, but it's still a question worth asking.

dalboris commented 8 years ago

@Emasoft Yes, thank you! For this specific shot I would just make the whole train one layer, without sublayers for individual cars, since indeed Z-ordering is fixed between layers, but not between strokes within a layer.

Basically, the issue is that "grouping" is useful for different reasons:

In theory, the hierarchy used for scene management might be different from the hierarchy for Z-ordering, different from the hierarchy used for cascading styling, and different from the transform hierarchy, since those concepts are orthogonal. But in practice, that's just too much of a hassle and some, if not all, of these hierarchies are shared, which makes for easier management and mental model of what's happening. But that comes with limitations, for instance you can't have different "entities" for each train's car, because scene management is merged with Z-ordering, which is unfortunate in this case.

In the short term, the concept of layer in VPaint would "group" (oops, sorry ;-) ) together scene management, Z-ordering, and transform hierarchy. In addition, there would be an independent Z-ordering system to deal with ordering within a layer (the current system, where ordering can be manually specified per-frame per-stroke).

In the long term, implementing orthogonal mechanisms to save/restore selections (possibly smart: i.e. if at a given frame, you split an edge into two edges, then saved selections containing the initial edge should now contain the two new edges instead), or to share style (the simplest being that each per-frame stroke may optionally store a pointer to a "named style" to use, instead of actual style data) are all options to consider. :-)

dalboris commented 8 years ago

@scribblemaniac No, I do not consider multiple parents, which is indeed more likely to introduce more problems than it solves ;-)

blurymind commented 8 years ago

"Smil falls as svg rises" http://alistapart.com/article/web-animation-past-present-and-future

dalboris commented 4 years ago

Hi @scribblemaniac ! I'm going through the list of bugs / feature requests in preparation of VPaint 1.7 in the coming weeks. It might be a good idea to consider merging your work on importing SVG, so that it's not wasted work.

I know this was a long time ago, but would you consider it in a somewhat releasable state? It will still be tagged with "BETA" so it doesn't have to be perfect, but I see that the last commit is "Began refactoring for the g tag", so I wouldn't want to try merging something in a half-refactored state ;) Does it support <g> tags at all right now? What happens if you try? (crash? ignore the <g> tag and its children? Ignore the <g> tag but not its children, and behave as if all children were outside the group?) I know I can try myself but you probably have better insight.

Cheers!

scribblemaniac commented 4 years ago

I'll take a second look at this. If memory serves me correctly, I did not get the <g> tag working. It won't crash, but it probably won't render anything inside of <g> tags. Ignoring them but not the children is not a good idea because their attributes affect all of their children so you could end up with elements in the wrong place, wrong color, etc.

dalboris commented 4 years ago

You're right, very good point, especially the transform attribute of the group is super important.

dalboris commented 4 years ago

Still doing a little work on the SVG export.

We're now a bit more compliant w.r.t. stroke/fill opacity, although we can't be 100% compliant because VPaint rendering model doesn't currently allow for per-group alpha compositing. So it's impossible to get the correct behavior for the second example of the bottom-row of:

https://www.w3.org/TR/SVG11/images/masking/opacity01.svg:

(top: Chrome; bottom: VPaint)

image

For future reference when implementing this in VGC, I've added a detailed comment in SvgPresentationAttributes::applyChildStyle(XmlStreamReader &xml)

dalboris commented 4 years ago

VPaint now supports the transform attribute :-)

Here are some examples from https://www.w3.org/TR/SVG11/coords.html (with text converted to paths in Inkscape prior to importing in VPaint):

image

dalboris commented 4 years ago

Good news: VPaint now also supports the CSS style-attribute way of setting stroke, stroke-width, fill, opacity, etc. This means that simple files (that is, not using gradients, patterns, dashes, markers, non-round stroke join/cap, symbols, clipping, masking, or embedded JPG/PNG images) generated by Inkscape should now be correctly imported by VPaint as it. :)

Emasoft commented 2 years ago

Any update on SVG/SMIL import and export?

Even a simple frame by frame SMIL animation would do:

<g id="frames60fps"  style="display:inline">
<animate
       attributeName="display"
       fill="freeze"
       repeatCount="indefinite"
       dur="1.3s"
       keyTimes="0;0.961;0.992;1"
       values="none;inline;none;none" />
...
<g id="frame001" style="display:inline">
 (...paste the svg frame as a group here...)
</g>
<g id="frame002" style="display:inline">
 (...paste the svg frame as a group here...)
</g>
<g id="frame003" style="display:inline">
 (...paste the svg frame as a group here...)
</g>

…
...
</g>

And for embedded/linked bitmap animations:

<svg version="1.1" baseProfile="tiny" id="svg-root"
  width="100%" height="100%" viewBox="0 0 480 360"
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

   <image width="320" height="240" xlink:href="frame001.jpg">
      <animate id='frame_001' attributeName='display' values='inline;none'
               dur='0.01667s' fill='freeze' begin="0s" repeatCount="indefinite"/>
   </image>

   <image width="320" height="240" xlink:href="frame002.jpg">
      <animate id='frame_002' attributeName='display' values='none;inline'
               dur='0.01667s' fill='freeze' begin="0.01667s" repeatCount="indefinite" />
   </image>

   <image width="320" height="240" xlink:href="frame003.jpg">
      <animate id='frame_003' attributeName='display' values='none;inline'
               dur='0.01667s' fill='freeze' begin="0,03334s" repeatCount="indefinite" />
   </image>

….
</svg>
dalboris commented 2 years ago

Thanks @Emasoft for the examples! I haven't worked on that yet, and I don't think I will take the time to implement it in VPaint as I'm focusing on the dev of VGC Illustration/Animation. But yes, I would like to have such import/export in VGC, although I don't have a timeline for this yet.

Emasoft commented 2 years ago

I’ve found a very efficient way to encode frame by frame animations in SVG. Much better than the last code I posted above.


<svg version="1.1" baseProfile="tiny" id="svg-root"
  width="100%" height="100%" viewBox="0 0 160 160"
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">

<desc>
    SIMPLE FRAME BY FRAME ANIMATION
    Coded by Emasoft

    Example implementation of basic SVG frame-by-frame animation, for 
    educational purpouses. The frame based animation is easy to export.
    No javascript or CSS, just pure SVG.
    In this way the SVG format is used like an MPEG for vector animations.

    This animation has only 3 frames:
    Frame 01 = square
    Frame 02 = circle
    Frame 03 = triangle

    This file is divided in two sections:

    1) The Definitions (defs) : Here you need to put all frames and clipping paths.
    All frames require an unique id. For example: frame0001, frame0002, etc.
    All elements of a frame must be wrapped in a group with such id.
    Objects written here are not displayed until the animation cycle instances them.

    2) The Animation Scene (a group called "animation_scene") : Here you need to put
    the elements on the scene. First the background rectangle (with or without a
    clipping path), then the animated group of objects (called "animated_group").
    The animation element needs the following parameters:

    values : the id of the frames, with a leading # char and separated by a ; char.
    The frames must be named in the exact order you need them to be displayed.
    Also the first frame id must be indicated in the "use" element as the href
    (example: xlink:href="#frame0001" ).

    begin : the start time delay, in seconds (example: 0s).

    repeatCount : this should set on "indefinite" if you want a looping animation. 
    Otherwise on "0" if you want the animation to stop at the last frame. Any other
    number would be the number of repetitions of the entire sequence.

    dur : the duration in seconds of each frame. For example: "1.0" means 1 frame
    per second. For 60fps is "0.01667". For 24fps you have "0.04167" seconds.
    (just divide 1 for the number of frames, i.e. 1/60=0.0166666.., aprox: "0.01667").

         Gist: https://gist.github.com/Emasoft/e86b227d7acd5b21794a5af1a50126d6

    </desc>

     <defs>

          <clipPath id="_clipPath_background">
            <rect width="512" height="512"/>
          </clipPath>

 <g id="frame0001" style="display:inline">
  <rect
    style="fill:#edd154;fill-opacity:1;stroke:#3c352d;stroke-width:2;stroke-opacity:1;"
    id="square"
    width="100"
    height="100"
    x="30"
    y="30">
  </rect>
    </g>

<g id="frame0002" style="display:inline">
  <path
    style="fill:#edd154;fill-opacity:1;stroke:#3c352d;stroke-width:2;stroke-opacity:1;"
    id="circle"
    d="m 125,80 a 45,45 0 1 1 -90,0 45,45 0 1 1 90,0 z">
  </path>
</g>

<g id="frame0003" style="display:inline">
  <path
    style="fill:#edd154;fill-opacity:1;stroke:#3c352d;stroke-width:2;stroke-opacity:1;"
    id="triangle"
    d="M 105,105 47,89 89,46 105,105 z">
  </path>
</g>

</defs>

<g id="animation_scene">
 <g id="background" clip-path="url(#_clipPath_background)" style="transform: scale(3); transform-origin: center;">
          <rect x="0" y="0" width="512" height="512" transform="matrix(1,0,0,1,0,0)" fill="rgb(90,145,240)"/>
</g>
<g id="animated_group">
  <use width="160" height="160" xlink:href="url(#frame0001)">
    <animate attributeName="xlink:href" 
      values="
            #frame0001;
            #frame0002;
            #frame0003" 
      begin="0s" repeatCount="indefinite" dur="0.5s"/>
  </use>
    </g>
</g>
</svg>

GIST 1 : basic prototype 3 frames svg smil GIST 2 : basic walk cycle frame by frame svg smil GIST 3 : panther frame by frame animation svg smil GIST 4 : seagull frame by frame animation smil fbfsvg

CODEPEN 1 : basic prototype 3 frames svg CODEPEN 2 : basic walk cycle frame by frame in pure smil CODEPEN 3: panther and bird frame by frame animation in pure smil CODEPEN 4: seagull frame by frame animation smil fbfsvg

basic prototype of frame by frame animation in pure smil svg

walk cycle frame by frame animation in pure smil svg

panther frame by frame animation in pure SMIL

seagull frame by frame animation in pure SMIL