Collection of mostly command line tools / PHP scripts. Somewhat out of date.

⌈⌋ branch:  scripts + snippets


Check-in [58300ed51a]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:implement repeatCount, some tests
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 58300ed51a5725ba65262370548691a8ffd5b2a9
User & Date: mario 2022-10-23 20:02:22
Context
2022-10-31
09:37
more pacing tests check-in: 64ef7120d0 user: mario tags: trunk
2022-10-23
20:02
implement repeatCount, some tests check-in: 58300ed51a user: mario tags: trunk
2022-10-12
13:01
recombine zip_args into imagick_gif(), distribute some flags onto frame.pngs check-in: 4621d9c6bd user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to inkscape/export_gif.py.

468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
        self.gif = parent
        self.convert_png = parent.convert_png
        self.svg = svg  # ← already a deepcopy
        self.layer = layer
        self.frames = layer.animate
        self.delay = float(layer.args.get("delay", self.gif.options.delay))
        self.layer.args.update({
            "delay": self.delay / self.frames   # frame time split between animation steps
        })

    def export(self):
        """ applies transforms in each subframe, yields filename+args list """
        transforms = []
        for anim in self.svg.xpath("//svg:animate"):
            transforms.append([self.animate_style(anim, **anim.attrib), self.pace(anim)])







|







468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
        self.gif = parent
        self.convert_png = parent.convert_png
        self.svg = svg  # ← already a deepcopy
        self.layer = layer
        self.frames = layer.animate
        self.delay = float(layer.args.get("delay", self.gif.options.delay))
        self.layer.args.update({
            "delay": self.delay / self.frames   # frame time split between animation steps / TODO: might need reset
        })

    def export(self):
        """ applies transforms in each subframe, yields filename+args list """
        transforms = []
        for anim in self.svg.xpath("//svg:animate"):
            transforms.append([self.animate_style(anim, **anim.attrib), self.pace(anim)])
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
            return animation.getparent()

    # pylint: disable=function-redefined, no-else-return, bare-except
    def pace(self, anim, smooth=False):
        """
        Some relative time slice folding into linear 0.0 … 1.0 frame interval.
        Might be able to recognize some of: begin, dur, keyTimes, calcMode, etc.
        Simply chains bounding functions. Enabled on [pace],[time],[smooth] label flags.
        (Global enable option would probably make more sense, right now we just
        have a disable flag....
        """
        attr = anim.attrib.get
        r_begin = self.seconds(attr("begin")) / self.delay
        r_end = r_begin + self.seconds(attr("dur"), self.delay) / self.delay

        def vary(r_time):
            """ no transform / calcMode=linear """







|
<
<







608
609
610
611
612
613
614
615


616
617
618
619
620
621
622
            return animation.getparent()

    # pylint: disable=function-redefined, no-else-return, bare-except
    def pace(self, anim, smooth=False):
        """
        Some relative time slice folding into linear 0.0 … 1.0 frame interval.
        Might be able to recognize some of: begin, dur, keyTimes, calcMode, etc.
        Simply chains bounding functions. Enabled on [pace] flag or global option.


        """
        attr = anim.attrib.get
        r_begin = self.seconds(attr("begin")) / self.delay
        r_end = r_begin + self.seconds(attr("dur"), self.delay) / self.delay

        def vary(r_time):
            """ no transform / calcMode=linear """
632
633
634
635
636
637
638



639

640
641
642
643
644
645
646
647
                if r_time < r_begin:
                    return 0.0
                elif r_time > r_end:
                    return 1.0
                else:
                    return (r_time - r_begin) / r_end




        # repeatCount could be handled like shift here (if begin+dur > frame_delay, reset to 0.0 … 1.0 interval)


        if attr("calcMode") == "discrete":
            def vary(r_time):
                """ just snaps from one to the other extreme; nobody will ever use it """
                return 0.0 if r_time < 0.5 else 1.0

        if attr("calcMode") == "paced" or smooth and not attr("calcMode"):
            def vary(r_time, vary=vary):







>
>
>
|
>
|







630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
                if r_time < r_begin:
                    return 0.0
                elif r_time > r_end:
                    return 1.0
                else:
                    return (r_time - r_begin) / r_end

        if attr("repeatCount"):
            # meh: ignorantly zigzags the repeat into our .delay time slice
            def vary(r_time, count=int(attr("repeatCount")), vary=vary):
                """ amplify by repeatCount, modulo into 0.0 … 1.0 interval """
                return (count * vary(r_time)) % 1.0
                
        if attr("calcMode") == "discrete":
            def vary(r_time):
                """ just snaps from one to the other extreme; nobody will ever use it """
                return 0.0 if r_time < 0.5 else 1.0

        if attr("calcMode") == "paced" or smooth and not attr("calcMode"):
            def vary(r_time, vary=vary):

Added inkscape/pytest.ini.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
[pytest]
minversion = 1.0
addopts = -ra -q --ignore=test/util.py --ignore=test/_*.py --ignore=test/__*.py  -p no:warnings
testpaths = test/
python_files = *.py
# Yes sure pytest, why not do the obvious thing anyway
python_functions = !_* [a-z]* [A-Z]* !_*

Added inkscape/test/conftest.py.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
# id: __init__
import sys
sys.path.append(".")
sys.path.append("..")
sys.path.append("/usr/share/inkscape/extensions/")
import pylint
import export_gif
import animate_yo

Added inkscape/test/pace.py.

















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# t: t
# encoding:utf-8
# title: AnimationSteps().pace() interval mapping
#
# That's really the least worthwhile thing to test,
# but the only thing that can realistically be.
# (Generating SVGs and asserting binary GIF output is way beyond scope.)
#

import pytest
import export_gif as xg
from argparse import Namespace

# lazier @dataclass
class AnimTest():
    def __init__(self, **kwargs):
        self.attrib = kwargs

# stub out with parent attributes
class PaceTest(xg.AnimationSteps):
    def __init__(self):
        self.frames = 10
        self.delay = 10
        self.layer = Namespace(
            tags = {"pace"},
            args = {"delay": 10},
        )
        self.gif = Namespace(
            options = Namespace(
                all_pace = True,
                delay = 10,
            )
        )

@pytest.fixture
def times():
    return [i/10.0 for i in range(0, 11)]

# round result list    
def _roundy(ls):
    return [round(x, 2) for x in ls]


# simple interval 2s-4s within 10s frame
def pace_delay_dur(times):
    anim = AnimTest(begin="2s", dur="4s")
    vary = PaceTest().pace(anim)
    assert _roundy(vary(r) for r in times) == [0.0, 0.0, 0.0, 0.17, 0.33, 0.5, 0.67, 1.0, 1.0, 1.0, 1.0]

# oscillate twice in 10s frame
def pace_repeat(times):
    anim = AnimTest(begin="0s", dur="10s", repeatCount="2")
    vary = PaceTest().pace(anim)
    assert _roundy(vary(r+0.0000) for r in times) == [0.0, 0.2, 0.4, 0.6, 0.8, 0.0, 0.2, 0.4, 0.6, 0.8, 0.0]
    assert _roundy(vary(r-0.0001) for r in times) == [0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 0.2, 0.4, 0.6, 0.8, 1.0]

Added inkscape/test/t.py.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# t: t
# title: calcmodespline


def _map(r_time, steps=[0.0, 0.5]):
    """ only linear [0, 0.33, 0.66, 1.0] here, values=[] would require interpretation in handlers """
    dist = 1.0 / len(steps)  # 0.25 for len=4
    offs = int(r_time * len(steps))   # 0.30 → map [1]
    steps = steps[:] + [1.0, 1.0]
    val_diff = steps[offs+1] - steps[offs] # 0.33-0.25
    off_diff = (r_time - dist * offs) / dist # offset [2]+0.1 to quantify value
    return steps[offs] + val_diff * off_diff

def test_map_calcmode_spline():
    for r in range(0, 21):
        print(_map(r/20.0))