File indexing completed on 2025-04-27 04:01:13

0001 import math
0002 from .base import LottieObject, LottieProp, LottieEnum, NVector
0003 from .properties import Value, MultiDimensional, GradientColors, ShapeProperty, Bezier, ColorValue
0004 from ..utils.color import Color
0005 from .helpers import Transform
0006 
0007 
0008 class BoundingBox:
0009     """!
0010     Shape bounding box
0011     """
0012     def __init__(self, x1=None, y1=None, x2=None, y2=None):
0013         self.x1 = x1
0014         self.y1 = y1
0015         self.x2 = x2
0016         self.y2 = y2
0017 
0018     def include(self, x, y):
0019         """!
0020         Expands the box to include the point at x, y
0021         """
0022         if x is not None:
0023             if self.x1 is None or self.x1 > x:
0024                 self.x1 = x
0025             if self.x2 is None or self.x2 < x:
0026                 self.x2 = x
0027         if y is not None:
0028             if self.y1 is None or self.y1 > y:
0029                 self.y1 = y
0030             if self.y2 is None or self.y2 < y:
0031                 self.y2 = y
0032 
0033     def expand(self, other):
0034         """!
0035         Expands the bounding box to include another bounding box
0036         """
0037         self.include(other.x1, other.y1)
0038         self.include(other.x2, other.y2)
0039 
0040     def center(self):
0041         """!
0042         Center point of the bounding box
0043         """
0044         return NVector((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
0045 
0046     def isnull(self):
0047         """!
0048         Whether the box is default-initialized
0049         """
0050         return self.x1 is None or self.y2 is None
0051 
0052     def __repr__(self):
0053         return "<BoundingBox [%s, %s] - [%s, %s]>" % (self.x1, self.y1, self.x2, self.y2)
0054 
0055     @property
0056     def width(self):
0057         if self.isnull():
0058             return 0
0059         return self.x2 - self.x1
0060 
0061     @property
0062     def height(self):
0063         if self.isnull():
0064             return 0
0065         return self.y2 - self.y1
0066 
0067     def size(self):
0068         return NVector(self.width, self.height)
0069 
0070 
0071 ## @ingroup Lottie
0072 class ShapeElement(LottieObject):
0073     """!
0074     Base class for all elements of ShapeLayer and Group
0075     """
0076     _props = [
0077         #LottieProp("match_name", "mn", str, False),
0078         LottieProp("hidden", "hd", bool, False),
0079         LottieProp("name", "nm", str, False),
0080         LottieProp("type", "ty", str, False),
0081         LottieProp("property_index", "ix", int, False),
0082         LottieProp("bm", "bm", int, False),
0083     ]
0084     ## %Shape type.
0085     type = None
0086     _shape_classses = None
0087 
0088     def __init__(self):
0089         # After Effect's Match Name. Used for expressions.
0090         #self.match_name = ""
0091 
0092         ## After Effect's Name. Used for expressions.
0093         self.name = None
0094         ## Property index
0095         self.property_index = None
0096         ## Hide element
0097         self.hidden = None
0098         ## @todo figure out?
0099         self.bm = None
0100 
0101     def bounding_box(self, time=0):
0102         """!
0103         Bounding box of the shape element at the given time
0104         """
0105         return BoundingBox()
0106 
0107     @classmethod
0108     def _load_get_class(cls, lottiedict):
0109         if not ShapeElement._shape_classses:
0110             ShapeElement._shape_classses = {}
0111             ShapeElement._load_sub(ShapeElement._shape_classses)
0112         return ShapeElement._shape_classses[lottiedict["ty"]]
0113 
0114     @classmethod
0115     def _load_sub(cls, dict):
0116         for sc in cls.__subclasses__():
0117             if sc.type:
0118                 dict[sc.type] = sc
0119             sc._load_sub(dict)
0120 
0121     def __str__(self):
0122         return self.name or super().__str__()
0123 
0124 
0125 ## @ingroup Lottie
0126 class Shape(ShapeElement):
0127     """!
0128     Drawable shape
0129     """
0130     _props = [
0131         LottieProp("direction", "d", float, False),
0132     ]
0133 
0134     def __init__(self):
0135         ShapeElement.__init__(self)
0136         ## After Effect's Direction. Direction how the shape is drawn. Used for trim path for example.
0137         self.direction = 0
0138 
0139     def to_bezier(self):
0140         """!
0141         Returns a Path corresponding to this Shape
0142         """
0143         raise NotImplementedError()
0144 
0145 
0146 ## @ingroup Lottie
0147 class Rect(Shape):
0148     """!
0149     A simple rectangle shape
0150     """
0151     _props = [
0152         LottieProp("position", "p", MultiDimensional, False),
0153         LottieProp("size", "s", MultiDimensional, False),
0154         LottieProp("rounded", "r", Value, False),
0155     ]
0156     ## %Shape type.
0157     type = "rc"
0158 
0159     def __init__(self, pos=None, size=None, rounded=0):
0160         Shape.__init__(self)
0161         ## Rect's position
0162         self.position = MultiDimensional(pos or NVector(0, 0))
0163         ## Rect's size
0164         self.size = MultiDimensional(size or NVector(0, 0))
0165         ## Rect's rounded corners
0166         self.rounded = Value(rounded)
0167 
0168     def bounding_box(self, time=0):
0169         pos = self.position.get_value(time)
0170         sz = self.size.get_value(time)
0171 
0172         return BoundingBox(
0173             pos[0] - sz[0]/2,
0174             pos[1] - sz[1]/2,
0175             pos[0] + sz[0]/2,
0176             pos[1] + sz[1]/2,
0177         )
0178 
0179     def to_bezier(self):
0180         """!
0181         Returns a Shape corresponding to this rect
0182         """
0183         shape = Path()
0184         kft = set()
0185         if self.position.animated:
0186             kft |= set(kf.time for kf in self.position.keyframes)
0187         if self.size.animated:
0188             kft |= set(kf.time for kf in self.size.keyframes)
0189         if self.rounded.animated:
0190             kft |= set(kf.time for kf in self.rounded.keyframes)
0191         if not kft:
0192             shape.shape.value = self._bezier_t(0)
0193         else:
0194             for time in sorted(kft):
0195                 shape.shape.add_keyframe(time, self._bezier_t(time))
0196         return shape
0197 
0198     def _bezier_t(self, time):
0199         bezier = Bezier()
0200         bb = self.bounding_box(time)
0201         rounded = self.rounded.get_value(time)
0202         tl = NVector(bb.x1, bb.y1)
0203         tr = NVector(bb.x2, bb.y1)
0204         br = NVector(bb.x2, bb.y2)
0205         bl = NVector(bb.x1, bb.y2)
0206 
0207         if not self.rounded.animated and rounded == 0:
0208             bezier.add_point(tl)
0209             bezier.add_point(tr)
0210             bezier.add_point(br)
0211             bezier.add_point(bl)
0212         else:
0213             hh = NVector(rounded/2, 0)
0214             vh = NVector(0, rounded/2)
0215             hd = NVector(rounded, 0)
0216             vd = NVector(0, rounded)
0217             bezier.add_point(tl+vd, outp=-vh)
0218             bezier.add_point(tl+hd, -hh)
0219             bezier.add_point(tr-hd, outp=hh)
0220             bezier.add_point(tr+vd, -vh)
0221             bezier.add_point(br-vd, outp=vh)
0222             bezier.add_point(br-hd, hh)
0223             bezier.add_point(bl+hd, outp=-hh)
0224             bezier.add_point(bl-vd, vh)
0225 
0226         bezier.close()
0227         return bezier
0228 
0229 
0230 ## @ingroup Lottie
0231 class StarType(LottieEnum):
0232     Star = 1
0233     Polygon = 2
0234 
0235 
0236 ## @ingroup Lottie
0237 class Star(Shape):
0238     """!
0239     Star shape
0240     """
0241     _props = [
0242         LottieProp("position", "p", MultiDimensional, False),
0243         LottieProp("inner_radius", "ir", Value, False),
0244         LottieProp("inner_roundness", "is", Value, False),
0245         LottieProp("outer_radius", "or", Value, False),
0246         LottieProp("outer_roundness", "os", Value, False),
0247         LottieProp("rotation", "r", Value, False),
0248         LottieProp("points", "pt", Value, False),
0249         LottieProp("star_type", "sy", StarType, False),
0250     ]
0251     ## %Shape type.
0252     type = "sr"
0253 
0254     def __init__(self):
0255         Shape.__init__(self)
0256         ## Star's position
0257         self.position = MultiDimensional(NVector(0, 0))
0258         ## Star's inner radius. (Star only)
0259         self.inner_radius = Value()
0260         ## Star's inner roundness. (Star only)
0261         self.inner_roundness = Value()
0262         ## Star's outer radius.
0263         self.outer_radius = Value()
0264         ## Star's outer roundness.
0265         self.outer_roundness = Value()
0266         ## Star's rotation.
0267         self.rotation = Value()
0268         ## Star's number of points.
0269         self.points = Value(5)
0270         ## Star's type. Polygon or Star.
0271         self.star_type = StarType.Star
0272 
0273     def bounding_box(self, time=0):
0274         pos = self.position.get_value(time)
0275         r = self.outer_radius.get_value(time)
0276 
0277         return BoundingBox(
0278             pos[0] - r,
0279             pos[1] - r,
0280             pos[0] + r,
0281             pos[1] + r,
0282         )
0283 
0284     def to_bezier(self):
0285         """!
0286         Returns a Shape corresponding to this star
0287         """
0288         shape = Path()
0289         kft = set()
0290         if self.position.animated:
0291             kft |= set(kf.time for kf in self.position.keyframes)
0292         if self.inner_radius.animated:
0293             kft |= set(kf.time for kf in self.inner_radius.keyframes)
0294         if self.inner_roundness.animated:
0295             kft |= set(kf.time for kf in self.inner_roundness.keyframes)
0296         if self.points.animated:
0297             kft |= set(kf.time for kf in self.points.keyframes)
0298         if self.rotation.animated:
0299             kft |= set(kf.time for kf in self.rotation.keyframes)
0300         # TODO inner_roundness / outer_roundness
0301         if not kft:
0302             shape.shape.value = self._bezier_t(0)
0303         else:
0304             for time in sorted(kft):
0305                 shape.shape.add_keyframe(time, self._bezier_t(time))
0306         return shape
0307 
0308     def _bezier_t(self, time):
0309         bezier = Bezier()
0310         pos = self.position.get_value(time)
0311         r1 = self.inner_radius.get_value(time)
0312         r2 = self.outer_radius.get_value(time)
0313         rot = -(self.rotation.get_value(time)) * math.pi / 180 + math.pi
0314         p = self.points.get_value(time)
0315         halfd = -math.pi / p
0316 
0317         for i in range(int(p)):
0318             main_angle = rot + i * halfd * 2
0319             dx = r2 * math.sin(main_angle)
0320             dy = r2 * math.cos(main_angle)
0321             bezier.add_point(NVector(pos.x + dx, pos.y + dy))
0322 
0323             if self.star_type == StarType.Star:
0324                 dx = r1 * math.sin(main_angle+halfd)
0325                 dy = r1 * math.cos(main_angle+halfd)
0326                 bezier.add_point(NVector(pos.x + dx, pos.y + dy))
0327 
0328         bezier.close()
0329         return bezier
0330 
0331 
0332 ## @ingroup Lottie
0333 class Ellipse(Shape):
0334     """!
0335     Ellipse shape
0336     """
0337     _props = [
0338         LottieProp("position", "p", MultiDimensional, False),
0339         LottieProp("size", "s", MultiDimensional, False),
0340     ]
0341     ## %Shape type.
0342     type = "el"
0343 
0344     def __init__(self, position=None, size=None):
0345         Shape.__init__(self)
0346         ## Ellipse's position
0347         self.position = MultiDimensional(position or NVector(0, 0))
0348         ## Ellipse's size
0349         self.size = MultiDimensional(size or NVector(0, 0))
0350 
0351     def bounding_box(self, time=0):
0352         pos = self.position.get_value(time)
0353         sz = self.size.get_value(time)
0354 
0355         return BoundingBox(
0356             pos[0] - sz[0]/2,
0357             pos[1] - sz[1]/2,
0358             pos[0] + sz[0]/2,
0359             pos[1] + sz[1]/2,
0360         )
0361 
0362     def to_bezier(self):
0363         """!
0364         Returns a Shape corresponding to this ellipse
0365         """
0366         shape = Path()
0367         kft = set()
0368         if self.position.animated:
0369             kft |= set(kf.time for kf in self.position.keyframes)
0370         if self.size.animated:
0371             kft |= set(kf.time for kf in self.size.keyframes)
0372         if not kft:
0373             shape.shape.value = self._bezier_t(0)
0374         else:
0375             for time in sorted(kft):
0376                 shape.shape.add_keyframe(time, self._bezier_t(time))
0377         return shape
0378 
0379     def _bezier_t(self, time):
0380         from ..utils.ellipse import Ellipse as EllipseConverter
0381 
0382         bezier = Bezier()
0383         position = self.position.get_value(time)
0384         radii = self.size.get_value(time) / 2
0385 
0386         el = EllipseConverter(position, radii, 0)
0387         points = el.to_bezier(0, math.pi*2)
0388         for point in points[1:]:
0389             bezier.add_point(point.vertex, point.in_tangent, point.out_tangent)
0390 
0391         bezier.close()
0392         return bezier
0393 
0394 
0395 ## @ingroup Lottie
0396 class Path(Shape):
0397     """!
0398     Animatable Bezier curve
0399     """
0400     _props = [
0401         LottieProp("shape", "ks", ShapeProperty, False),
0402         LottieProp("index", "ind", int, False),
0403     ]
0404     ## %Shape type.
0405     type = "sh"
0406 
0407     def __init__(self, bezier=None):
0408         Shape.__init__(self)
0409         ## Shape's vertices
0410         self.shape = ShapeProperty(bezier or Bezier())
0411         ## @todo Index?
0412         self.index = None
0413 
0414     def bounding_box(self, time=0):
0415         pos = self.shape.get_value(time)
0416 
0417         bb = BoundingBox()
0418         for v in pos.vertices:
0419             bb.include(*v)
0420 
0421         return bb
0422 
0423     def to_bezier(self):
0424         return self.clone()
0425 
0426 
0427 ## @ingroup Lottie
0428 class Group(ShapeElement):
0429     """!
0430     ShapeElement that can contain other shapes
0431     @note Shapes inside the same group will create "holes" in other shapes
0432     """
0433     _props = [
0434         LottieProp("number_of_properties", "np", float, False),
0435         LottieProp("shapes", "it", ShapeElement, True),
0436     ]
0437     ## %Shape type.
0438     type = "gr"
0439 
0440     def __init__(self):
0441         ShapeElement.__init__(self)
0442         ## Group number of properties. Used for expressions.
0443         self.number_of_properties = None
0444         ## Group list of items
0445         self.shapes = [TransformShape()]
0446 
0447     @property
0448     def transform(self):
0449         return self.shapes[-1]
0450 
0451     def bounding_box(self, time=0):
0452         bb = BoundingBox()
0453         for v in self.shapes:
0454             bb.expand(v.bounding_box(time))
0455 
0456         if not bb.isnull():
0457             mat = self.transform.to_matrix(time)
0458             points = [
0459                 mat.apply(NVector(bb.x1, bb.y1)),
0460                 mat.apply(NVector(bb.x1, bb.y2)),
0461                 mat.apply(NVector(bb.x2, bb.y2)),
0462                 mat.apply(NVector(bb.x2, bb.y1)),
0463             ]
0464             x1 = min(p.x for p in points)
0465             x2 = max(p.x for p in points)
0466             y1 = min(p.y for p in points)
0467             y2 = max(p.y for p in points)
0468             return BoundingBox(x1, y1, x2, y2)
0469         return bb
0470 
0471     def add_shape(self, shape):
0472         self.shapes.insert(-1, shape)
0473         return shape
0474 
0475     def insert_shape(self, index, shape):
0476         self.shapes.insert(index, shape)
0477         return shape
0478 
0479 
0480 ## @ingroup Lottie
0481 class FillRule(LottieEnum):
0482     NonZero = 1
0483     EvenOdd = 2
0484 
0485 
0486 ## @ingroup Lottie
0487 class Fill(ShapeElement):
0488     """!
0489     Solid fill color
0490     """
0491     _props = [
0492         LottieProp("opacity", "o", Value, False),
0493         LottieProp("color", "c", ColorValue, False),
0494         LottieProp("fill_rule", "r", FillRule, False),
0495     ]
0496     ## %Shape type.
0497     type = "fl"
0498 
0499     def __init__(self, color=None):
0500         ShapeElement.__init__(self)
0501         ## Fill Opacity
0502         self.opacity = Value(100)
0503         ## Fill Color
0504         self.color = ColorValue(color or Color(1, 1, 1))
0505         ## Fill rule
0506         self.fill_rule = None
0507 
0508 
0509 ## @ingroup Lottie
0510 class GradientType(LottieEnum):
0511     Linear = 1
0512     Radial = 2
0513 
0514 
0515 ## @ingroup Lottie
0516 class Gradient(LottieObject):
0517     _props = [
0518         LottieProp("start_point", "s", MultiDimensional, False),
0519         LottieProp("end_point", "e", MultiDimensional, False),
0520         LottieProp("gradient_type", "t", GradientType, False),
0521         LottieProp("highlight_length", "h", Value, False),
0522         LottieProp("highlight_angle", "a", Value, False),
0523         LottieProp("colors", "g", GradientColors, False),
0524     ]
0525 
0526     def __init__(self, colors=[]):
0527         ## Fill Opacity
0528         self.opacity = Value(100)
0529         ## Gradient Start Point
0530         self.start_point = MultiDimensional(NVector(0, 0))
0531         ## Gradient End Point
0532         self.end_point = MultiDimensional(NVector(0, 0))
0533         ## Gradient Type
0534         self.gradient_type = GradientType.Linear
0535         ## Gradient Highlight Length. Only if type is Radial
0536         self.highlight_length = Value()
0537         ## Highlight Angle. Only if type is Radial
0538         self.highlight_angle = Value()
0539         ## Gradient Colors
0540         self.colors = GradientColors(colors)
0541 
0542 
0543 ## @ingroup Lottie
0544 class GradientFill(ShapeElement, Gradient):
0545     """!
0546     Gradient fill
0547     """
0548     _props = [
0549         LottieProp("opacity", "o", Value, False),
0550         LottieProp("fill_rule", "r", FillRule, False),
0551     ]
0552     ## %Shape type.
0553     type = "gf"
0554 
0555     def __init__(self, colors=[]):
0556         ShapeElement.__init__(self)
0557         Gradient.__init__(self, colors)
0558         ## Fill Opacity
0559         self.opacity = Value(100)
0560         ## Fill rule
0561         self.fill_rule = None
0562 
0563 
0564 ## @ingroup Lottie
0565 class LineJoin(LottieEnum):
0566     Miter = 1
0567     Round = 2
0568     Bevel = 3
0569 
0570 
0571 ## @ingroup Lottie
0572 class LineCap(LottieEnum):
0573     Butt = 1
0574     Round = 2
0575     Square = 3
0576 
0577 
0578 ## @ingroup Lottie
0579 class StrokeDashType(LottieEnum):
0580     Dash = "d"
0581     Gap = "g"
0582     Offset = "o"
0583 
0584 
0585 ## @ingroup Lottie
0586 class StrokeDash(LottieObject):
0587     _props = [
0588         LottieProp("name", "nm", str, False),
0589         LottieProp("type", "n", StrokeDashType, False),
0590         LottieProp("length", "v", Value, False),
0591     ]
0592 
0593     def __init__(self, length=0, type=StrokeDashType.Dash):
0594         self.name = type.name.lower()
0595         self.type = type
0596         self.length = Value(length)
0597 
0598     def __str__(self):
0599         return self.name or super().__str__()
0600 
0601 
0602 ## @ingroup Lottie
0603 class BaseStroke(LottieObject):
0604     _props = [
0605         LottieProp("line_cap", "lc", LineCap, False),
0606         LottieProp("line_join", "lj", LineJoin, False),
0607         LottieProp("miter_limit", "ml", float, False),
0608         LottieProp("opacity", "o", Value, False),
0609         LottieProp("width", "w", Value, False),
0610         LottieProp("dashes", "d", StrokeDash, True),
0611     ]
0612 
0613     def __init__(self, width=1):
0614         ## Stroke Line Cap
0615         self.line_cap = LineCap.Round
0616         ## Stroke Line Join
0617         self.line_join = LineJoin.Round
0618         ## Stroke Miter Limit. Only if Line Join is set to Miter.
0619         self.miter_limit = 0
0620         ## Stroke Opacity
0621         self.opacity = Value(100)
0622         ## Stroke Width
0623         self.width = Value(width)
0624         ## Dashes
0625         self.dashes = None
0626 
0627 
0628 ## @ingroup Lottie
0629 class Stroke(ShapeElement, BaseStroke):
0630     """!
0631     Solid stroke
0632     """
0633     _props = [
0634         LottieProp("color", "c", MultiDimensional, False),
0635     ]
0636     ## %Shape type.
0637     type = "st"
0638 
0639     def __init__(self, color=None, width=1):
0640         ShapeElement.__init__(self)
0641         BaseStroke.__init__(self, width)
0642         ## Stroke Color
0643         self.color = ColorValue(color or Color(0, 0, 0))
0644 
0645 
0646 ## @ingroup Lottie
0647 class GradientStroke(ShapeElement, BaseStroke, Gradient):
0648     """!
0649     Gradient stroke
0650     """
0651     ## %Shape type.
0652     type = "gs"
0653 
0654     def __init__(self, stroke_width=1):
0655         ShapeElement.__init__(self)
0656         BaseStroke.__init__(self, stroke_width)
0657         Gradient.__init__(self)
0658 
0659     def bounding_box(self, time=0):
0660         return BoundingBox()
0661 
0662 
0663 ## @ingroup Lottie
0664 class TransformShape(ShapeElement, Transform):
0665     """!
0666     Group transform
0667     """
0668     ## %Shape type.
0669     type = "tr"
0670 
0671     def __init__(self):
0672         ShapeElement.__init__(self)
0673         Transform.__init__(self)
0674         self.anchor_point = MultiDimensional(NVector(0, 0))
0675 
0676 
0677 ## @ingroup Lottie
0678 class Composite(LottieEnum):
0679     Above = 1
0680     Below = 2
0681 
0682 
0683 ## @ingroup Lottie
0684 class RepeaterTransform(Transform):
0685     _props = [
0686         LottieProp("start_opacity", "so", Value, False),
0687         LottieProp("end_opacity", "eo", Value, False),
0688     ]
0689 
0690     def __init__(self):
0691         Transform.__init__(self)
0692         self.start_opacity = Value(100)
0693         self.end_opacity = Value(100)
0694 
0695 
0696 ## @ingroup Lottie
0697 class Modifier(ShapeElement):
0698     pass
0699 
0700 
0701 ## @ingroup Lottie
0702 class TrimMultipleShapes(LottieEnum):
0703     Simultaneously = 1
0704     Individually = 2
0705 
0706 
0707 ## @ingroup Lottie
0708 ## @todo Implement SIF Export
0709 class Trim(Modifier):
0710     """
0711     Trims shapes into a segment
0712     """
0713     _props = [
0714         LottieProp("start", "s", Value, False),
0715         LottieProp("end", "e", Value, False),
0716         LottieProp("offset", "o", Value, False),
0717         LottieProp("multiple", "m", TrimMultipleShapes, False),
0718     ]
0719     ## %Shape type.
0720     type = "tm"
0721 
0722     def __init__(self):
0723         ShapeElement.__init__(self)
0724         ## Start of the segment, as a percentage
0725         self.start = Value(0)
0726         ## End of the segment, as a percentage
0727         self.end = Value(100)
0728         ## start/end offset, as an angle (0, 360)
0729         self.offset = Value(0)
0730         ## @todo?
0731         self.multiple = None
0732 
0733 
0734 ## @ingroup Lottie
0735 class Repeater(Modifier):
0736     """
0737     Duplicates previous shapes in a group
0738     """
0739     _props = [
0740         LottieProp("copies", "c", Value, False),
0741         LottieProp("offset", "o", Value, False),
0742         LottieProp("composite", "m", Composite, False),
0743         LottieProp("transform", "tr", RepeaterTransform, False),
0744     ]
0745     ## %Shape type.
0746     type = "rp"
0747 
0748     def __init__(self, copies=1):
0749         Modifier.__init__(self)
0750         ## Number of Copies
0751         self.copies = Value(copies)
0752         ## Offset of Copies
0753         self.offset = Value()
0754         ## Composite of copies
0755         self.composite = Composite.Above
0756         ## Transform values for each repeater copy
0757         self.transform = RepeaterTransform()
0758 
0759 
0760 ## @ingroup Lottie
0761 ## @todo Implement SIF Export
0762 class RoundedCorners(Modifier):
0763     """
0764     Rounds corners of other shapes
0765     """
0766     _props = [
0767         LottieProp("radius", "r", Value, False),
0768     ]
0769     ## %Shape type.
0770     type = "rd"
0771 
0772     def __init__(self):
0773         Modifier.__init__(self)
0774         ## Rounded Corner Radius
0775         self.radius = Value()
0776 
0777 
0778 ## @ingroup Lottie
0779 ## @ingroup LottieCheck
0780 ## @note marked as unsupported by lottie
0781 class Merge(ShapeElement):
0782     _props = [
0783         LottieProp("merge_mode", "mm", float, False),
0784     ]
0785     ## %Shape type.
0786     type = "mm"
0787 
0788     def __init__(self):
0789         ShapeElement.__init__(self)
0790         ## Merge Mode
0791         self.merge_mode = 1
0792 
0793 
0794 ## @ingroup Lottie
0795 ## @note marked as unsupported by lottie
0796 class Twist(ShapeElement):
0797     _props = [
0798         LottieProp("angle", "a", Value, False),
0799         LottieProp("center", "c", MultiDimensional, False),
0800     ]
0801     ## %Shape type.
0802     type = "tw"
0803 
0804     def __init__(self):
0805         ShapeElement.__init__(self)
0806         self.angle = Value(0)
0807         self.center = MultiDimensional(NVector(0, 0))