def get_last_control_point(interval): """ Returns the last control point of a bezier interval Args: interval (dict) : Holds one interval of the bezier curve that is two waypoints Returns: (common.Vector.Vector) If the interval holds position bezier (float) Else : the interval holds value bezier """ if len(interval["e"]) >= 2: en = Vector(interval["e"][0], interval["e"][1]) else: en = interval["e"][0] return en
def get_first_control_point(interval): """ Returns the first control point of a bezier interval Args: interval (dict) : Holds one interval of the bezier curve that is two waypoints Returns: (misc.Vector) If the interval holds position bezier (float) Else : the interval holds value bezier """ if len(interval["s"]) >= 2: st = Vector(interval["s"][0], interval["s"][1]) else: st = interval["s"][0] return st
def get_outline_param_at_frame(composite, fr): """ Given a composite and frame, returns the parameters of the outline layer at that frame Args: composite (lxml.etree._Element) : Vertex of outline layer in Synfig format fr (int) : frame number Returns: (misc.Vector) : position of the vertex (float) : width of the vertex (misc.Vector) : Tangent 1 of the vertex (misc.Vector) : Tangent 2 of the vertex (bool) : True if radius split is ticked at this frame (bool) : True if tangent split is ticked at this frame """ for child in composite: if child.tag == "point_path": dictionary = ast.literal_eval(child.text) pos = get_vector_at_frame(dictionary, fr) elif child.tag == "width_path": dictionary = ast.literal_eval(child.text) width = to_Synfig_axis(get_vector_at_frame(dictionary, fr), "real") elif child.tag == "t1": t1 = child[0] elif child.tag == "t2": t2 = child[0] elif child.tag == "split_radius": split_r = child elif child.tag == "split_angle": split_a = child # Convert pos back to Synfig coordinates pos = to_Synfig_axis(pos, "vector") pos_ret = Vector(pos[0], pos[1]) t1, t2 = get_tangent_at_frame(t1, t2, split_r, split_a, fr) # Convert to Synfig units t1 /= settings.PIX_PER_UNIT t2 /= settings.PIX_PER_UNIT split_r_val = get_bool_at_frame(split_r[0], fr) split_a_val = get_bool_at_frame(split_a[0], fr) return pos_ret, width, t1, t2, split_r_val, split_a_val
def get_outline_param_at_frame(entry, fr): """ Given a entry and frame, returns the parameters of the outline layer at that frame Args: entry (dict) : Vertex of outline layer in Synfig format fr (int) : frame number Returns: (common.Vector.Vector) : position of the vertex (float) : width of the vertex (common.Vector.Vector) : Tangent 1 of the vertex (common.Vector.Vector) : Tangent 2 of the vertex (bool) : True if radius split is ticked at this frame (bool) : True if tangent split is ticked at this frame """ pos = entry["point"].get_value(fr) # Convert pos back to Synfig coordinates pos = to_Synfig_axis(pos, "vector") pos_ret = Vector(pos[0], pos[1]) width = entry["width"].get_value(fr) width = to_Synfig_axis(width, "real") t1 = entry["t1"] t2 = entry["t2"] split_r = entry["split_radius"] split_a = entry["split_angle"] t1, t2 = get_tangent_at_frame(t1, t2, split_r, split_a, fr) # Convert to Synfig units t1 /= settings.PIX_PER_UNIT t2 /= settings.PIX_PER_UNIT split_r_val = split_r.get_value(fr) split_a_val = split_a.get_value(fr) return pos_ret, width, t1, t2, split_r_val, split_a_val
def get_vector(waypoint): """ Given a waypoint, it parses the string vector into Vector class defined in this converter Args: waypoint (lxml.etree._Element) : Synfig format waypoint Returns: (common.Vector.Vector) : x and y axis values stores in Vector format """ # converting radius and angle to a vector if waypoint.tag == "radial_composite": for child in waypoint: if child.tag == "radius": radius = float(child[0].attrib["value"]) radius *= settings.PIX_PER_UNIT elif child.tag == "theta": angle = float(child[0].attrib["value"]) x, y = radial_to_tangent(radius, angle) else: x = float(waypoint[0][0].text) y = float(waypoint[0][1].text) return Vector(x, y)
def synfig_circle(st_val, origin_param, radius_param, fr): """ Calculates the points for the circle layer as in Synfig: https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/circle.cpp Args: st_val (dict) : Lottie format circle is stored in this origin (common.Param.Param) : Lottie format origin of circle radius (common.Param.Param) : Lottie format radius of circle fr (int) : Frame number Returns: (None) """ num_splines = 8 angle = 180.0 / num_splines angle *= ((math.pi * 2) / 360) k = 1.0 / math.cos(angle) radius = abs(to_Synfig_axis(radius_param.get_value(fr), "real")) origin = origin_param.get_value(fr) matrix = Matrix2() matrix.set_rotate(angle) p0, p1, p2 = Vector(), Vector(), Vector(radius, 0) # Setup chunk list chunk_list = [] chunk_list.append([p2, Vector(), Vector()]) i = 0 while i < num_splines: p0 = p2 p1 = matrix.get_transformed(p0) p2 = matrix.get_transformed(p1) cp1, cp2 = quadratic_to_cubic(p2, k * p1, p0) cur_tan = p2 - cp1 chunk_list[-1][2] = cp2 - p0 chunk_list.append([p2, cur_tan, Vector()]) i += 1 add(chunk_list, st_val, origin)
def synfig_outline(bline, st_val, origin_p, outer_width_p, sharp_cusps_p, expand_p, r_tip0_p, r_tip1_p, homo_width_p, fr): """ Calculates the points for the outline layer as in Synfig: https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/outline.cpp Args: bline_point (common.Bline.Bline) : Synfig format bline points of outline layer st_val (dict) : Lottie format outline stored in this origin_p (common.Param.Param) : Lottie format origin of outline layer outer_width_p (common.Param.Param) : Lottie format outer width sharp_cusps_p (common.Param.Param) : sharp cusps in Synfig format expand_p (common.Param.Param) : Lottie format expand parameter r_tip0_p (common.Param.Param) : Round tip[0] in Synfig format r_tip1_p (common.Param.Param) : Round tip[1] in Synfig format homo_width_p (common.Param.Param) : Homogeneous width in Synfig format fr (int) : Frame number Returns: (None) """ EPSILON = 0.000000001 SAMPLES = 50 CUSP_TANGENT_ADJUST = 0.025 CUSP_THRESHOLD = 0.40 SPIKE_AMOUNT = 4 ROUND_END_FACTOR = 4 bline_list = bline.get_list_at_frame(fr) outer_width = to_Synfig_axis(outer_width_p.get_value(fr), "real") expand = to_Synfig_axis(expand_p.get_value(fr), "real") sharp_cusps = sharp_cusps_p.get_value(fr) r_tip0 = r_tip0_p.get_value(fr) r_tip1 = r_tip1_p.get_value(fr) homo_width = homo_width_p.get_value(fr) gv = get_outline_grow(fr) # Setup chunk list side_a, side_b = [], [] # Check if looped loop = bline.get_loop() # Iterators end_it = len(bline_list) next_it = 0 if loop: iter_it = end_it - 1 else: iter_it = next_it next_it += 1 first_point = bline_list[iter_it] first_tangent = bline_list[0].get_tangent2() last_tangent = first_point.get_tangent1() # If we are looped and drawing sharp cusps, we 'll need a value # for the incoming tangent. This code fails if we have a "degraded" spline # with just one vertex, so we avoid such case. if loop and sharp_cusps and last_tangent.is_equal_to(Vector(0, 0)) and len(bline_list) > 1: prev_it = iter_it prev_it -= 1 prev_it %= len(bline_list) prev_point = bline_list[prev_it] curve = Hermite(prev_point.get_vertex(), first_point.get_vertex(), prev_point.get_tangent2(), first_point.get_tangent1()) last_tangent = curve.derivative(1.0 - CUSP_TANGENT_ADJUST) first = not loop while next_it != end_it: bp1 = bline_list[iter_it] bp2 = bline_list[next_it] # Setup tangents prev_t = bp1.get_tangent1() iter_t = bp1.get_tangent2() next_t = bp2.get_tangent1() split_flag = bp1.get_split_tangent_angle() or bp1.get_split_tangent_radius() # If iter_it.t2 == 0 and next.t1 == 0, this is a straight line if iter_t.is_equal_to(Vector(0, 0)) and next_t.is_equal_to(Vector(0, 0)): iter_t = next_t = bp2.get_vertex() - bp1.get_vertex() # If the 2 points are on top of each other, ignore this segment # leave 'first' true if was before if iter_t.is_equal_to(Vector(0, 0)): iter_it = next_it iter_it %= len(bline_list) next_it += 1 continue # Setup the curve curve = Hermite(bp1.get_vertex(), bp2.get_vertex(), iter_t, next_t) # Setup width's iter_w = gv*(bp1.get_width() * outer_width * 0.5 + expand) next_w = gv*(bp2.get_width() * outer_width * 0.5 + expand) if first: first_tangent = curve.derivative(CUSP_TANGENT_ADJUST) # Make cusps as necassary if not first and \ sharp_cusps and \ split_flag and \ ((not prev_t.is_equal_to(iter_t)) or (iter_t.is_equal_to(Vector(0, 0)))) and \ (not last_tangent.is_equal_to(Vector(0, 0))): curr_tangent = curve.derivative(CUSP_TANGENT_ADJUST) t1 = last_tangent.perp().norm() t2 = curr_tangent.perp().norm() cross = t1*t2.perp() perp = (t1 - t2).mag() if cross > CUSP_THRESHOLD: p1 = bp1.get_vertex() + t1*iter_w p2 = bp1.get_vertex() + t2*iter_w side_a.append([line_intersection(p1, last_tangent, p2, curr_tangent), Vector(0, 0), Vector(0, 0)]) elif cross < -CUSP_THRESHOLD: p1 = bp1.get_vertex() - t1*iter_w p2 = bp1.get_vertex() - t2*iter_w side_b.append([line_intersection(p1, last_tangent, p2, curr_tangent), Vector(0, 0), Vector(0, 0)]) elif cross > 0.0 and perp > 1.0: amount = max(0.0, cross/CUSP_THRESHOLD) * (SPIKE_AMOUNT - 1.0) + 1.0 side_a.append([bp1.get_vertex() + (t1 + t2).norm()*iter_w*amount, Vector(0, 0), Vector(0, 0)]) elif cross < 0 and perp > 1: amount = max(0.0, -cross/CUSP_THRESHOLD) * (SPIKE_AMOUNT - 1.0) + 1.0 side_b.append([bp1.get_vertex() - (t1 + t2).norm()*iter_w*amount, Vector(0, 0), Vector(0, 0)]) # Precalculate positions and coefficients length = 0.0 points = [] dists = [] n = 0.0 itr = 0 while n < 1.000001: points.append(curve.value(n)) if n: length += (points[itr] - points[itr-1]).mag() dists.append(length) n += 1.0/SAMPLES itr += 1 length += (curve.value(1) - points[itr-1]).mag() div_length = 1 if length > EPSILON: div_length = 1.0 / length # Might not need /3 for the tangents generated finally - VERY IMPORTANT # Make the outline pt = curve.derivative(CUSP_TANGENT_ADJUST) / 3 n = 0.0 itr = 0 while n < 1.000001: t = curve.derivative(min(max(n, CUSP_TANGENT_ADJUST), 1.0 - CUSP_TANGENT_ADJUST)) / 3 d = t.perp().norm() k = dists[itr] * div_length if not homo_width: k = n w = (next_w - iter_w)*k + iter_w if False and n: # create curve a = points[itr-1] + d*w b = points[itr] + d*w tk = (b - a).mag() * div_length side_a.append([b, pt*tk, -t*tk]) a = points[itr-1] - d*w b = points[itr] - d*w tk = (b - a).mag() * div_length side_b.append([b, pt*tk, -t*tk]) else: side_a.append([points[itr] + d*w, Vector(0, 0), Vector(0, 0)]) side_b.append([points[itr] - d*w, Vector(0, 0), Vector(0, 0)]) pt = t itr += 1 n += 1.0/SAMPLES last_tangent = curve.derivative(1.0 - CUSP_TANGENT_ADJUST) side_a.append([curve.value(1.0) + last_tangent.perp().norm()*next_w, Vector(0, 0), Vector(0, 0)]) side_b.append([curve.value(1.0) - last_tangent.perp().norm()*next_w, Vector(0, 0), Vector(0, 0)]) first = False iter_it = next_it iter_it %= len(bline_list) next_it += 1 if len(side_a) < 2 or len(side_b) < 2: return origin_cur = origin_p.get_value(fr) move_to(side_a[0][0], st_val, origin_cur) if loop: add(side_a, st_val, origin_cur) add_reverse(side_b, st_val, origin_cur) else: # Insert code for adding end tip if r_tip1: bp = bline_list[-1] vertex = bp.get_vertex() tangent = last_tangent.norm() w = gv*(bp.get_width() * outer_width * 0.5 + expand) a = vertex + tangent.perp()*w b = vertex - tangent.perp()*w p1 = a + tangent*w*(ROUND_END_FACTOR/3.0) p2 = b + tangent*w*(ROUND_END_FACTOR/3.0) tan = tangent*w*(ROUND_END_FACTOR/3.0) # replace the last point side_a[-1] = [a, Vector(0, 0), tan] add(side_a, st_val, origin_cur) add([[b, -tan, Vector(0, 0)]], st_val, origin_cur) else: add(side_a, st_val, origin_cur) # Insert code for adding beginning tip if r_tip0: bp = bline_list[0] vertex = bp.get_vertex() tangent = first_tangent.norm() w = gv*(bp.get_width() * outer_width * 0.5 + expand) a = vertex - tangent.perp()*w b = vertex + tangent.perp()*w p1 = a - tangent*w*(ROUND_END_FACTOR/3.0) p2 = b - tangent*w*(ROUND_END_FACTOR/3.0) tan = -tangent*w*(ROUND_END_FACTOR/3.0) # replace the first point side_b[0] = [a, Vector(0, 0), tan] add_reverse(side_b, st_val, origin_cur) add([[b, -tan, Vector(0, 0)]], st_val, origin_cur) else: add_reverse(side_b, st_val, origin_cur)
def parse_position(animated, i): """ To convert the synfig coordinates from units(initially a string) to pixels Depends on whether a vector is provided to it or a real value If real value is provided, then time is also taken into consideration Args: animated (lxml.etree._Element) : Stores animation which contains waypoints i (int) : Iterator over animation Returns: (common.Vector.Vector) If the animated type is not color (common.Color.Color) Else if the animated type is color """ if animated.attrib["type"] == "vector": pos = [float(animated[i][0][0].text), float(animated[i][0][1].text)] pos = [settings.PIX_PER_UNIT * x for x in pos] elif animated.attrib["type"] in {"real", "circle_radius"}: pos = parse_value(animated, i) elif animated.attrib["type"] == "angle": pos = [ get_angle(float(animated[i][0].attrib["value"])), get_frame(animated[i]) ] elif animated.attrib["type"] == "base": pos = [ float(animated[i][0].attrib["value"]) * settings.PIX_PER_UNIT, get_frame(animated[i]) ] elif animated.attrib["type"] in { "composite_convert", "region_angle", "star_angle_new", "scalar_multiply" }: pos = [float(animated[i][0].attrib["value"]), get_frame(animated[i])] elif animated.attrib["type"] == "rotate_layer_angle": # Angle needs to made neg of what they are pos = [-float(animated[i][0].attrib["value"]), get_frame(animated[i])] elif animated.attrib["type"] == "opacity": pos = [ float(animated[i][0].attrib["value"]) * settings.OPACITY_CONSTANT, get_frame(animated[i]) ] elif animated.attrib["type"] == "effects_opacity": pos = [float(animated[i][0].attrib["value"]), get_frame(animated[i])] elif animated.attrib["type"] == "points": pos = [ int(animated[i][0].attrib["value"]) * settings.PIX_PER_UNIT, get_frame(animated[i]) ] elif animated.attrib["type"] == "bool": val = animated[i][0].attrib["value"] if val == "false": val = 0 else: val = 1 pos = [val, get_frame(animated[i])] elif animated.attrib["type"] == "rectangle_size": pos = parse_value(animated, i) vec = Vector(pos[0], pos[1], animated.attrib["type"]) vec.add_new_val( float(animated[i][0].attrib["value2"]) * settings.PIX_PER_UNIT) return vec elif animated.attrib["type"] == "image_scale": val = float(animated[i][0].attrib["value"]) val2 = get_frame(animated[i]) vec = Vector(val, val2, animated.attrib["type"]) vec.add_new_val(float(animated[i][0].attrib["value2"])) return vec elif animated.attrib["type"] == "scale_layer_zoom": val = (math.e**float(animated[i][0].attrib["value"])) * 100 val2 = get_frame(animated[i]) vec = Vector(val, val2, animated.attrib["type"]) vec.add_new_val(val) return vec elif animated.attrib["type"] == "stretch_layer_scale": val1 = float(animated[i][0][0].text) * 100 val3 = float(animated[i][0][1].text) * 100 vec = Vector(val1, get_frame(animated[i]), animated.attrib["type"]) vec.add_new_val(val3) return vec elif animated.attrib["type"] in { "group_layer_scale", "blur_anim_x", "blur_anim_y" }: if animated.attrib["type"] == "group_layer_scale": val1 = float(animated[i][0][0].text) * 100 val3 = float(animated[i][0][1].text) * 100 vec = Vector(val1, get_frame(animated[i]), animated.attrib["type"]) vec.add_new_val(val3) elif animated.attrib["type"] == "blur_anim_x": val1 = float(animated[i][0][0].text) * 100 vec = Vector(val1, get_frame(animated[i]), animated.attrib["type"]) else: val1 = float(animated[i][0][1].text) * 100 vec = Vector(val1, get_frame(animated[i]), animated.attrib["type"]) return vec elif animated.attrib["type"] == "time": val = parse_time(animated[i][0].attrib["value"]) # Needed in seconds val2 = get_frame(animated[i]) # Needed in frames return Vector(val, val2, animated.attrib["type"]) elif animated.attrib["type"] == "color": red = float(animated[i][0][0].text) green = float(animated[i][0][1].text) blue = float(animated[i][0][2].text) alpha = float(animated[i][0][3].text) red = red**(1 / settings.GAMMA[0]) green = green**(1 / settings.GAMMA[1]) blue = blue**(1 / settings.GAMMA[2]) return Color(red, green, blue, alpha) elif animated.attrib["type"] == "gradient": return Gradient(animated[i][0]) return Vector(pos[0], pos[1], animated.attrib["type"])
def synfig_rectangle(st_val, point1_p, point2_p, expand_p, bevel_p, bevCircle, fr): """ Calculates the points for the rectangle layer as in Synfig: https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/rectangle.cpp Args: st_val (dict) : Lottie format rectangle will be stored in this point1_p (self.Param.Param) : Lottie format point1 animation point2_p (self.Param.Param) : Lottie format point2 animation expand_p (self.Param.Param) : Lottie format expand parameter animation bevel_p (self.Param.Param) : Lottie format bevel parameter animation bevCircle (lxml.etree._Element) : Animation of bevCircle in Synfig format fr (int) : Frame Number Returns: (None) """ expand = abs(to_Synfig_axis(expand_p.get_value(fr), "real")) bevel = abs(to_Synfig_axis(bevel_p.get_value(fr), "real")) p0 = to_Synfig_axis(point1_p.get_value(fr), "vector") p0 = Vector(p0[0], p0[1]) p1 = to_Synfig_axis(point2_p.get_value(fr), "vector") p1 = Vector(p1[0], p1[1]) if p1[0] < p0[0]: p0[0], p1[0] = p1[0], p0[0] if p1[1] < p0[1]: p0[1], p1[1] = p1[1], p0[1] bev_circle = bevCircle.get_value(fr) w = p1[0] - p0[0] + 2*expand h = p1[1] - p0[1] + 2*expand bev = bevel if bevel > 1: bev = 1 if bev_circle: bevx = min(w*bev/2.0, h*bev/2.0) bevy = min(w*bev/2.0, h*bev/2.0) else: bevx = w*bev/2.0 bevy = h*bev/2.0 # Setup chunk list chunk_list = [] if approximate_equal(bevel, 0.0): chunk_list.append([Vector(p0[0] - expand, p0[1] - expand), Vector(), Vector()]) chunk_list.append([Vector(p1[0] + expand, p0[1] - expand), Vector(), Vector()]) chunk_list.append([Vector(p1[0] + expand, p1[1] + expand), Vector(), Vector()]) chunk_list.append([Vector(p0[0] - expand, p1[1] + expand), Vector(), Vector()]) else: cur = Vector(p1[0] + expand - bevx, p0[1] - expand) chunk_list.append([cur, Vector(), Vector()]) prev = cur cur = Vector(p1[0] + expand, p0[1] - expand + bevy) cp1, cp2 = quadratic_to_cubic(cur, Vector(p1[0] + expand, p0[1] - expand), prev) chunk_list[-1][2] = cp2 - prev chunk_list.append([cur, cur - cp1, Vector()]) prev = cur cur = Vector(p1[0] + expand, p1[1] + expand - bevy) chunk_list.append([cur, Vector(), Vector()]) prev = cur cur = Vector(p1[0] + expand - bevx, p1[1] + expand) cp1, cp2 = quadratic_to_cubic(cur, Vector(p1[0] + expand, p1[1] + expand), prev) chunk_list[-1][2] = cp2 - prev chunk_list.append([cur, cur - cp1, Vector()]) prev = cur cur = Vector(p0[0] - expand + bevx, p1[1] + expand) chunk_list.append([cur, Vector(), Vector()]) prev = cur cur = Vector(p0[0] - expand, p1[1] + expand - bevy) cp1, cp2 = quadratic_to_cubic(cur, Vector(p0[0] - expand, p1[1] + expand), prev) chunk_list[-1][2] = cp2 - prev chunk_list.append([cur, cur - cp1, Vector()]) prev = cur cur = Vector(p0[0] - expand, p0[1] - expand + bevy) chunk_list.append([cur, Vector(), Vector()]) prev = cur cur = Vector(p0[0] - expand + bevx, p0[1] - expand) cp1, cp2 = quadratic_to_cubic(cur, Vector(p0[0] - expand, p0[1] - expand), prev) chunk_list[-1][2] = cp2 - prev chunk_list.append([cur, cur - cp1, Vector()]) prev = cur add(chunk_list, st_val, [0, 0], True)
def get_list_at_frame(self, fr): """ Returns the Bline list at a particular frame Refer: https://github.com/synfig/synfig/blob/15607089680af560ad031465d31878425af927eb/synfig-core/src/synfig/valuenodes/valuenode_bline.cpp """ EPSILON = 0.0000001 ret_list = [] first_flag = True rising = [False] next_scale = 1.0 first = BlinePoint(Vector(0, 0), 1, True, False, Vector(0, 0), Vector(0, 0)) first.set_origin(100) iterr = 0 while iterr != self.get_len(): entry = self.get_entry_list()[iterr] amount = entry["ActivepointList"].amount_at_time(fr, rising) assert (amount >= 0.0) assert (amount <= 1.0) # It's fully on if amount > 1.0 - EPSILON: if first_flag: first_iter = iterr first = prev = self.get_blinepoint(iterr, fr) first_flag = False ret_list.append(copy.deepcopy(first)) iterr += 1 continue curr = self.get_blinepoint(iterr, fr) if next_scale != 1.0: ret_list[-1].set_split_tangent_both(True) ret_list[-1].set_tangent2(prev.get_tangent2() * next_scale) ret_list.append(copy.deepcopy(curr)) ret_list[-1].set_split_tangent_both(True) ret_list[-1].set_tangent2(curr.get_tangent2()) ret_list[-1].set_tangent1(curr.get_tangent1() * next_scale) next_scale = 1.0 else: ret_list.append(copy.deepcopy(curr)) prev = curr # It's partly on elif amount > 0.0: blp_here_off = BlinePoint(Vector(0, 0), 1, True, False, Vector(0, 0), Vector(0, 0)) blp_here_now = BlinePoint(Vector(0, 0), 1, True, False, Vector(0, 0), Vector(0, 0)) blp_prev_off = BlinePoint(Vector(0, 0), 1, True, False, Vector(0, 0), Vector(0, 0)) dist_from_begin = 0 dist_from_end = 0 if not rising[0]: try: on_time = self.get_entry_list( )[iterr]["ActivepointList"].find_prev(fr).get_time() except Exception as e: on_time = settings.SOT try: off_time = self.get_entry_list( )[iterr]["ActivepointList"].find_next(fr).get_time() except Exception as e: off_time = settings.EOT else: try: off_time = self.get_entry_list( )[iterr]["ActivepointList"].find_prev(fr).get_time() except Exception as e: off_time = settings.SOT try: on_time = self.get_entry_list( )[iterr]["ActivepointList"].find_next(fr).get_time() except Exception as e: on_time = settings.EOT blp_here_on = self.get_blinepoint(iterr, on_time) end_iter = iterr end_iter += 1 while end_iter != self.get_len(): if self.get_entry_list( )[end_iter]["ActivepointList"].amount_at_time(fr) > amount: break end_iter += 1 if end_iter == self.get_len(): if self.get_loop() and (not first_flag): end_iter = first_iter else: end_iter = self.get_len() - 1 blp_next_off = self.get_blinepoint(end_iter, off_time) begin_iter = iterr blp_prev_off.set_origin(100) while True: if begin_iter == 0: if self.get_loop(): begin_iter = self.get_len() else: break begin_iter -= 1 dist_from_begin += 1 if begin_iter == iterr: break if self.get_entry_list()[begin_iter][ "ActivepointList"].amount_at_time(fr) > amount: blp_prev_off = self.get_blinepoint( begin_iter, off_time) break if blp_prev_off.get_origin() == 100: if first_flag: begin_iter = 0 else: begin_iter = first_iter blp_prev_off = self.get_blinepoint(begin_iter, off_time) curve = Hermite(blp_prev_off.get_vertex(), blp_next_off.get_vertex(), blp_prev_off.get_tangent2(), blp_next_off.get_tangent1()) blp_here_off.set_vertex(curve.value(blp_here_on.get_origin())) blp_here_off.set_width( (blp_next_off.get_width() - blp_prev_off.get_width()) * blp_here_on.get_origin() + blp_prev_off.get_width()) blp_here_off.set_tangent1( curve.derivative(blp_here_on.get_origin())) blp_here_off.set_tangent2( curve.derivative(blp_here_on.get_origin())) if begin_iter == (iterr - 1) or dist_from_begin == 1: prev_tangent_scalar = self.linear_interpolation( blp_here_on.get_origin(), 1.0, amount) else: prev_tangent_scalar = self.linear_interpolation( blp_here_on.get_origin() - prev.get_origin(), 1.0, amount) if end_iter == (iterr + 1) or dist_from_end == 1: next_tangent_scalar = self.linear_interpolation( 1.0 - blp_here_on.get_origin(), 1.0, amount) elif self.get_len() != (iterr + 1): nextt = self.get_blinepoint(iterr + 1, fr) next_tangent_scalar = self.linear_interpolation( nextt.get_origin() - blp_here_on.get_origin(), 1.0, amount) else: next_tangent_scalar = self.linear_interpolation( blp_next_off.get_origin() - blp_here_on.get_origin(), 1.0, amount) next_scale = next_tangent_scalar # My second try off_coord_sys = [] on_coord_sys = [] curr_coord_sys = [] end_pos_at_off_time = self.get_blinepoint( end_iter, off_time).get_vertex() begin_pos_at_off_time = self.get_blinepoint( begin_iter, off_time).get_vertex() off_coord_origin = (begin_pos_at_off_time + end_pos_at_off_time) / 2 off_coord_sys.append( (begin_pos_at_off_time - end_pos_at_off_time).norm()) off_coord_sys.append(off_coord_sys[0].perp()) end_pos_at_on_time = self.get_blinepoint(end_iter, on_time).get_vertex() begin_pos_at_on_time = self.get_blinepoint( begin_iter, on_time).get_vertex() on_coord_origin = (begin_pos_at_on_time + end_pos_at_on_time) / 2 on_coord_sys.append( (begin_pos_at_on_time - end_pos_at_on_time).norm()) on_coord_sys.append(on_coord_sys[0].perp()) end_pos_at_current_time = self.get_blinepoint(end_iter, fr).get_vertex() begin_pos_at_current_time = self.get_blinepoint( begin_iter, fr).get_vertex() curr_coord_origin = (begin_pos_at_current_time + end_pos_at_current_time) / 2 curr_coord_sys.append((begin_pos_at_current_time - end_pos_at_current_time).norm()) curr_coord_sys.append(curr_coord_sys[0].perp()) # swapping temp = curr_coord_sys[0][1] curr_coord_sys[0][1] = curr_coord_sys[1][0] curr_coord_sys[1][0] = temp trans_on_point = Vector(0, 0) trans_off_point = Vector(0, 0) trans_on_t1 = Vector(0, 0) trans_off_t1 = Vector(0, 0) trans_on_t2 = Vector(0, 0) trans_off_t2 = Vector(0, 0) trans_on_point = self.transform_coords( blp_here_on.get_vertex(), trans_on_point, on_coord_origin, on_coord_sys) trans_off_point = self.transform_coords( blp_here_off.get_vertex(), trans_off_point, off_coord_origin, off_coord_sys) trans_on_t1 = self.transform_coords(blp_here_on.get_tangent1(), trans_on_t1, Vector(0, 0), on_coord_sys) trans_off_t1 = self.transform_coords( blp_here_off.get_tangent1(), trans_off_t1, Vector(0, 0), off_coord_sys) if blp_here_on.get_split_tangent_both(): trans_on_t2 = self.transform_coords( blp_here_on.get_tangent2(), trans_on_t2, Vector(0, 0), on_coord_sys) trans_off_t2 = self.transform_coords( blp_here_off.get_tangent2(), trans_off_t2, Vector(0, 0), off_coord_sys) tmp = Vector(0, 0) tmp = self.untransform_coords( self.linear_interpolation(trans_off_point, trans_on_point, amount), tmp, curr_coord_origin, curr_coord_sys) blp_here_now.set_vertex(tmp) tmp = Vector(0, 0) tmp = self.untransform_coords( self.radial_interpolation(trans_off_t1, trans_on_t1, amount), tmp, Vector(0, 0), curr_coord_sys) blp_here_now.set_tangent1(tmp) # blp_here_now.set_tangent1(self.radial_interpolation(blp_here_off.get_tangent1(), blp_here_on.get_tangent1(), amount)) if blp_here_on.get_split_tangent_both(): blp_here_now.set_split_tangent_both(True) tmp = Vector(0, 0) tmp = self.untransform_coords( self.radial_interpolation(trans_off_t2, trans_on_t2, amount), tmp, Vector(0, 0), curr_coord_sys) blp_here_now.set_tangent2(tmp) else: blp_here_now.set_split_tangent_both(False) blp_here_now.set_origin(blp_here_on.get_origin()) blp_here_now.set_width( self.linear_interpolation(blp_here_off.get_width(), blp_here_on.get_width(), amount)) if first_flag: blp_here_now.set_tangent1(blp_here_now.get_tangent1() * prev_tangent_scalar) first_iter = iterr first = prev = blp_here_now first_flag = False ret_list.append(copy.deepcopy(blp_here_now)) iterr += 1 continue ret_list[-1].set_split_tangent_both(True) ret_list[-1].set_tangent2(prev.get_tangent2() * prev_tangent_scalar) ret_list.append(copy.deepcopy(blp_here_now)) ret_list[-1].set_split_tangent_both(True) ret_list[-1].set_tangent1(blp_here_now.get_tangent1() * prev_tangent_scalar) prev = blp_here_now iterr += 1 if next_scale != 1: ret_list[-1].set_split_tangent_both(True) ret_list[-1].set_tangent2(prev.get_tangent2() * next_scale) return ret_list
def gen_dynamic_list_polygon(lottie, dynamic_list): """ Generates the bline corresponding to polygon layer Args: lottie (dict) : Lottie format polygon layer will be stored here dynamic_list (lxml.etree._Element) : Synfig format points of polygon Returns: (None) """ ################## SECTION 1 ################ # Inserting the waypoints if not animated, finding the first and last frame # Calculating the path after this window = {} window["first"] = sys.maxsize window["last"] = -1 count = 0 for entry in dynamic_list: pos = entry update_frame_window(pos[0], window) new_pos = gen_dummy_waypoint(pos, "entry", "vector") pos.getparent().remove(pos) dynamic_list.insert(count, new_pos) append_path(new_pos[0], dynamic_list[count], "pos_path", "vector") count += 1 layer = dynamic_list.getparent().getparent() for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "origin": origin = chld # Animating the origin update_frame_window(origin[0], window) origin_parent = origin.getparent() origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(origin_parent, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################ END OF SECTION 1 ############## ################ SECTION 2 ##################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, False) for entry in dynamic_list: # Only two childs, one should be animated, other one is path for child in entry: if child.tag == "pos_path": dictionary = ast.literal_eval(child.text) pos_cur = get_vector_at_frame(dictionary, fr) pos_next = get_vector_at_frame(dictionary, fr + 1) tangent1_cur, tangent2_cur = Vector(0, 0), Vector(0, 0) tangent1_next, tangent2_next = Vector(0, 0), Vector(0, 0) # Adding origin to each vertex origin_cur = get_vector_at_frame(origin_dict, fr) origin_next = get_vector_at_frame(origin_dict, fr + 1) for i in range(len(pos_cur)): pos_cur[i] += origin_cur[i] for i in range(len(pos_next)): pos_next[i] += origin_next[i] # Store values in dictionary st_val["i"].append(tangent1_cur.get_list()) st_val["o"].append(tangent2_cur.get_list()) st_val["v"].append(pos_cur) en_val["i"].append(tangent1_next.get_list()) en_val["o"].append(tangent2_next.get_list()) en_val["v"].append(pos_next) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def gen_dynamic_list_polygon(lottie, dynamic_list): """ Generates the bline corresponding to polygon layer Args: lottie (dict) : Lottie format polygon layer will be stored here dynamic_list (common.Param.Param) : Synfig format points of polygon Returns: (None) """ ################## SECTION 1 ################ # Inserting the waypoints if not animated, finding the first and last frame # Calculating the path after this window = {} window["first"] = sys.maxsize window["last"] = -1 dynamic_list = Bline(dynamic_list[0], dynamic_list) for entry in dynamic_list.get_entry_list(): pos = entry["vector"] pos.update_frame_window(window) z = Param(pos.getparent(), pos.getparent().getparent()) z.animate("vector", True) entry["vector"] = z layer = dynamic_list.get_layer().get_layer() origin = layer.get_param("origin") # Animating the origin origin.update_frame_window(window) origin.animate("vector") if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################ END OF SECTION 1 ############## ################ SECTION 2 ##################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, False) for entry in dynamic_list.get_entry_list(): pos_cur = entry["vector"].get_value(fr) pos_next = entry["vector"].get_value(fr + 1) tangent1_cur, tangent2_cur = Vector(0, 0), Vector(0, 0) tangent1_next, tangent2_next = Vector(0, 0), Vector(0, 0) # Adding origin to each vertex origin_cur = origin.get_value(fr) origin_next = origin.get_value(fr + 1) for i in range(len(pos_cur)): pos_cur[i] += origin_cur[i] for i in range(len(pos_next)): pos_next[i] += origin_next[i] # Store values in dictionary st_val["i"].append(tangent1_cur.get_list()) st_val["o"].append(tangent2_cur.get_list()) st_val["v"].append(pos_cur) en_val["i"].append(tangent1_next.get_list()) en_val["o"].append(tangent2_next.get_list()) en_val["v"].append(pos_next) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def synfig_star(st_val, mx_points, origin_p, radius1_p, radius2_p, angle_p, points_p, regular_polygon_p, fr): """ Calculates the points for the rectangle layer as in Synfig: https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/star.cpp Args: st_val (dict) : Lottie format star will be stored here mx_points (int) : Maximum points ever in star animation radius1_p (common.Param.Param) : Lottie format radius1 animation radius2_p (common.Param.Param) : Lottie format radius2 animation angle_p (common.Param.Param) : Lottie format angle animation points_p (common.Param.Param) : Lottie format points animation regular_polygon_p (common.Param.Param) : Synfig format regularPolygon animation fr (int) : Frame number Returns: (None) """ angle = angle_p.get_value(fr) points = int(to_Synfig_axis(points_p.get_value(fr), "real")) radius1 = to_Synfig_axis(radius1_p.get_value(fr), "real") radius2 = to_Synfig_axis(radius2_p.get_value(fr), "real") regular_polygon = regular_polygon_p.get_value(fr) origin_cur = origin_p.get_value(fr) angle = math.radians(angle) dist_between_points = (math.pi * 2) / float(points) vector_list = [] #### tot_points = 2*mx_points i = 0 while i < points: dist1 = dist_between_points*i + angle dist2 = dist_between_points*i + dist_between_points/2 + angle vector_list.append(Vector(math.cos(dist1)*radius1, math.sin(dist1)*radius1)) if not regular_polygon: vector_list.append(Vector(math.cos(dist2)*radius2, math.sin(dist2)*radius2)) else: # This condition is needed because in lottie a shape must have equal # number of vertices at each frame vector_list.append(Vector(math.cos(dist1)*radius1, math.sin(dist1)*radius1)) tot_points -= 2 i += 1 if len(vector_list) < 3: # Should throw error return while tot_points > 0: vector_list.append(vector_list[-1]) tot_points -= 1 # Setup chunk list chunk_list = [] chunk_list.append([vector_list[0], Vector(), Vector()]) i = 1 while i < len(vector_list): if math.isnan(vector_list[i][0]) or math.isnan(vector_list[i][1]): break chunk_list.append([vector_list[i], Vector(), Vector()]) i += 1 add(chunk_list, st_val, origin_cur)