surface

Parametric surfaces – the curved 3-D geometry primitive.

A Surface samples a uniform grid of (u, v) points from the unit square, maps each through a coordinate function into 3-D space, and tiles the result with triangles. Every curved shape in Algan is one of these: a sphere is a Surface whose coordinate function is a sphere.

The grid resolution is chosen for you, one axis at a time: geometry_tolerance bounds how far the PN-triangle approximation may stray from the analytic surface, and the search sizes each parameter axis against its own contribution to that error, so an axis the surface is straight along (a cylinder’s length, a cone’s slant) costs the minimum however curved the other axis is. The search is cached per subclass and geometry configuration. render_tolerance_pixels then bounds the on-screen error when each PN triangle is diced into flat render triangles – per triangle, per frame, so detail is spent only where the surface is near the camera. It is an absolute pixel count at the renderer’s reference frame height, scaled down in proportion on shorter frames.

Surfaces carry the texture-map API: color_texture plus per-texel reflectivity, roughness, refractive-index, normal and glow maps, all sampled in the ray-tracing kernel. Surface.set_color_by_function() colors by (u, v), Surface.set_color_by_image() paints an image across the domain, and Surface.set_shape_to() morphs one surface into another.

See Images and Textures.

Classes

Surface

A smooth 2-D surface, embedded in 3-D space, A.K.A a manifold.

Functions

compute_grid_vertex_normals(grid)[source]

Area-weighted vertex normals for a surface grid [..., W, H, 3], with closed-seam and pole merging. All computations broadcast over any leading dims (time, or a stack of same-shaped surfaces), which lets get_render_primitives_batched() run this once for many surfaces.

get_grid_to_triangle_indices(grid_width, grid_height, device, weld=(False, False, False))[source]

Internal: get the vertex indices that split a grid into triangles.

Each grid cell becomes two triangles. The result is cached per grid shape and device, since surfaces of the same resolution all share it.

Parameters:
  • grid_width (int) – Number of grid points across.

  • grid_height (int) – Number of grid points down.

  • device – Torch device the indices should live on.

  • weld(wrap_x, pole_lo, pole_hi) from surface_weld_flags(). Each welds one boundary of a closed grid into shared vertices instead of coincident duplicates (DESIGN_mesh_identity.md ss3.1). Defaults to no welding, which is the shipped topology exactly.

Returns:

Vertex indices into the flattened grid, shape [(W-1) * (H-1) * 2, 3] – minus W-1 triangles per welded pole.

Return type:

torch.Tensor

get_render_primitives_batched(surfaces)[source]

Build render primitives for N surfaces that share a grid shape and frame count, running the geometry pipeline (normal computation and triangle-vertex gathers) once on a [N, T, W, H, 3] stack instead of once per surface. Numerically identical to calling get_render_primitives() on each surface (all ops are elementwise or reduce over non-batch dims), but with N times fewer Python/torch dispatches. Callers must ensure every surface uses the stock Surface.get_render_primitives, has no color_texture, has ignore_normals False, and has identical grid dimensions and grid.location shape.

grid_to_triangle_vertices(grid, weld=(False, False, False))[source]

Internal: gather a per-grid-point quantity into per-triangle-vertex form.

Turns values laid out on the surface grid into the flat triangle-vertex layout the renderer consumes. Works for positions, normals and colors alike.

Parameters:

grid – Grid-shaped values, [..., W, H, C]. A 1-D input is returned unchanged.

Returns:

The same values gathered per triangle vertex.

Return type:

torch.Tensor

surface_closed_axes(grid)[source]

Which parameter axes of a materialized grid close on themselves.

Returns (closed_u, closed_v): closed_u when column W-1 coincides with column 0 (a surface of revolution’s u-seam – a Sphere, a Cylinder, a Cone), closed_v when row H-1 coincides with row 0 (a Torus closes on both).

This is surface_weld_flags()’s wrap_x test on both axes, but it answers a different question – how a texture must be sampled, not how triangles are indexed – so it is deliberately not gated on ALGAN_WELD_SURFACE_SEAMS: a closed surface’s texture has to wrap whether or not its seam vertices are shared.

Returns Python bools, so this synchronises with the device once per textured primitive build. The result changes a tensor’s shape (see wrap_pad_texture()), which a device value cannot do.

surface_weld_flags(grid)[source]

Which of a grid’s boundaries can be welded: (wrap_x, pole_lo, pole_hi).

wrap_x when column W-1 duplicates column 0 (a closed surface of revolution’s u-seam), pole_lo/pole_hi when every column of row 0 / row H-1 coincides (a collapsed pole, e.g. a Sphere’s or a Cone’s tip). All three are False unless ALGAN_WELD_SURFACE_SEAMS is on.

Returns Python bools, so this synchronises with the device once per primitive build. That is deliberate: the result selects a cached index tensor, which cannot be a device value.

wrap_pad_texture(texture, closed_axes)[source]

Repeat a texture’s first row/column at its far edge on closed axes.

The renderer addresses a [W, H] map as u * (W - 1) and clamps, so texel 0 sits at u == 0 and texel W-1 at u == 1. On a surface whose u axis closes those are the same place, and the sampler has no way to blend the last texel back into the first: the map lands on the surface stretched by W / (W - 1) and cut by a hard seam wherever column 0 disagrees with column W-1.

Appending a copy of column 0 as column W fixes both at once. Texel i then sits at u == i / W, so every column spans the same 1 / W of the way around, and the wrap cell interpolates column W-1 into column 0 exactly as an interior cell interpolates its neighbours.

Parameters:
  • texture – A texture map [T, W, H, C] (u along W, v along H), or None.

  • closed_axes(closed_u, closed_v) from surface_closed_axes().

Returns:

The padded map, or texture unchanged when neither axis closes.

Return type:

torch.Tensor