def check_temporary_storage(config): if config["directories"][ "temporary_storage"] == "" and sys.platform == "win32": log.warning( "You may be using Windows platform and have not specified the path of" " `temporary_storage`, which may cause OSError. So it is recommended" " to specify the `temporary_storage` in the config file (.yml)")
def __init__(self, text, **kwargs): self.full2short(kwargs) digest_config(self, kwargs) if self.size: log.warning( "`self.size` has been deprecated and will " "be removed in future.", ) self.font_size = self.size if self.lsh == -1: self.lsh = self.font_size + self.font_size * DEFAULT_LINE_SPACING_SCALE else: self.lsh = self.font_size + self.font_size * self.lsh text_without_tabs = text if text.find('\t') != -1: text_without_tabs = text.replace('\t', ' ' * self.tab_width) self.text = text_without_tabs file_name = self.text2svg() PangoUtils.remove_last_M(file_name) self.remove_empty_path(file_name) SVGMobject.__init__(self, file_name, **kwargs) self.text = text if self.disable_ligatures: self.apply_space_chars() if self.t2c: self.set_color_by_t2c() if self.gradient: self.set_color_by_gradient(*self.gradient) if self.t2g: self.set_color_by_t2g() # anti-aliasing if self.height is None: self.scale(TEXT_MOB_SCALE_FACTOR)
def on_key_press(self, symbol: int, modifiers: int) -> None: try: char = chr(symbol) except OverflowError: log.warning("The value of the pressed key is too large.") return event_data = {"symbol": symbol, "modifiers": modifiers} propagate_event = EVENT_DISPATCHER.dispatch(EventType.KeyPressEvent, **event_data) if propagate_event is not None and propagate_event is False: return if char == RESET_FRAME_KEY: self.camera.frame.to_default_state() elif char == "z" and modifiers == COMMAND_MODIFIER: self.undo() elif char == "z" and modifiers == COMMAND_MODIFIER | SHIFT_MODIFIER: self.redo() # command + q elif char == QUIT_KEY and modifiers == COMMAND_MODIFIER: self.quit_interaction = True # Space or right arrow elif char == " " or symbol == ARROW_SYMBOLS[2]: self.hold_on_wait = False
def get_mobjects_from(self, svg: se.SVG) -> list[VMobject]: result = [] for shape in svg.elements(): if isinstance(shape, se.Group): continue elif isinstance(shape, se.Path): mob = self.path_to_mobject(shape) elif isinstance(shape, se.SimpleLine): mob = self.line_to_mobject(shape) elif isinstance(shape, se.Rect): mob = self.rect_to_mobject(shape) elif isinstance(shape, se.Circle): mob = self.circle_to_mobject(shape) elif isinstance(shape, se.Ellipse): mob = self.ellipse_to_mobject(shape) elif isinstance(shape, se.Polygon): mob = self.polygon_to_mobject(shape) elif isinstance(shape, se.Polyline): mob = self.polyline_to_mobject(shape) # elif isinstance(shape, se.Text): # mob = self.text_to_mobject(shape) elif type(shape) == se.SVGElement: continue else: log.warning(f"Unsupported element type: {type(shape)}") continue if not mob.has_points(): continue self.apply_style_to_mobject(mob, shape) if isinstance(shape, se.Transformable) and shape.apply: self.handle_transform(mob, shape.transform) result.append(mob) return result
def generate_mobject(self) -> None: super().generate_mobject() labels_count = len(self.labelled_spans) if not labels_count: for submob in self.submobjects: submob.label = -1 return labelled_content = self.get_content(is_labelled=True) file_path = self.get_file_path_by_content(labelled_content) labelled_svg = SVGMobject(file_path) if len(self.submobjects) != len(labelled_svg.submobjects): log.warning("Cannot align submobjects of the labelled svg " "to the original svg. Skip the labelling process.") for submob in self.submobjects: submob.label = -1 return self.rearrange_submobjects_by_positions(labelled_svg) unrecognizable_colors = [] for submob, labelled_svg_submob in zip(self.submobjects, labelled_svg.submobjects): color_int = self.hex_to_int( self.color_to_hex(labelled_svg_submob.get_fill_color())) if color_int > labels_count: unrecognizable_colors.append(color_int) color_int = 0 submob.label = color_int - 1 if unrecognizable_colors: log.warning( "Unrecognizable color labels detected (%s, etc). " "The result could be unexpected.", self.int_to_hex(unrecognizable_colors[0]))
def get_mobjects_from(self, element, style): result = [] if not isinstance(element, minidom.Element): return result style = cascade_element_style(element, style) if element.tagName == 'defs': self.update_ref_to_element(element, style) elif element.tagName == 'style': pass # TODO, handle style elif element.tagName in ['g', 'svg', 'symbol']: result += it.chain(*(self.get_mobjects_from(child, style) for child in element.childNodes)) elif element.tagName == 'path': result.append( self.path_string_to_mobject(element.getAttribute('d'), style)) elif element.tagName == 'use': result += self.use_to_mobjects(element, style) elif element.tagName == 'rect': result.append(self.rect_to_mobject(element, style)) elif element.tagName == 'circle': result.append(self.circle_to_mobject(element, style)) elif element.tagName == 'ellipse': result.append(self.ellipse_to_mobject(element, style)) elif element.tagName in ['polygon', 'polyline']: result.append(self.polygon_to_mobject(element, style)) else: log.warning(f"Unsupported element type: {element.tagName}") pass # TODO result = [m.insert_n_curves(0) for m in result if m is not None] self.handle_transforms(element, VGroup(*result)) if len(result) > 1 and not self.unpack_groups: result = [VGroup(*result)] return result
def get_content_prefix_and_suffix(self, is_labelled: bool) -> tuple[str, str]: global_attr_dict = { "foreground": self.base_color_hex, "font_family": self.font, "font_style": self.slant, "font_weight": self.weight, "font_size": str(self.font_size * 1024), } global_attr_dict.update(self.global_config) # `line_height` attribute is supported since Pango 1.50. pango_version = manimpango.pango_version() if tuple(map(int, pango_version.split("."))) < (1, 50): if self.lsh is not None: log.warning( "Pango version %s found (< 1.50), " "unable to set `line_height` attribute", pango_version) else: line_spacing_scale = self.lsh or DEFAULT_LINE_SPACING_SCALE global_attr_dict["line_height"] = str( ((line_spacing_scale) + 1) * 0.6) return self.get_cmd_str_pair( global_attr_dict, label_hex=self.int_to_hex(0) if is_labelled else None)
def update_local_attr(self, span: tuple[int, int], key: str, value: typing.Any) -> None: if span[0] >= span[1]: log.warning(f"Span {span} doesn't match any part of the string") return if span in self.local_attrs.keys(): _TextParser.update_attr_dict(self.local_attrs[span], key, value) return span_triplets = [] for sp, attr_dict in self.local_attrs.items(): if sp[1] <= span[0] or span[1] <= sp[0]: continue span_to_become = (max(sp[0], span[0]), min(sp[1], span[1])) spans_to_add = [] if sp[0] < span[0]: spans_to_add.append((sp[0], span[0])) if span[1] < sp[1]: spans_to_add.append((span[1], sp[1])) span_triplets.append((sp, span_to_become, spans_to_add)) for span_to_remove, span_to_become, spans_to_add in span_triplets: attr_dict = self.local_attrs.pop(span_to_remove) for span_to_add in spans_to_add: self.local_attrs[span_to_add] = attr_dict.copy() self.local_attrs[span_to_become] = attr_dict _TextParser.update_attr_dict(self.local_attrs[span_to_become], key, value)
def play(self, *args, **kwargs) -> None: if len(args) == 0: log.warning("Called Scene.play with no animations") return animations = self.anims_from_play_args(*args, **kwargs) self.begin_animations(animations) self.progress_through_animations(animations) self.finish_animations(animations)
def get_parts_by_text(self, word): if self.is_markup: log.warning("Slicing MarkupText via `get_parts_by_text`, " "the result could be unexpected.") elif not self.apply_space_chars: log.warning( "Slicing Text via `get_parts_by_text` without applying spaces, " "the result could be unexpected.") return VGroup(*(self[i:j] for i, j in self.find_indexes(word)))
def play(self, *proto_animations, **animation_config) -> None: if len(proto_animations) == 0: log.warning("Called Scene.play with no animations") return animations = self.prepare_animations(proto_animations, animation_config) self.begin_animations(animations) self.progress_through_animations(animations) self.finish_animations(animations)
def use_to_mobjects(self, use_element, local_style): # Remove initial "#" character ref = use_element.getAttribute("xlink:href")[1:] if ref not in self.ref_to_element: log.warning(f"{ref} not recognized") return VGroup() def_element, def_style = self.ref_to_element[ref] style = local_style.copy() style.update(def_style) return self.get_mobjects_from(def_element, style)
def play(self, *args, **kwargs): if len(args) == 0: log.warning("Called Scene.play with no animations") return animations = self.anims_from_play_args(*args, **kwargs) self.lock_static_mobject_data(*animations) self.begin_animations(animations) self.progress_through_animations(animations) self.finish_animations(animations) self.unlock_mobject_data()
def combine_movie_files(self) -> None: kwargs = { "remove_non_integer_files": True, "extension": self.movie_file_extension, } if self.scene.start_at_animation_number is not None: kwargs["min_index"] = self.scene.start_at_animation_number if self.scene.end_at_animation_number is not None: kwargs["max_index"] = self.scene.end_at_animation_number else: kwargs["remove_indices_greater_than"] = self.scene.num_plays - 1 partial_movie_files = get_sorted_integer_files( self.partial_movie_directory, **kwargs) if len(partial_movie_files) == 0: log.warning("No animations in this scene") return # Write a file partial_file_list.txt containing all # partial movie files file_list = os.path.join(self.partial_movie_directory, "partial_movie_file_list.txt") with open(file_list, 'w') as fp: for pf_path in partial_movie_files: if os.name == 'nt': pf_path = pf_path.replace('\\', '/') fp.write(f"file \'{pf_path}\'\n") movie_file_path = self.get_movie_file_path() commands = [ FFMPEG_BIN, '-y', # overwrite output file if it exists '-f', 'concat', '-safe', '0', '-i', file_list, '-loglevel', 'error', '-c', 'copy', movie_file_path ] if not self.includes_sound: commands.insert(-1, '-an') combine_process = sp.Popen(commands) combine_process.wait()
def break_up_by_scripts(self) -> None: # Match subscripts & superscripts. tex_string = self.tex_string whitespace_indices = self.whitespace_indices brace_indices_dict = self.brace_indices_dict script_spans = [] for script_index in self.script_indices: script_char = tex_string[script_index] extended_begin = script_index while extended_begin - 1 in whitespace_indices: extended_begin -= 1 script_begin = script_index + 1 while script_begin in whitespace_indices: script_begin += 1 if script_begin in brace_indices_dict.keys(): script_end = brace_indices_dict[script_begin] + 1 else: pattern = re.compile(r"[a-zA-Z0-9]|\\[a-zA-Z]+") match_obj = pattern.match(tex_string, pos=script_begin) if not match_obj: script_name = { "_": "subscript", "^": "superscript" }[script_char] log.warning( f"Unclear {script_name} detected while parsing. " "Please use braces to clarify" ) continue script_end = match_obj.end() tex_span = (script_begin, script_end) script_span = (extended_begin, script_end) script_spans.append(script_span) self.add_tex_span(tex_span) self.script_span_to_char_dict[script_span] = script_char self.script_span_to_tex_span_dict[script_span] = tex_span if not script_spans: return _, sorted_script_spans = zip(*sorted([ (index, script_span) for script_span in script_spans for index in script_span ])) for span_0, span_1 in _get_neighbouring_pairs(sorted_script_spans): if span_0[1] == span_1[0]: self.neighbouring_script_span_pairs.append((span_0, span_1))
def on_key_press(self, symbol, modifiers): try: char = chr(symbol) except OverflowError: log.warning("The value of the pressed key is too large.") return event_data = {"symbol": symbol, "modifiers": modifiers} propagate_event = EVENT_DISPATCHER.dispatch(EventType.KeyPressEvent, **event_data) if propagate_event is not None and propagate_event is False: return if char == "r": self.camera.frame.to_default_state() elif char == "q": self.quit_interaction = True
def get_full_markup_str(self): if self.t2g: log.warning( "Manim currently cannot parse gradient from svg. " "Please set gradient via `set_color_by_gradient`.", ) config_style_dict = self.generate_config_style_dict() global_attr_dict = { "line_height": ((self.lsh or DEFAULT_LINE_SPACING_SCALE) + 1) * 0.6, "font_family": self.font or get_customization()["style"]["font"], "font_size": self.font_size * 1024, "font_style": self.slant, "font_weight": self.weight, # TODO, it seems this doesn't work "font_features": "liga=0,dlig=0,clig=0,hlig=0" if self.disable_ligatures else None, "foreground": config_style_dict.get("fill", None), "alpha": config_style_dict.get("fill-opacity", None) } global_attr_dict = { k: v for k, v in global_attr_dict.items() if v is not None } global_attr_dict.update(self.global_config) self.parser.update_global_attrs(global_attr_dict) local_attr_items = [(word_or_text_span, { key: value }) for t2x_dict, key in ((self.t2c, "foreground"), (self.t2f, "font_family"), (self.t2s, "font_style"), (self.t2w, "font_weight")) for word_or_text_span, value in t2x_dict.items()] local_attr_items.extend(self.local_configs.items()) for word_or_text_span, local_config in local_attr_items: for text_span in self.find_indexes(word_or_text_span): self.parser.update_local_attrs(text_span, local_config) return self.parser.get_markup_str_with_attrs()
def __init__(self, text: str, **kwargs): self.full2short(kwargs) digest_config(self, kwargs) if not self.font: self.font = get_customization()["style"]["font"] if self.is_markup: self.validate_markup_string(text) self.text = text super().__init__(text, **kwargs) if self.t2g: log.warning( "Manim currently cannot parse gradient from svg. " "Please set gradient via `set_color_by_gradient`.", ) if self.gradient: self.set_color_by_gradient(*self.gradient) if self.height is None: self.scale(TEXT_MOB_SCALE_FACTOR)
def on_key_press(self, symbol: int, modifiers: int) -> None: try: char = chr(symbol) except OverflowError: log.warning("The value of the pressed key is too large.") return event_data = {"symbol": symbol, "modifiers": modifiers} propagate_event = EVENT_DISPATCHER.dispatch(EventType.KeyPressEvent, **event_data) if propagate_event is not None and propagate_event is False: return if char == "r": self.camera.frame.to_default_state() elif char == "q": self.quit_interaction = True elif char == " " or symbol == 65363: # Space or right arrow self.hold_on_wait = False elif char == "e" and modifiers == 3: # ctrl + shift + e self.embed(close_scene_on_exit=False)
def get_mobject_from(self, shape: se.GraphicObject) -> VMobject | None: shape_class_to_func_map: dict[type, Callable[ [se.GraphicObject], VMobject]] = { se.Path: self.path_to_mobject, se.SimpleLine: self.line_to_mobject, se.Rect: self.rect_to_mobject, se.Circle: self.circle_to_mobject, se.Ellipse: self.ellipse_to_mobject, se.Polygon: self.polygon_to_mobject, se.Polyline: self.polyline_to_mobject, # se.Text: self.text_to_mobject, # TODO } for shape_class, func in shape_class_to_func_map.items(): if isinstance(shape, shape_class): mob = func(shape) self.apply_style_to_mobject(mob, shape) return mob shape_class_name = shape.__class__.__name__ if shape_class_name != "SVGElement": log.warning(f"Unsupported element type: {shape_class_name}") return None