Animation

class taichi_glsl.gui.Animation(img=None, circles=None, title='Animation', res=(512, 512), **kwargs)

Bases: taichi_glsl.odop.DataOriented

Handy Shadertoy-alike GUI base class.

I’m able to:

  1. Enable you to focus on computation, no need to hand-write a GUI event loop.

  2. Easy-to-use image / video export wrapper like self.set_output_video(path).

  3. Shadertoy-alike input variables including self.iMouse, self.iKeyDirection.

  4. Callback style event processing system incuding self.on_click(x, y).

  5. Easily port to Jupyter notebook by simply self.gui_backend = 'ipython'.

See examples/export_video.py for example:

import taichi as ti
import taichi_glsl as ts

ti.init()


class MyAnimation(ts.Animation):
    def on_init(self):
        self.img = ti.Vector.field(3, ti.f32, (512, 512))
        self.set_output_video('/tmp/video.gif')
        self.define_input()

    @ti.kernel
    def on_render(self):
        for I in ti.grouped(self.img):
            uv = I / self.iResolution
            self.img[I] = ti.cos(uv.xyx + self.iTime +
                                 ts.vec(0, 2, 4)) * 0.5 + 0.5


MyAnimation().start()

And what’s more, examples/particles.py:

import taichi as ti
import taichi_glsl as ts

ti.init()


class MyAnimation(ts.Animation):
    def on_init(self):
        self.N = 8192
        self.dt = 0.01
        self.pos = ti.Vector.field(2, ti.f32, self.N)
        self.vel = ti.Vector.field(2, ti.f32, self.N)
        self.circles = self.pos  # alias to make ts.Animation know
        self.attract_strength = ti.field(ti.f32, ())
        self.attract_pos = ti.Vector.field(2, ti.f32, ())
        self.resolution = (512, 512)
        self.title = 'Particles'
        self.define_input()

    @ti.kernel
    def on_start(self):
        for i in self.pos:
            self.pos[i] = ts.randND(2)
            self.vel[i] = ts.randSolid2D()

    @ti.kernel
    def on_advance(self):
        for i in self.pos:
            acc = ts.vec(0.0, -1.0)
            if any(self.iKeyDirection):  # ASWD?
                acc = self.iKeyDirection
            if any(self.iMouseButton):
                dir = ts.normalize(self.iMouse - self.pos[i]) * 2
                if self.iMouseButton[0]:  # LMB pressed?
                    acc += dir
                if self.iMouseButton[1]:  # RMB pressed?
                    acc -= dir
            self.vel[i] += acc * self.dt
        for i in self.pos:
            self.vel[i] = ts.boundReflect(self.pos[i], self.vel[i], 0, 1, 0.8)
        for i in self.pos:
            self.pos[i] += self.vel[i] * self.dt


MyAnimation().start()

Attributes Summary

frame

(PS, int, RO) Get current frame number start from 0.

iFrame

(TS, int32, RO) Current frame number start from 0.

iKeyDirection

(TS, 2D float32 vector, RO) Direction according to ASWD / arrow keys.

iMouse

(TS, 2D float32 vector, RO) Current mouse position from 0 to 1.

iMouseButton

(TS, 3D int32 vector, RO) Current mouse button status.

iResolution

(TS, 2D int32 vector, RO) Window size / screen resolution.

iTime

(TS, float32, RO) Current time in seconds.

mouse

(PS, tuple of two float, RO) Get mouse position / cursor coordinate from 0 to 1.

resolution

(PS, tuple of two int, RW) Get or set window size / screen resolution.

time

(PS, float32, RO) Get current time in seconds.

Methods Summary

define_input()

Should be called if you wish to use self.iXXX as uniform scalars.

on_advance()

Called to advance / step the physics scene.

on_click(x, y, btn)

on_clicking(x, y, btn)

on_close()

on_drag(x, y)

on_escape()

on_event(e)

Called when a event occurred, hook me if you want a raw event control.

on_exit()

on_hover(x, y)

on_init()

Called when initializing Animation().

on_not_clicking(x, y)

on_not_pressing()

on_post_render()

on_pre_event()

Called per GUI main loop.

on_pre_exit()

on_pre_render()

on_press(key)

on_pressing(key)

on_release(key)

on_render()

Called to render the displayed image.

on_show()

on_start()

Called when GUI main loop started, i.e.

on_unclick(x, y, btn)

on_update_input()

on_wheel(x, y, dx, dy)

set_output_video(path[, framerate])

Export frames painted in GUI to a video.

start()

Call me when GUI is ready to start shows up.

Attributes Documentation

frame

(PS, int, RO) Get current frame number start from 0.

iFrame

(TS, int32, RO) Current frame number start from 0.

iKeyDirection

(TS, 2D float32 vector, RO) Direction according to ASWD / arrow keys.

If A or left arrow is pressed, then self.iKeyDirection is vec(-1.0, 0.0). If D or right arrow is pressed, then self.iKeyDirection is vec(1.0, 0.0). If W or up arrow is pressed, then self.iKeyDirection is vec(0.0, 1.0). If S or down arrow is pressed, then self.iKeyDirection is vec(0.0, -1.0).

iMouse

(TS, 2D float32 vector, RO) Current mouse position from 0 to 1.

iMouseButton

(TS, 3D int32 vector, RO) Current mouse button status.

self.iMouseButton[0] is 1 if LMB is pressed. self.iMouseButton[1] is 1 if MMB is pressed. self.iMouseButton[2] is 1 if RMB is pressed. Otherwise, 0.

iResolution

(TS, 2D int32 vector, RO) Window size / screen resolution.

iTime

(TS, float32, RO) Current time in seconds.

mouse

(PS, tuple of two float, RO) Get mouse position / cursor coordinate from 0 to 1.

resolution

(PS, tuple of two int, RW) Get or set window size / screen resolution.

time

(PS, float32, RO) Get current time in seconds.

Methods Documentation

define_input()

Should be called if you wish to use self.iXXX as uniform scalars.

If you are familiar with Shadertoy, then this is for you :)

on_advance()

Called to advance / step the physics scene.

I.e. update self.circles if you’re using it.

on_click(x, y, btn)
on_clicking(x, y, btn)
on_close()
on_drag(x, y)
on_escape()
on_event(e)

Called when a event occurred, hook me if you want a raw event control.

on_exit()
on_hover(x, y)
on_init()

Called when initializing Animation().

Set up self.* properties for application usage here:

Property

Type

Default

Description

img

np.array

None

Image to display.

circles

np.array

None

Circles to paint.

circle_radius

scalar

1

Radius of circles.

circle_color

RGB hex

0x000000

Color of circles.

background_color

RGB hex

0x000000

background color of window.

title

string

"Animation"

Title of the window.

screenshot_dir

string

None

Path to save screenshots.

resolution

tuple

img.shape

The size of window / screen.

auto_clean

boolean

False

Zero the image before render.

colormap

MPL CMap

None

matplotlib.cm color map.

gui_backend

string

native

native, matplotlib, or none

on_not_clicking(x, y)
on_not_pressing()
on_post_render()
on_pre_event()

Called per GUI main loop.

on_pre_exit()
on_pre_render()
on_press(key)
on_pressing(key)
on_release(key)
on_render()

Called to render the displayed image.

I.e. update self.img if it’s used.

on_show()
on_start()

Called when GUI main loop started, i.e. Animation().start().

on_unclick(x, y, btn)
on_update_input()
on_wheel(x, y, dx, dy)
set_output_video(path, framerate=24)

Export frames painted in GUI to a video.

FIXME: Only work for self.img render, doesn’t work for self.circles for now. Use self.screenshot_dir = '/tmp', then cd /tmp && ti video if you wish to.

start()

Call me when GUI is ready to start shows up.

A common usage for application can be: MyAnimation().start().