Surface

Qualified name: algan.mobs.surfaces.surface.Surface

class Surface(coord_function=None, grid_height=None, grid_width=None, color_texture=None, reflectivity_texture=None, roughness_texture=None, refractive_index_texture=None, normal_texture=None, glow_texture=None, ignore_normals=False, geometry_tolerance=0.0005, render_tolerance_pixels=0.5, min_grid_resolution=2, max_grid_resolution=200, *args, u_range=None, v_range=None, resolution=None, **kwargs)[source]

Bases: Mob

A smooth 2-D surface, embedded in 3-D space, A.K.A a manifold. The surface is implemented by sampling a uniform grid of 2-D points from the unit square (known as intrinsic coordinates, or “UV coordinates”), tiling this grid with triangles, and then mapping the triangle corners to 3-D world coordinates as defined by the manifold function.

Parameters:
  • coord_function – The function mapping 2-D intrinsic coordinates (ranging from [0,1]), to 3-D world coordinates, which defines the manifold’s shape.

  • grid_height – Number of sampled points along the v axis. Defaults to None, meaning the resolution is chosen automatically to meet geometry_tolerance; giving either grid size turns that search off.

  • grid_width – Number of sampled points along the u axis. Defaults to None, as grid_height does; giving only one of the two uses it for both.

  • ignore_normals – Whether to draw the surface as flat, faceted triangles instead of smoothing it. Defaults to False, meaning smooth vertex normals are computed from the sampled grid and the triangles are curved into PN patches. True skips both, so each triangle is shaded flat and the surface reads as a low-poly facet mesh.

  • geometry_tolerance – Maximum sampled world-space distance between the analytic surface and its PN-triangle approximation at construction time, in world units. It measures how far the approximation strays off the surface, not how the surface is parameterized, so a coordinate function that stretches or collapses its parameters (a polar cap, a superellipse meridian) costs no extra vertices. Shapes with a known exact surface-distance expression use it directly; general parametric surfaces search for each sample’s nearest analytic point. The selected grid is cached per concrete Surface subclass and geometry configuration, so constructing the same shape again does not repeat the resolution search.

  • render_tolerance_pixels

    Maximum sampled deviation between a PN triangle and the flat render triangles it is dynamically diced into, in output pixels. Defaults to 0.5 – half a pixel, so the dice is invisible; None removes the bound entirely. Lower it for a sharper silhouette on a surface that fills the frame, raise it to buy back the triangles.

    Every PN triangle is diced only as finely as it itself needs, in every frame, so detail spent where the surface is close to the camera is not spent on the rest of the mesh or on the frames where it is far away.

    The budget is stated at the renderer’s reference frame height (1000 px) and scaled down in proportion on anything shorter, since a low-resolution frame needs finer dicing than its pixel count alone suggests – the analytic-coverage antialiasing computes a pixel’s coverage from the microtriangles crossing it, and at PREVIEW each pixel covers far more of the object. So the default is worth 0.5 px from 1080p up and 0.2 px at PREVIEW, and halving it halves both.

  • min_grid_resolution – Bounds for automatic grid sizing, measured in vertices per axis. Default to 2 and 200. The floor is two vertices – one cell – because a surface that is straight along an axis needs no more than that, and the search measures rather than assumes it.

  • max_grid_resolution – Bounds for automatic grid sizing, measured in vertices per axis. Default to 2 and 200. The floor is two vertices – one cell – because a surface that is straight along an axis needs no more than that, and the search measures rather than assumes it.

  • u_range – The interval of each parameter the surface is built over, as (start, end). Default to (0, 1), the whole unit square. These are stored for a subclass’s coord_function to read – the base class does not itself remap (u, v) – which is how Sphere and its siblings cut open shells from partial sweeps. Those classes take theirs as angles, in degrees like every other angle in Algan.

  • v_range – The interval of each parameter the surface is built over, as (start, end). Default to (0, 1), the whole unit square. These are stored for a subclass’s coord_function to read – the base class does not itself remap (u, v) – which is how Sphere and its siblings cut open shells from partial sweeps. Those classes take theirs as angles, in degrees like every other angle in Algan.

  • resolution – Grid size as (u_patches, v_patches), or one int for both. Counts patches, one less than the vertices grid_width / grid_height count, matching Manim. Defaults to None. Ignored if either grid size is given.

  • color_texture – Optional color texture map [W, H, 5] – one image, sampled bilinearly in-kernel by the ray tracer. It is an ordinary animatable attribute: assign a new map of the same shape to animate it. procedural_textures generates the common patterns, so a checkerboard is color_texture=get_checkerboard((BLUE, WHITE)).

  • reflectivity_texture – Optional per-texel material property maps, each [W, H, 1] (or [W, H]). Like color_texture they are sampled bilinearly per fragment inside the ray tracing kernel (only the general wavefront tracer implements this; batches containing such maps are routed to it automatically, for both flat and curved PN triangles). Properties without a map keep the per-vertex system. Maps of different resolutions are resampled to a common resolution.

  • roughness_texture – Optional per-texel material property maps, each [W, H, 1] (or [W, H]). Like color_texture they are sampled bilinearly per fragment inside the ray tracing kernel (only the general wavefront tracer implements this; batches containing such maps are routed to it automatically, for both flat and curved PN triangles). Properties without a map keep the per-vertex system. Maps of different resolutions are resampled to a common resolution.

  • refractive_index_texture – Optional per-texel material property maps, each [W, H, 1] (or [W, H]). Like color_texture they are sampled bilinearly per fragment inside the ray tracing kernel (only the general wavefront tracer implements this; batches containing such maps are routed to it automatically, for both flat and curved PN triangles). Properties without a map keep the per-vertex system. Maps of different resolutions are resampled to a common resolution.

  • normal_texture – Optional tangent-space normal map [W, H, 3], with components in [-1, 1]: x along increasing u, y along increasing v, z along the smooth surface normal ((0, 0, 1) = unperturbed). Perturbs the shading normal per fragment in-kernel. Note: under the default vertex-shaded pipeline lighting is baked at the vertices, so a normal map only affects effects evaluated per fragment (mirror reflections, refraction, ray traced shadows, and fragment shading when enabled).

  • glow_texture – Optional glow strength/radius maps, each [W, H, 1] (or [W, H]). These are consumed per-vertex by the glow accumulator, so they are baked to the surface grid resolution (raise grid_width/ grid_height for more detail).

  • *args – Passed to Mob – notably color, opacity and location.

  • **kwargs – Passed to Mob – notably color, opacity and location.

grid

The surface’s vertices, as a child Mob, and the way to reach anything that varies from vertex to vertex. grid.location holds their 3-D world positions, shape (*, grid_width * grid_height, 3) row-major over the sample grid, and grid.color their colors – both animatable like any other Mob attribute, so writing either records an animation.

These colors are the surface’s albedo, interpolated across each triangle from its corners. Setting a color_texture replaces them as the albedo source, which is then sampled bilinearly from the texture’s texels instead; shading itself is per-fragment either way. The grid’s resolution is fixed at construction and a texture’s is not, which is why the two are kept separate.

vertices is shorthand for grid.location.

Type:

Mob

See also

Sphere

And Cylinder / Cone / Torus, the built-in surfaces.

get_checkerboard()

And its siblings, which build the color_texture for a procedural pattern.

set_color_by_function()

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

set_location_by_function()

Reshape it after construction.

Examples

A saddle, from its parametric equation. The coordinate function takes the whole (u, v) grid at once and returns a 3-D point per sample:

Example: Example1Surface

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

def saddle(uv):
    x = uv[..., :1] * 4 - 2
    y = uv[..., 1:] * 4 - 2
    return torch.cat((x, y, (x ** 2 - y ** 2) * 0.4), -1)

Surface(
    saddle, color_texture=get_checkerboard((BLUE, BLUE_E))
).rotate(60, RIGHT).spawn()

Scene.save_video()

Methods

clear_geometry_resolution_cache

Clear construction-time resolution entries for this exact class.

coord_function

Map the surface's (u, v) parameters to positions in space.

from_batches

Build many independently indexable surfaces without per-surface mobs.

get_base_grid

Get the surface's parameter grid, the (u, v) domain it is built from.

get_default_color

Get the color a Surface uses when none was given.

get_render_primitives

Build the triangles the renderer draws this surface from.

get_texture_locations

Get where in the world each texel of this surface's texture maps sits.

get_unit_normals

Return one smooth unit normal for each sampled surface vertex.

set_checkerboard_colors

Paint the surface in a checkerboard of alternating colors.

set_color_by_axis

Color the surface by how far along one axis each point sits.

set_color_by_function

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

set_color_by_image

Paint an image across the surface.

set_location_by_function

Shape the surface by a function of its (u, v) parameters.

set_shape_to

Reshape this surface into the shape of another one.

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.

color_texture

An image painted across the surface, as an [W, H, 5] RGBA+glow tensor.

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.

geometry_tolerance

How far this surface's mesh may sit from the exact shape, in world units.

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.

render_tolerance_pixels

How far a drawn triangle may sit from the true surface, in pixels.

right

Get the Mob's own rightward direction.

scale_coefficient

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

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.

vertices

The surface's vertex positions, shape (*, grid_width * grid_height, 3).

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).

classmethod clear_geometry_resolution_cache()[source]

Clear construction-time resolution entries for this exact class.

property color_texture

An image painted across the surface, as an [W, H, 5] RGBA+glow tensor.

Assigning an image maps it over the surface’s parameter domain, so it follows the surface as it deforms. None means the surface is drawn from its per-vertex colors instead. Assigning a texture whose resolution differs from the current one detaches history, since the two cannot be interpolated.

Reading it back gives the image in that same [W, H, 5] layout – not the flat W * H * 5 row the timeline stores it as – so arithmetic on it can be assigned straight back:

surface.color_texture = surface.color_texture * 0.5  # half brightness

The value is a Color, one per texel, so the color API applies to a whole map at once:

surface.color_texture = surface.color_texture.mult_opacity(0.5)

Its five channels are (R, G, B, glow, alpha), which is why the plain multiplication above dims the alpha and the glow along with the color. Reach for one of them through .rgb, .glow or .opacity and assign the result back – the read is a copy, so writing into it alone changes nothing:

texels = surface.color_texture
texels.rgb = texels.rgb * 0.5
surface.color_texture = texels

On an axis where the surface closes on itself – u on a Sphere, both axes on a Torus – the image wraps: its last column of texels neighbours its first, each column spanning 1 / W of the way around, so a map whose edges join draws no seam. On an open axis the first and last texels sit on the two edges.

Animation

An ordinary animatable attribute: assigning a new image is recorded as an animation, interpolating texel by texel from the old texture to the new one over the current context’s runtime (1 second by default). Assign inside Off() to swap it instantly. The new image must match the current resolution to interpolate – see the note above about detaching history.

coord_function(uv)[source]

Map the surface’s (u, v) parameters to positions in space.

This is what defines the surface’s shape, and what each 3-D shape class overrides: Sphere maps the unit square onto a sphere, Torus onto a torus, and so on. The base implementation gives a flat plane spanning [-1, 1] on both axes.

Parameters:

uv (Tensor) – Parameter coordinates to map, shape (*, 2), with both components in [0, 1].

Returns:

Positions relative to the surface’s location, shape (*, 3).

Return type:

torch.Tensor

classmethod from_batches(centers, *args, colors=None, **kwargs)[source]

Build many independently indexable surfaces without per-surface mobs.

One packed Mob covers every centre in centers: it is a single Scene actor whose vertex grids are concatenated into one tensor, so the whole collection costs one construction and one get_render_primitives() call per frame batch rather than one each. Index it (spheres[3]) for a view onto a single member, which shares the pack’s timeline rows.

Every member has the same shape, resolution and material – only its centre and color vary. Anything else that differs needs separate Mobs.

Parameters:
  • centers – World-space centre of each surface, shape (N, 3) in world units. Any nested sequence is cast to a tensor and reshaped.

  • colors – Per-member color, shape (N, 3) as RGB, (N, 4) as RGBA or (N, 5) as Algan’s RGB+glow+opacity. Defaults to None, giving every member the color passed in kwargs.

  • *args – Passed to the ordinary constructor, which builds one representative member – so radius, resolution, color and the texture maps all mean what they usually do, and apply to the whole pack.

  • **kwargs – Passed to the ordinary constructor, which builds one representative member – so radius, resolution, color and the texture maps all mean what they usually do, and apply to the whole pack.

Returns:

The packed Mob, of whichever subclass this was called on.

Return type:

Surface

Animation

Not animated: this only constructs. Animating the pack moves every member; animating pack[i] moves just that one. All members share one lifespan, so they spawn and despawn together – stagger an entrance with opacity rather than with separate spawns.

Examples

A lattice of spheres as one Mob:

Example: Example1SurfaceFromBatches

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

grid = torch.linspace(-2, 2, 6)
centers = torch.cartesian_prod(grid, grid, torch.zeros(1))
Sphere.from_batches(centers, radius=0.15, color=BLUE).spawn()

Scene.save_video()
property geometry_tolerance: float

How far this surface’s mesh may sit from the exact shape, in world units.

Fixed when the surface is constructed – it is what chose the grid resolution – and read-only thereafter. Set it in the constructor to trade vertices against accuracy.

See also

render_tolerance_pixels

The per-frame budget, in screen terms.

get_base_grid()[source]

Get the surface’s parameter grid, the (u, v) domain it is built from.

Values run from 0 to 1 along both axes. This is the input the set_*_by_function methods evaluate their functions over, so it is what to write those functions in terms of.

Returns:

The (u, v) coordinates, shape [W, H, 2].

Return type:

torch.Tensor

get_default_color()[source]

Get the color a Surface uses when none was given.

Returns:

GREEN.

Return type:

Color

get_render_primitives()[source]

Build the triangles the renderer draws this surface from.

Called once per render batch. Vertex normals are computed from the grid unless ignore_normals is set, which is what makes a deformed surface light correctly without the author supplying normals.

Returns:

The surface’s triangles for every frame of the batch.

Return type:

TrianglePrimitive

get_texture_locations(resolution=None)[source]

Get where in the world each texel of this surface’s texture maps sits.

Textures are addressed in the surface’s own (u, v) coordinates, which makes a map easy to write in terms of the surface’s parameters and awkward to write in terms of space. This is the bridge: it hands back the world position of every texel, laid out exactly like a texture map, so a map can be built from arithmetic on 3-D coordinates.

xyz = surface.get_texture_locations()
surface.color_texture = WHITE.mult_opacity(xyz[..., 1:2])

The positions are read from the surface’s current mesh, so they are right whether the shape came from its coordinate function, from a deformation, or from writing surface.grid.location yourself. They describe the surface now: a texture is carried in (u, v), so colors derived from world position travel with the surface when it later moves. Recompute them in an add_updater() callback for a texture that stays locked to world space.

Animation

A query, not a change: nothing is recorded and the surface is untouched. The positions are those of the surface’s current state, so call it after the transforms you want reflected in it.

Parameters:

resolution (int | tuple[int, int] | None) – Texel counts (W, H) along u and v, or one int for a square map. Defaults to None, meaning the resolution of the surface’s current color_texture, or its grid resolution when it has no texture yet. Pass the resolution explicitly to build a map for one of the material texture arguments instead.

Returns:

World-space positions, shape [W, H, 3] – the layout color_texture takes, one row per texel of the map that will be sampled there. Called where the surface’s state spans several frames (inside an updater), it keeps a leading frame axis, [F, W, H, 3].

Return type:

torch.Tensor

Raises:

ValueError – If this is a packed surface built by from_batches(), whose members share one texture and therefore have no single answer; if the grid has fewer than two points on an axis, so it has no triangles to sit on; or if resolution is not positive on both axes.

See also

get_base_grid()

The (u, v) domain these positions correspond to.

set_color_by_function()

Color the surface’s vertices by (u, v) instead.

Examples

Paint a sphere’s northern half, cutting the boundary in world space rather than along a parameter line:

Example: Example1SurfaceGetTextureLocations

../_images/Example1SurfaceGetTextureLocations-1.png
from algan import *

globe = Sphere(radius=1.5)
height = globe.get_texture_locations((128, 128))[..., 1:2]
globe.color_texture = BLUE.mult_opacity((height > 0).float())
globe.spawn()

Scene.save_video()
get_unit_normals()[source]

Return one smooth unit normal for each sampled surface vertex.

Oriented the way the renderer orients them (outward, for the built-in shapes), because both go through _reshape_grid_for_render(). The v axis that reorientation reverses is put back before flattening, so row i is still the normal of grid.location’s row i.

property render_tolerance_pixels: float

How far a drawn triangle may sit from the true surface, in pixels.

The budget at the renderer’s reference frame height, scaled down in proportion on shorter frames. Fixed at construction and read-only. Unlike geometry_tolerance it is spent afresh every frame, on whichever parts of the surface are near the camera. inf when the surface declares no bound at all.

set_checkerboard_colors(*colors, opacity=None)[source]

Paint the surface in a checkerboard of alternating colors.

Colors are assigned to grid vertices in rotation along both axes, so two colors give the usual checkerboard and three or more give diagonal stripes. The pattern is laid over the surface’s (u, v) grid, so it follows the shape as it deforms, and its resolution is the grid’s – raise grid_width / grid_height for finer squares.

Animation

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

Parameters:
  • *colors – Two or more colors to alternate between. Each is an Algan Color, a named constant such as BLUE, or anything Color() accepts. Passing none leaves the surface unchanged.

  • opacity – Opacity to apply to the whole surface alongside the colors, from 0 to 1. Defaults to None, leaving it as it is.

Returns:

This surface, so calls can be chained.

Return type:

Surface

See also

set_color_by_function()

Color it by an arbitrary function of (u, v).

Examples

Example: Example1SurfaceSetCheckerboardColors

../_images/Example1SurfaceSetCheckerboardColors-1.png
from algan import *

Sphere(grid_width=17, grid_height=17).set_checkerboard_colors(
    BLUE, YELLOW
).rotate(20, RIGHT).spawn()

Scene.save_video()
set_color_by_axis(axes=None, colorscale=None, axis=2, **kwargs)[source]

Color the surface by how far along one axis each point sits.

The classic height map: low points take the first color of the scale, high points the last, and everything between is interpolated. Because the colors are assigned per grid vertex they travel with the surface, so a shape colored by height keeps those colors when it is later moved – recolor it in an updater if the map should stay locked to world space.

Animation

Recorded as an animation over the current context’s runtime (1 second by default), so the surface cross-fades into the new coloring. Wrap the call in Off() to apply it instantly.

Parameters:
  • axes – A plot axes object supplying the value range to spread the scale over, read from its x_range / y_range / z_range. Defaults to None, meaning the range is taken from the surface’s own extent along axis.

  • colorscale – The colors to interpolate between, either as a plain sequence (spread evenly over the range) or as (color, value) pairs pinning each color to a coordinate. Defaults to None, which leaves the surface unchanged.

  • axis (int) – Which world axis the value is read along: 0 for x, 1 for y, 2 for z. Defaults to 2, colouring by height.

Returns:

This surface, so calls can be chained.

Return type:

Surface

Raises:

ValueError – If an unrecognized keyword argument is passed.

See also

set_color_by_function()

Color it by (u, v) rather than by position.

Examples

Example: Example1SurfaceSetColorByAxis

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

def saddle(uv):
    x = uv[..., :1] * 4 - 2
    y = uv[..., 1:] * 4 - 2
    return torch.cat((x, y, (x ** 2 - y ** 2) * 0.4), -1)

Surface(saddle).set_color_by_axis(
    colorscale=[BLUE, GREEN, YELLOW]
).rotate(60, RIGHT).spawn()

Scene.save_video()
set_color_by_function(function)[source]

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

Gives each point of the grid its own color, for gradients, heat maps or anything where color carries data. The colors travel with the surface as it deforms.

Animation

Recorded as an animation over the current context’s runtime (1 second by default), so the colors cross-fade smoothly.

Parameters:

function – Callable taking a (u, v) tensor of shape [..., 2] 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 over the whole grid.

Returns:

This surface, so calls can be chained.

Return type:

Surface

See also

set_color_by_image()

Paint an image on instead.

set_color_by_image(rgba_array_or_file_path)[source]

Paint an image across the surface.

The image is mapped over the surface’s (u, v) domain at its own resolution and sampled per fragment by the renderer, so it stays sharp however coarse the surface’s grid is, and it follows the surface as it deforms. This sets color_texture, which you can assign directly when you already hold the image as a tensor.

Animation

Recorded as an animation over the current context’s runtime (1 second by default): the surface cross-fades, texel by texel, to the image.

Parameters:

rgba_array_or_file_path – Path to an image file, or an RGBA array. 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 surface, so calls can be chained.

Return type:

Surface

See also

color_texture

The attribute this writes, for images already loaded as tensors.

set_location_by_function(function)[source]

Shape the surface by a function of its (u, v) parameters.

The function maps each point of the parameter grid to a 3-D offset from the surface’s location, which is how a sphere, a torus or an arbitrary parametric surface is defined. Animating between two such shapes is what makes a surface morph.

Animation

Recorded as an animation: the grid points travel to their new positions over the current context’s runtime (1 second by default), so calling this on a spawned surface deforms it smoothly. Changing the grid resolution is a different matter – that needs detach_history() first.

Parameters:

function – Callable taking a (u, v) tensor of shape [..., 2] and returning offsets of shape [..., 3]. It must be vectorized – it is called once on the whole grid, not per point.

Returns:

This surface, so calls can be chained.

Return type:

Surface

set_shape_to(other_surface)[source]

Reshape this surface into the shape of another one.

Takes other_surface’s coord_function() and applies it to this surface’s own grid, which is how one parametric shape morphs into another. Any grid axis coarser than other_surface’s is refined to match first, so the target shape is not under-sampled. The other surface is left untouched.

Animation

Recorded as an animation: this surface’s vertices travel to their new positions over the current context’s runtime (1 second by default). Wrap the call in Off() to reshape instantly.

Parameters:

other_surface (Surface) – The surface whose shape to take.

Returns:

This surface, so calls can be chained.

Return type:

Surface

See also

set_location_by_function()

Shape it by a function you write.

become()

Morph into a Mob of any kind, not just another Surface.

property vertices: Tensor

The surface’s vertex positions, shape (*, grid_width * grid_height, 3).

Shorthand for surface.grid.location: the live tensor the renderer tessellates from, laid out row-major over the grid_height x grid_width sample grid. Writing it moves the surface’s vertices, which is the lowest-level way to deform a shape – set_location_by_function() is the usual one. Reach for grid itself when you want the vertices’ colors (grid.color) rather than their positions.

The assigned value must carry the same number of vertices the surface already has: the grid resolution is chosen once, at construction, and nothing here can change it. Positions are absolute, in world units, not offsets from the surface’s location.

Animation

Assignment is recorded, so the vertices travel to their new positions over the current context’s runtime (1 second by default). Wrap the write in Off() to move them instantly.