BezierCircuitCubic

Qualified name: algan.mobs.bezier\_circuit.BezierCircuitCubic

class BezierCircuitCubic(control_points, normals=None, stroke_width=5, stroke_color=Color([1., 1., 1., 0., 1.]), filled=True, add_texture_grid=True, grid_width=1, grid_height=None, empty=False, z_index=0, shade_in_3d=False, **kwargs)[source]

Bases: Mob

A closed loop of cubic bezier curves – the geometry every 2-D shape is made of.

The curves are evaluated analytically by the renderer rather than tessellated, so a circuit stays exactly smooth at any zoom, and any circuit can become() any other. On a filled circuit the border is drawn inside the outline, so raising stroke_width eats into the fill instead of growing the silhouette; an unfilled circuit has no interior to eat into, so its stroke stays centred on the path.

Color across a circuit. A circuit carries a rectangular grid of colored points, laid across its own frame and sampled bilinearly per fragment by the renderer. grid_width x grid_height is therefore the resolution of everything painted on the shape – a gradient, an image, a color wave – and it defaults to a single point, i.e. one flat color. Raise it and fill it in with set_color_by_function() or set_color_by_image().

This grid is the counterpart of Surface.grid, and shares its name deliberately. A Surface keeps its vertex colors and its texture maps apart because a curved surface wants geometry and image detail at different resolutions; a circuit is always flat, so the distinction buys it nothing and one grid serves as both.

The grid’s (u, v) domain is the circuit’s own frame, exactly as Surface’s is: u runs from 0 to 1 along the first basis row and v along the second, which for an upright 2-D shape means u left to right and v bottom to top. Both rows are as long as the distance from the centre to the furthest control point, so the frame spans the square that circumscribes the shape and the shape itself covers the middle of the domain rather than all of it.

Parameters:
  • control_points – The cubic bezier control points, shape (*, 3) in world units, in groups of four: P0, P1, P2, P3 per segment, with each segment starting where the previous one ended. A segment that starts somewhere else begins a new sub-circuit, which is how a shape gets holes.

  • normals – Per-control-point normals, shape (*, 3), used for lighting. Defaults to None, meaning the circuit’s own plane normal is used.

  • stroke_width – Width of the border stroke, in pixels against PREVIEW’s frame height (396): the renderer scales it by its own frame height over that, so a border keeps its apparent weight at any resolution. Defaults to 5; pass 0 for no border.

  • stroke_color – Color of the border stroke. Defaults to WHITE. The circuit’s color is its fill color and does not touch the border; see stroke_color.

  • filled – Whether the interior is painted. Defaults to True; False leaves an outline whose stroke is centred on the path (what Line uses).

  • add_texture_grid – Whether to build the texture grid at all. Defaults to True. False leaves the circuit one color and no per-texel storage, and the set_color_by_* methods then have nothing to write to.

  • grid_width – Number of color samples along the circuit’s first basis row – u, left to right on an upright shape. Defaults to 1: one flat color, which is what a shape wants unless you are painting something across it.

  • grid_height – Number of color samples along the second basis row (v). Defaults to None, meaning match grid_width – except on a circuit whose control points are collinear (a straight Line), where the second row is synthesized perpendicular to the path and carries no extent of the shape, so it defaults to 1 and the grid runs along the line only.

  • empty – Whether the circuit is invisible: fill and border are forced to zero opacity. Defaults to False. Used for shapes that exist only to position or morph into something else.

  • z_index

    Which of two exactly coplanar circuits draws in front: the higher z_index wins. Defaults to 0, which leaves the shape in author order – coplanar 2-D geometry draws in the order it was created, each composite Mob kept whole and drawn parent-first, so an arrow crossing a grid authored before it lands on top of that grid without being asked to. Raise it to override that: a label over a panel authored after it, a highlight over the shape it marks. Setting it propagates to the whole sub-hierarchy (see z_index).

    It is not a general depth override. The renderer spends it as a bias of a few ten-thousandths of a world unit toward the camera – enough to settle a tie between surfaces at the same depth, far too little to reorder anything genuinely in front of or behind. Values are small integers; a few hundred would start to shift the shape visibly. Matches Manim’s attribute of the same name, both in meaning and in being a stable sort key over the authored order, and ManimMob carries it across on import.

  • **kwargs – Passed to Mob – notably color, which is the fill color. location is the exception: a circuit’s own is derived from the control points (the centroid of the region they enclose, so the shape turns about itself), so one given here is applied as a move onto that point once the frame has been derived, rather than replacing it.

See also

set_color_by_function()

Color it by a function of (u, v).

set_color_by_image()

Paint an image across it.

Surface

The 3-D counterpart, with the same (u, v) conventions.

Examples

Example: Example1BezierCircuitCubic

../_images/Example1BezierCircuitCubic-1.png
from algan import *
import torch

square = Square(grid_width=32, grid_height=32, stroke_width=0)
square.set_color_by_function(
    lambda uv: torch.cat(
        (uv[..., :1], uv[..., 1:], torch.zeros_like(uv[..., :1])), -1
    )
)
square.spawn()

Scene.save_video()

Methods

draw

Draw the circuit on, as though traced by a pen.

from_batches

Build many independently indexable circuits without per-circuit mobs.

get_animatable_attrs

get_base_grid

Get the circuit's texture grid, the (u, v) domain it is colored over.

get_default_color

Get the color a circuit uses when none was given.

get_render_primitives

set_color_by_function

Color the circuit by a function of its (u, v) parameters.

set_color_by_image

Paint an image across the circuit.

Attributes

animation_manager

This mob's scene-owned animation manager.

basis

The Mob's orientation and scale, as a flattened 3x3 matrix of shape (*, 9).

casts_shadows

Whether this Mob's geometry blocks light on its way from a light source to another surface -- whether it casts a shadow.

children

The Mobs attached below this one, in attachment order.

closed_shell

Whether this Mob's triangles form a CLOSED shell -- every camera ray that enters the geometry crosses a second time on its way out.

draws_descendants

Whether get_render_primitives returns geometry belonging to this Mob's DESCENDANTS as well as its own.

forward

Get the direction the Mob is facing.

lifespan

This mob's [spawn, despawn) interval on its Scene timeline (a Lifespan).

location

The Mob's position in world space, shape (*, 3).

normalized_basis

The Mob's orientation with scale divided out, shape (*, 9).

parents

The Mobs this one is attached to, in attachment order.

receives_shadows

Whether this Mob's surfaces are darkened by shadows cast onto them.

right

Get the Mob's own rightward direction.

scale_coefficient

The Mob's scale along its own right, up and forward axes, shape (*, 3).

stroke_color

The color of the circuit's border stroke.

two_sided

Whether this Mob's geometry should be lit from whichever side the ray arrives on.

up

Get the Mob's own upward direction.

x

The Mob's x coordinate in world units, shape (*, 1).

xy

The Mob's x and y coordinates in world units, shape (*, 2).

y

The Mob's y coordinate in world units, shape (*, 1).

z

The Mob's z coordinate in world units, shape (*, 1).

z_index

Which of two exactly coplanar circuits draws in front (higher wins).

draw(t=1.0)[source]

Draw the circuit on, as though traced by a pen.

The path is revealed from its start point to a fraction t of the way round, so animating it is what makes a shape appear stroke by stroke rather than fading in. A shape drawn this way ends up with exactly the geometry it started with, so it can be moved and morphed afterwards as usual.

Animation

Recorded as an animation: the drawn portion sweeps from 0 to t over the current context’s runtime (1 second by default). Wrap the call to change that – with Seq(runtime=3): shape.draw() – or in Off() to jump straight to the end state. Applies to this circuit only, not to its descendants, so a composite Mob draws each of its parts separately.

Parameters:

t (float) – How much of the path is drawn by the end, from 0 (nothing) to 1 (the whole circuit). Defaults to 1.0.

Returns:

This circuit, so calls can be chained.

Return type:

BezierCircuitCubic

Examples

Example: Example1BezierCircuitCubicDraw

from algan import *

square = Square().spawn()
square.draw()

Scene.save_video()
classmethod from_batches(control_point_batches, *args, **kwargs)[source]

Build many independently indexable circuits without per-circuit mobs.

control_point_batches contains one cubic-bezier tensor per logical object. Geometry is concatenated once while parent_batch_sizes retains the control-point boundaries used by rendering and indexed views.

get_base_grid()[source]

Get the circuit’s texture grid, the (u, v) domain it is colored over.

Values run from 0 to 1 along both axes: u along the circuit’s first basis row, v along its second, which on an upright 2-D shape means u left to right and v bottom to top. Both rows are as long as the distance from the circuit’s centre to its furthest control point, so the domain covers the square that circumscribes the shape and the shape sits in the middle of it. An axis with a single sample carries one color for the whole span and is evaluated at its centre, 0.5.

This is the input the set_color_by_* methods evaluate their functions over, so it is what to write those functions in terms of.

Returns:

The (u, v) coordinates, shape [grid_width, grid_height, 2].

Return type:

torch.Tensor

See also

set_color_by_function()

Color the circuit over this grid.

get_default_color()[source]

Get the color a circuit uses when none was given.

Returns:

PURPLE.

Return type:

Color

set_color_by_function(function)[source]

Color the circuit by a function of its (u, v) parameters.

Gives each texel of the circuit’s texture grid its own color, for gradients, heat maps or anything where color carries data, and the renderer interpolates between them across the shape. The colors travel with the circuit as it moves and morphs.

The grid is the resolution of the result, and it is a single flat color unless you asked for more: build the shape with grid_width / grid_height (see BezierCircuitCubic). On a filled circuit this colors the fill, leaving stroke_color alone; on an unfilled one, where the stroke is all there is, it colors the stroke. A multi-circuit mob (a Text, a Tex) colors every circuit over its own frame, so the pattern repeats per glyph.

Animation

Recorded as an animation over the current context’s runtime (1 second by default), so the colors cross-fade smoothly. Wrap the call in Off() to apply it instantly.

Parameters:

function – Callable taking a (u, v) tensor of shape [..., 2], with both coordinates in [0, 1], and returning colors of shape [..., 3] (RGB), [..., 4] (RGBA) or [..., 5] (RGB, glow, alpha – Algan’s internal channel order). Channels are in [0, 1]; a missing alpha defaults to 1 and a missing glow to 0. Must be vectorized – it is called once on the whole grid, not per texel.

Returns:

This circuit, so calls can be chained.

Return type:

BezierCircuitCubic

Raises:

ValueError – If the circuit has a single-texel texture grid, or if function returns the wrong number of colors.

See also

set_color_by_image()

Paint an image on instead.

get_base_grid()

The (u, v) grid this evaluates over.

Examples

Example: Example1BezierCircuitCubicSetColorByFunction

../_images/Example1BezierCircuitCubicSetColorByFunction-1.png
from algan import *
import torch

circle = Circle(grid_width=48, grid_height=48)
circle.set_color_by_function(
    lambda uv: torch.cat(
        (uv[..., :1], torch.zeros_like(uv[..., :1]), uv[..., 1:]), -1
    )
)
circle.spawn()

Scene.save_video()
set_color_by_image(rgba_array_or_file_path)[source]

Paint an image across the circuit.

The image is resampled onto the circuit’s texture grid and interpolated across the shape by the renderer, and it follows the shape as it moves and morphs. The image’s top-left corner lands at the top left of the frame, which on an upright 2-D shape is (u, v) == (0, 1): v runs up the frame, as it does on a Surface, while an image’s rows run down the picture.

Unlike set_color_by_image(), which keeps the image at its own resolution, a circuit has no separate texture map: the texture grid is the resolution, so build the shape with a grid_width / grid_height matching the detail you need. Remember too that the grid spans the square circumscribing the shape, so the shape shows the middle of the picture.

Animation

Recorded as an animation over the current context’s runtime (1 second by default): the circuit cross-fades, texel by texel, to the image. Wrap the call in Off() to apply it instantly.

Parameters:

rgba_array_or_file_path – Path to an image file, or an RGBA array of shape [H, W, 4] or [H, W, 5] with channels in [0, 1]. Paths resolve relative to the working directory and then the main script’s directory, so an image beside your script is found either way.

Returns:

This circuit, so calls can be chained.

Return type:

BezierCircuitCubic

Raises:

ValueError – If the circuit has a single-texel texture grid.

See also

set_color_by_function()

Color it by a function instead.

ImageMob

An image as a Mob of its own, at full resolution.

property stroke_color

The color of the circuit’s border stroke.

Separate from color, which is the fill: setting one never changes the other, and on a filled circuit the border is drawn inside the outline unless SETTINGS.style.border_placement is "centered". Accepts anything a color attribute does – a Color, a named constant, a hex string.

Reading it back gives the border’s per-texel colors as a (N, 5) tensor (RGB, glow, alpha), one row per texel of the circuit’s texture grid, rather than the single value that was assigned – so compare against stroke_color[0] rather than against a Color directly.

Animation

Recorded like any other color attribute: the border cross-fades to the new color over the current context’s runtime (1 second by default). Wrap the write in Off() to change it instantly.

property z_index

Which of two exactly coplanar circuits draws in front (higher wins).

0 (the default) means author order, which already keeps a composite Mob whole and parent-first; this is the override for when that is not what you want. Assigning propagates to every circuit below this one in the hierarchy, matching Manim’s set_z_index(..., family=True), so a composite raises as one thing rather than leaving its parts on opposite sides of whatever they cross.

Animation

Takes effect immediately and is not animated: it selects between discrete orderings, so there is nothing to interpolate and no context (Seq, Sync, Off) changes how it applies. The write reaches every circuit in this Mob’s sub-hierarchy; plain Mobs in between, such as a circuit’s texture points, have no draw order and are skipped. It may be set before or after spawn() – the renderer reads it afresh for every frame batch.