mirror of
https://github.com/OpenVGLab/OmniLottie.git
synced 2026-09-17 15:46:26 +00:00
166 lines
5.9 KiB
Python
166 lines
5.9 KiB
Python
from xml.dom import minidom
|
|
import enum
|
|
from uuid import uuid4
|
|
|
|
from lottie.nvector import NVector
|
|
from lottie.parsers.sif.xml.utils import xml_text, str_to_bool
|
|
from lottie.parsers.sif.xml.utils import xml_child_elements, value_from_xml_string, xml_make_text, value_to_xml_string
|
|
from lottie.parsers.sif.sif.frame_time import FrameTime
|
|
|
|
|
|
class ObjectRegistry:
|
|
def __init__(self):
|
|
self.registry = {}
|
|
|
|
def register_as(self, object, key):
|
|
self.registry[key] = object
|
|
|
|
def register(self, object):
|
|
guid = getattr(object, "guid", None)
|
|
if guid is None:
|
|
guid = self.guid()
|
|
object.guid = guid
|
|
self.registry[guid] = object
|
|
|
|
@classmethod
|
|
def guid(cls):
|
|
return str(uuid4()).replace("-", "").upper()
|
|
|
|
def get_object(self, guid):
|
|
return self.registry[guid]
|
|
|
|
|
|
def noop(x):
|
|
return x
|
|
|
|
|
|
class TypeDescriptor:
|
|
_type_tag_names = {
|
|
"bone_object": "bone"
|
|
}
|
|
|
|
def __init__(self, typename, default=None, type_wrapper=noop):
|
|
self.typename = typename
|
|
self.type_wrapper = type_wrapper
|
|
self.default_value = default
|
|
|
|
def value_to_xml_element(self, value, dom: minidom.Document):
|
|
element = dom.createElement(self.tag_name)
|
|
|
|
if self.typename == "vector":
|
|
element.appendChild(xml_make_text(dom, "x", str(value.x)))
|
|
element.appendChild(xml_make_text(dom, "y", str(value.y)))
|
|
if hasattr(value, "guid"):
|
|
element.setAttribute("guid", value.guid)
|
|
elif self.typename == "color":
|
|
element.appendChild(xml_make_text(dom, "r", str(value[0])))
|
|
element.appendChild(xml_make_text(dom, "g", str(value[1])))
|
|
element.appendChild(xml_make_text(dom, "b", str(value[2])))
|
|
element.appendChild(xml_make_text(dom, "a", str(value[3])))
|
|
if hasattr(value, "guid"):
|
|
element.setAttribute("guid", value.guid)
|
|
elif self.typename == "gradient":
|
|
for point in value:
|
|
element.appendChild(point.to_dom(dom))
|
|
elif self.typename == "bool":
|
|
element.setAttribute("value", "true" if value else "false")
|
|
elif self.typename == "bone_object":
|
|
element.setAttribute("guid", value.guid)
|
|
element.setAttribute("type", self.typename)
|
|
elif self.typename == "string":
|
|
element.appendChild(dom.createTextNode(value))
|
|
else:
|
|
if isinstance(value, enum.Enum):
|
|
value = value.value
|
|
element.setAttribute("value", str(value))
|
|
|
|
return element
|
|
|
|
@property
|
|
def tag_name(self):
|
|
return self._type_tag_names.get(self.typename, self.typename)
|
|
|
|
def value_from_xml_element(self, xml: minidom.Element, registry: ObjectRegistry):
|
|
if xml.tagName != self.tag_name:
|
|
raise ValueError("Wrong value type (%s instead of %s)" % (xml.tagName, self.tag_name))
|
|
|
|
guid = xml.getAttribute("guid")
|
|
if guid and guid in registry.registry:
|
|
value = registry.registry[guid]
|
|
elif self.typename == "vector":
|
|
value = NVector(
|
|
float(xml_text(xml.getElementsByTagName("x")[0])),
|
|
float(xml_text(xml.getElementsByTagName("y")[0]))
|
|
)
|
|
if xml.getAttribute("guid"):
|
|
value.guid = xml.getAttribute("guid")
|
|
registry.register(value)
|
|
elif self.typename == "color":
|
|
value = NVector(
|
|
float(xml_text(xml.getElementsByTagName("r")[0])),
|
|
float(xml_text(xml.getElementsByTagName("g")[0])),
|
|
float(xml_text(xml.getElementsByTagName("b")[0])),
|
|
float(xml_text(xml.getElementsByTagName("a")[0]))
|
|
)
|
|
elif self.typename == "gradient":
|
|
value = [
|
|
GradientPoint.from_dom(sub, registry)
|
|
for sub in xml_child_elements(xml, GradientPoint.type.typename)
|
|
]
|
|
elif self.typename == "real" or self.typename == "angle":
|
|
value = float(xml.getAttribute("value"))
|
|
elif self.typename == "integer":
|
|
value = int(xml.getAttribute("value"))
|
|
elif self.typename == "time":
|
|
value = FrameTime.parse_string(xml.getAttribute("value"), registry)
|
|
elif self.typename == "bool":
|
|
value = str_to_bool(xml.getAttribute("value"))
|
|
elif self.typename == "string":
|
|
return xml_text(xml)
|
|
elif self.typename == "bone_object":
|
|
# Already done above but this forces the guid to be present
|
|
return registry.get_object(xml.getAttribute("guid"))
|
|
else:
|
|
raise ValueError("Unsupported type %s" % self.typename)
|
|
|
|
return self.type_wrapper(value)
|
|
|
|
|
|
class GradientPoint:
|
|
type = TypeDescriptor("color")
|
|
|
|
def __init__(self, pos: float, color: NVector):
|
|
self.pos = pos
|
|
self.color = color
|
|
|
|
def to_dom(self, dom: minidom.Document):
|
|
element = self.type.value_to_xml_element(self.color, dom)
|
|
element.setAttribute("pos", value_to_xml_string(self.pos, float))
|
|
return element
|
|
|
|
@classmethod
|
|
def from_dom(cls, xml: minidom.Element, registry: ObjectRegistry):
|
|
return GradientPoint(
|
|
value_from_xml_string(xml.getAttribute("pos"), float, registry),
|
|
cls.type.value_from_xml_element(xml, registry)
|
|
)
|
|
|
|
def __repr__(self):
|
|
return "<GradientPoint %s %s>" % (self.pos, self.color)
|
|
|
|
|
|
class SifNodeMeta(type):
|
|
def __new__(cls, name, bases, attr):
|
|
props = []
|
|
for base in bases:
|
|
if type(base) == cls:
|
|
props += base._nodes
|
|
attr["_nodes"] = props + attr.get("_nodes", [])
|
|
if "_tag" not in attr:
|
|
attr["_tag"] = name.lower()
|
|
attr["_nodemap"] = {
|
|
node.att_name: node
|
|
for node in attr["_nodes"]
|
|
}
|
|
return super().__new__(cls, name, bases, attr)
|