0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

manimの作法 その27

Posted at

#概要

manimの作法、調べてみた。
Histogram使ってみた。

#サンプルコード

from manimlib.imports import *

def text_range(start,stop,step):
	numbers = np.arange(start,stop,step)
	labels = []
	for x in numbers:
		labels.append(str(x))
	return labels

class Histogram(VMobject):
	CONFIG = {
		"start_color": RED,
		"end_color": BLUE,
		"x_scale": 1.0,
		"y_scale": 1.0,
		"x_labels": "auto",
		"y_labels": "auto",
		"y_label_position": "top",
		"x_min": 0,
		"bar_stroke_width": 5,
		"outline_stroke_width": 0,
		"stroke_color" : WHITE
	}
	def __init__(self, x_values, y_values, mode = "widths", **kwargs):
		digest_config(self, kwargs)
		if mode == "widths" and len(x_values) != len(y_values):
			raise Exception("Array lengths do not match up!")
		elif mode == "posts" and len(x_values) != len(y_values) + 1:
			raise Exception("Array lengths do not match up!")
		self.y_values = y_values
		self.x_values = x_values
		self.mode = mode
		self.process_values()
		VMobject.__init__(self, **kwargs)
	def process_values(self):
		self.y_values = np.array(self.y_values)
		if self.mode == "widths":
			self.widths = self.x_values
			self.posts = np.cumsum(self.widths)
			self.posts = np.insert(self.posts, 0, 0)
			self.posts += self.x_min
			self.x_max = self.posts[-1]
		elif self.mode == "posts":
			self.posts = self.x_values
			self.widths = self.x_values[1:] - self.x_values[:-1]
			self.x_min = self.posts[0]
			self.x_max = self.posts[-1]
		else:
			raise Exception("Invalid mode or no mode specified!")
		self.x_mids = 0.5 * (self.posts[:-1] + self.posts[1:])
		self.widths_scaled = self.x_scale * self.widths
		self.posts_scaled = self.x_scale * self.posts
		self.x_min_scaled = self.x_scale * self.x_min
		self.x_max_scaled = self.x_scale * self.x_max
		self.y_values_scaled = self.y_scale * self.y_values
	def generate_points(self):
		self.process_values()
		for submob in self.submobjects:
			self.remove(submob)
		def empty_string_array(n):
			arr = []
			for i in range(n):
				arr.append("")
			return arr
		def num_arr_to_string_arr(arr):
			ret_arr = []
			for x in arr:
				if x == np.floor(x):
					new_x = int(np.floor(x))
				else:
					new_x = x
				ret_arr.append(str(new_x))
			return ret_arr
		previous_bar = ORIGIN
		self.bars = VGroup()
		self.x_labels_group = VGroup()
		self.y_labels_group = VGroup()
		outline_points = []
		if self.x_labels == "widths":
			self.x_labels = num_arr_to_string_arr(self.widths)
		elif self.x_labels == "mids":
			self.x_labels = num_arr_to_string_arr(self.x_mids)
		elif self.x_labels == "auto":
			self.x_labels = num_arr_to_string_arr(self.x_mids)
		elif self.x_labels == "none":
			self.x_labels = empty_string_array(len(self.widths))
		if self.y_labels == "auto":
			self.y_labels = num_arr_to_string_arr(self.y_values)
		elif self.y_labels == "none":
			self.y_labels = empty_string_array(len(self.y_values))
		for (i,x) in enumerate(self.x_mids):
			bar = Rectangle(width = self.widths_scaled[i],height = self.y_values_scaled[i],stroke_width = self.bar_stroke_width,stroke_color = self.stroke_color,)
			if bar.height == 0:
				bar.height = 0.01
				bar.generate_points()
			t = float(x - self.x_min)/(self.x_max - self.x_min)
			bar_color = interpolate_color(self.start_color,self.end_color,t)
			bar.set_fill(color = bar_color, opacity = 1)
			bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN)
			self.bars.add(bar)
			x_label = TextMobject(self.x_labels[i])
			x_label.next_to(bar,DOWN)
			self.x_labels_group.add(x_label)
			y_label = TextMobject(self.y_labels[i])
			if self.y_label_position == "top":
				y_label.next_to(bar, UP)
			elif self.y_label_position == "center":
				y_label.move_to(bar)
			else:
				raise Exception("y_label_position must be top or center")
			self.y_labels_group.add(y_label)
			if i == 0:
				outline_points.append(bar.get_anchors()[-2])
			outline_points.append(bar.get_anchors()[0])
			outline_points.append(bar.get_anchors()[1])
			previous_bar = bar
		outline_points.append(bar.get_anchors()[2])
		outline_points.append(outline_points[0])
		self.outline = Polygon(*outline_points,stroke_width = self.outline_stroke_width,stroke_color = self.stroke_color)
		self.add(self.bars, self.x_labels_group, self.y_labels_group, self.outline)
		self.move_to(ORIGIN)
	def get_lower_left_point(self):
		return self.bars[0].get_anchors()[-2]

class BuildUpHistogram(Animation):
	def __init__(self, hist, **kwargs):
		self.histogram = hist

class FlashThroughHistogram(Animation):
	CONFIG = {
		"cell_color" : WHITE,
		"cell_opacity" : 0.8,
		"hist_opacity" : 0.2
	}
	def __init__(self, mobject, direction = "horizontal", mode = "random", **kwargs):
		digest_config(self, kwargs)
		self.cell_height = mobject.y_scale
		self.prototype_cell = Rectangle(width = 1,height = self.cell_height,fill_color = self.cell_color,fill_opacity = self.cell_opacity,stroke_width = 0,)
		x_values = mobject.x_values
		y_values = mobject.y_values
		self.mode = mode
		self.direction = direction
		self.generate_cell_indices(x_values,y_values)
		Animation.__init__(self,mobject,**kwargs)
	def generate_cell_indices(self,x_values,y_values):
		self.cell_indices = []
		for (i,x) in enumerate(x_values):
			nb_cells = int(np.floor(y_values[i]))
			for j in range(nb_cells):
				self.cell_indices.append((i, j))
		self.reordered_cell_indices = self.cell_indices
		if self.mode == "random":
			shuffle(self.reordered_cell_indices)
	def cell_for_index(self,i,j):
		if self.direction == "vertical":
			width = self.mobject.x_scale
			height = self.mobject.y_scale
			x = (i + 0.5) * self.mobject.x_scale
			y = (j + 0.5) * self.mobject.y_scale
			center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
		elif self.direction == "horizontal":
			width = self.mobject.x_scale / self.mobject.y_values[i]
			height = self.mobject.y_scale * self.mobject.y_values[i]
			x = i * self.mobject.x_scale + (j + 0.5) * width
			y = height / 2
			center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
		cell = Rectangle(width = width, height = height)
		cell.move_to(center)
		return cell
	def interpolate_mobject(self,t):
		if t == 0:
			self.mobject.add(self.prototype_cell)
		flash_nb = int(t * (len(self.cell_indices))) - 1
		(i,j) = self.reordered_cell_indices[flash_nb]
		cell = self.cell_for_index(i,j)
		self.prototype_cell.width = cell.get_width()
		self.prototype_cell.height = cell.get_height()
		self.prototype_cell.generate_points()
		self.prototype_cell.move_to(cell.get_center())
		if t == 1:
		   self.mobject.remove(self.prototype_cell)
	def clean_up_from_scene(self, scene = None):
		Animation.clean_up_from_scene(self, scene)
		self.update(1)
		if scene is not None:
			if self.is_remover():
				scene.remove(self.prototype_cell)
			else:
				scene.add(self.prototype_cell)
		return self

class OutlineableBars(VGroup):
	CONFIG = {
		"outline_stroke_width": 3,
		"stroke_color": WHITE
	}
	def create_outline(self, animated = False, **kwargs):
		outline_points = []
		for (i, bar) in enumerate(self.submobjects):
			if i == 0:
				outline_points.append(bar.get_corner(DOWN + LEFT))
			outline_points.append(bar.get_corner(UP + LEFT))
			outline_points.append(bar.get_corner(UP + RIGHT))
			previous_bar = bar
		outline_points.append(previous_bar.get_corner(DOWN + RIGHT))
		outline_points.append(outline_points[0])
		self.outline = Polygon(*outline_points,stroke_width = self.outline_stroke_width,stroke_color = self.stroke_color)
		if animated:
			self.play(FadeIn(self.outline, **kwargs))
		return self.outline

class test(Scene):
	def construct(self):
		self.probs = 1.0 / 6 * np.ones(6)
		x_scale = 1.3
		y_labels = ["${1\over 6}$"] * 6
		hist = Histogram(np.ones(6), self.probs, mode = "widths", x_labels = "none",y_labels = y_labels,y_label_position = "center",y_scale = 20,x_scale = x_scale,)
		hist.rotate(-TAU / 4)
		for label in hist.y_labels_group:
			label.rotate(TAU / 4)
		hist.remove(hist.y_labels_group)
		self.play(FadeIn(hist))
		self.play(LaggedStartMap(FadeIn, hist.y_labels_group))



#生成した動画

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?