mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 05:52:34 +08:00
Code() in file Code_mobject.py to display code with color highlighted added Paragraph() and "exact_spaces" parameter to Text() (#1036)
This commit is contained in:
@ -5,8 +5,10 @@ import hashlib
|
||||
import cairo
|
||||
import manimlib.constants as consts
|
||||
from manimlib.constants import *
|
||||
from manimlib.mobject.geometry import Dot
|
||||
from manimlib.container.container import Container
|
||||
from manimlib.mobject.geometry import Dot, Rectangle
|
||||
from manimlib.mobject.svg.svg_mobject import SVGMobject
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
|
||||
|
||||
@ -51,14 +53,17 @@ class Text(SVGMobject):
|
||||
def __init__(self, text, **config):
|
||||
self.full2short(config)
|
||||
digest_config(self, config)
|
||||
text_without_tabs = text
|
||||
if text.find('\t') != -1:
|
||||
text = text.replace('\t', ' '*self.tab_width)
|
||||
self.text = text
|
||||
text_without_tabs = text.replace('\t', ' '*self.tab_width)
|
||||
self.text = text_without_tabs
|
||||
self.lsh = self.size if self.lsh == -1 else self.lsh
|
||||
|
||||
file_name = self.text2svg()
|
||||
self.remove_last_M(file_name)
|
||||
SVGMobject.__init__(self, file_name, **config)
|
||||
self.apply_front_and_end_spaces()
|
||||
self.text = text
|
||||
self.apply_space_chars()
|
||||
|
||||
nppc = self.n_points_per_cubic_curve
|
||||
@ -86,20 +91,76 @@ class Text(SVGMobject):
|
||||
if self.height is None and self.width is None:
|
||||
self.scale(TEXT_MOB_SCALE_FACTOR)
|
||||
|
||||
def apply_space_chars(self):
|
||||
indexes = self.find_indexes(' ') + self.find_indexes('\n')
|
||||
indexes = sorted(indexes, key=lambda i: i[0])
|
||||
if len(self.text) == len(indexes):
|
||||
space = Dot(fill_opacity=0, stroke_opacity=0)
|
||||
self.submobjects = [space.copy() for _ in range(len(indexes))]
|
||||
return
|
||||
for start, _ in indexes:
|
||||
space = Dot(fill_opacity=0, stroke_opacity=0)
|
||||
if start == 0:
|
||||
space.move_to(self.submobjects[0].get_center())
|
||||
def get_space_width(self):
|
||||
size = self.size * 10
|
||||
|
||||
dir_name = consts.TEXT_DIR
|
||||
file_name = os.path.join(dir_name, "space") + '.svg'
|
||||
|
||||
surface = cairo.SVGSurface(file_name, 600, 400)
|
||||
context = cairo.Context(surface)
|
||||
context.set_font_size(size)
|
||||
context.move_to(START_X, START_Y)
|
||||
context.select_font_face(self.font, self.str2slant(self.slant), self.str2weight(self.weight))
|
||||
context.move_to(START_X, START_Y)
|
||||
context.show_text("_")
|
||||
surface.finish()
|
||||
svg_with_space = SVGMobject(file_name, height=self.height,
|
||||
width=self.width,
|
||||
stroke_width=self.stroke_width,
|
||||
should_center=self.should_center,
|
||||
unpack_groups=self.unpack_groups, )
|
||||
space_width = svg_with_space.get_width()
|
||||
return space_width
|
||||
|
||||
def apply_front_and_end_spaces(self):
|
||||
space_width = self.get_space_width()
|
||||
max_height = self.get_height()
|
||||
front_spaces_count = 0
|
||||
i = -1
|
||||
for i in range(self.text.__len__()):
|
||||
if self.text[i] == " ":
|
||||
front_spaces_count += 1
|
||||
continue
|
||||
else:
|
||||
space.move_to(self.submobjects[start-1].get_center())
|
||||
self.submobjects.insert(start, space)
|
||||
break
|
||||
first_visible_char_index = i
|
||||
if first_visible_char_index != 0:
|
||||
space = Rectangle(width=space_width * front_spaces_count, height=max_height, fill_opacity=0,
|
||||
stroke_opacity=0,
|
||||
stroke_width=0)
|
||||
text_width = self.get_width()
|
||||
space.move_to(np.array([-text_width / 2, max_height / 2, 0]))
|
||||
self.next_to(space, direction=RIGHT, buff=0)
|
||||
self.submobjects.insert(0, space)
|
||||
|
||||
i = -1
|
||||
last_spaces_count = 0
|
||||
for i in range(self.text.__len__() - 1, -1, -1):
|
||||
if self.text[i] == " ":
|
||||
last_spaces_count += 1
|
||||
continue
|
||||
else:
|
||||
break
|
||||
last_visible_char_index = i
|
||||
if last_visible_char_index != self.text.__len__() - 1:
|
||||
space = Rectangle(width=space_width * last_spaces_count, height=max_height, fill_opacity=0,
|
||||
stroke_opacity=0,
|
||||
stroke_width=0)
|
||||
text_width = self.get_width()
|
||||
space.move_to(np.array([-text_width / 2, max_height / 2, 0]))
|
||||
self.next_to(space, direction=LEFT, buff=0)
|
||||
self.submobjects.append(space)
|
||||
self.move_to(np.array([0,0,0]))
|
||||
|
||||
def apply_space_chars(self):
|
||||
char_index = 0
|
||||
while char_index < self.text.__len__() - 1:
|
||||
char_index += 1
|
||||
if self.text[char_index] == " " or self.text[char_index] == "\t" or self.text[char_index] == "\n":
|
||||
space = Dot(fill_opacity=0, stroke_opacity=0)
|
||||
space.move_to(self.submobjects[char_index - 1].get_center())
|
||||
self.submobjects.insert(char_index, space)
|
||||
|
||||
def remove_last_M(self, file_name):
|
||||
with open(file_name, 'r') as fpr:
|
||||
@ -226,7 +287,8 @@ class Text(SVGMobject):
|
||||
lsh = self.lsh * 10
|
||||
|
||||
if self.font == '':
|
||||
print(NOT_SETTING_FONT_MSG)
|
||||
if NOT_SETTING_FONT_MSG != '':
|
||||
print(NOT_SETTING_FONT_MSG)
|
||||
|
||||
dir_name = consts.TEXT_DIR
|
||||
hash_name = self.text2hash()
|
||||
@ -257,3 +319,81 @@ class Text(SVGMobject):
|
||||
offset_x += context.text_extents(text)[4]
|
||||
|
||||
return file_name
|
||||
|
||||
class TextWithFixHeight(Text):
|
||||
def __init__(self, text, **kwargs):
|
||||
Text.__init__(self, text, **kwargs)
|
||||
max_height = Text("(gyt{[/QW", **kwargs).get_height()
|
||||
rectangle = Rectangle(width=0, height=max_height, fill_opacity=0,
|
||||
stroke_opacity=0,
|
||||
stroke_width=0)
|
||||
self.submobjects.append(rectangle)
|
||||
|
||||
class Paragraph(VGroup):
|
||||
CONFIG = {
|
||||
"line_spacing": 0.1,
|
||||
"alignment": "center",
|
||||
}
|
||||
|
||||
def __init__(self, *text, **config):
|
||||
Container.__init__(self, **config)
|
||||
self.lines_list = list(text)
|
||||
self.lines = []
|
||||
self.lines.append([])
|
||||
for line_no in range(self.lines_list.__len__()):
|
||||
if "\n" in self.lines_list[line_no]:
|
||||
self.lines_list[line_no:line_no + 1] = self.lines_list[line_no].split("\n")
|
||||
for line_no in range(self.lines_list.__len__()):
|
||||
self.lines[0].append(TextWithFixHeight(self.lines_list[line_no], **config))
|
||||
self.char_height = TextWithFixHeight("(", **config).get_height()
|
||||
self.lines.append([])
|
||||
self.lines[1].extend([self.alignment for _ in range(self.lines_list.__len__())])
|
||||
self.lines[0][0].move_to(np.array([0, 0, 0]))
|
||||
self.align_lines()
|
||||
VGroup.__init__(self, *[self.lines[0][i] for i in range(self.lines[0].__len__())], **config)
|
||||
self.move_to(np.array([0, 0, 0]))
|
||||
|
||||
def set_all_lines_alignment(self, alignment):
|
||||
self.lines[1] = [alignment for _ in range(self.lines_list.__len__())]
|
||||
for line_no in range(0, self.lines[0].__len__()):
|
||||
self.change_alignment_for_a_line(alignment, line_no)
|
||||
return self
|
||||
|
||||
def set_alignment(self, alignment, line_no):
|
||||
self.change_alignment_for_a_line(alignment, line_no)
|
||||
return self
|
||||
|
||||
def change_alignment_for_a_line(self, alignment, line_no):
|
||||
self.lines[1][line_no] = alignment
|
||||
if self.lines[1][line_no] == "center":
|
||||
self[line_no].move_to(self.get_top() +
|
||||
np.array([0, -self.char_height / 2, 0]) +
|
||||
np.array([0, - line_no * (self.char_height + self.line_spacing), 0]))
|
||||
elif self.lines[1][line_no] == "right":
|
||||
self[line_no].move_to(self.get_top() +
|
||||
np.array([0, -self.char_height / 2, 0]) +
|
||||
np.array([self.get_width() / 2 - self.lines[0][line_no].get_width() / 2,
|
||||
- line_no * (self.char_height + self.line_spacing), 0])
|
||||
)
|
||||
elif self.lines[1][line_no] == "left":
|
||||
self[line_no].move_to(self.get_top() +
|
||||
np.array([0, -self.char_height / 2, 0]) +
|
||||
np.array([- self.get_width() / 2 + self.lines[0][line_no].get_width() / 2,
|
||||
- line_no * (self.char_height + self.line_spacing), 0])
|
||||
)
|
||||
|
||||
def align_lines(self):
|
||||
for line_no in range(0, self.lines[0].__len__()):
|
||||
if self.lines[1][line_no] == "center":
|
||||
self.lines[0][line_no].move_to(
|
||||
np.array([0, 0, 0]) + np.array([0, - line_no * (self.char_height + self.line_spacing), 0]))
|
||||
elif self.lines[1][line_no] == "left":
|
||||
self.lines[0][line_no].move_to(np.array([0, 0, 0]) +
|
||||
np.array([self.lines[0][line_no].get_width() / 2,
|
||||
- line_no * (self.char_height + self.line_spacing), 0])
|
||||
)
|
||||
elif self.lines[1][line_no] == "right":
|
||||
self.lines[0][line_no].move_to(np.array([0, 0, 0]) +
|
||||
np.array([- self.lines[0][line_no].get_width() / 2,
|
||||
- line_no * (self.char_height + self.line_spacing), 0])
|
||||
)
|
||||
|
Reference in New Issue
Block a user