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
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 letsget_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)fromsurface_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]– minusW-1triangles 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 callingget_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 stockSurface.get_render_primitives, has nocolor_texture, hasignore_normalsFalse, and has identical grid dimensions andgrid.locationshape.
- 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_uwhen columnW-1coincides with column 0 (a surface of revolution’s u-seam – aSphere, aCylinder, aCone),closed_vwhen rowH-1coincides with row 0 (aToruscloses on both).This is
surface_weld_flags()’swrap_xtest 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 onALGAN_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_xwhen columnW-1duplicates column 0 (a closed surface of revolution’s u-seam),pole_lo/pole_hiwhen every column of row 0 / rowH-1coincides (a collapsed pole, e.g. aSphere’s or aCone’s tip). All three areFalseunlessALGAN_WELD_SURFACE_SEAMSis 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 asu * (W - 1)and clamps, so texel 0 sits atu == 0and texelW-1atu == 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 byW / (W - 1)and cut by a hard seam wherever column 0 disagrees with columnW-1.Appending a copy of column 0 as column
Wfixes both at once. Texelithen sits atu == i / W, so every column spans the same1 / Wof the way around, and the wrap cell interpolates columnW-1into column 0 exactly as an interior cell interpolates its neighbours.- Parameters:
texture – A texture map
[T, W, H, C](u alongW, v alongH), or None.closed_axes –
(closed_u, closed_v)fromsurface_closed_axes().
- Returns:
The padded map, or
textureunchanged when neither axis closes.- Return type:
torch.Tensor