← 返回首页

等周问题

Bezier 曲线可以用来画不规则曲线 创建点 创建线条和虚线条 镜面对称. 这 … 继续阅读 →

Bezier 曲线可以用来画不规则曲线

from manim import *

class ClosedBezierCurveWithControlPoints(Scene):
    def construct(self):
        # 定义控制点的坐标
        control_points = [
            [0, 0, 0],    # 起始点
            [.5, 1, 0],
            [2, .5, 0],    # 控制点 1
            [3.5, 1, 0],
            [4, 0, 0],    # 控制点 3
            [2, -1.2, 0],   # 控制点 4
            [0, 0, 0]     # 回到起始点,封闭曲线
        ]

        # 创建 VMobject 对象并设置贝塞尔曲线的控制点
        closed_curve = VMobject()
        closed_curve.set_points_smoothly(control_points)
        closed_curve.close_path()  # 确保路径封闭
        closed_curve.set_color(BLUE)

        # 创建并显示贝塞尔曲线
        self.play(Create(closed_curve))

        # 在控制点的位置上添加 Dot 来标记
        dots = [Dot(point=point, color=RED) for point in control_points]
        labels = [Text(f"P{i}", font_size=24).next_to(dots[i], UP) for i in range(len(dots))]

        for dot, label in zip(dots, labels):
            self.play(FadeIn(dot), Write(label))

        self.wait(2)

创建点

dot_P1 = Dot(P1, color=RED)
dot_P3 = Dot(P3, color=RED)
self.play(FadeIn(dot_P1), FadeIn(dot_P3))

创建线条和虚线条

tangent_line1 = Line(P1, P3, color=YELLOW)
self.play(Create(tangent_line))
tangent_line2 = DashedLine(P1, P3, color=YELLOW)
self.play(Create(tangent_line), run_time=.5)

镜面对称. 这里的对称轴是过 about_point 以 axis 为方向的直线

如果希望展示旋转过程可以直接 self.play(Rotate())

partial_curve_copy = partial_curve.copy()
self.play(Rotate(partial_curve_copy, angle=-PI, axis=tangent_vector, about_point=midpoint))
       

如果不希望展示旋转过程就这样

extra_area2_copy = extra_area2.copy()
self.add(extra_area2_copy)
extra_area2_copy.rotate(angle=-PI, axis=[0,1,0], about_point=ORIGIN)
self.play(FadeIn(extra_area2_copy))
self.wait(1)

如何将不同的曲线段合并成一条闭合曲线?

extra_area1 = VMobject()
extra_area1.append_vectorized_mobject(partial_curve)
extra_area1.append_vectorized_mobject(partial_curve_copy)

如何提取一条曲线的一部分?这里的 0.14 和 0.61 分别是起点和终点, 每个点在范围 [0,1] 中.

partial_curve = VMobject()
partial_curve.pointwise_become_partial(closed_curve,0.14, 0.61)
partial_curve.set_color(RED)

动态更新点的位置

# 线段AB定义为动态更新
line_ab = always_redraw(lambda: DashedLine(curve_ap.get_start(), 
                                           curve_bp.get_end(), 
                                           color=YELLOW))

设置图层

# 使用 set_z_index() 方法来设置图层顺序
circle1.set_z_index(1)  # 最后渲染,显示在最前面
circle2.set_z_index(0)  # 默认图层
circle3.set_z_index(-1)  # 最先渲染,显示在最底部

标签

label_a = MathTex("A").next_to(dot_a, RIGHT)

在 Python 中的切片操作遵循以下规则:

  1. 索引从 0 开始:第一个元素的索引是 0,第二个是 1,以此类推。
  2. 负数索引:负数索引从列表的末尾开始,-1 表示最后一个元素,-2 表示倒数第二个元素,以此类推。
  3. 切片的结束位置是排他的:也就是说,start:end 中的 end 索引对应的元素并不包含在切片结果中,切片会取到 end 的前一个位置。

下面代码表示从索引为 7 的元素取到最后一位

one[0][7:]

来回往复运动效果

self.play(Rotate(curve_ap, angle=PI/3, about_point=point_p, rate_func=there_and_back),
         Rotate(area1, angle=PI/3, about_point=point_p, rate_func=there_and_back),
          run_time=4)

角的圆弧 Angle, 可以用 quadrant 选择象限, other_angle=True 显示补角

Angle( line_bp,line_ap, quadrant=(1,-1), radius=0.5, color=YELLOW)

直角符号

RightAngle(line1, line2, length=0.4, quadrant=(1,-1))

清除更新函数

angle.clear_updaters()

获取线段中点坐标

line.get_center()

轨迹追踪

# 计算半径为A到中点的距离
radius = np.linalg.norm(midpoint- point_a)

# 创建半圆的路径(隐藏半圆,仅用于让点P沿路径运动)
hidden_semicircle = ArcBetweenPoints(point_a, point_b, angle=PI, radius=radius, color=WHITE, stroke_opacity=0)

# 创建点P,初始位置在A
point_p = Dot(point_a, color=RED)

# 创建线段PA和PB
line_pa = always_redraw(lambda: Line(point_p.get_center(), point_a))
line_pb = always_redraw(lambda: Line(point_p.get_center(), point_b))

# 使用 TracedPath 动态追踪P点的路径,绘制出圆弧
traced_path = TracedPath(point_p.get_center, stroke_color=YELLOW, stroke_width=4)

# 将点P、线段PA和PB以及追踪路径添加到场景中
self.add(point_p, line_pa, line_pb, traced_path)

# 让点P沿着隐藏的半圆路径运动,逐步绘制圆弧
self.play(MoveAlongPath(point_p, hidden_semicircle), run_time=4, rate_func=linear)
self.wait()

平滑的移动效果

object.animate.move_to(target_point)

清除所有更新器和上移所有对象

for mob in self.mobjects:
    mob.clear_updaters()
self.play(*[obj.animate.shift(UP) for obj in self.mobjects])

打包所有对象

everything1 = Group(*self.mobjects)

打包所有对象,除了某个特定对象

everything1 = Group(*self.mobjects)
everything1.remove(title)

也可以这样

self.play(*[obj.animate.shift(UP) for obj in self.mobjects if obj!= title])

导入图片

image = ImageMobject("path/to/your/image.png")

贴到屏幕左边

Weierstrass.to_edge(LEFT, buff=0)