Giter Club home page Giter Club logo

nanim's Introduction

Nanim

"Totally not manim in nim" - Erik

Tested and works on:
Linux | Windows | MacOS

About

Nanim is an easy-to-use framework to create smooth GPU-accelerated animations that can be previewed live inside a glfw window and, when ready, rendered to videos at an arbitrary resolution and framerate.

What can be done using Nanim?

I have a series of animations made using nanim posted to my Instagram Page. Some of them include:

Bricks Triangle Ball Dot Attack
Lots o' dots Daily Art Web

I also post art to my OpenSea Page where they can be bought as NFT:s.

Installation

From directory

Simply do nimble install nanim

From Source

do git clone [email protected]:EriKWDev/nanim.git --depth=1, cd nanim and finally nimble install. Once installed, you can add the optional dependency ffmpeg. For debian/ubuntu that would be sudo apt install ffmpeg. This allows you to render your animations to videos using --render.

Run one of the examples using nim c -r examples/example_001.nim --size:700 --debug:false!

Usage

Create a normal nim program where you create a Nanim Scene. This scene will carry the state of all animations and entities. This is what the bare minimum looks like:

import nanim


proc testScene(): Scene =
  let scene = newScene()

  return scene


when isMainModule:
  render(testScene)

But that's not very fun! Let's add some animation! Here is what a simple scene might look like:

# example.nim

import nanim

proc testScene(): Scene =
  # Creates a scene-state
  let scene = newScene()

  # You can load some nice colors from a palette on coolors.co!
  var colors = colorsFromCoolors("https://coolors.co/33658a-86bbd8-758e4f-f6ae2d-f26419")
  scene.randomize() # randomize the seed of the scene

  colors.shuffle()
  let bg = colors[0]
  colors.del(0)
  scene.setBackgroundColor(bg)

  # We can have text too! You have to put your TTF-fonts in a 'fonts' directory
  # next to the binary. 'Montserrat-Thin.ttf' will be known as 'montserrat-thin'
  var
    text = newText("Hello, World!", font="montserrat-thin")
    rect = newSquare()

  # We must add our entities to the scene in order for them to be drawn
  scene.add(rect, text)

  # Set some colors!
  text.fill(colors[1])
  rect.fill(colors[2])
  rect.stroke(colors[3], 4.0)

  # By discarding tweens, we can "set" values without animating the change
  discard text.move(150, 150)

  scene.wait(500)
  scene.showAllEntities()
  scene.wait(500)

  # scene.play() and scene.animate() animates any number of tweens and
  # can be used interchangeably
  scene.play(text.move(500, 500),
             rect.move(100, 500),
             rect.rotate(45))

  scene.animate(rect.pscale(3))
  scene.play(rect.setTension(0.6))
  scene.wait(500)

  scene.play(rect.pscale(1/3))

  scene.play(rect.setTension(0),
             rect.rotate(360*2),
             rect.pscale(4))

  scene.wait(500)
  scene.play(rect.move(600), rect.setCornerRadius(30))

  # Want to repeat an animation? Simply add a loop!
  for i in 0..5:
    scene.play(rect.move(-20),
               rect.rotate(-300),
               rect.pscale(if i mod 2 == 0: 1.0/10.0 else: 10.0))

  scene.wait(500)

  # ..and finally return our scene. Scenes don't have to be created inside a proc/func like
  # this one, but it helps a lot when we want to combine multiple scenes in the future, so
  # it should be considered "best practice".
  return scene


when isMainModule:
  # Finally, call render to render our scene.
  # render(testScene()) works as well.
  render(testScene)

The scenes can then be run by simply compiling the file like so: nim c -r <file_containing_scene>.nim. Once your scene is compiled, you can run it either in "live" mode (default), which opens a window and renders the scene in realtime where you can scrub around with your mouse and resize the window at will. That, or you can render it to a video by supplying --render with a specified --size:<size> after. (Make Sure ffmpeg is installed for this to work!).

Here are all the options (keep in mind that it is the last option(s) supplied that takes priority over others):

Options:
  -v, --video, --render
    Enables video rendering mode. Will output video to renders/<name>.mp4
  --gif
    WARNING: Huge files. Please use with --size:400 --fps:15 or, preferably,
             manually convert the mp4 from --render to a GIF.
    Enables gif rendering mode. Will output gif to renders/<name>.gif
  --snap, --screenshot, --image, --picture, --png
    Will create a PNG screenshot of the Scene. Will output to renders/<name>.png
  --fullhd, --1080p
    width 1920, height 1080 (16:9)
  --2k, --1440p
    width 2880, height 2560 (18:9)
  --4k, --2160p
    width 3840, height 2160 (18:9)
  --shorts
    width 1440, height 2560 (9:16)
  --square
    width 1000, height 1000 (1:1)
  --ratio:W:H
    Sets the ratio between width and height. Example: --ratio:16:9 --width:1920
    will set width to 1920 and height to 1080
  -w:WIDTH, --width:WIDTH
    Sets width to WIDTH
  -h:HEIGHT, --height:HEIGHT
    Sets height to HEIGHT
  -s:SIZE, --size:SIZE
    Sets both width andd height to SIZE
  --fps:FPS, --rate:FPS
    Sets the desired framerate to FPS
  --debug:true|false
    Enables debug mode which will visualize the scene's tracks.
    Default behaviour is to show the visualization in live mode
    but not in render mode.

Remember that the rendering to video requires FFMpeg to be installed and available in your PATH.

Legal

  • The majority[1] of the source for this project is released under the MIT license. See the LICENSE file for details on what this means for distribution. All source currently in this repository is free.
  • The Montserrat font families used in the examples in the examples/fonts directory are used under the OFL and are subject to copyright by The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat). See examples/fonts/OFL.txt
  • This project has no association with 3b1b nor ManimCommunity's manim, but is indeed inspired by it.

[1]: Some files, like artwork and special entities, are not made public currently. This might change in the future.

nanim's People

Contributors

erikwdev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

nanim's Issues

Add time location to --png

Now that --png was added, It would be nice to also be able to have something like --png-at:10 to capture a screenshot at time 10 seconds.

(--snap-at:10, --screenshot-at:10, ...)

Maybe also a way to take a screenshot every delta? --png-delta:1 for a screenshot once every second..?

Templetize Simple Tweens using Nim's Templates/Macros

I tried doing a simpleSingleValueTween template here, but when I did some testing later it seemed like it really didn't work as I expected. Currently, a lot of tweens follow a very copy-paste-ey pattern which seems ideal for a template, but I currently don't know enough about the system to get it to work.

Anyone with more experience in Nim or time to get it to function properly are welcome to Fix the issue :)

This is what my attempt looked like:

template simpleSingleValueTween*(target: typed, startValue: typed, endValue: typed, valueName: untyped) =
  var interpolators: seq[proc(t: float)]

  let interpolator = proc(t: float) =
    target.valueName = interpolate(startValue, endValue, t)

  interpolators.add(interpolator)

  target.valueName = endValue

  result = newTween(interpolators)

but it seemed like the untyped valueName didn't quite work. It would also be nice to support multiple valueNames in a vararg[untyped] kind-of way, but that would require a macro.

Improve Performance

Areas with Known Big Performance Impact

  • needs fixing

  • fixed

  • tween.nim's evaluate(tweenTrack, time)

    • The evaluation currently executes every single tween every single frame when in reality we only need to execute the "current" tweens. oldTweens and futureTweens only need to be evaluated the very first time and/or whenever we seek to a time that results in a different set of "currentTweens".
  • rendering.nim's way of handling FFMpeg Instances

    • Due to some issues I had with the osproc module, I couldn't keep one single instance of FFMpeg but rather have to instantiate one, use it for a short amount of time and then kill it. This createds a bunch of "parts" files which have to be stitched together by a final FFMpeg command. This whole process could be avoided if we only had one single process. This most likely has to do with osproc's way of handling stdin has a hidden buffer of some sort that gets filled by frame-data.
  • core.nim's drawPointsWithTension and drawPointsWithRoundedCornerRadius

    • Sooner or later both of these will be deprecated and replaced with one better "drawListOfBezierPointsAndHandles" or something like that, but currently these two procs recalculate the new points every single frame for every point in every entity. This could probably be cached.

issue with size and positioning of canvas (not expected and inconsistent behavior)

hi, nanim looks great, thanks for making this!

I am having trouble understanding the size and positioning of the canvas and it seems to me it is not what I would expect and inconsistent between what is shown in the rendering video and the final result in mp4. I might be missing something.

as an ad hoc example that allows me to understand better the issue (the issue presents itself for me also on the examples in repo), here is what I am running:

import nanim

let
  colors = colorsFromCoolors("https://coolors.co/202b38-009900-ffff66-dea584-b5453a")
  dark = colors[0]
  green = colors[1]
  yellow = colors[2]
  rust = colors[3]
  crab = colors[4]

let scene = newScene() # a ref object, let would be also fine
scene.setBackgroundColor(dark)
scene.width = 500
scene.height = 500

var circle = newCircle(radius=100)
circle.fill(yellow)
scene.add(circle)

scene.wait 500
scene.showAllEntities()
scene.wait 500
scene.play circle.move(500, 0)
scene.wait 500
scene.play circle.move(0, 500)
scene.wait 500
scene.play circle.move(-500, 0)
scene.wait 500
scene.play circle.move(0, -500)
scene.wait 500

render(scene)
  • I expect a yellow circle on dark background only partially visible with its center that runs on the border of a 500x500 square canvas area.

I run with nim c -r example.nim --video --debug:false and output in terminal is:

[Info]: Width: 500, Height: 500 (ratio: 1.00:1.00), FPS: 60.0
[Info]: Launching FFmpeg subprocess with: ffmpeg -y -f rawvideo -pix_fmt rgba -s 500x500 -r 60.0 -i - -vf vflip -an -c:v libx264 -preset medium -profile:v high -crf 17 -coder 1 -tune animation -pix_fmt yuv420p -movflags +faststart -g 30 -bf 2 <mypath>/renders/final_17_46_28.mp4

here are two screenshots that I take when the circle has moved in the bottom right corner. Note that in both screenshots the behavior is not as expected above:

when rendering plays in a window:

rendering

  • it seems the canvas is 1000x1000 instead of 500x500

when reproducing the mp4 file:

final_video

  • it seems like we are seeing the bottom left quadrant of a canvas sized 1000x1000

this is on a recent Mac Air M1 with Nim 1.6 and latest nanim

Add support for Audio

I have never worked with Audio and FFmpeg and have no idea how to structure audio data and pipe it to the ffmpeg-subprocess, but it would be really neat if we could play audio and have it sync with the animations.

FFMPeg Issue on MacOS (others too?)

When --render-ing a scene, I get the following output on my mac:

[Info]: Loading fonts from: /Users/erik/Documents/GitHub/nanim/examples/fonts
[Info]: Launching FFMpeg subprocess with ffmpeg -y -f rawvideo -pix_fmt rgba -s 1920x1080 -r 60 -i - -vf vflip -an -c:v libx264 -preset medium -crf 18 -tune animation -pix_fmt yuv444p /Users/erik/Documents/GitHub/nanim/examples/renders/parts/scene_0.mp4
[Info]: Launching FFMpeg subprocess with ffmpeg -y -f rawvideo -pix_fmt rgba -s 1920x1080 -r 60 -i - -vf vflip -an -c:v libx264 -preset medium -crf 18 -tune animation -pix_fmt yuv444p /Users/erik/Documents/GitHub/nanim/examples/renders/parts/scene_483.mp4
[Info]: Stitching parts together with ffmpeg -y -f concat -safe 0 -loglevel warning -i /Users/erik/Documents/GitHub/nanim/examples/renders/parts/parts.txt -c copy /Users/erik/Documents/GitHub/nanim/examples/renders/final.mp4

[mov,mp4,m4a,3gp,3g2,mj2 @ 0x7fca3f815a00] moov atom not found
[concat @ 0x7fca40009400] Impossible to open '/Users/erik/Documents/GitHub/nanim/examples/renders/parts/scene_483.mp4'
/Users/erik/Documents/GitHub/nanim/examples/renders/parts/parts.txt: Invalid data found when processing input

All the individual parts files can be played in VLC without problem, but some of the parts are not added to the final.ml4 render. Also, when executing the command manually, no errors and the final.mp4 render works as expected.
ffmpeg -y -f concat -safe 0 -loglevel warning -i /Users/erik/Documents/GitHub/nanim/examples/renders/parts/parts.txt -c copy /Users/erik/Documents/GitHub/nanim/examples/renders/final.mp4

Add More Styles and Patterns

One thing I want to be able to do with nanim is to replicate some of the amazing art pieces that I have saved on my pinterest here https://www.pinterest.se/ErikWGren/animation/

Many of them utilize textures and gradients to create a unique look. I want nanim to support textures and patterns and a lot of those same concepts.

Todo

Fonts load but no text shown

I'm just trying to run the demo code from the frontpage readme.
The debug output says the ttf font is being loaded (I had to make a fonts/ dir). But there's no text displayed.

Linux
Nim 1.6.0
nanim default nimble & head

Create Documentation

Todo

  • Add docs page
  • Add comments with runnable examples to common functions
  • Add docstrings to files
  • Add more comprehensive and explained examples
  • Document coordinate system

Fading fades everything, should only fade the specified Entity.

Example:

import nanim

proc myScene(): Scene =
  let
    scene = newScene()
    dot1 = newDot(100)
    dot2 = newDot(100)

  scene.add(dot1, dot2)

  dot1.moveTo(200, 500)
  dot2.moveTo(800, 500)

  scene.play(dot1.fadeOut())
  scene.play(dot2.fadeOut())

  return scene

when isMainModule:
  render(myScene)

I expect first dot1 to fadeOut, then dot2 to follow. Currently, both fades out at the same time:
fade

The First Two Frames of a Rendered Animation seem off

When compiling a scene and rendering it to a video file with --render, the first two frames of the animation seems to have incorrect scaling as well as some other artifacts. First two frames could probably just be skipped in almost every scenario, but would be nice if the scaling just worked from the get-go.

Don't know what the cause is.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.