Attributes: Time-Varying Values

VectorMation’s core idea is that every visual property is a function of time. The attribute system provides a composable way to define and modify these functions.

Attribute Types

Type

Holds

Example

Real

A single number

radius, opacity, x-coordinate

Coor

A 2D coordinate (x, y)

centre of a circle

Color

An RGB/RGBA colour

fill colour

String

A string

SVG path data, text content

Tup

An arbitrary-length tuple

rotation (deg, cx, cy)

Note

Every shape exposes its properties as attributes. For example a Circle has circle.r (radius) and circle.c (centre), a Rectangle has rect.width and rect.height, and a Text has text.font_size. You can animate any of these by calling methods like move_to on them.

Reading Values

Every attribute has .at_time(t) to get its value at time t:

circle = Circle(r=100, cx=500, cy=500)
print(circle.r.at_time(0))   # 100
print(circle.c.at_time(0))   # (500, 500)

Setting Values

set(start, end, func_inner)

Replace the attribute’s value with func_inner(t) on the interval [start, end]:

# Move the circle's radius from 100 to 200 over 2 seconds
circle.r.set(0, 2, lambda t: 100 + 50 * t)

set_onward(start, value)

Set the attribute to a constant (or function) from start onwards:

circle.r.set_onward(3, 50)  # radius becomes 50 at t=3 and stays
circle.r.set_onward(3, lambda t: 50 + 10 * t)  # or a function

add_onward(start, func_or_value)

Add to the existing value from start onwards. Accepts a constant or a function:

circle.r.add_onward(2, 20)  # radius increases by 20 from t=2

# With a function: oscillate the radius around its current value
import math
circle.r.add_onward(0, lambda t: 10 * math.sin(t * 4))

move_to(start, end, end_val, easing=smooth)

Smoothly animate from the current value to end_val:

circle.r.move_to(0, 2, 200)  # smoothly grow radius to 200

Example: animating radius with move_to

Smoothly grow and shrink a circle’s radius using move_to.

"""Animate a circle's radius with move_to."""
from vectormation.objects import *

v = VectorMathAnim()
v.set_background()

circle = Circle(r=40, cx=960, cy=540, fill='#58C4DD', fill_opacity=0.8, stroke_width=3)
circle.fadein(start=0, end=0.5)

# Smoothly grow the radius from 40 to 200
circle.r.move_to(0.5, 2.5, 200)
# Then shrink it back
circle.r.move_to(2.5, 4.5, 40)

v.add(circle)

v.show(end=5)

Composing Attribute Calls

Attribute methods compose naturally over time. You can chain move_to, set_onward, and other calls to build complex animations from simple primitives:

Example: chaining attribute calls

A dot slides to the centre, jumps up instantly with set_onward, then slides to the right with another move_to.

"""Chaining multiple attribute calls over time."""
from vectormation.objects import *

v = VectorMathAnim()
v.set_background()

dot = Dot(cx=360, cy=540, r=20, fill='#E74C3C')
dot.fadein(start=0, end=0.5)

# Chain: move right, then set a constant position, then move again
dot.c.move_to(0.5, 2, (960, 540))       # slide to centre
dot.c.set_onward(2, (960, 300))          # jump up instantly
dot.c.move_to(2.5, 4, (1560, 540))      # slide to right

v.add(dot)

v.show(end=4.5)

Coordinate-Specific Methods

Coor attributes have additional methods:

rotate_around(start, end, pivot_point, degrees)

dot.c.rotate_around(0, 5, pivot_point=(500, 500), degrees=360)

The coordinate rotates around the pivot point. pivot_point can be a tuple or a callable returning (x, y).

move_to(start, end, end_pos, easing=smooth)

dot.c.move_to(0, 2, (800, 200))  # smoothly move to (800, 200)

Easing Functions

Easing functions control the rate of change over a normalised [0, 1] interval.

_images/easing_curves.svg

Available easings (from vectormation.easings):

Function

Description

linear

Constant speed

smooth

Sigmoid-based smooth start/stop

rush_into

Fast start, smooth end

rush_from

Smooth start, fast end

there_and_back

Goes to 1 at midpoint, back to 0

ease_in_quad, ease_out_quad, ease_in_out_quad

Quadratic easings

ease_in_cubic, ease_out_cubic, ease_in_out_cubic

Cubic easings

ease_in_elastic, ease_out_elastic

Spring-like bounce

ease_in_bounce, ease_out_bounce

Bouncing effect

And many more – see vectormation/easings.py for the full list.

Example: comparing easings

Three circles shift across the screen using linear, smooth, and ease_out_bounce easings.

"""Side-by-side circles shifting with different easings."""
from vectormation.objects import *
from vectormation.easings import linear, smooth, ease_out_bounce

v = VectorMathAnim()
v.set_background()

labels = ['linear', 'smooth', 'bounce']
easings = [linear, smooth, ease_out_bounce]
colors = ['#E74C3C', '#3498DB', '#2ECC71']

for i, (label, easing, color) in enumerate(zip(labels, easings, colors)):
    y = 270 + i * 270
    # Label
    txt = Text(label, x=100, y=y, font_size=36, fill=color)
    txt.fadein(start=0, end=0.3)
    # Circle that shifts right using this easing
    c = Circle(r=30, cx=400, cy=y, fill=color, fill_opacity=0.8)
    c.fadein(start=0, end=0.3)
    c.shift(dx=800, start=0.5, end=3, easing=easing)
    v.add(txt, c)

v.show(end=3.5)

Color Attributes

Colors can be specified as hex strings, colour names, or RGB tuples:

circle = Circle(fill='#58C4DD')         # hex
circle = Circle(fill='RED')             # colour name
circle = Circle(fill=(88, 196, 221))    # RGB tuple

Color attributes support set(), set_onward(), and interpolate() just like Real attributes.

HSL Interpolation

By default, colours interpolate in RGB space. For smoother hue transitions (e.g. rainbow effects), use HSL:

circle.set_color(start=0, end=2, fill='#FF0000', stroke='#0000FF', color_space='hsl')

The color_space parameter is available on set_color() and Color.interpolate().

Example: RGB vs HSL interpolation

Top circle interpolates from red to blue in RGB (goes through purple). Bottom circle interpolates in HSL (sweeps through the hue spectrum).

"""Color interpolation: RGB vs HSL."""
from vectormation.objects import *

v = VectorMathAnim()
v.set_background()

# Top: RGB interpolation (goes through muddy middle)
rgb_label = Text('RGB', x=150, y=340, font_size=36, fill='WHITE')
rgb_circle = Circle(r=80, cx=960, cy=340, fill='#FF0000', fill_opacity=1, stroke_width=0)
rgb_circle.set_color(start=0.5, end=3.5, fill='#0000FF', color_space='rgb')

# Bottom: HSL interpolation (smooth hue transition through rainbow)
hsl_label = Text('HSL', x=150, y=740, font_size=36, fill='WHITE')
hsl_circle = Circle(r=80, cx=960, cy=740, fill='#FF0000', fill_opacity=1, stroke_width=0)
hsl_circle.set_color(start=0.5, end=3.5, fill='#0000FF', color_space='hsl')

v.add(rgb_label, rgb_circle, hsl_label, hsl_circle)

v.show(end=4)