def __init__(self, vmob, alpha, **kwargs): digest_config(self, kwargs) da = self.d_alpha a1 = clip(alpha - da, 0, 1) a2 = clip(alpha + da, 0, 1) super().__init__(vmob.pfp(a1), vmob.pfp(a2), **kwargs) self.scale(self.length / self.get_length())
def get_arc_center_and_angles(x0, y0, rx, ry, phi, large_arc_flag, sweep_flag, x1, y1): """ The parameter functions of an ellipse rotated `phi` radians counterclockwise is (on `alpha`): x = cx + rx * cos(alpha) * cos(phi) + ry * sin(alpha) * sin(phi), y = cy + rx * cos(alpha) * sin(phi) - ry * sin(alpha) * cos(phi). Now we have two points sitting on the ellipse: `(x0, y0)`, `(x1, y1)`, corresponding to 4 equations, and we want to hunt for 4 variables: `cx`, `cy`, `alpha0` and `alpha_1`. Let `d_alpha = alpha1 - alpha0`, then: if `sweep_flag = 0` and `large_arc_flag = 1`, then `PI <= d_alpha < 2 * PI`; if `sweep_flag = 0` and `large_arc_flag = 0`, then `0 < d_alpha <= PI`; if `sweep_flag = 1` and `large_arc_flag = 0`, then `-PI <= d_alpha < 0`; if `sweep_flag = 1` and `large_arc_flag = 1`, then `-2 * PI < d_alpha <= -PI`. """ xd = x1 - x0 yd = y1 - y0 if close_to_zero(xd) and close_to_zero(yd): raise Exception( "Cannot find arc center since the start point and the end point meet." ) # Find `p = cos(alpha1) - cos(alpha0)`, `q = sin(alpha1) - sin(alpha0)` eq0 = [rx * np.cos(phi), ry * np.sin(phi), xd] eq1 = [rx * np.sin(phi), -ry * np.cos(phi), yd] p, q = solve_2d_linear_equation(*zip(eq0, eq1)) # Find `s = (alpha1 - alpha0) / 2`, `t = (alpha1 + alpha0) / 2` # If `sin(s) = 0`, this requires `p = q = 0`, # implying `xd = yd = 0`, which is impossible. sin_s = (p**2 + q**2)**0.5 / 2 if sweep_flag: sin_s = -sin_s sin_s = clip(sin_s, -1, 1) s = np.arcsin(sin_s) if large_arc_flag: if not sweep_flag: s = PI - s else: s = -PI - s sin_t = -p / (2 * sin_s) cos_t = q / (2 * sin_s) cos_t = clip(cos_t, -1, 1) t = np.arccos(cos_t) if sin_t <= 0: t = -t # We can make sure `0 < abs(s) < PI`, `-PI <= t < PI`. alpha0 = t - s alpha_1 = t + s cx = x0 - rx * np.cos(alpha0) * np.cos(phi) - ry * np.sin( alpha0) * np.sin(phi) cy = y0 - rx * np.cos(alpha0) * np.sin(phi) + ry * np.sin( alpha0) * np.cos(phi) return cx, cy, alpha0, alpha_1
def smooth(t, inflection=10.0): error = sigmoid(-inflection / 2) return clip( (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error), 0, 1, )
def get_sub_alpha(self, alpha, index, num_submobjects): # TODO, make this more understanable, and/or combine # its functionality with AnimationGroup's method # build_animations_with_timings lag_ratio = self.lag_ratio full_length = (num_submobjects - 1) * lag_ratio + 1 value = alpha * full_length lower = index * lag_ratio return clip((value - lower), 0, 1)
def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> float: """ Returns the angle between two 3D vectors. This angle will always be btw 0 and pi """ n1 = get_norm(v1) n2 = get_norm(v2) if n1 == 0 or n2 == 0: return 0 cos_angle = np.dot(v1, v2) / np.float64(n1 * n2) return math.acos(clip(cos_angle, -1, 1))
def update(m, dt): run_time = animation.get_run_time() time_ratio = animation.total_time / run_time if cycle: alpha = time_ratio % 1 else: alpha = clip(time_ratio, 0, 1) if alpha >= 1: animation.finish() m.remove_updater(update) return animation.interpolate(alpha) animation.update_mobjects(dt) animation.total_time += dt
def interpolate(self, alpha: float) -> None: # Note, if the run_time of AnimationGroup has been # set to something other than its default, these # times might not correspond to actual times, # e.g. of the surrounding scene. Instead they'd # be a rescaled version. But that's okay! time = alpha * self.max_end_time for anim, start_time, end_time in self.anims_with_timings: anim_time = end_time - start_time if anim_time == 0: sub_alpha = 0 else: sub_alpha = clip((time - start_time) / anim_time, 0, 1) anim.interpolate(sub_alpha)
def rotate(self, angle: float, axis: np.ndarray = OUT, **kwargs): curr_rot_T = self.get_inverse_camera_rotation_matrix() added_rot_T = rotation_matrix_transpose(angle, axis) new_rot_T = np.dot(curr_rot_T, added_rot_T) Fz = new_rot_T[2] phi = np.arccos(clip(Fz[2], -1, 1)) theta = angle_of_vector(Fz[:2]) + PI / 2 partial_rot_T = np.dot( rotation_matrix_transpose(phi, RIGHT), rotation_matrix_transpose(theta, OUT), ) gamma = angle_of_vector(np.dot(partial_rot_T, new_rot_T.T)[:, 0]) self.set_euler_angles(theta, phi, gamma) return self
def interpolate(self, alpha: float) -> None: alpha = clip(alpha, 0, 1) self.interpolate_mobject(self.rate_func(alpha))
def increment_phi(self, dphi): phi = self.data["euler_angles"][1] new_phi = clip(phi + dphi, 0, PI) self.data["euler_angles"][1] = new_phi self.refresh_rotation_matrix() return self
def interpolate(self, alpha): alpha = clip(alpha, 0, 1) self.interpolate_mobject(self.rate_func(alpha))
def angle_between_vectors(v1, v2): """ Returns the angle between two 3D vectors. This angle will always be btw 0 and pi """ return math.acos(clip(np.dot(normalize(v1), normalize(v2)), -1, 1))