Spaces:
Running
on
L4
Running
on
L4
from kivy.app import App | |
from kivy.uix.widget import Widget | |
from kivy.uix.boxlayout import BoxLayout | |
from kivy.uix.button import Button | |
from kivy.uix.scatter import Scatter | |
from kivy.uix.label import Label | |
from kivy.uix.scrollview import ScrollView | |
from kivy.properties import BooleanProperty, StringProperty, NumericProperty, ListProperty, ObjectProperty | |
from kivy.uix.behaviors import ButtonBehavior | |
from kivy.vector import Vector | |
from kivy.metrics import dp | |
from kivy.clock import Clock | |
from kivy.uix.popup import Popup | |
from kivy.config import Config | |
Config.set('graphics', 'width', '1400') | |
Config.set('graphics', 'height', '800') | |
from kivy.core.window import Window | |
import os | |
from typing import List | |
from deepsvg.svglib.geom import Point | |
from deepsvg.svglib.svg_command import SVGCommandMove, SVGCommandLine, SVGCommandBezier | |
from deepsvg.svgtensor_dataset import SVGTensorDataset | |
from .layout.aligned_textinput import AlignedTextInput | |
from .state.state import State, ToolMode, DrawMode, LoopMode, PlaybackMode | |
from .state.project import Frame | |
from .config import ROOT_DIR | |
from .interpolate import compute_interpolation | |
from .utils import * | |
if not os.path.exists(ROOT_DIR): | |
os.makedirs(ROOT_DIR) | |
state = State() | |
state.load_state() | |
state.load_project() | |
class HeaderIcon(Button): | |
index = NumericProperty(0) | |
source = StringProperty("") | |
def on_press(self): | |
state.header.selected_tool = self.index | |
class Header(BoxLayout): | |
selected_tool = NumericProperty(0) | |
title = StringProperty(state.project.name) | |
is_playing = BooleanProperty(False) | |
delay = NumericProperty(state.delay) | |
def on_selected_tool(self, *args): | |
if self.selected_tool in [ToolMode.MOVE, ToolMode.PEN, ToolMode.PENCIL] and state.header.is_playing: | |
state.header.pause_animation() | |
def on_done(self, *args): | |
if self.selected_tool == ToolMode.PEN and state.draw_mode == DrawMode.DRAW: | |
path = state.current_path | |
last_segment = path.children[-1] | |
path.remove_widget(last_segment) | |
state.draw_viewbox.on_path_done(state.current_path) | |
state.draw_mode = DrawMode.STILL | |
state.current_path = None | |
self.selected_tool = ToolMode.MOVE | |
def on_erase(self): | |
state.modified = True | |
state.draw_viewbox.clear() | |
state.timeline.make_keyframe(False) | |
def add_frame(self, keyframe=False): | |
frame_idx = state.timeline._add_frame(keyframe=keyframe) | |
state.project.frames.append(Frame(frame_idx, keyframe)) | |
self.load_next_frame(frame_idx=frame_idx) | |
def play_animation(self): | |
self.is_playing = True | |
state.sidebar.selected_path_idx = -1 | |
self.clock = Clock.schedule_once(self.load_next_frame) | |
def load_next_frame(self, dt=0, frame_idx=None, *args): | |
if state.timeline.nb_frames > 0: | |
if frame_idx is None: | |
frame_idx_tmp = state.timeline.selected_frame + state.loop_orientation | |
if frame_idx_tmp < 0 or frame_idx_tmp >= state.timeline.nb_frames: | |
if state.loop_mode in [LoopMode.NORMAL, LoopMode.REVERSE]: | |
frame_idx = frame_idx_tmp % state.timeline.nb_frames | |
else: # LoopMode.PINGPONG | |
state.loop_orientation *= -1 | |
frame_idx = (state.timeline.selected_frame + state.loop_orientation) % state.timeline.nb_frames | |
else: | |
frame_idx = frame_idx_tmp | |
state.timeline.selected_frame = frame_idx | |
if self.is_playing: | |
if state.playback_mode == PlaybackMode.EASE: | |
t = frame_idx / state.timeline.nb_frames | |
delay = 2 * state.delay / (1 + d_easein_easeout(t)) | |
else: | |
delay = state.delay | |
self.clock = Clock.schedule_once(self.load_next_frame, delay) | |
def pause_animation(self): | |
self.clock.cancel() | |
state.sidebar.selected_path_idx = -1 | |
self.is_playing = False | |
state.timeline.on_selected_frame() # re-render frame to display sidebar layers | |
def on_title(self, title): | |
state.project.name = title | |
def interpolate(self): | |
state.draw_viewbox.save_frame() | |
compute_interpolation(state.project) | |
class PathLayerView(ButtonBehavior, BoxLayout): | |
index = NumericProperty(0) | |
source = StringProperty("") | |
def __init__(self, index, **kwargs): | |
super().__init__(**kwargs) | |
self.index = index | |
self.source = os.path.join(state.project.cache_dir, f"{state.timeline.selected_frame}_{index}.png") | |
def on_press(self): | |
state.sidebar.selected_path_idx = self.index | |
def move_up(self): | |
if self.index > 0: | |
state.sidebar.swap_paths(self.index, self.index - 1) | |
def move_down(self): | |
if self.index < state.sidebar.nb_paths - 1: | |
state.sidebar.swap_paths(self.index, self.index + 1) | |
def reverse(self): | |
state.sidebar.reverse_path(self.index) | |
class Sidebar(ScrollView): | |
selected_path_idx = NumericProperty(-1) | |
def sidebar(self): | |
return self.ids.sidebar | |
def nb_paths(self): | |
return len(self.sidebar.children) | |
def on_selected_path_idx(self, *args): | |
state.draw_viewbox.unselect_all() | |
if self.selected_path_idx >= 0: | |
state.draw_viewbox.get_path(self.selected_path_idx).selected = True | |
def _add_path(self, idx=None): | |
if idx is None: | |
idx = self.nb_paths | |
new_pathlayer = PathLayerView(idx) | |
self.sidebar.add_widget(new_pathlayer) | |
return idx | |
def get_path(self, path_idx): | |
index = self.nb_paths - 1 - path_idx | |
return self.sidebar.children[index] | |
def erase(self): | |
self.sidebar.clear_widgets() | |
self.selected_path_idx = -1 | |
def swap_paths(self, idx1, idx2): | |
path_layer1, path_layer2 = self.get_path(idx1), self.get_path(idx2) | |
path1, path2 = state.draw_viewbox.get_path(idx1), state.draw_viewbox.get_path(idx2) | |
path_layer1.index, path_layer2.index = idx2, idx1 | |
path1.color, path2.color = path2.color, path1.color | |
path1.index, path2.index = path2.index, path1.index | |
id1, id2 = self.nb_paths - 1 - idx1, self.nb_paths - 1 - idx2 | |
self.sidebar.children[id1], self.sidebar.children[id2] = path_layer2, path_layer1 | |
state.draw_viewbox.children[id1], state.draw_viewbox.children[id2] = path2, path1 | |
self.selected_path_idx = idx2 | |
state.modified = True | |
def reverse_path(self, idx): | |
path = state.draw_viewbox.get_path(idx) | |
svg_path = path.to_svg_path().reverse() | |
new_path = BezierPath.from_svg_path(svg_path, color=path.color, index=path.index, selected=path.selected) | |
id = self.nb_paths - 1 - idx | |
state.draw_viewbox.remove_widget(path) | |
state.draw_viewbox.add_widget(new_path, index=id) | |
self.selected_path_idx = idx | |
state.modified = True | |
def select(self, path_idx): | |
if self.selected_path_idx >= 0: | |
state.draw_viewbox.get_path(state.sidebar.selected_path_idx).selected = False | |
self.selected_path_idx = path_idx | |
class BezierSegment(Widget): | |
is_curved = BooleanProperty(True) | |
is_finished = BooleanProperty(True) | |
select_dist = NumericProperty(3) | |
p1 = ListProperty([0, 0]) | |
q1 = ListProperty([0, 0]) | |
q2 = ListProperty([0, 0]) | |
p2 = ListProperty([0, 0]) | |
def clone(self): | |
segment = BezierSegment() | |
segment.is_curved = self.is_curved | |
segment.p1 = self.p1 # shallow copy | |
segment.q1 = self.q1 | |
segment.q2 = self.q2 | |
segment.p2 = self.p2 | |
return segment | |
def line(p1, p2): | |
segment = BezierSegment() | |
segment.is_curved = False | |
segment.p1 = segment.q1 = p1 | |
segment.p2 = segment.q2 = p2 | |
return segment | |
def bezier(p1, q1, q2, p2): | |
segment = BezierSegment() | |
segment.is_curved = True | |
segment.q1, segment.q2 = q1, q2 | |
segment.p1, segment.p2 = p1, p2 | |
return segment | |
def get_point(self, key): | |
return getattr(self, key) | |
def on_touch_down(self, touch): | |
max_dist = dp(self.select_dist) | |
if not self.parent.selected: | |
return super().on_touch_down(touch) | |
keys_to_test = ["p1", "q1", "q2", "p2"] if self.is_curved else ["p1", "p2"] | |
for key in keys_to_test: | |
if dist(touch.pos, getattr(self, key)) < max_dist: | |
touch.ud['selected'] = key | |
touch.grab(self) | |
state.modified = True | |
return True | |
def on_touch_move(self, touch): | |
if touch.grab_current is not self: | |
return super().on_touch_move(touch) | |
key = touch.ud['selected'] | |
setattr(self, key, touch.pos) | |
if state.header.selected_tool == ToolMode.PEN: | |
self.is_curved = True | |
self.is_finished = False | |
state.draw_mode = DrawMode.HOLDING_DOWN | |
setattr(self, "p2", touch.pos) | |
if key in ["p1", "p2"]: | |
self.parent.move(self, key, touch.pos) | |
def on_touch_up(self, touch): | |
if touch.grab_current is not self: | |
return super().on_touch_up(touch) | |
touch.ungrab(self) | |
if state.header.selected_tool == ToolMode.PEN: | |
self.is_finished = True | |
state.draw_mode = DrawMode.DRAW | |
class BezierPath(Widget): | |
color = ListProperty([1, 1, 1]) | |
index = NumericProperty(0) | |
selected = BooleanProperty(False) | |
def __init__(self, segments: List[BezierSegment], color=None, index=None, selected=False, **kwargs): | |
super().__init__(**kwargs) | |
if color is not None: | |
self.color = color | |
if index is not None: | |
self.index = index | |
self.selected = selected | |
for segment in segments: | |
self.add_segment(segment) | |
def clone(self): | |
segments = [segment.clone() for segment in self.children] | |
return BezierPath(segments, self.color, self.index, self.selected) | |
def add_segment(self, segment: BezierSegment): | |
self.add_widget(segment, index=len(self.children)) | |
def move(self, segment, key, pos): | |
idx = self.children.index(segment) | |
if not (idx == 0 and key == "p1") and not (idx == len(self.children) - 1 and key == "p2"): | |
idx2, key2 = (idx-1, "p2") if key == "p1" else (idx+1, "p1") | |
setattr(self.children[idx2], key2, pos) | |
def add_widget(self, widget, index=0, canvas=None): | |
super().add_widget(widget, index=index, canvas=canvas) | |
def remove_widget(self, widget): | |
super().remove_widget(widget) | |
def from_svg_path(svg_path: SVGPath, *args, **kwargs): | |
segments = [] | |
for command in svg_path.path_commands: | |
if isinstance(command, SVGCommandBezier): | |
segment = BezierSegment.bezier(flip_vertical(command.p1.tolist()), flip_vertical(command.q1.tolist()), | |
flip_vertical(command.q2.tolist()), flip_vertical(command.p2.tolist())) | |
segments.append(segment) | |
elif isinstance(command, SVGCommandLine): | |
segment = BezierSegment.line(flip_vertical(command.start_pos.tolist()), | |
flip_vertical(command.end_pos.tolist())) | |
segments.append(segment) | |
path = BezierPath(segments, *args, **kwargs) | |
return path | |
def to_svg_path(self): | |
path_commands = [] | |
for segment in self.children: | |
if segment.is_curved: | |
command = SVGCommandBezier(Point(*flip_vertical(segment.p1)), Point(*flip_vertical(segment.q1)), | |
Point(*flip_vertical(segment.q2)), Point(*flip_vertical(segment.p2))) | |
else: | |
command = SVGCommandLine(Point(*flip_vertical(segment.p1)), Point(*flip_vertical(segment.p2))) | |
path_commands.append(command) | |
svg_path = SVGPath(path_commands) | |
return svg_path | |
class Sketch(Widget): | |
color = ListProperty([1, 1, 1]) | |
points = ListProperty([]) | |
def __init__(self, points, color=None, **kwargs): | |
super().__init__(**kwargs) | |
if color is not None: | |
self.color = color | |
self.points = points | |
def on_touch_move(self, touch): | |
if touch.grab_current is not self: | |
return super().on_touch_move(touch) | |
self.points.extend(touch.pos) | |
def on_touch_up(self, touch): | |
if touch.grab_current is not self: | |
return super().on_touch_up(touch) | |
touch.ungrab(self) | |
self.parent.on_sketch_done(self) | |
def to_svg_path(self): | |
points = [Point(x, 255 - y) for x, y in zip(self.points[::2], self.points[1::2])] | |
commands = [SVGCommandMove(points[0])] + [SVGCommandLine(p1, p2) for p1, p2 in zip(points[:-1], points[1:])] | |
svg_path = SVGPath.from_commands(commands).path | |
return svg_path | |
class EditorView(Scatter): | |
def on_touch_down(self, touch): | |
if self.collide_point(*touch.pos) and touch.is_mouse_scrolling: | |
if touch.button == 'scrolldown': | |
if self.scale < 10: | |
self.scale = self.scale * 1.1 | |
elif touch.button == 'scrollup': | |
if self.scale > 1: | |
self.scale = self.scale * 0.8 | |
return True | |
return super().on_touch_down(touch) | |
class DrawViewbox(Widget): | |
def __init__(self, **kwargs): | |
super().__init__(**kwargs) | |
Window.bind(mouse_pos=self.on_mouse_pos) | |
def nb_paths(self): | |
return len(self.children) | |
def _get_color(self, idx): | |
color = color_dict[colors[idx % len(colors)]] | |
return color | |
def on_mouse_pos(self, _, abs_pos): | |
pos = (Vector(abs_pos) - Vector(self.parent.pos)) / self.parent.scale | |
if state.header.selected_tool == ToolMode.PEN and state.draw_mode == DrawMode.DRAW: | |
segment = state.current_path.children[-1] | |
segment.p2 = segment.q2 = pos | |
def on_sketch_done(self, sketch: Sketch): | |
# Digitalize points to Bézier path | |
svg_path = preprocess_svg_path(sketch.to_svg_path(), force_smooth=True) | |
path_idx = state.sidebar.nb_paths | |
path = BezierPath.from_svg_path(svg_path, color=sketch.color, index=path_idx, selected=True) | |
self.remove_widget(sketch) | |
self.add_new_path(path, svg_path) | |
def on_path_done(self, path: BezierPath): | |
svg_path = preprocess_svg_path(path.to_svg_path()) | |
path_idx = state.sidebar.nb_paths | |
new_path = BezierPath.from_svg_path(svg_path, color=path.color, index=path_idx, selected=True) | |
self.remove_widget(path) | |
self.add_new_path(new_path, svg_path) | |
def paste(self, path: BezierPath): | |
path = path.clone() | |
path_idx = state.sidebar.nb_paths | |
path.color = self._get_color(path_idx) | |
path.selected = True | |
svg_path = path.to_svg_path() | |
self.add_new_path(path, svg_path) | |
def unselect_all(self): | |
for path in self.children: | |
path.selected = False | |
def get_path(self, path_idx): | |
index = self.nb_paths - 1 - path_idx | |
return self.children[index] | |
def add_new_path(self, path: BezierSegment, svg_path: SVGPath): | |
self.add_path(path, svg_path, force_rerender_miniature=True) | |
state.modified = True | |
state.timeline.make_keyframe(True) | |
state.sidebar.select(path.index) | |
def add_path(self, path: BezierPath, svg_path: SVGPath, force_rerender_miniature=False): | |
path_idx = state.sidebar.nb_paths | |
self.add_widget(path) | |
miniature_path = os.path.join(state.project.cache_dir, f"{state.timeline.selected_frame}_{path_idx}.png") | |
if not os.path.exists(miniature_path) or force_rerender_miniature: | |
svg_path = normalized_path(svg_path) | |
svg_path.draw(viewbox=svg_path.bbox().make_square(min_size=12), | |
file_path=os.path.join(state.project.cache_dir, f"{state.timeline.selected_frame}_{path_idx}.png"), | |
do_display=False) | |
if not state.header.is_playing: | |
state.sidebar._add_path() | |
def on_touch_down(self, touch): | |
if state.header.selected_tool == ToolMode.PLAY: | |
return False | |
if state.header.selected_tool == ToolMode.PEN and self.collide_point(*touch.pos): | |
state.draw_mode = DrawMode.DRAW | |
if state.current_path is None: | |
path = BezierPath([], color=self._get_color(len(self.children)), selected=True) | |
self.add_widget(path) | |
state.current_path = path | |
l = BezierSegment.line(touch.pos, touch.pos) | |
touch.ud["selected"] = "q1" | |
touch.grab(l) | |
state.current_path.add_segment(l) | |
state.modified = True | |
return True | |
if state.header.selected_tool == ToolMode.PENCIL and self.collide_point(*touch.pos): | |
l = Sketch([*touch.pos], color=self._get_color(len(self.children))) | |
self.add_widget(l) | |
touch.grab(l) | |
state.modified = True | |
return True | |
if super().on_touch_down(touch): | |
return True | |
def clear(self): | |
state.draw_viewbox.clear_widgets() | |
state.sidebar.erase() | |
def add_widget(self, widget, index=0, canvas=None): | |
super().add_widget(widget, index=index, canvas=canvas) | |
def remove_widget(self, widget): | |
super().remove_widget(widget) | |
def to_svg(self): | |
svg_path_groups = [] | |
for path in reversed(self.children): | |
svg_path_groups.append(path.to_svg_path().to_group()) | |
svg = SVG(svg_path_groups, viewbox=Bbox(256)) | |
return svg | |
def load_svg(self, svg: SVG, frame_idx): | |
kivy_bezierpaths = [] | |
for idx, svg_path in enumerate(svg.paths): | |
path = BezierPath.from_svg_path(svg_path, color=self._get_color(idx), index=idx, selected=False) | |
kivy_bezierpaths.append(path) | |
self.add_path(path, svg_path, force_rerender_miniature=True) | |
state.project.frames[frame_idx].svg = svg | |
state.project.frames[frame_idx].kivy_bezierpaths = kivy_bezierpaths | |
def load_cached(self, svg: SVG, kivy_bezierpaths: List[BezierPath]): | |
for path, svg_path in zip(kivy_bezierpaths, svg.paths): | |
self.add_path(path, svg_path) | |
def load_frame(self, frame_idx): | |
svg = state.project.frames[frame_idx].svg | |
kivy_bezierpaths = state.project.frames[frame_idx].kivy_bezierpaths | |
if kivy_bezierpaths is None: | |
self.load_svg(svg, frame_idx) | |
else: | |
self.load_cached(svg, kivy_bezierpaths) | |
self.unselect_all() | |
def save_frame(self): | |
svg = self.to_svg() | |
state.project.frames[state.current_frame].svg = svg | |
state.project.frames[state.current_frame].kivy_bezierpaths = [child for child in reversed(self.children) if isinstance(child, BezierPath)] | |
class HeaderButton(Button): | |
pass | |
class UpButton(Button): | |
def on_press(self): | |
self.parent.move_up() | |
class DownButton(Button): | |
def on_press(self): | |
self.parent.move_down() | |
class ReverseButton(Button): | |
def on_press(self): | |
self.parent.reverse() | |
class FrameView(Button): | |
index = NumericProperty(0) | |
keyframe = BooleanProperty(False) | |
def __init__(self, index, keyframe=False, **kwargs): | |
super().__init__(**kwargs) | |
self.index = index | |
self.keyframe = keyframe | |
def on_press(self): | |
state.timeline.selected_frame = self.index | |
class TimeLine(ScrollView): | |
selected_frame = NumericProperty(-1) | |
def timeline(self): | |
return self.ids.timeline | |
def nb_frames(self): | |
return len(self.timeline.children) | |
def on_selected_frame(self, *args): | |
self._update_frame(self.selected_frame) | |
def _update_frame(self, new_frame_idx): | |
if state.current_frame >= 0 and state.modified: | |
state.draw_viewbox.save_frame() | |
state.current_frame = new_frame_idx | |
state.draw_viewbox.clear() | |
state.modified = False | |
state.draw_viewbox.load_frame(new_frame_idx) | |
def _add_frame(self, keyframe=False): | |
idx = self.nb_frames | |
new_frame = FrameView(idx, keyframe=keyframe) | |
self.timeline.add_widget(new_frame) | |
return idx | |
def get_frame(self, frame_idx): | |
index = self.nb_frames - 1 - frame_idx | |
return self.timeline.children[index] | |
def make_keyframe(self, is_keyframe=None): | |
if is_keyframe is None: | |
is_keyframe = not self.get_frame(state.timeline.selected_frame).keyframe | |
self.get_frame(state.timeline.selected_frame).keyframe = is_keyframe | |
state.project.frames[state.timeline.selected_frame].keyframe = is_keyframe | |
class TitleWidget(AlignedTextInput): | |
pass | |
class Padding(Label): | |
pass | |
class FileChoosePopup(Popup): | |
load = ObjectProperty() | |
path = StringProperty(".") | |
class DeepSVGWidget(BoxLayout): | |
def __init__(self, **kwargs): | |
super().__init__(**kwargs) | |
state.main_widget = self | |
state.header = self.ids.header | |
state.sidebar = self.ids.sidebar_scroll | |
state.draw_viewbox = self.ids.editor.ids.draw_viewbox | |
state.timeline = self.ids.timeline_scroll | |
self._load_project() | |
def _load_project(self): | |
for frame in state.project.frames: | |
state.timeline._add_frame(keyframe=frame.keyframe) | |
state.timeline.selected_frame = 0 | |
class DeepSVGApp(App): | |
def build(self): | |
self.title = 'DeepSVG Editor' | |
Window.bind(on_request_close=self.on_request_close) | |
Window.bind(on_keyboard=self.on_keyboard) | |
return DeepSVGWidget() | |
def save(self): | |
state.draw_viewbox.save_frame() | |
state.save_state() | |
state.project.save_project() | |
def on_request_close(self, *args, **kwargs): | |
self.save() | |
self.stop() | |
def on_keyboard(self, window, key, scancode, codepoint, modifier): | |
CTRL_PRESSED = (modifier == ['ctrl'] or modifier == ['meta']) | |
if codepoint == "h" and not CTRL_PRESSED: | |
# Hand tool | |
state.header.selected_tool = ToolMode.MOVE | |
elif codepoint == "p" and not CTRL_PRESSED: | |
# Pen tool | |
state.header.selected_tool = ToolMode.PEN | |
elif CTRL_PRESSED and codepoint == "p": | |
# Pencil tool | |
state.header.selected_tool = ToolMode.PENCIL | |
elif codepoint == "k" and not CTRL_PRESSED: | |
# Make keypoint | |
state.timeline.make_keyframe() | |
elif CTRL_PRESSED and codepoint == 'q': | |
# Quit | |
self.on_request_close() | |
elif CTRL_PRESSED and codepoint == 'i': | |
# Import | |
self.file_chooser = FileChoosePopup(load=self.on_file_chosen) | |
self.file_chooser.open() | |
elif CTRL_PRESSED and codepoint == "e": | |
# Export | |
state.project.export_to_gif(loop_mode=state.loop_mode) | |
elif CTRL_PRESSED and codepoint == 'c': | |
# Copy | |
if state.sidebar.selected_path_idx >= 0: | |
state.clipboard = state.draw_viewbox.get_path(state.sidebar.selected_path_idx).clone() | |
elif CTRL_PRESSED and codepoint == 'v': | |
# Paste | |
if isinstance(state.clipboard, BezierPath): | |
state.draw_viewbox.paste(state.clipboard) | |
elif CTRL_PRESSED and codepoint == 's': | |
# Save | |
self.save() | |
elif key == Keys.SPACEBAR: | |
# Play/Pause | |
state.header.selected_tool = ToolMode.PLAY | |
if state.header.is_playing: | |
state.header.pause_animation() | |
else: | |
state.header.play_animation() | |
elif key == Keys.LEFT: | |
# Previous frame | |
if state.current_frame > 0: | |
state.timeline.selected_frame = state.current_frame - 1 | |
elif key == Keys.RIGHT: | |
# Next frame | |
if state.current_frame < state.timeline.nb_frames - 1: | |
state.timeline.selected_frame = state.current_frame + 1 | |
def on_file_chosen(self, selection): | |
file_path = str(selection[0]) | |
self.file_chooser.dismiss() | |
if file_path: | |
if not file_path.endswith(".svg"): | |
return | |
svg = SVG.load_svg(file_path) | |
svg = SVGTensorDataset.simplify(svg) | |
svg = SVGTensorDataset.preprocess(svg, mean=True) | |
state.draw_viewbox.load_svg(svg, frame_idx=state.timeline.selected_frame) | |
state.modified = True | |
state.timeline.make_keyframe(True) | |
if __name__ == "__main__": | |
DeepSVGApp().run() | |