Initial Commit

This commit is contained in:
OmniLottie
2026-03-01 21:36:54 +08:00
commit a386c803e1
199 changed files with 42253 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
from xml.dom import minidom
import enum
from lottie.parsers.sif.sif.core import TypeDescriptor, ObjectRegistry, SifNodeMeta, FrameTime
from lottie.parsers.sif.xml.utils import xml_child_elements, xml_first_element_child
class SifAstNode:
_subclasses = None
_tag = None
@staticmethod
def from_dom(xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
if xml.tagName == param.typename:
return SifValue.from_dom(xml, param, registry)
xmltype = xml.getAttribute("type")
if xmltype != param.typename and xmltype != "weighted_" + param.typename:
raise ValueError("Invalid type %s (should be %s)" % (xmltype, param.typename))
return SifAstNode.ast_node_types()[xml.tagName].from_dom(xml, param, registry)
@staticmethod
def ast_node_types():
if SifAstNode._subclasses is None:
from . import nodes
SifAstNode._subclasses = {}
SifAstNode._gather_ast_types(SifAstNode)
return SifAstNode._subclasses
@staticmethod
def _gather_ast_types(cls):
for subcls in cls.__subclasses__():
if subcls._tag:
SifAstNode._subclasses[subcls._tag] = subcls
SifAstNode._gather_ast_types(subcls)
def _prepare_to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = dom.createElement(self._tag)
element.setAttribute("type", param.typename)
return element
class SifValue(SifAstNode):
def __init__(self, value=None):
self.value = value
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.value)
@classmethod
def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
return SifValue(param.value_from_xml_element(xml, registry))
def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
return param.value_to_xml_element(self.value, dom)
class Interpolation(enum.Enum):
Auto = "auto"
Linear = "linear"
Clamped = "clamped"
Ease = "halt"
Constant = "constant"
class SifKeyframe:
def __init__(self, value, time: FrameTime, before=Interpolation.Clamped, after=Interpolation.Clamped):
self.value = value
self.time = time
self.before = before
self.after = after
@classmethod
def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
return cls(
param.value_from_xml_element(xml_first_element_child(xml), registry),
FrameTime.parse_string(xml.getAttribute("time"), registry),
Interpolation(xml.getAttribute("before")),
Interpolation(xml.getAttribute("after"))
)
def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = dom.createElement("waypoint")
element.setAttribute("time", str(self.time))
element.setAttribute("before", self.before.value)
element.setAttribute("after", self.after.value)
element.appendChild(param.value_to_xml_element(self.value, dom))
return element
def __repr__(self):
return "<SifKeyframe %s %s>" % (self.time, self.value)
class SifAnimated(SifAstNode):
_tag = "animated"
def __init__(self):
self.keyframes = []
@classmethod
def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
obj = SifAnimated()
for waypoint in xml_child_elements(xml, "waypoint"):
obj.keyframes.append(SifKeyframe.from_dom(waypoint, param, registry))
return obj
def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = self._prepare_to_dom(dom, param)
for kf in self.keyframes:
element.appendChild(kf.to_dom(dom, param))
return element
def add_keyframe(self, *args, **kwargs):
if not kwargs and len(args) == 1 and isinstance(args[0], SifKeyframe):
keyframe = args[0]
else:
keyframe = SifKeyframe(*args, **kwargs)
self.keyframes.append(keyframe)
return keyframe
+321
View File
@@ -0,0 +1,321 @@
from xml.dom import minidom
import enum
from lottie.nvector import NVector
from lottie.parsers.sif.ast_impl.base import SifAstNode, TypeDescriptor, ObjectRegistry
from lottie.parsers.sif.xml.animatable import XmlAnimatable
from lottie.parsers.sif.xml.wrappers import XmlBoneReference, XmlSifElement, XmlList
from lottie.parsers.sif.sif.nodes import Segment, WeightedVector, Bline
from lottie.parsers.sif.sif.enums import Smooth
from lottie.parsers.sif.sif.core import SifNodeMeta, FrameTime
class SifAstComplex(SifAstNode, metaclass=SifNodeMeta):
_nodes = []
def __init__(self, **kw):
for node in self._nodes:
node.initialize_object(kw, self)
@classmethod
def from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
outcls = cls.get_class_from_dom(xml, param, registry)
instance = outcls()
for node in outcls._nodes:
node.from_xml(instance, xml, registry)
return instance
@classmethod
def get_class_from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
return cls
def to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = self._prepare_to_dom(dom, param)
for node in self._nodes:
node.to_xml(self, element, dom, param)
return element
class SifAstBoneLink(SifAstComplex):
_tag = "bone_link"
_nodes = [
XmlBoneReference("bone"),
XmlAnimatable("base_value", "vector", NVector(0, 0)),
XmlAnimatable("translate", "bool", True),
XmlAnimatable("rotate", "bool", True),
XmlAnimatable("skew", "bool", True),
XmlAnimatable("scale_x", "bool", True),
XmlAnimatable("scale_y", "bool", True),
]
class SifAstBoneInfluence(SifAstComplex):
_tag = "boneinfluence"
_nodes = [
# TODO bone_weight_list
XmlAnimatable("link", "vector", NVector(0, 0)),
]
class SifSegCalcTangent(SifAstComplex):
_tag = "segcalctangent"
_nodes = [
XmlSifElement("segment", Segment),
XmlAnimatable("amount", "real", .5),
]
class SifSegCalcVertex(SifAstComplex):
_tag = "segcalcvertex"
_nodes = [
XmlSifElement("segment", Segment),
XmlAnimatable("amount", "real", .5),
]
class WeightedAverage(SifAstComplex):
_tag = "weighted_average"
_nodes = [
XmlList(WeightedVector, "vectors", "entry"),
]
def _prepare_to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = dom.createElement(self._tag)
element.setAttribute("type", "weighted_vector")
return element
class SifAdd(SifAstComplex):
_tag = "add"
_nodes = [
XmlAnimatable("lhs", "_recurse"),
XmlAnimatable("rhs", "_recurse"),
XmlAnimatable("scalar", "real", 1.),
]
class SifAnimatedFile(SifAstComplex):
_tag = "animated_file"
_nodes = [
XmlAnimatable("filename", "string"),
]
class Accuracy(enum.Enum):
Rough = 0
Normal = 1
Fine = 2
Extreme = 3
class DerivativeOrder:
FirstDerivative = 0
SecondDerivative = 1
class SifDerivative(SifAstComplex):
_tag = "derivative"
_nodes = [
XmlAnimatable("link", "_recurse"),
XmlAnimatable("interval", "real", 0.01),
XmlAnimatable("accuracy", "integer", Accuracy.Normal, Accuracy),
XmlAnimatable("order", "integer", DerivativeOrder.FirstDerivative, DerivativeOrder),
]
class SifDynamic(SifAstComplex):
_tag = "dynamic"
_nodes = [
XmlAnimatable("tip_static", "vector", NVector(0, 0)),
XmlAnimatable("origin", "vector", NVector(0, 0)),
XmlAnimatable("force", "vector", NVector(0, 0)),
XmlAnimatable("torque", "real", 0.),
XmlAnimatable("damping", "real", 0.4),
XmlAnimatable("friction", "real", 0.4),
XmlAnimatable("spring", "real", 30.),
XmlAnimatable("torsion", "real", 30.),
XmlAnimatable("mass", "real", 0.3),
XmlAnimatable("inertia", "real", 0.3),
XmlAnimatable("spring_rigid", "bool", False),
XmlAnimatable("torsion_rigid", "bool", False),
XmlAnimatable("origin_drags_tip", "bool", True),
]
class SifGreyed(SifAstComplex):
_tag = "greyed"
_nodes = [
XmlAnimatable("link", "_recurse"),
]
class SifLinear(SifAstComplex):
_tag = "linear"
_nodes = [
XmlAnimatable("slope", "vector", NVector(0, 0)),
XmlAnimatable("offset", "vector", NVector(0, 0)),
]
class SifRadialComposite(SifAstComplex):
_tag = "radial_composite"
_nodes = [
XmlAnimatable("radius", "real", 0.),
XmlAnimatable("theta", "angle", 0.),
]
class SifComposite(SifAstComplex):
_tag = "composite"
@classmethod
def get_class_from_dom(cls, xml: minidom.Element, param: TypeDescriptor, registry: ObjectRegistry):
type = xml.getAttribute("type")
if type == "vector":
return SifVectorComposite
return None
def _prepare_to_dom(self, dom: minidom.Document, param: TypeDescriptor):
element = dom.createElement("composite")
element.setAttribute("type", param.typename)
return element
class SifVectorComposite(SifComposite):
_type = "vector"
_nodes = [
XmlAnimatable("x", "real", 0.),
XmlAnimatable("y", "real", 0.),
]
class SifRandom(SifAstComplex):
_tag = "random"
_nodes = [
XmlAnimatable("link", "_recurse"),
XmlAnimatable("radius", "real", 0.),
XmlAnimatable("seed", "integer", 0),
XmlAnimatable("speed", "real", 1.),
XmlAnimatable("smooth", "integer", Smooth.Cubic, Smooth),
XmlAnimatable("loop", "real", 0.),
]
class SifReference(SifAstComplex):
_tag = "link"
_nodes = [
XmlAnimatable("reference", "_recurse"),
]
class SifScale(SifAstComplex):
_tag = "scale"
_nodes = [
XmlAnimatable("link", "_recurse"),
XmlAnimatable("scalar", "real", 1.),
]
class SifStep(SifAstComplex):
_tag = "step"
_nodes = [
XmlAnimatable("link", "_recurse"),
XmlAnimatable("duration", "time", FrameTime(1, FrameTime.Unit.Seconds)),
XmlAnimatable("start_time", "time", FrameTime(0, FrameTime.Unit.Seconds)),
XmlAnimatable("intersection", "real", 0.5),
]
class SifSubtract(SifAstComplex):
_tag = "subtract"
_nodes = [
XmlAnimatable("lhs", "_recurse"),
XmlAnimatable("rhs", "_recurse"),
XmlAnimatable("scalar", "real", 1.),
]
class SifSwitch(SifAstComplex):
_tag = "switch"
_nodes = [
XmlAnimatable("link_off", "_recurse"),
XmlAnimatable("link_on", "_recurse"),
XmlAnimatable("switch", "bool", False),
]
class SifTimedSwap(SifAstComplex):
_tag = "timed_swap"
_nodes = [
XmlAnimatable("before", "_recurse"),
XmlAnimatable("after", "_recurse"),
XmlAnimatable("time", "time", FrameTime(0, FrameTime.Unit.Seconds)),
XmlAnimatable("length", "time", FrameTime(0, FrameTime.Unit.Seconds)),
]
class SifTimeLoop(SifAstComplex):
_tag = "timeloop"
_nodes = [
XmlAnimatable("link", "_recurse"),
XmlAnimatable("link_time", "time", FrameTime(0, FrameTime.Unit.Seconds)),
XmlAnimatable("local_time", "time", FrameTime(0, FrameTime.Unit.Seconds)),
XmlAnimatable("duration", "time", FrameTime(0, FrameTime.Unit.Seconds)),
]
class SifPower(SifAstComplex):
_tag = "power"
_nodes = [
XmlAnimatable("base", "real", 1.),
XmlAnimatable("power", "real", 1.),
XmlAnimatable("epsilon", "real", 0.000001),
XmlAnimatable("infinite", "real", 999999.),
]
class SifBlineCalcTangent(SifAstComplex):
_tag = "blinecalctangent"
_nodes = [
XmlSifElement("bline", Bline),
XmlAnimatable("loop", "bool", False),
XmlAnimatable("amount", "real", 0.5),
XmlAnimatable("offset", "angle", 0.),
XmlAnimatable("scale", "real", 1.),
XmlAnimatable("fixed_length", "bool", False),
XmlAnimatable("homogeneous", "bool", False),
]
class SifBlineCalcVertex(SifAstComplex):
_tag = "blinecalcvertex"
_nodes = [
XmlSifElement("bline", Bline),
XmlAnimatable("loop", "bool", False),
XmlAnimatable("amount", "real", 0.5),
XmlAnimatable("homogeneous", "bool", False),
]