Skip to content

Latest commit

 

History

History
299 lines (238 loc) · 23.9 KB

牛顿分型注释.md

File metadata and controls

299 lines (238 loc) · 23.9 KB

关于NewtonFractal分型渲染茱莉亚集合的输入分析

manimlib document官方文档地址

    j_plane = self.get_plane()
    j_plane.set_y(jy)
    julia = NewtonFractal(j_plane, coefs=coefs, colors=5 * [GREY_A])
    julia.set_julia_highlight(1e-3)
    j_group = Group(julia, j_plane)

NewtonFractal分型需要输入:

  • 一个平面j_plane属性,用于确定渲染的范围。j_plane的值被get_plane方法初始化,get_plane方法隶属于Scene类。
  • 一个系数列表coef属性,用于确定系数(话说真的需要确定有理多项式的系数吗)
  • 一个颜色的初始化参数colors属性(是列表吗?还是什么其它的东西) 在被传入后貌似不需要再进行初始化操作,NewtonFractal就能自动返回一个Mobject类型结果。 重点调研这几个对象在被传入后所被进行的操作。尤其是NumberPlane类型下属的ComplexPlane类型对象在被传入后发生了什么,跟我最开始一个一个点的传入相比有什么异同? 在被传入之后,需要调用set_julia_highlight方法设置高亮 在此时,j_group这个Mobject已经被初步设置完毕,可以在Scene.play里面调用动画的呈现方式GrowFromCenter来针对其进行渲染。

get_plane方法与被传入的plane属性

此方法返回一个CompelxNumber类型的平面对象,此类型隶属于NumberPlane类。 通过跟踪NewtonFractal的声明的基本属性我发现了以下事情。

  • 首先,NewtonFractal是拥有一个初始化__init__的特殊方法的,这也是其在被实例化之后不需要接受其其它方法进行后续处理的主要原因。
  • 其次,初始化特殊方法接受了plane类型的传入,并且对其进行了相应的操作。其把plane变量进行了缩放,并且将自己的偏置放在了中心,这一步是为了将自己放在显示平面的中心,但是现在新版本的manimce里面有使用质心的方法来达到这个目的,我们说不定能进行改进。 综上所述,在初始化方法里面,我们并没有看到其对plane参数进行进一步的设置。这一切可能要靠后面的系数参数来进行规整。

coef属性

进一步观察Mobject下属的NewtonFractal类我们会发现,其除了最开始的__init__初始化方法,还有其它的初始化方法在起着作用。

  def init_data(self):
          self.set_points([UL, DL, UR, DR])

这句定义隶属于其父类Mobject下属的Mobject->types->mobject.py的初始化方法__init__

  def __init__:
      ...
          def init_data(self, length: int = 0):
              self.data = np.zeros(length, dtype=self.shader_dtype)

上面的那一段初始化方法的意思是初始化所有点的坐标,接受一个名为length的初始化数组长度的数字,然后将数组里面的数字统统归零,最后数据的类型变为shader_dtype

另一段初始化方法:

    def init_uniforms(self):
        super().init_uniforms()
        self.set_colors(self.colors)
        self.set_julia_highlight(self.julia_highlight)
        self.set_coefs(self.coefs)
        self.set_scale(self.scale_factor)
        self.set_offset(self.offset)
        self.set_n_steps(self.n_steps)
        self.set_saturation_factor(self.saturation_factor)
        self.set_opacity(self.opacity)
        self.uniforms["black_for_cycles"] = float(self.black_for_cycles)
        self.uniforms["is_parameter_space"] = float(self.is_parameter_space)

其也隶属于其父类Mobject下属的Mobject->types->mobject.py的初始化方法__init__

  def __init__:
      def init_uniforms(self):
          self.uniforms: UniformDict = {
              "is_fixed_in_frame": float(self._is_fixed_in_frame),
              "shading": np.array(self.shading, dtype=float),
          }

上面那一段代码的意思是init_uniforms方法创建一个名为self.uniforms的字典,这个字典包含了两个键值对:

  • "is_fixed_in_frame":这个键对应的值是self._is_fixed_in_frame的浮点数形式。这个值表示Mobject对象是否在帧中固定。
  • "shading":这个键对应的值是self.shading的numpy数组形式,数据类型为浮点数。这个值表示Mobject对象的阴影。 这意味着在事实上,就算我们没有直接把这些代码直接放入NewtonFractal的初始化方法里面,其也会因为实例化时直接调用父类的初始化方法代码里面的方法而被自动的调用,这也是不需要对其实例化之后再调用里面的其它方法对其进行进一步的加工的主要原因,因为其会自动调用这两个方法对自己进行初始化。

前置方法

  • poly(x, coefs):这是一个多项式函数,它接受一个数值x和一个系数列表coefs,然后返回多项式在x处的值。多项式的形式为 coefs[0]*x^0 + coefs[1]*x^1 + coefs[2]*x^2 + ...

  • dpoly(x, coefs):这是多项式函数的导数,它也接受一个数值x和一个系数列表coefs,然后返回多项式在x处的导数值。

  • roots_to_coefficients(roots):这个函数接受一个根的列表roots,然后返回对应的多项式系数。这里使用了组合数学和numpy的乘积函数。 这是一个用于根据根生成系数的函数,它接受一个根的列表roots作为参数,返回一个系数列表coefs,使得f(x) = (x - roots[0]) * (x - roots[1]) * … * (x - roots[n-1])

  • find_root(func, dfunc, seed=complex(1, 1), tol=1e-8, max_steps=100):这个函数使用牛顿法来寻找给定函数func的根。它还需要一个导数函数dfunc,一个初始猜测值(种子)seed,一个容忍误差tol,以及最大迭代次数max_steps。如果连续两次迭代的结果之间的差小于容忍误差,或者达到最大迭代次数,则停止迭代。

  • coefficients_to_roots(coefs):这个函数接受一个系数列表coefs,然后返回对应的多项式根。 这是一个用于根据系数生成根的函数,它使用find_root函数运用牛顿法来逐个寻找根。它接受一个系数列表coefs作为参数,返回一个根列表roots,从而解得方程$coefs[0] + coefs[1] * x + coefs[2] * x^2 + … + coefs[n] * x^n = 0$

NewtonFractal

这段代码定义了一个名为 NewtonFractal 的类,它是 VMobject 类的子类。这个类用于创建和操作牛顿分形图像。

类的属性包括:

  • shader_folder:着色器文件夹的名称。
  • shader_dtype:着色器数据类型。
  • colors:分形图像的颜色。
  • coefs:多项式的系数。
  • scale_factor:缩放因子。
  • offset:偏移量。
  • n_steps:迭代步数。
  • julia_highlight:朱利亚高亮值。
  • max_degree:最大度数。
  • saturation_factor:饱和度因子。
  • opacity:透明度。
  • black_for_cycles:是否为循环设置黑色背景。
  • is_parameter_space:是否为参数空间。

类的方法包括:

  • __init__():初始化方法,用于设置对象的初始状态。
  • init_data():初始化数据,设置点的位置。
  • init_uniforms():初始化统一变量,设置颜色、高亮、系数、缩放、偏移、步数、饱和度和透明度等属性。
  • 以及一系列的 set_ 方法,用于设置各种属性。

这个类主要用于创建和操作牛顿分形图像,通过改变各种属性来改变分形图像的外观。例如,可以通过改变颜色、透明度、饱和度等来改变分形图像的颜色;可以通过改变系数来改变分形图像的形状;可以通过改变缩放和偏移来改变分形图像的大小和位置等。这个类提供了一种灵活且强大的方式来创建和操作牛顿分形图像。

这个PYTHON代码定义了一个名为NewtonFractal的类,它是VMobject的子类,用于创建和显示牛顿分形图像。牛顿分形是一种使用牛顿法求解复数多项式方程根的方法,根据不同的根和迭代次数给平面上的点着色。这个类的属性和方法有:

  • shader_folder: 一个字符串,表示存放着色器文件的文件夹的名称,用于渲染分形图像。
  • shader_dtype: 一个列表,表示着色器数据类型,包含一个元组,元组中有三个元素:("point", np.float32, (3,)),表示每个点的数据是一个包含三个浮点数的数组,分别表示点的x, y, z坐标。
  • colors: 一个列表,表示用于着色不同根的颜色,使用ROOT_COLORS_DEEP常量定义。
  • coefs: 一个列表,表示复数多项式方程的系数,从最高次到最低次排列,例如[1.0, -1.0, 1.0, 0.0, 0.0, 1.0]表示方程z^5 + z^2 - z + 1 = 0。
  • scale_factor: 一个浮点数,表示分形图像的缩放因子,用于调整图像的大小。
  • offset: 一个数组,表示分形图像的偏移量,用于调整图像的位置。
  • n_steps: 一个整数,表示牛顿法迭代求解根的最大步数,用于控制图像的精度和效果。
  • julia_highlight: 一个浮点数,表示朱利亚集高亮效果的强度,用于在分形图像中突出显示朱利亚集的边界。
  • max_degree: 一个整数,表示复数多项式方程的最大次数,用于限制方程的复杂度。
  • saturation_factor: 一个浮点数,表示分形图像的饱和度因子,用于调整图像的颜色饱和度。
  • opacity: 一个浮点数,表示分形图像的透明度,用于调整图像的可见度。
  • black_for_cycles: 一个布尔值,表示是否使用黑色来着色那些在迭代过程中出现周期性行为的点,用于显示分形图像中的奇异点。
  • is_parameter_space: 一个布尔值,表示是否将分形图像视为参数空间,用于在参数空间中显示不同系数对应的分形图像。

这个类有以下方法:

  • init(self, plane, ** kwargs): 这是类的构造方法,接受一个plane对象作为参数,表示要在哪个平面上显示分形图像,并使用**kwargs传递其他属性值。这个方法调用了父类VMobject的__init__方法,并使用replace方法将自身替换为plane对象,并设置stretch为True。
  • init_data(self): 这是类的初始化数据方法,设置了自身的points属性为[UL, DL, UR, DR]四个点,表示平面上左上、左下、右上、右下四个角落的位置。
  • init_uniforms(self): 这是类的初始化统一变量方法,调用了父类VMobject的init_uniforms方法,并使用set_colors, set_julia_highlight, set_coefs, set_scale, set_offset, set_n_steps, set_saturation_factor, set_opacity等方法设置了自身的uniforms属性中各个统一变量的值。统一变量是一种在着色器程序中使用的变量类型,它们在所有顶点或片段中保持不变。这些统一变量包括了颜色、高亮、系数、根、缩放、偏移、步数、饱和度、透明度等信息。
  • set_colors(self, colors): 这是类的设置颜色方法,接受一个颜色列表作为参数,并更新自身的uniforms属性中的color{n}变量的值,n是颜色的索引。参数 colors 是一个颜色列表。set_colors()方法将 colors 中的颜色更新到 mobjectuniforms 属性中。
   class MyMobject(Mobject):
    def __init__(self):
        super().__init__()
        self.set_colors(["red", "green", "blue"])

m = MyMobject()
print(m.uniforms)

输出为:

{‘color0’: array([1., 0., 0., 1.], dtype=float32), ‘color1’: array([0., 1., 0., 1.], dtype=float32), ‘color2’: array([0., 0., 1., 1.], dtype=float32)}

mobjectuniforms 属性是一个字典,用于存储mobject的所有变量,这些变量可以通过Manim的属性动画(Property Animation)系统进行动画处理。

set_colors()方法首先将 colors 中的颜色转换为RGBA颜色。然后,它将 RGBA 颜色存储在 mobjectuniforms 属性中,键名为 f"color{n}",其中 n 是颜色的索引。

最后,set_colors()方法返回 mobject 本身。这个方法返回自身对象,以便进行链式调用。

方法返回self方便链式调用的原因是,链式调用是指在一个语句中连续调用多个方法,而每个方法的返回值都是下一个方法的调用对象。如果方法返回self,就相当于返回了当前对象本身,这样就可以继续在后面添加其他方法,形成一个链式结构。

例如,假设有一个类Person,它有三个方法:say_hello, introduce, say_bye,分别用于打招呼、自我介绍和道别。如果这三个方法都返回self,那么可以这样写:

  p = Person('Alice')
  p.say_hello().introduce().say_bye()

这样就可以在一行代码中完成三个操作,而不需要分开写:

  p = Person('Alice')
  p.say_hello()
  p.introduce()
  p.say_bye()

这样的好处是可以使代码更简洁、清晰和易读,也可以避免重复输入对象的名字。 此处的uniforms字典是在一开始的self.init_uniform()里面所添加的一个字典,并且此处使用了字典下属的update方法来对字典进行进一步的更新。Python 字典里面的 update() 方法用于更新字典。

update() 方法的语法是:

dict.update(dict2)

参数 dict2 是另一个字典。update() 方法将 dict2 中的键值对更新到 dict 中。

如果 dict2 中存在 dict 中已经存在的键,则 update() 方法将 dict2 中的值覆盖 dict 中的值。

  • set_julia_highlight(self, value): 这是类的设置朱利亚集高亮方法,接受一个浮点数作为参数,并更新自身的uniforms属性中的julia_highlight变量的值。这个方法返回自身对象,以便进行链式调用。
  • set_coefs(self, coefs, reset_roots=True): 这是类的设置系数方法,接受一个系数列表和一个布尔值作为参数,并更新自身的uniforms属性中的coefn变量的值,其中n是从0到最大次数的整数,coefn是一个包含两个浮点数的数组,表示复数系数的实部和虚部。首先,这个方法创建一个名为full_coefs的列表,这个列表包含了输入的系数coefs,以及一些零,使得列表的长度等于self.max_degree + 1(就是为了补足0到最高阶次,这个算法的最高阶次max_degree默认为5)。如果reset_roots为True,则调用set_roots方法根据系数计算并设置根的值,否则不改变根的值。这个方法使用了complex函数将系数转换为复数。这个方法返回自身对象,以便进行链式调用。
  • set_roots(self, roots, reset_coefs=True): 这是类的设置根方法,接受一个根列表和一个布尔值作为参数,并更新自身的uniforms属性中的n_roots和rootn变量的值,其中n_roots是一个浮点数,表示根的个数,rootn是一个包含两个浮点数的数组,表示复数根的实部和虚部。如果reset_coefs为True,则调用set_coefs方法根据根计算并设置系数的值,否则不改变系数的值。这个方法使用了complex函数将根转换为复数。这个方法返回自身对象,以便进行链式调用。
  • set_scale(self, scale_factor): 这是类的设置缩放方法,接受一个浮点数作为参数,并更新自身的uniforms属性中的scale_factor变量的值。这个方法返回自身对象,以便进行链式调用。
  • set_offset(self, offset): 这是类的设置偏移方法,接受一个数组作为参数,并更新自身的uniforms属性中的offset变量的值。这个方法返回自身对象,以便进行链式调用。
  • set_n_steps(self, n_steps): 这是类的设置步数方法,接受一个整数作为参数,并更新自身的uniforms属性中的n_steps变量的值。这个方法返回自身对象,以便进行链式调用。
  • set_saturation_factor(self, saturation_factor): 这是类的设置饱和度方法,接受一个浮点数作为参数,并更新自身的uniforms属性中的saturation_factor变量的值。这个方法返回自身对象,以便进行链式调用。
  • set_opacities(self, *opacities): 这是类的设置透明度方法,接受若干个浮点数作为参数,并更新自身的uniforms属性中colorn变量中第四个元素(即透明度)的值。这个方法返回自身对象,以便进行链式调用。
  • set_opacity(self, opacity, recurse=True): 这是类的设置透明度方法,接受一个浮点数和一个布尔值作为参数,并调用set_opacities方法将所有颜色的透明度设置为相同的值。如果recurse为True,则还会递归地调用子对象(如果有)的set_opacity方法。这个方法返回自身对象,以便进行链式调用。

关键在于Newton Fractal和Scene里面的get_fractals方法里面

get_fractals()方法

get_fractals()方法的主要作用是生成Fatou集和Julia集。Fatou集是指那些在迭代过程中不会逃逸到无穷大的点组成的集合。Julia集是指那些在迭代过程中会逃逸到无穷大的点组成的集合。

Fatou集和Julia集的形状取决于多项式的系数。在这个方法中,多项式的系数为roots_to_coefficients([-1.5, 1.5, 1j, -1j])

get_fractals()方法首先创建一个VGroup来包含Fatou集的各个部分。然后,它创建一个Group来包含Fatou集的各个部分和Julia集。最后,它为每个Fatou集和Julia集添加一个updater,以确保它们的位置和大小与各自的平面相同。

get_fractals()方法的输出是一个Group,其中包含Fatou集和Julia集。

以下是对get_fractals()方法中的一些关键代码的解释:

coefs = roots_to_coefficients([-1.5, 1.5, 1j, -1j])
n = len(coefs) - 1

这部分代码计算多项式的系数和次数。

f_planes = VGroup(*(self.get_plane() for x in range(n)))
f_planes.arrange(RIGHT, buff=LARGE_BUFF)

这部分代码创建一个VGroup来包含Fatou集的各个部分。Fatou集的各个部分是通过调用self.get_plane()来创建的。self.get_plane()返回一个平面,其中包含一个单位圆。

plusses = [Tex("+") for _ in range(n - 1)]  # Tex("+").replicate(n - 1)
f_group = Group(*it.chain(*zip(f_planes, plusses)))
f_group.add(f_planes[-1])
f_group.arrange(RIGHT)

这部分代码创建一个Group来包含Fatou集的各个部分和Julia集。f_group包含n个平面,每个平面代表Fatou集的一个部分。f_group还包含一个VGroup,其中包含n - 1个加号。加号用于将Fatou集的各个部分连接起来。

fatou = Group(
            *(
                NewtonFractal(f_plane, coefs=coefs, colors=colors)
                for f_plane in f_planes
            )
        )

这部分代码为Fatou集的各个部分创建NewtonFractal对象。

for i, fractal in enumerate(fatou):
            opacities = n * [0.2]
            opacities[i] = 1
            fractal.set_opacities(*opacities)

这部分代码设置Fatou集的各个部分的不透明度。只有当前的Fatou集部分是不透明的,其他部分都是透明的。

j_plane = self.get_plane()
j_plane.set_y(jy)
julia = NewtonFractal(j_plane, coefs=coefs, colors=5 * [GREY_A])
julia.set_julia_highlight(1e-3)
j_group = Group(julia, j_plane)

这部分代码为Julia集创建NewtonFractal对象。

for fractal, plane in zip((*fatou, julia), (*f_planes, j_plane)):
            fractal.plane = plane
            fractal.add_updater(
                lambda m: m.set_offset(m.plane.get_center())
                .set_scale(m.plane.get_x_unit_size())
                .replace(m.plane)
            )

这部分代码为每个Fatou集和Julia集添加一个updater,以确保它们的位置和大小与各自的平面相同。

fractals = Group(f_group, j_group)
return fractals

这部分代码返回一个Group,其中包含Fatou集和Julia集。

您好,这是Bing。我可以为您解释这段代码的作用和输出。这段代码是定义一个类方法的,它的参数是jy和fy,分别表示Julia集和Fatou集的y轴位置。这个方法的主要作用是生成一组分形图像,包括n个Fatou集和一个Julia集,其中n是多项式的次数。这个方法的输出是一个Group对象,它包含了所有分形图像和对应的平面对象。

具体来说,这段代码的步骤如下:

  • 首先,定义一个coefs变量,它是一个列表,表示多项式的系数。这里使用roots_to_coefficients方法,根据多项式的根来计算系数。这里的根是[-1.5, 1.5, 1j, -1j],表示四个复数。
  • 然后,定义一个n变量,它是一个整数,表示多项式的次数。这里使用len(coefs) - 1来计算次数,因为系数列表的长度比次数多一。
  • 接着,定义一个colors变量,它是一个列表,表示分形图像的颜色。这里使用self.colors属性来赋值。
  • 然后,定义一个f_planes变量,它是一个VGroup对象,表示n个Fatou集对应的平面对象。这里使用self.get_plane方法来创建每个平面对象,并且使用VGroup方法来将它们组合在一起。然后使用arrange方法来将它们按照右对齐的方式排列,并且设置缓冲区为LARGE_BUFF。
  • 接着,定义一个plusses变量,它是一个列表,表示n - 1个加号符号对象。这里使用Tex方法来创建每个加号符号对象,并且使用列表推导式来生成列表。
  • 然后,定义一个f_group变量,它是一个Group对象,表示Fatou集和加号符号的组合。这里使用Group方法来创建对象,并且使用it.chain和zip方法来将f_planes和plusses交错地添加到对象中。然后再添加f_planes中的最后一个元素,并且使用arrange方法来将它们按照右对齐的方式排列。
  • 接着,定义一个fatou变量,它是一个Group对象,表示n个Fatou集的分形图像对象。这里使用NewtonFractal方法来创建每个分形图像对象,并且传入对应的平面对象、系数列表和颜色列表作为参数。然后使用Group方法来将它们组合在一起。
  • 然后,对于每个分形图像对象,设置其透明度属性。这里使用enumerate方法来遍历fatou中的元素,并且获取其索引和值。然后定义一个opacities变量,它是一个列表,表示n个分形图像对象的透明度值。这里将所有元素的值设为0.2,除了当前索引对应的元素设为1.0。然后使用set_opacities方法来设置每个分形图像对象的透明度属性。
  • 然后,将fatou中的所有元素添加到f_group中,并且使用set_y方法来设置f_group的y轴位置为fy参数的值。
  • 接着,定义一个j_plane变量,它是一个Mobject对象,表示Julia集对应的平面对象。这里使用self.get_plane方法来创建平面对象,并且使用set_y方法来设置其y轴位置为jy参数的值。
  • 然后,定义一个julia变量,它是一个NewtonFractal对象,表示Julia集的分形图像对象。这里使用NewtonFractal方法来创建分形图像对象,并且传入j_plane、coefs和5个GREY_A颜色作为参数。然后使用set_julia_highlight方法来设置其高亮属性为1e-3。
  • 接着,定义一个j_group变量,它是一个Group对象,表示Julia集和平面的组合。这里使用Group方法来创建对象,并且添加julia和j_plane作为元素。
  • 然后,对于每个分形图像对象和对应的平面对象,设置其更新函数。这里使用zip方法来遍历fatou和julia中的元素,并且获取其值。然后对于每个分形图像对象,设置其plane属性为对应的平面对象,并且使用add_updater方法来添加一个匿名函数作为更新函数。这个函数的作用是根据平面对象的中心位置、x轴单位长度和形状来调整分形图像对象的偏移量、缩放比例和替换效果。
  • 最后,定义一个fractals变量,它是一个Group对象,表示所有分形图像和平面的组合。这里使用Group方法来创建对象,并且添加f_group和j_group作为元素。然后返回这个对象作为方法的输出。

我希望这对您有所帮助。如果您有其他问题,请随时提出。😊

我决定在搞明白整个架构之后将整个模块在manimce里面进行重构,这将是很大的工程,但是我不后悔