commit b9e1625425be2dd3bf108ade64a26949cdd239f4 Author: dave Date: Sun Nov 20 14:48:52 2022 -0800 test code diff --git a/dvd.png b/dvd.png new file mode 100644 index 0000000..557d5ba Binary files /dev/null and b/dvd.png differ diff --git a/pyanimate.py b/pyanimate.py new file mode 100644 index 0000000..064cdd3 --- /dev/null +++ b/pyanimate.py @@ -0,0 +1,124 @@ +import os +from PIL import Image +import tempfile + + +class Video(object): + def __init__(self, framerate, width, height): + self.framerate = framerate + self.width = width + self.height = height + self.sprites = [] + + def add_sprite(self, sprite): + self.sprites.append(sprite) + + def render(self, outpath): + """ + render the video to a file + + for frame in frames: + canvas = image() + for sprite in sprites: + sprite.next_frame() + sprite.draw(canvas) + canvas.save(...) + + ffmepeg() + """ + + with tempfile.TemporaryDirectory() as tmpdir: + for framenum in range(0, 30): + canvas = Image.new('RGB', (self.width, self.height, )) + for sprite in self.sprites: + sprite.draw(canvas) + + with open(os.path.join(tmpdir, "frame_{:08d}.png".format(framenum)), "wb") as f: + canvas.save(f, "PNG") + + for sprite in self.sprites: + sprite.next_frame() + + print("done!") + os.system("open {}".format(tmpdir)) + input() + + + +class Source(object): + def __init__(self, file_path): + self.file_path = file_path + + def load(self): + """ + load the media from disk + """ + raise NotImplementedError() + + +class Sprite(object): + def __init__(self, source, position=(0, 0)): + self.source = source + self.position = position + self.visible = False + self.animations = [] + + def add_animation(self, animation): + self.animations.append(animation) + + #TODO transforms that can modify the image e.g. scale it + # also, AddTransformAnimation/RemoveTransformAnimation to make modifications on the fly + # also, TweenTransformationAnimation + # def add_transform(self, transform): + # pass + + def get_pending_animations(self): + return self.animations#TODO + + def next_frame(self): + for animation in self.get_pending_animations(): + animation.apply() + + def draw(self, canvas): + """ + Draw the sprite on top of the given canvas + """ + raise NotImplementedError() + + +def ImageSprite(Sprite): + def __init__(self, source, position=(0, 0)): + raise Exception("huh") + # super().__init__(source, position) + + def draw(self, canvas): + """ + Draw the sprite on top of the given canvas + """ + raise NotImplementedError() + + +class Animation(object): + # def __init__(self, path=None, filters=None, transforms=None, after=None): + # pass + + def apply(self, sprite): + raise NotImplementedError() + + +class ImageSource(Source): + def load(self): + self.img = Image.open(self.file_path) + + +class VideoSource(Source): + pass + + +class ShowAnimation(Animation): + def apply(self, sprite): + sprite.visible = True + +class HideAnimation(Animation): + def apply(self, sprite): + sprite.visible = False diff --git a/test.py b/test.py new file mode 100644 index 0000000..c8b65cb --- /dev/null +++ b/test.py @@ -0,0 +1,59 @@ +# python-based animation software + +# describe an animation using python code then render it to a video, gif, etc + +# tl;dr we draw PNGs and assemble them with ffmpeg + +# data model: +# source - input media such as a still image or a video +# sprite - an instance of a source's media + +# animation - description of how a source is used in the video + + +# example video of the dvd bounce logo + +from pyanimate import Video, \ + Source, ImageSource, \ + Sprite, ImageSprite, \ + Animation, ShowAnimation, HideAnimation + + +# the video is the canvas that we'll animate objects on top of +video = Video(framerate=30, width=640, height=480) + +# a Source is a piece of media that we can include in the animation +# there should not be any reason to create two Source()s of the same input media. +logo_img = ImageSource("dvd_logo.png") + +# a Sprite is an instance of a piece of media. They contain contextual information such as the position of the sprite +# on the canvas. +logo = ImageSprite(logo_img) #, position=(0, 0)) + + +# now the sprite appears at 0,0 +video.add_sprite(logo) + + +# Animations are things that happen to sprites. Sprites are hidden by default so you need a ShowAnimation() to +# make them visible +logo.add_animation(ShowAnimation()) + +# Now lets make the logo move. In this case, we just move it diagonally +# logo_bounce_1 = Animation( +# # path=xxx, +# # filters=xxx, +# # transforms=xxx, +# # ... +# after=None, +# ) + +# Animations are added to sprites. When the video is rendered, animations are applied to the sprite in each frame. +# Animations begin on frame 0 by default, unless they are delayed by setting a start frame=... or after=.... +# logo.add_animation(logo_bounce_1) + + +# output the final thing +video.render("output.mp4") +# video.render("output.gif") +