mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Add FlashAround and FlashUnder
This commit is contained in:
parent
5126dd1f52
commit
f6ff070a8e
1 changed files with 56 additions and 24 deletions
|
@ -14,6 +14,7 @@ from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.mobject.geometry import Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
from manimlib.mobject.geometry import Dot
|
from manimlib.mobject.geometry import Dot
|
||||||
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
||||||
|
from manimlib.mobject.shape_matchers import Underline
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.geometry import Line
|
from manimlib.mobject.geometry import Line
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
|
@ -156,45 +157,50 @@ class ShowPassingFlash(ShowPartial):
|
||||||
class VShowPassingFlash(Animation):
|
class VShowPassingFlash(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"time_width": 0.3,
|
"time_width": 0.3,
|
||||||
"taper_width": 0.1,
|
"taper_width": 0.02,
|
||||||
"remover": True,
|
"remover": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
def begin(self):
|
def begin(self):
|
||||||
self.mobject.align_stroke_width_data_to_points()
|
self.mobject.align_stroke_width_data_to_points()
|
||||||
|
# Compute an array of stroke widths for each submobject
|
||||||
|
# which tapers out at either end
|
||||||
|
self.submob_to_anchor_widths = dict()
|
||||||
|
for sm in self.mobject.get_family():
|
||||||
|
original_widths = sm.get_stroke_widths()
|
||||||
|
anchor_widths = np.array([*original_widths[0::3], original_widths[-1]])
|
||||||
|
|
||||||
|
def taper_kernel(x):
|
||||||
|
if x < self.taper_width:
|
||||||
|
return x
|
||||||
|
elif x > 1 - self.taper_width:
|
||||||
|
return 1.0 - x
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
taper_array = list(map(taper_kernel, np.linspace(0, 1, len(anchor_widths))))
|
||||||
|
self.submob_to_anchor_widths[hash(sm)] = anchor_widths * taper_array
|
||||||
super().begin()
|
super().begin()
|
||||||
|
|
||||||
def interpolate_submobject(self, submobject, starting_sumobject, alpha):
|
def interpolate_submobject(self, submobject, starting_sumobject, alpha):
|
||||||
original_widths = starting_sumobject.get_stroke_widths()
|
anchor_widths = self.submob_to_anchor_widths[hash(submobject)]
|
||||||
# anchor_widths = np.array([*original_widths[0::3, 0], original_widths[-1, 0]])
|
|
||||||
anchor_widths = np.array([0, *original_widths[3::3, 0], 0])
|
|
||||||
n_anchors = len(anchor_widths)
|
|
||||||
time_width = self.time_width
|
|
||||||
# taper_width = self.taper_width
|
|
||||||
# Create a gaussian such that 3 sigmas out on either side
|
# Create a gaussian such that 3 sigmas out on either side
|
||||||
# will equals time_width * (number of points)
|
# will equals time_width
|
||||||
sigma = time_width / 6
|
tw = self.time_width
|
||||||
mu = interpolate(-time_width / 2, 1 + time_width / 2, alpha)
|
sigma = tw / 6
|
||||||
offset = math.exp(-4.5) # 3 sigmas out
|
mu = interpolate(-tw / 2, 1 + tw / 2, alpha)
|
||||||
|
|
||||||
def kernel_func(x):
|
def gauss_kernel(x):
|
||||||
result = math.exp(-0.5 * ((x - mu) / sigma)**2) - offset
|
if abs(x - mu) > 3 * sigma:
|
||||||
result = max(result, 0)
|
return 0
|
||||||
# if x < taper_width:
|
z = (x - mu) / sigma
|
||||||
# result *= x / taper_width
|
return math.exp(-0.5 * z * z)
|
||||||
# elif x > 1 - taper_width:
|
|
||||||
# result *= (1 - x) / taper_width
|
|
||||||
return result
|
|
||||||
|
|
||||||
kernel_array = np.array([
|
kernel_array = list(map(gauss_kernel, np.linspace(0, 1, len(anchor_widths))))
|
||||||
kernel_func(n / (n_anchors - 1))
|
|
||||||
for n in range(n_anchors)
|
|
||||||
])
|
|
||||||
scaled_widths = anchor_widths * kernel_array
|
scaled_widths = anchor_widths * kernel_array
|
||||||
new_widths = np.zeros(submobject.get_num_points())
|
new_widths = np.zeros(submobject.get_num_points())
|
||||||
new_widths[0::3] = scaled_widths[:-1]
|
new_widths[0::3] = scaled_widths[:-1]
|
||||||
new_widths[1::3] = (scaled_widths[:-1] + scaled_widths[1:]) / 2
|
|
||||||
new_widths[2::3] = scaled_widths[1:]
|
new_widths[2::3] = scaled_widths[1:]
|
||||||
|
new_widths[1::3] = (new_widths[0::3] + new_widths[2::3]) / 2
|
||||||
submobject.set_stroke(width=new_widths)
|
submobject.set_stroke(width=new_widths)
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
|
@ -203,6 +209,32 @@ class VShowPassingFlash(Animation):
|
||||||
submob.match_style(start)
|
submob.match_style(start)
|
||||||
|
|
||||||
|
|
||||||
|
class FlashAround(VShowPassingFlash):
|
||||||
|
CONFIG = {
|
||||||
|
"stroke_width": 4.0,
|
||||||
|
"color": YELLOW,
|
||||||
|
"buff": SMALL_BUFF,
|
||||||
|
"time_width": 1.0,
|
||||||
|
"n_inserted_curves": 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, mobject, **kwargs):
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
path = self.get_path(mobject)
|
||||||
|
path.insert_n_curves(self.n_inserted_curves)
|
||||||
|
path.set_points(path.get_points_without_null_curves())
|
||||||
|
path.set_stroke(self.color, self.stroke_width)
|
||||||
|
super().__init__(path, **kwargs)
|
||||||
|
|
||||||
|
def get_path(self, mobject):
|
||||||
|
return SurroundingRectangle(mobject, buff=self.buff)
|
||||||
|
|
||||||
|
|
||||||
|
class FlashUnder(FlashAround):
|
||||||
|
def get_path(self, mobject):
|
||||||
|
return Underline(mobject, buff=self.buff)
|
||||||
|
|
||||||
|
|
||||||
class ShowCreationThenDestruction(ShowPassingFlash):
|
class ShowCreationThenDestruction(ShowPassingFlash):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"time_width": 2.0,
|
"time_width": 2.0,
|
||||||
|
|
Loading…
Add table
Reference in a new issue