Coverage for lib/lottie/parsers/sif/ast_impl/base.py: 94%
85 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-20 16:17 +0100
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-20 16:17 +0100
1from xml.dom import minidom
2import enum
3from lottie.parsers.sif.sif.core import TypeDescriptor, ObjectRegistry, SifNodeMeta, FrameTime
4from lottie.parsers.sif.xml.utils import xml_child_elements, xml_first_element_child
7class SifAstNode:
8 _subclasses = None
9 _tag = None
11 @staticmethod
12 def from_dom(xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
13 if xml.tagName == param.typename:
14 return SifValue.from_dom(xml, param, registry)
16 xmltype = xml.getAttribute("type")
17 if xmltype != param.typename and xmltype != "weighted_" + param.typename: 17 ↛ 18line 17 didn't jump to line 18, because the condition on line 17 was never true
18 raise ValueError("Invalid type %s (should be %s)" % (xmltype, param.typename))
20 return SifAstNode.ast_node_types()[xml.tagName].from_dom(xml, param, registry)
22 @staticmethod
23 def ast_node_types():
24 if SifAstNode._subclasses is None:
25 from . import nodes
26 SifAstNode._subclasses = {}
27 SifAstNode._gather_ast_types(SifAstNode)
28 return SifAstNode._subclasses
30 @staticmethod
31 def _gather_ast_types(cls):
32 for subcls in cls.__subclasses__():
33 if subcls._tag:
34 SifAstNode._subclasses[subcls._tag] = subcls
35 SifAstNode._gather_ast_types(subcls)
37 def _prepare_to_dom(self, dom: minidom.Document, param: TypeDescriptor):
38 element = dom.createElement(self._tag)
39 element.setAttribute("type", param.typename)
40 return element
43class SifValue(SifAstNode):
44 def __init__(self, value=None):
45 self.value = value
47 def __repr__(self):
48 return "<%s %r>" % (self.__class__.__name__, self.value)
50 @classmethod
51 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
52 return SifValue(param.value_from_xml_element(xml, registry))
54 def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
55 return param.value_to_xml_element(self.value, dom)
58class Interpolation(enum.Enum):
59 Auto = "auto"
60 Linear = "linear"
61 Clamped = "clamped"
62 Ease = "halt"
63 Constant = "constant"
66class SifKeyframe:
67 def __init__(self, value, time: FrameTime, before=Interpolation.Clamped, after=Interpolation.Clamped):
68 self.value = value
69 self.time = time
70 self.before = before
71 self.after = after
73 @classmethod
74 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
75 return cls(
76 param.value_from_xml_element(xml_first_element_child(xml), registry),
77 FrameTime.parse_string(xml.getAttribute("time"), registry),
78 Interpolation(xml.getAttribute("before")),
79 Interpolation(xml.getAttribute("after"))
80 )
82 def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
83 element = dom.createElement("waypoint")
84 element.setAttribute("time", str(self.time))
85 element.setAttribute("before", self.before.value)
86 element.setAttribute("after", self.after.value)
87 element.appendChild(param.value_to_xml_element(self.value, dom))
88 return element
90 def __repr__(self):
91 return "<SifKeyframe %s %s>" % (self.time, self.value)
94class SifAnimated(SifAstNode):
95 _tag = "animated"
97 def __init__(self):
98 self.keyframes = []
100 @classmethod
101 def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
102 obj = SifAnimated()
103 for waypoint in xml_child_elements(xml, "waypoint"):
104 obj.keyframes.append(SifKeyframe.from_dom(waypoint, param, registry))
105 return obj
107 def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
108 element = self._prepare_to_dom(dom, param)
109 for kf in self.keyframes:
110 element.appendChild(kf.to_dom(dom, param))
111 return element
113 def add_keyframe(self, *args, **kwargs):
114 if not kwargs and len(args) == 1 and isinstance(args[0], SifKeyframe): 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true
115 keyframe = args[0]
116 else:
117 keyframe = SifKeyframe(*args, **kwargs)
119 self.keyframes.append(keyframe)
120 return keyframe