2021-11-22 12:56:02 +01:00
|
|
|
#!/usr/bin/env python3
|
2021-10-16 02:18:15 +02:00
|
|
|
#
|
|
|
|
# Copyright (C) 2021 roberta bennett repeatingshadow@protonmail.com
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# 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 for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
#
|
|
|
|
"""
|
2021-11-22 12:56:02 +01:00
|
|
|
Create svg path animation as SMIL from frames.
|
2021-10-16 02:18:15 +02:00
|
|
|
|
|
|
|
Place each frame of the animation in a layer named 'frame'.
|
|
|
|
Each of these layers should have the same number of paths,
|
|
|
|
and each path should have the same number of points as the corresponding
|
|
|
|
path in other layers.
|
2021-11-22 12:56:02 +01:00
|
|
|
Note if there are more than one path in the frames, the Z order of the paths
|
|
|
|
must match. It helps to use the XML editor option to observe the z order.
|
2021-10-16 02:18:15 +02:00
|
|
|
|
|
|
|
The animation is applied to the paths in the first layer in the sequence, so the
|
2021-11-22 12:56:02 +01:00
|
|
|
properties of that layer are used. In particular, the first layer ought to be set
|
|
|
|
visible.
|
2021-10-16 02:18:15 +02:00
|
|
|
|
|
|
|
Animations with different numbers of frames can be put into different sequences,
|
|
|
|
named 'sequence', using sub-groups:
|
|
|
|
|
|
|
|
Layers:
|
|
|
|
not_animated_layer1
|
|
|
|
sequence
|
|
|
|
frame
|
|
|
|
path1a
|
|
|
|
path2a
|
|
|
|
frame
|
|
|
|
path1b
|
|
|
|
path2b
|
|
|
|
frame
|
|
|
|
path1c
|
|
|
|
path2c
|
|
|
|
frame
|
|
|
|
path1d
|
|
|
|
path2d
|
|
|
|
sequence
|
|
|
|
frame
|
|
|
|
frame
|
|
|
|
frame
|
|
|
|
|
2021-11-22 12:56:02 +01:00
|
|
|
Layer names must contain 'frame' and groups names contain 'sequence',
|
|
|
|
eg, frame1 frame 2 frame 30, sequence 1, rythm sequence, rocket sequence
|
2021-10-16 02:18:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
import inkex
|
|
|
|
|
|
|
|
class AnimateElement(inkex.BaseElement):
|
|
|
|
"""animation Elements do not have a visible representation on the canvas"""
|
|
|
|
tag_name = 'animate'
|
|
|
|
@classmethod
|
|
|
|
def new(cls, **attrs):
|
|
|
|
return super().new( **attrs)
|
|
|
|
|
|
|
|
|
|
|
|
class AnimationExtension(inkex.EffectExtension):
|
|
|
|
|
|
|
|
def add_arguments(self, pars):
|
2021-11-22 12:56:02 +01:00
|
|
|
pars.add_argument("--delete", type=inkex.Boolean, help="Remove frames")
|
2021-10-16 02:18:15 +02:00
|
|
|
pars.add_argument("--begin_str", default="0", help="begin string: eg 0;an2.end;an3.begin")
|
2021-11-22 12:56:02 +01:00
|
|
|
pars.add_argument("--repeat_str", default="indefinite", help="indefinite or an integer")
|
|
|
|
pars.add_argument("--dur_str", default="7.9", help="duration in seconds. Do not decorate with units")
|
|
|
|
|
|
|
|
|
|
|
|
def crunchFrames(self,frames):
|
|
|
|
if frames is None:
|
|
|
|
raise inkex.AbortExtension("layer named frame does not exist.")
|
|
|
|
frame0paths = frames[0].findall('svg:path')
|
|
|
|
Dlists = [p.get_path() for p in frame0paths]
|
|
|
|
for frame in frames[1:]:
|
|
|
|
paths = frame.findall("svg:path")
|
|
|
|
for i,p in enumerate(paths):
|
|
|
|
Dlists[i] += ";\n"+p.get_path()
|
|
|
|
for i,dl in enumerate(Dlists):
|
|
|
|
animel = AnimateElement(
|
|
|
|
attributeName="d",
|
|
|
|
attributeType="XML",
|
|
|
|
begin=self.options.begin_str,
|
|
|
|
dur=self.options.dur_str,
|
|
|
|
repeatCount=self.options.repeat_str,
|
|
|
|
values=dl)
|
|
|
|
frame0paths[i].append(animel)
|
|
|
|
for frame in frames[1:]:
|
|
|
|
if self.options.delete:
|
|
|
|
frame.delete()
|
|
|
|
return
|
|
|
|
|
2021-10-16 02:18:15 +02:00
|
|
|
|
|
|
|
def effect(self):
|
2021-11-22 12:56:02 +01:00
|
|
|
sequences = [ elem for elem in self.svg.findall("svg:g[@inkscape:label]")
|
|
|
|
if "sequence" in (elem.get('inkscape:label'))
|
|
|
|
]
|
|
|
|
if len(sequences)==0:
|
|
|
|
frames = [ elem for elem in self.svg.findall("svg:g[@inkscape:label]")
|
|
|
|
if "frame" in (elem.get('inkscape:label'))
|
|
|
|
]
|
|
|
|
self.crunchFrames(frames)
|
|
|
|
return
|
2021-10-16 02:18:15 +02:00
|
|
|
for sequence in sequences:
|
2021-11-22 12:56:02 +01:00
|
|
|
frames = [ elem for elem in sequence.findall("svg:g[@inkscape:label]")
|
|
|
|
if "frame" in (elem.get('inkscape:label'))
|
|
|
|
]
|
|
|
|
self.crunchFrames(frames)
|
|
|
|
|
|
|
|
|
2021-10-16 02:18:15 +02:00
|
|
|
if __name__ == '__main__':
|
2021-11-22 12:56:02 +01:00
|
|
|
AnimationExtension().run()
|