首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何用manim创建正确的饼图

如何用manim创建正确的饼图
EN

Stack Overflow用户
提问于 2021-01-19 00:16:19
回答 2查看 795关注 0票数 7

总之,我正在尝试(这实际上是我的第一个manim程序)。

代码语言:javascript
运行
复制
from manim import *
import copy
import numpy as np
import random

color_palette = [BLUE, GREEN, YELLOW, GREY_BROWN]

class TestPie(Scene):
    def construct(self):
        n_elements = 3
        radius = 1
        weights = np.random.rand(n_elements)
        weights /= weights.sum()
        angles = weights*np.pi*2
        angles_offset = [0]+np.cumsum(weights*np.pi*2)[:-1].tolist()
        arcs = [Arc(angles_offset[i], angles[i]) for i in range(n_elements)]
        arcs2 = copy.deepcopy(arcs)
        triangles = [Polygon(*[
            # first element
            (radius*np.cos(angles_offset[i]), radius*np.sin(angles_offset[i]),0),
            (0, 0, 0), # second element
            # third element
            (radius*np.cos(angles_offset[(i+1)%n_elements]),
             radius*np.sin(angles_offset[(i+1)%n_elements]), 0)], stroke_width=0)
                    for i in range(n_elements)]
        lines = [Line((0,0,0),
                      (radius*np.cos(angles_offset[i]), radius*np.sin(angles_offset[i]), 0))
                 for i in range(n_elements)]
        for i in range(n_elements):
            arcs2[i].set_fill(color_palette[i%len(color_palette)], opacity=0.5)
            triangles[i].set_fill(color_palette[i%len(color_palette)], opacity=0.5)

        self.play(
            *map(lambda obj: ShowCreation(obj, run_time=1), arcs),
            *map(lambda obj: ShowCreation(obj, run_time=1), lines),
        )
        self.play(
            *map(lambda i: Transform(arcs[i], arcs2[i], runtime=1), range(n_elements)),
            *map(lambda obj: FadeIn(obj, run_time=1), triangles),

        )

        self.wait()
        weights = np.random.rand(n_elements)
        weights /= weights.sum()
        angles = weights*np.pi*2
        angles_offset = [0]+np.cumsum(weights*np.pi*2)[:-1].tolist()
        arcs2 = [Arc(angles_offset[i], angles[i]) for i in range(n_elements)]
        lines2 = [Line((0,0,0),
                       (radius*np.cos(angles_offset[i]), radius*np.sin(angles_offset[i]), 0))
                  for i in range(n_elements)]
        triangles2 = [Polygon(*[
            # first element
            (radius*np.cos(angles_offset[i]), radius*np.sin(angles_offset[i]),0),
            (0, 0, 0), # second element
            # third element
            (radius*np.cos(angles_offset[(i+1)%n_elements]),
             radius*np.sin(angles_offset[(i+1)%n_elements]), 0)], stroke_width=0)
                    for i in range(n_elements)]

        for i in range(n_elements):
            arcs2[i].set_fill(color_palette[i%len(color_palette)], opacity=0.5)
            triangles2[i].set_fill(color_palette[i%len(color_palette)], opacity=0.5)

        self.play(
            *map(lambda i: Transform(lines[i], lines2[i],
                                     runtime=1), range(n_elements)),
            *map(lambda i: Transform(arcs[i], arcs2[i],
                                     runtime=1), range(n_elements)),
            *map(lambda i: Transform(triangles[i], triangles2[i],
                                     runtime=1), range(n_elements)),
        )
        self.wait(2)

产出:

因此,我现在的计划有两个问题。我希望你能帮我个忙。

1.,因为我使用的是三角形和弧线,所以我得到了一个丑陋的缺口,你可以在下面的图片中看到。

2. --我正在使用ArcTriangeLine类进行丑陋的转换,这些转换应该遵循圆周,这不是现在的情况。您可以欣赏更多的中间丑陋的步骤在下面的图像。(如你所见,它不再是圆的了)

EN

Stack Overflow用户

发布于 2021-01-25 12:21:50

对于第一个问题,避免通过准确地排列单独的形状来创建形状。事实上,完全避免将不同的形状排列在一起:图形渲染引擎通常是在呈现这种情况时有困难。与其从一个圆形段和一个三角形中创建一个圆形扇区,不如创建一个表示整个扇区的单一形状,该扇区将被绘制为一个单元。在本例中,使用Sector类来表示扇区,而不是单独的ArcPolygon

对于第二个问题,问题是默认情况下,manim计算中间形状点态。插值行为由形状的interpolate方法控制。通过对形状进行子类化,您可以重写interpolate方法,而可以从定义形状的更自然的高级参数中计算中间形状:在这种情况下,中间形状是中心、角度和半径。

这两个修复程序都包含在下面的示例中。

代码语言:javascript
运行
复制
from manim import *
import numpy as np

class MySector(Sector):
    """ Circular sector shape with a custom interpolation method. """

    def interpolate(self, mobject1, mobject2, alpha, path_func=straight_path):
        if not (isinstance(mobject1, MySector) and isinstance(mobject2, MySector)):
            return super().interpolate(mobject1, mobject2, alpha, path_func=path_func)

        for attr in (
            'start_angle', 'angle',
            'inner_radius', 'outer_radius',
        ):
            v1 = getattr(mobject1, attr)
            v2 = getattr(mobject2, attr)
            setattr(self, attr, path_func(v1, v2, alpha))

        self.arc_center = path_func(
            mobject1.get_arc_center(),
            mobject2.get_arc_center(),
            alpha
        )
        self.interpolate_color(mobject1, mobject2, alpha)
        self.clear_points()
        self.generate_points()
        return self


color_palette = [BLUE, GREEN, YELLOW, GREY_BROWN]

class TestPie(Scene):
    def construct(self):
        weights = np.array([2.0, 3.0, 4.0])
        weights /= weights.sum()

        angles = weights * TAU
        angles_offset = np.cumsum((0, *angles[:-1]))

        sectors1 = [
            MySector(start_angle=ao, angle=a,
                stroke_width=DEFAULT_STROKE_WIDTH,
                fill_opacity=0)
            for ao, a in zip(angles_offset, angles)
        ]

        sectors2 = [
            MySector(start_angle=ao, angle=a,
                stroke_width=DEFAULT_STROKE_WIDTH,
                fill_color=color_palette[i % len(color_palette)], fill_opacity=0.5)
            for i, (ao, a) in enumerate(zip(angles_offset, angles))
        ]

        self.play(
            *(ShowCreation(a1, run_time=1) for a1 in sectors1)
        )

        self.play(
            *(Transform(a1, a2, runtime=1) for (a1, a2) in zip(sectors1, sectors2))
        )

        self.wait()

        weights = np.array([4.0, 3.0, 2.0])
        weights /= weights.sum()

        angles = weights * TAU
        angles_offset = np.cumsum((0, *angles[:-1]))

        sectors2 = [
            MySector(start_angle=ao, angle=a,
                stroke_width=DEFAULT_STROKE_WIDTH,
                fill_color=color_palette[i % len(color_palette)], fill_opacity=0.5)
            for i, (ao, a) in enumerate(zip(angles_offset, angles))
        ]

        self.play(
            *(Transform(a1, a2, runtime=1) for (a1, a2) in zip(sectors1, sectors2))
        )
        self.wait(2)

下面是由此产生的动画:

以上不保留组成饼图扇区的线条的初始绘图动画。您可以重写pointwise_become_partial方法以使其返回,也可以简单地从原始代码中还原Line形状。

票数 8
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65783858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档