Anyway, I'm kind of trying (this is actually my first manim
program).
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)
The output:
Thus, I have two problems with my current program. And I would appreciate a little help.
1. Since I'm using triangles and arcs, I get an ugly gap as you can see in the following picture.
2. I'm getting ugly transformations with the Arc
, Triange
and Line
classes, the transformations should follow the circumference which is not the case right now. You can appreciate more one of the intermediary ugly steps in the image below. (As you can see, it's not round any more)
Top Reasons SmartDraw is the Best Pie Chart Software You can easily import data to make your pie chart. Edit your chart to change colors, legend placement and more. You can even easily swap between chart types.
If creating the pie chart by hand, you will have to determine how many degrees of the circle each slice is. Since the circle has 360 degrees, multiply the percentage for each category by 360 to determine how big to make each slice.
For the first problem, avoid creating shapes by exactly lining up separate shapes. In fact, avoid exactly lining up separate shapes at all: graphics rendering engines often have trouble rendering such situations. Instead of creating a circular sector out of a circular segment and a triangle, create a single shape that will represent the whole sector that will be drawn as a single unit. In this case, use the Sector
class to represent the sector instead of separate Arc
and Polygon
.
For the second problem, the issue is that by default manim
computes intermediate shapes pointwise. The interpolation behaviour is controlled by the shape’s interpolate
method. By subclassing the shape, you can override the interpolate
method and instead compute intermediate shapes from more natural high-level parameters that define the shape: in this case, the centre, angles and radius.
Both of those fixes are incorporated in the sample below.
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)
And here’s the resulting animation:
The above doesn’t preserve the initial drawing animation for the lines that make up the pie chart sectors. You can override the pointwise_become_partial
method to bring it back, or you may simply restore the Line
shapes from your original code.
I know this was made a month ago, but for anyone looking, I have a better solution. I have created a manim graphing plugin (manim-graphing), that supports pie charts at a really high level.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With