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
---------------
.. list-table::
:header-rows: 1
:widths: auto
* - 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``:
.. code-block:: python
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]``:
.. code-block:: python
# 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:
.. code-block:: python
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:
.. code-block:: python
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``:
.. code-block:: python
circle.r.move_to(0, 2, 200) # smoothly grow radius to 200
.. admonition:: Example: animating radius with move_to
:class: example
.. raw:: html
Smoothly grow and shrink a circle's radius using ``move_to``.
.. literalinclude:: ../../examples/reference/attr_move_to.py
:language: python
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:
.. admonition:: Example: chaining attribute calls
:class: example
.. raw:: html
A dot slides to the centre, jumps up instantly with ``set_onward``, then
slides to the right with another ``move_to``.
.. literalinclude:: ../../examples/reference/attr_composition.py
:language: python
Coordinate-Specific Methods
----------------------------
``Coor`` attributes have additional methods:
``rotate_around(start, end, pivot_point, degrees)``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
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)``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
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.
.. image:: _static/images/easing_curves.svg
:width: 520
:align: center
Available easings (from ``vectormation.easings``):
.. list-table::
:header-rows: 1
:widths: auto
* - 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.
.. admonition:: Example: comparing easings
:class: example
.. raw:: html
Three circles shift across the screen using ``linear``, ``smooth``, and
``ease_out_bounce`` easings.
.. literalinclude:: ../../examples/reference/attr_easing.py
:language: python
Color Attributes
----------------
Colors can be specified as hex strings, colour names, or RGB tuples:
.. code-block:: python
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:
.. code-block:: python
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()``.
.. admonition:: Example: RGB vs HSL interpolation
:class: example
.. raw:: html
Top circle interpolates from red to blue in RGB (goes through purple).
Bottom circle interpolates in HSL (sweeps through the hue spectrum).
.. literalinclude:: ../../examples/reference/attr_color.py
:language: python