This is a step by step guide for usage of the flash-tools library.
All the classes a I use are extensions of the Ming classes and
therefore it's not a problem to mix Ming classes with FlashTools classes.
One thing to keep in mind is that the SWF/Flash coordinate system is defined
starting in top left hand corner and extending downwards and right, i.e:
Origin(0,0) .---------> Positive X
|
|
|
V
Positive Y
Any negative values and values greater than the movies dimension are "off
screen". Note though, it is ok to define a shape that uses negative or
large coordinates, as long as when it is added to the movie, the shape is
scaled/moved to fit into the movie.
The following describes how to construct an Flash animation using Flash
Tools and tries to describe this in detail. If you have comments or
questions or problems please send me an email.
All the example code show here is included in the examples directory in
this directory.
In the beginning, there is the FlashShape for drawing shapes (circles,
retangles, beziers, ellipses...). Besides giving these shapes color and
fill, FlashShapes can't really do anything else. In order to include
images, you need to use a SWFBitmap and fill a shape using the
bitmap. There is a helper class for this: FlashImage. Example 1
demostrates using a shape:
Lines 1 to 3 defines our shape. It has no fill but a line width of 2 pixels
and red stroke color. The rectangle (line 3) is defined such that the center
of the shape is at (50,50): top left hand corner is located at (40,40) and
the width and height are both 20 pixels.
You always need a FlashMovie, it's basically the entire animation and
every flash film has exactly one movie. The movie is created in line 5, it
has a size of 100 by 100 (line 7) and it shows 10 frames per second
(line 8). The dimension defines the viewable region of the film: if the
shaped defined outside of this dimension, it would not be seen.
In line 9, we add our shape to the movie. Note, that because we drew a
rectangle (line 3) centered at 50,50, it is located in the center of the
movie. This is only possible because we knew, when we constructed our
shape, that the movie will be 100 by 100.
Line 10 creates the single frame that makes up the movie.
Note, that without the nextFrame(), the movie would not display
the shape: a frame is not automatically created when an object is added to
the movie. Finally, line 11 saves the SWF file.
You may have noticed that there is not much animation in example 1, here
we now add animation. To do this we require a DisplayItem (DI) object. A
DI allows modifications of the shape once it has been added to a movie.
Using a DisplayItem, you can skew, move, rotate, scale, ... the shape
once it has been added to the movie. The intention is that the shape is
modified using the DI once per frame so as to create an animation. The DI
reference remains valid the entire movie or until remove() is called.
Lines 1 through 3 create the shape, this time however, the rectangle
is defined such that the center point of the shape is (0,0). This has
the advantage that when we construct our movie, we can position the
shape anywhere without worrying about a shape specific offset.
Line 10 adds the shape to the movie, however this time we keep a reference
to the display item object that is returned when adding the shape to the
movie. Note that we can not explicitly create a DisplayItem instance, rather
this is created by the movie object we when add something to the movie.
Line 11 moves the shape to position (50,30) in the same frame
(frame 0) where it was added (inserting a m.nextFrame() between lines 10
and 11 would cause a jump at the beginning of the movie).
Line 13 sets the frame pointer of the movie to frame 1 and line 14 moves
the shape to position (50,40). Line 16 sets the frame number to 2 and line
17 moves the shape to (50,50). Line 19 sets the frame number to 3 and line
20 moves the shape to (50,40). Line 22 commits the moveTo in line 20 to
the movie.
Because the film automatically loops, the shape seems to move up and down.
This is because at the beginning of the film the shape is located at (50,30)
and at the end it's located at (50,40). It
is not necessary to set the number of frames for the movie, it does this
automatically.
This is about animating an animation! This is done using a Sprite which is
the same as a movie except we can't save a sprite to a SWF film but we can
added numerous (probably infinitely many?) sprites to a movie.
Lines 1 to 3 create our rectangle. Line 5 creates the sprite containing
the shape. Notice that the sprite is added to the movie (line 16) and not
the shape. Line 6 adds the shape to the sprite, and lines 7 through 9
create 36 frames in which the shape is rotated by 10 degrees in each frame.
Note that if
the shape was not centered at (0,0), then the rotation would move the
entire shape around the origin.
If we now add the sprite to the movie, we would have simply have a
rotating rectangle somewhere in the movie. What lines 17 through 19 do is
move the shape from the top of the movie to the bottom.
If the movie is too slow, the rate in line 14 can be increased so that the
shape appears to move quicker.
In the next example does exactly the same but slightly differently.
The difference here is that we use a FlashSprite to make the rotation
animation move along given line. We then add the FlashSprite directly to
the movie instead of creating the line animation in the movie. This has a
side effect in that the main movie now only has one frame containing a
Sprite with 100 frames. In example 3, we created a movie with 100 frames
each moving the animation sprite.
Lines 1 to 9 are the same as in example 3. Line 12 defines the line along
which the rotation-sprite is to move. Lines are defined using begin and
end points. Line 13 tells the FlashSprite to add the rotation sprite and
then move it along the given line. The 0 and 100 are frame numbers and
tell the FlashSprite to start at frame 0 and be at the end of the line at
frame 100.
Line 14 advises the FlashSprite that we are done with adding shapes to it
and that it should build a complete SWFSprite. This needs to be called
before adding the FlashSprite to a movie (or another sprite). A
FlashSprite is an extension of SWFSprite which basically allows a user to
spring around in the frames and adding shapes to any frame, this is not
possible with SWFSprite: once nextFrame() is called, there is no going
back.
The followPath method computes the number of frames required and then
requests (via the get_points(...) method) the required number of points
(one for each frame) from the path. Once all points are available, the
followPath(...) adds the shape to the sprite and then does successive
nextFrame() calls. For each frame it does one moveTo(..) call on the
display item that was returned as it added the block to the sprite.
In order to position the rotating rectangle to the middle of the movie,
line 21 does a moveTo(..). This shifts the sprite to the middle. Without this
the animation would occur on the very left edge of the movie.
In the next example, we will create exactly the same animation but using a
third technique: frame hooks.
In this example we only have a single sprite which does both the move and
the rotation of the shape. It does this by using a hook that is called for
each new frame that is created, i.e. 100 times as the FlashSprite has
one hundred frames (line 11). So basically two things happen to the shape
for each frame of the sprite: moveTo and rotation. In the previous examples
it was two sprites/movies responsible for the movement of the shape, here
only one sprite.
The frame hook is defined in lines 1 and 2. Notice that as argument it is
passed a DisplayItem instance (the one for the corresponding shape) and the
frame number of the frame currently being created. In this case, the frame
number ranges from 0 to 99 (inclusive). Line 7 assigns the frame hook to
the shape. The hook is not specific to the shape and can be assigned to
any other shape.
The name of the hook must be 'follow_path_frame_hook'. The followPath(..)
method will check for a function or
method with this name on the object passed in. The hook is
called after followPath(..) has done the 'moveTo' on the display item.
The hook could do a further moveTo call on the display item, but this
would be a little counter-productive!
The next example demostrates how to following several paths.
FollowPath accepts a list of paths which the given block will follow in
the given frame range. The individual path do not need to be connected
although if they are not, then the block will appear to "jump" from the
end of one path to the beginning of the next.
The square follows a triangle, returning to it's origin. It moves faster,
this is because it needs to cover more ground but has the same number of
frames to do so. In order to slow it down: a) modify the number of frames
from 100 to something larger in line 13 or b) decrease the rate of the
entire film in line 19.
Lines 10,11 and 12 define the three lines along which the shape moves. The
lines follow on from one another, i.e. where one ends the other begins,
thus there is a continous movement. This is not a requirement and we could
easily have used three random lines but then the shape would
jump. Replacing the lines 10-12 with the following demostrates that:
In example 6 the square moves anti-clockwise along the triangle path, example 7
shows how we can reverse the order in which the points for the lines are returned
so that the square moves clockwise:
Lines 13-15 are new and advise the lines to return their points in reverse
order (method followPath requests from each path a set of points which
are used to move the block). A further modication is the order of paths:
l3 and l2 swapped position in line 16: order of the paths is important
when using followPath.
Of course reverse works with any path, replacing lines line 10 through 16
with:
As a result of reversing the path segment, the square seems to ping-pong
from one end to the other of the path. Also the point where the shape
begins to move along the path is differs: a) Rectangle: top left hand
corner as defined in the constructor and b) ellipse and circle both start
at the furthest right hand side point.
This might not be a problem for the above examples, however when combining
two or more shapes it becomes a problem:
Now the shape continous along a smooth path without jumping. The point
(40,50) happens to be on the perimeter of both shapes, this does not need
to be the case. Any point may be passed to setStartPoint(...) but the
point that is the closest to the start point and on the perimeter will
be returned as the first point.
Also, setting a start point only makes sense when using "closed" shapes,
because a line or bezier is not continous, setting a start point will
cause a jump: the only "smooth" path along a line is starting at either
end but not in the middle!
Line 14 does a reverse on the rectangle: this ensures that the square does
a figure-8 and not two-3s. We could have done a reverse on the ellipse,
this would have produced a figure-8 but in the other direction.
Scalable Vector Graphics (SVG) is a W3C standard
for the representation
for vector based graphics. There are a number of tools for creating SVG
images, including Gimp, Xfig
and InkScape (see Resources
for links).
Vector based: this is important since SWF/Flash is also vector based.
XML based: easy to read(!!) and easy to parse and intepret
Open standard: this allows graphic tools to use this as an representation
for vector based graphics.
SVG also has support for animation and scripting and text and
... i.e. lots of the functionality that SWF provides. Obviously, it would
be great to have a 100% SVG to SWF converter but this is not what
Svg2Flash does (yet)!
The intention of the Svg2Flash converter
is to convert basic SVG images to Python code so that they may be included
in other Python code that generates the SWF animation. Although the generated
Python code can also be used to generate a static SWF.
Svg2Flash converts basic shapes (see Section 9 of the SVG specification
version 1.1 (SVG1.1)) and paths (Section 8, SVG1.1) to their Flash Tools
equivalents. Also it supports group elements (Section 5.2, SVG1.1) but
only attributes that effect the color or fill specifications of elements.
CSS style specifications are also supported.
The methods described below are all defined on the 'SVGImage' class. This
class can be included in using the 'as' form of the import to allow
multiple generated classes to be included:
from svg_image_1 import SVGImage as SvgImage1
from svg_image_2 import SVGImage as SvgImage2
...
Hi, I'm empty!
Including several Images
For each of the shape and path definitions found in an SVG image, three
methods are generated: geomXX(), clrFillXX() and shapeXX() where XX ranges
from 01 to the number of elements found. The 'geom' methods returns the
pure geometry of the shape/path in form of a Path (see
flash_geometry/path.py), 'clrfill' returns a dictionary containing the
color and fill specification for the shape. These are indexed using the
SVG1.1 attribute names, i.e. stroke-width, stroke, fill, etc. The 'shape'
methods return a FlashShape instance which is a complete shape, i.e. a
combination of 'geom' and 'clrfill'.
Because the order of the shape definition might change, shape definitions
may be accessed using their 'id' value. That is, if a shape has an id
attribute, then the above values can be obtained using three extra
methods: getGeometry(name), getColorFill(name) and getShape(name) where
name is id.
Two other methods are defined: getAllShapes() which returns a list of
FlashShapes containing all the shapes and paths, getViewbox() which
returns the viewbox specification (Section 5.1.2, SVG1.1).
This returns a list of: min-x, min-y, width and height.
Also, calling the generated python file directly using python will create
a 'output.swf' which is basically the SVG image in form of a Flash
animation.
A couple of notes: the coordinates are not scaled and are returned
as defined in SVG, although the default SWF generated by the main function
centers image. Most of the values for stroke-linecap and
stroke-linejoin are not supported becuase these are non-trivial to
convert. Only shape, path and group elements are supported, all other
elements are TODOs.
Options that can be passed to svg2flash:
--input
name of the input svg file. Defaults to input.svg. Can also be '-', then
svg2flash will read from standard in.
--output
name of the output python file. Defaults to output.py. Can also be '-', in
which case the output is sent directly to standard output. Useful for doing compilation on the
fly:
svg2flash --input=some.svg --output=- | python -
--swf
name of the SWF file to generate if the generated python is
executed directly. Defaults to output.swf.
The following is a circle example. The SVG file defines a circle with a
radius of 3.41229 pixels, center at (288.242,85.8223). The fill is blue
but with an opacity of 0.5 (ranges from 0 to 1). Stroke color is yellow
and the width is 2. The full code can be found in the files example9.svg,
example9.py and example10.py in the example directory.
The generate Python class contains the three generated methods for the
shape. Also shown are the methods for accessing the shape via its id
value. The constructor method (__init__(self)) creates a lookup table for
mapping the Ids to their corresponding index values.
Line 01 is the import of the SVGImage class. In this case, it is not
necessary to give the imported class a new name since there is only one
SVGImage we're using. The variable 'img' is then an instance of the
class.
Lines 15 to 20 define an encapsulating sprite which translates the shape.
Line 16 obtains the geometry shape, which
we know is a circle and there is only one geometry shape (see geom00() in
example 9a), so calling getCenter() is ok. The coordinates of the center
point is SVG user space and when adding the shape to the sprite, the shape
needs to translated from the center point to the origin (line 18).
The hook defined in lines 5-13 is added to the encapsulating sprite in
line 20. Line 23 places the sprite directly in the middle of the movie and
the calls the sprite hook 70 times.
This time we will define a path in an SVG image and use this path make
shape follow this path. The code for this example: example11.svg,
example11.py and example12.py. The SVG was generated using the SVG-Export
script-fu, the source XCF: example11.xcf.
11 <circle __id__="_fc#e51616_sc#e51616_sw0_tpcircle_nmshape-fg_"
12 fill="#e51616"
13 stroke="#e51616"
14 stroke-width="0"
15 __type__="circle"
16 id="shape-fg"
17 cx="23.375" cy="50.3438" r="4.50022"/>
18 <rect __id__="_fc#416dea_sc#416dea_sw1_tprect_nmshape-bg_"
19 fill="#416dea"
20 stroke="#416dea"
21 stroke-width="1"
22 __type__="rect"
23 id="shape-bg"
24 x="15.875" y="41.75" width="14.8159" height="16.9377"/>
25 <path __id__="_nmpath_"
26 id="path"
27 d="M 9,11.25
28 C 22.75,3.75 62.75,3 81.75,12.5
29 C 95.25,22 96.75,69.75 85.75,87.25
30 C 70.75,96 41.25,98 24.75,90.25
31 C 13.25,76.75 14.75,34.75 24.75,27
32 C 33.75,19.75 58.25,19 68.75,28
33 C 81,37.25 79,67.5 72,75
34 C 61.5,82.25 49.5,85.5 36.5,74.75
35 C 28,66.75 27.5,47 36,40.5
36 C 47,34 51.5,34.75 60.25,40
37 C 67.5,48.25 67.5,57 61.75,66
38 C 56.75,69.75 53.75,71.5 47.25,66
39 C 43.25,61.75 44.5,58.25 47,54.75
40 C 59.5992,46.7138 57.75,65.75 52,60
41 C 52,60 9,11.25 9,11.25 Z"
42 />
Example 12: Using a path specification from SVG image
Line 1 and 3 import and initialise an instance of the SVG image. Lines 11
through 18 construct the encapsulating sprite with the shape, which
defined using two parts: a background and a foreground (in this case a
blue rectangle and red circle).
Line 21 retrieves the path specification minus the first path segment
which happens to be a single point corresponding the initial moveTo
operation of the SVG (Example 11, Line 27). The last argument of line 21
indicates that the followPath should equal space the points over the
entire length of the path.