Spaces:
Runtime error
Runtime error
| import numpy as np | |
| from xml.dom import minidom | |
| from pathlib import Path | |
| from src.postprocessing.get_svg_size_pos import get_midpoint_of_path_bbox, get_begin_values_by_starting_pos | |
| from src.postprocessing.transform_animation_predictor_output import transform_animation_predictor_output | |
| def create_animated_svg(file, animation_ids, model_output, filename_suffix="", save=True): | |
| """ Insert multiple animation statements. | |
| Args: | |
| file (str): Path of SVG file. | |
| animation_ids (list[int]): List of element IDs that get animated. | |
| model_output (ndarray): Array of 13 dimensional arrays with animation predictor model output. | |
| filename_suffix (str): Suffix of animated SVG. | |
| Returns: | |
| list(float): List of begin values of elements in SVG. | |
| xml.dom.minidom.Document: Parsed file with inserted animation statements. | |
| """ | |
| doc = svg_to_doc(file) | |
| begin_values = get_begin_values_by_starting_pos(file, animation_ids, start=1, step=0.25) | |
| for i in range(len(animation_ids)): | |
| if not (model_output[i][:6] == np.array([0] * 6)).all(): | |
| try: # there are some paths that can't be embedded and don't have style attributes | |
| output_dict = transform_animation_predictor_output(file, animation_ids[i], model_output[i]) | |
| output_dict["begin"] = begin_values[i] | |
| if output_dict["type"] == "translate": | |
| doc = insert_translate_statement(doc, animation_ids[i], output_dict) | |
| if output_dict["type"] == "scale": | |
| doc = insert_scale_statement(doc, animation_ids[i], output_dict, file) | |
| if output_dict["type"] == "rotate": | |
| doc = insert_rotate_statement(doc, animation_ids[i], output_dict) | |
| if output_dict["type"] in ["skewX", "skewY"]: | |
| doc = insert_skew_statement(doc, animation_ids[i], output_dict) | |
| if output_dict["type"] == "fill": | |
| doc = insert_fill_statement(doc, animation_ids[i], output_dict) | |
| if output_dict["type"] in ["opacity"]: | |
| doc = insert_opacity_statement(doc, animation_ids[i], output_dict) | |
| except Exception as e: | |
| print(f"File {file}, animation ID {animation_ids[i]} can't be animated. {e}") | |
| pass | |
| if save: | |
| filename = file.split('/')[-1].replace(".svg", "") + "_animated" | |
| save_animated_svg(doc, filename) | |
| return begin_values, doc | |
| def svg_to_doc(file): | |
| """ Parse an SVG file. | |
| Args: | |
| file (string): Path of SVG file. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| return minidom.parse(file) | |
| def save_animated_svg(doc, filename): | |
| """ Save animated SVGs to folder animated_svgs. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| filename (str): Name of output file. | |
| """ | |
| Path("data/animated_svgs").mkdir(parents=True, exist_ok=True) | |
| with open('data/animated_svgs/' + filename + '.svg', 'wb') as f: | |
| f.write(doc.toprettyxml(encoding="iso-8859-1")) | |
| def insert_translate_statement(doc, animation_id, model_output_dict): | |
| """ Insert translate statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| opacity_dict_1, opacity_dict_2 = create_opacity_pre_animation_dicts(model_output_dict) | |
| pre_animations.append(create_animation_statement(opacity_dict_1)) | |
| pre_animations.append(create_animation_statement(opacity_dict_2)) | |
| animation = create_animation_statement(model_output_dict) | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_scale_statement(doc, animation_id, model_output_dict, file): | |
| """ Insert scale statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| file (str): Path of SVG file. Needed to get midpoint of path bbox to suppress simultaneous translate movement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| opacity_dict_1, opacity_dict_2 = create_opacity_pre_animation_dicts(model_output_dict) | |
| pre_animations.append(create_animation_statement(opacity_dict_1)) | |
| pre_animations.append(create_animation_statement(opacity_dict_2)) | |
| x_midpoint, y_midpoint = get_midpoint_of_path_bbox(file, animation_id) | |
| if model_output_dict["from_"] > 1: | |
| model_output_dict["from_"] = 2 | |
| pre_animation_from = f"-{x_midpoint} -{y_midpoint}" # negative midpoint | |
| else: | |
| model_output_dict["from_"] = 0 | |
| pre_animation_from = f"{x_midpoint} {y_midpoint}" # positive midpoint | |
| translate_pre_animation_dict = {"type": "translate", | |
| "begin": model_output_dict["begin"], | |
| "dur": model_output_dict["dur"], | |
| "from_": pre_animation_from, | |
| "to": "0 0", | |
| "fill": "freeze"} | |
| pre_animations.append(create_animation_statement(translate_pre_animation_dict)) | |
| animation = create_animation_statement(model_output_dict) + ' additive="sum" ' | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_rotate_statement(doc, animation_id, model_output_dict): | |
| """ Insert rotate statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| opacity_dict_1, opacity_dict_2 = create_opacity_pre_animation_dicts(model_output_dict) | |
| pre_animations.append(create_animation_statement(opacity_dict_1)) | |
| pre_animations.append(create_animation_statement(opacity_dict_2)) | |
| animation = create_animation_statement(model_output_dict) | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_skew_statement(doc, animation_id, model_output_dict): | |
| """ Insert skew statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| opacity_dict_1, opacity_dict_2 = create_opacity_pre_animation_dicts(model_output_dict) | |
| pre_animations.append(create_animation_statement(opacity_dict_1)) | |
| pre_animations.append(create_animation_statement(opacity_dict_2)) | |
| animation = create_animation_statement(model_output_dict) | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_fill_statement(doc, animation_id, model_output_dict): | |
| """ Insert fill statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| model_output_dict['dur'] = 2 | |
| if model_output_dict['begin'] < 2: | |
| model_output_dict['begin'] = 0 | |
| else: # Wave | |
| pre_animation_dict = {"type": "fill", | |
| "begin": 0, | |
| "dur": model_output_dict["begin"], | |
| "from_": model_output_dict["to"], | |
| "to": model_output_dict["from_"], | |
| "fill": "remove"} | |
| pre_animations.append(create_animation_statement(pre_animation_dict)) | |
| animation = create_animation_statement(model_output_dict) | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_opacity_statement(doc, animation_id, model_output_dict): | |
| """ Insert opacity statement. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| model_output_dict (dict): Dictionary containing animation statement. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| pre_animations = [] | |
| opacity_pre_animation_dict = {"type": "opacity", | |
| "begin": "0", | |
| "dur": model_output_dict["begin"], | |
| "from_": "0", | |
| "to": "0", | |
| "fill": "remove"} | |
| pre_animations.append(create_animation_statement(opacity_pre_animation_dict)) | |
| animation = create_animation_statement(model_output_dict) | |
| doc = insert_animation(doc, animation_id, animation, pre_animations) | |
| return doc | |
| def insert_animation(doc, animation_id, animation, pre_animations=None): | |
| """ Insert animation statements including pre-animation statements. | |
| Args: | |
| doc (xml.dom.minidom.Document): Parsed file. | |
| animation_id (int): ID of element that gets animated. | |
| animation (string): Animation that needs to be inserted. | |
| pre_animations (list): List of animations that needs to be inserted before actual animation. | |
| Returns: | |
| xml.dom.minidom.Document: Parsed file with inserted animation statement. | |
| """ | |
| elements = doc.getElementsByTagName('path') + doc.getElementsByTagName('circle') + doc.getElementsByTagName( | |
| 'ellipse') + doc.getElementsByTagName('line') + doc.getElementsByTagName( | |
| 'polygon') + doc.getElementsByTagName('polyline') + doc.getElementsByTagName( | |
| 'rect') + doc.getElementsByTagName('text') | |
| for element in elements: | |
| if element.getAttribute('animation_id') == str(animation_id): | |
| if pre_animations is not None: | |
| for i in range(len(pre_animations)): | |
| element.appendChild(doc.createElement(pre_animations[i])) | |
| element.appendChild(doc.createElement(animation)) | |
| return doc | |
| def create_animation_statement(animation_dict): | |
| """ Set up animation statement from a dictionary. | |
| Args: | |
| animation_dict (dict): Dictionary that is transformed into animation statement. | |
| Returns: | |
| str: Animation statement. | |
| """ | |
| if animation_dict["type"] in ["translate", "scale", "rotate", "skewX", "skewY"]: | |
| return _create_animate_transform_statement(animation_dict) | |
| elif animation_dict["type"] in ["fill", "opacity"]: | |
| return _create_animate_statement(animation_dict) | |
| def _create_animate_transform_statement(animation_dict): | |
| """ Set up animation statement from model output for ANIMATETRANSFORM animations """ | |
| animation = f'animateTransform attributeName = "transform" attributeType = "XML" ' \ | |
| f'type = "{animation_dict["type"]}" ' \ | |
| f'begin = "{str(animation_dict["begin"])}" ' \ | |
| f'dur = "{str(animation_dict["dur"])}" ' \ | |
| f'from = "{str(animation_dict["from_"])}" ' \ | |
| f'to = "{str(animation_dict["to"])}" ' \ | |
| f'fill = "{str(animation_dict["fill"])}"' | |
| return animation | |
| def _create_animate_statement(animation_dict): | |
| """ Set up animation statement from model output for ANIMATE animations """ | |
| animation = f'animate attributeName = "{animation_dict["type"]}" ' \ | |
| f'begin = "{str(animation_dict["begin"])}" ' \ | |
| f'dur = "{str(animation_dict["dur"])}" ' \ | |
| f'from = "{str(animation_dict["from_"])}" ' \ | |
| f'to = "{str(animation_dict["to"])}" ' \ | |
| f'fill = "{str(animation_dict["fill"])}"' | |
| return animation | |
| def create_opacity_pre_animation_dicts(animation_dict): | |
| """ Set up pre_animation statements. | |
| Args: | |
| animation_dict (dict): Dictionary from animation that is needed to set up opacity pre-animations. | |
| Returns: | |
| str: Animation Statement. | |
| """ | |
| opacity_pre_animation_dict_1 = {"type": "opacity", | |
| "begin": "0", | |
| "dur": animation_dict["begin"], | |
| "from_": "0", | |
| "to": "0", | |
| "fill": "remove"} | |
| opacity_pre_animation_dict_2 = {"type": "opacity", | |
| "begin": animation_dict["begin"], | |
| "dur": "0.5", | |
| "from_": "0", | |
| "to": "1", | |
| "fill": "remove"} | |
| return opacity_pre_animation_dict_1, opacity_pre_animation_dict_2 | |