python-lottie  0.7.0+dev66cafb9
A framework to work with lottie files and telegram animated stickers (tgs)
ellipse.py
Go to the documentation of this file.
1 import math
2 
3 from ..nvector import NVector, PolarVector
4 from ..objects.bezier import BezierPoint, Bezier
5 
6 
7 ## @todo Just output a Bezier object
8 class Ellipse:
9  def __init__(self, center, radii, xrot):
10  """
11  @param center 2D vector, center of the ellipse
12  @param radii 2D vector, x/y radius of the ellipse
13  @param xrot Angle between the main axis of the ellipse and the x axis (in radians)
14  """
15  self.centercenter = center
16  self.radiiradii = radii
17  self.xrotxrot = xrot
18 
19  def point(self, t):
20  return NVector(
21  self.centercenter[0]
22  + self.radiiradii[0] * math.cos(self.xrotxrot) * math.cos(t)
23  - self.radiiradii[1] * math.sin(self.xrotxrot) * math.sin(t),
24 
25  self.centercenter[1]
26  + self.radiiradii[0] * math.sin(self.xrotxrot) * math.cos(t)
27  + self.radiiradii[1] * math.cos(self.xrotxrot) * math.sin(t)
28  )
29 
30  def derivative(self, t):
31  return NVector(
32  - self.radiiradii[0] * math.cos(self.xrotxrot) * math.sin(t)
33  - self.radiiradii[1] * math.sin(self.xrotxrot) * math.cos(t),
34 
35  - self.radiiradii[0] * math.sin(self.xrotxrot) * math.sin(t)
36  + self.radiiradii[1] * math.cos(self.xrotxrot) * math.cos(t)
37  )
38 
39  def to_bezier_points(self, anglestart, angle_delta, step=math.pi / 2):
40  points = []
41  angle1 = anglestart
42  angle_left = abs(angle_delta)
43  sign = -1 if anglestart+angle_delta < angle1 else 1
44  tolerance = math.pi / 100
45  if angle_left % step > tolerance:
46  step = angle_left / max(1, round(angle_left / step))
47 
48  # We need to fix the first handle
49  firststep = min(angle_left, step) * sign
50  alpha = self._alpha_alpha(firststep)
51  q1 = self.derivativederivative(angle1) * alpha
52  points.append(BezierPoint(self.pointpoint(angle1), NVector(0, 0), q1))
53 
54  # Then we iterate until the angle has been completed
55  half_step = step / 2
56  while True:
57  if angle_left < half_step:
58  break
59 
60  lstep = min(angle_left, step)
61  step_sign = lstep * sign
62  angle2 = angle1 + step_sign
63  angle_left -= abs(lstep)
64 
65  alpha = self._alpha_alpha(step_sign)
66  p2 = self.pointpoint(angle2)
67  q2 = self.derivativederivative(angle2) * alpha
68 
69  points.append(BezierPoint(p2, -q2, q2))
70  angle1 = angle2
71 
72  return points
73 
74  def to_bezier(self, angle_start, angle_delta, step=math.pi / 2):
75  bezier = Bezier()
76  points = self.to_bezier_pointsto_bezier_points(angle_start, angle_delta, step)
77 
78  if angle_delta == math.pi * 2:
79  points.pop(0)
80  bezier.close()
81 
82  for point in points:
83  bezier.add_point(point.vertex, point.in_tangent, point.out_tangent)
84  return bezier
85 
86  def _alpha(self, step):
87  return math.sin(step) * (math.sqrt(4+3*math.tan(step/2)**2) - 1) / 3
88 
89  @classmethod
90  def from_svg_arc(cls, start, rx, ry, xrot, large, sweep, dest):
91  rx = abs(rx)
92  ry = abs(ry)
93 
94  x1 = start[0]
95  y1 = start[1]
96  x2 = dest[0]
97  y2 = dest[1]
98  phi = math.pi * xrot / 180
99 
100  x1p, y1p = _matrix_mul(phi, (start-dest)/2, -1)
101 
102  cr = x1p ** 2 / rx**2 + y1p**2 / ry**2
103  if cr > 1:
104  s = math.sqrt(cr)
105  rx *= s
106  ry *= s
107 
108  dq = rx**2 * y1p**2 + ry**2 * x1p**2
109  pq = (rx**2 * ry**2 - dq) / dq
110  cpm = math.sqrt(max(0, pq))
111  if large == sweep:
112  cpm = -cpm
113  cp = NVector(cpm * rx * y1p / ry, -cpm * ry * x1p / rx)
114  c = _matrix_mul(phi, cp) + NVector((x1+x2)/2, (y1+y2)/2)
115  theta1 = _angle(NVector(1, 0), NVector((x1p - cp[0]) / rx, (y1p - cp[1]) / ry))
116  deltatheta = _angle(
117  NVector((x1p - cp[0]) / rx, (y1p - cp[1]) / ry),
118  NVector((-x1p - cp[0]) / rx, (-y1p - cp[1]) / ry)
119  ) % (2*math.pi)
120 
121  if not sweep and deltatheta > 0:
122  deltatheta -= 2*math.pi
123  elif sweep and deltatheta < 0:
124  deltatheta += 2*math.pi
125 
126  return cls(c, NVector(rx, ry), phi), theta1, deltatheta
127 
128 
129 def _matrix_mul(phi, p, sin_mul=1):
130  c = math.cos(phi)
131  s = math.sin(phi) * sin_mul
132 
133  xr = c * p.x - s * p.y
134  yr = s * p.x + c * p.y
135  return NVector(xr, yr)
136 
137 
138 def _angle(u, v):
139  arg = math.acos(max(-1, min(1, u.dot(v) / (u.length * v.length))))
140  if u[0] * v[1] - u[1] * v[0] < 0:
141  return -arg
142  return arg
Single bezier curve.
Definition: bezier.py:123
def _alpha(self, step)
Definition: ellipse.py:86
def from_svg_arc(cls, start, rx, ry, xrot, large, sweep, dest)
Definition: ellipse.py:90
def to_bezier_points(self, anglestart, angle_delta, step=math.pi/2)
Definition: ellipse.py:39
def __init__(self, center, radii, xrot)
Definition: ellipse.py:9
def to_bezier(self, angle_start, angle_delta, step=math.pi/2)
Definition: ellipse.py:74
def derivative(self, t)
Definition: ellipse.py:30