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))