geometry

Batched 3-D geometry primitives shared by the animation and render paths.

Every function here is vectorized over leading batch dimensions and operates on torch tensors, because it is called once per frame batch on whole arrays of points rather than per point.

The module covers rotations (building a rotation from an axis and angle, or between two vectors or two bases), projection and intersection (point onto line, segment or plane; line against plane), and basis changes between a Mob’s local frame and world space.

It used to carry polynomial root finding as well – closed-form quadratic and cubic solvers, a companion-matrix eigenvalue solver, and the recursive lower-degree fallbacks around them. All of it was unreachable, and the cubic had never worked: it raised on ordinary inputs, and the shapes it produced could not reconcile with its own fallback. It is gone rather than repaired. The ray tracer does its curve and surface intersection elsewhere, and always did.

These are internal building blocks: user-facing spatial operations live on Mob.

Functions

distance(x, y, memory=None, *args, **kwargs)[source]
get_2d_polygon_mask(polygon_vertices, grid_points, eps=1e-06)[source]

polygon_vertices: Tensor[batch[*], num_vertices, 2] grid_points: Tensor[batch[*], num_grid_points, 2]

get_2d_polygon_mask2(polygon_vertices, grid_points, eps=1e-06)[source]

polygon_vertices: Tensor[batch[*], num_vertices, 2] grid_points: Tensor[batch[*], num_grid_points, 2]

get_orthonormal_vector(*vectors)[source]

A unit vector orthogonal to every vector in vectors (batched over the leading dims). The choice among the valid orthogonal directions is deterministic.

It used to seed the Gram-Schmidt with torch.randn_like, which re-rolls every render. Since this builds the perpendicular basis of surfaces of revolution (e.g. Cylinders, via Cylinder._move_between_points), a random seed spun those meshes to a random angle about their axis on each render. The silhouette is rotation-symmetric so it looked stable, but which tessellated facet faced the light changed – making per-facet shading and ray-traced shadows flicker randomly between renders (most visible on thin tubes such as neural-net synapses). Seeding from the fixed standard basis instead keeps the orientation reproducible.

get_rotation_around_axis(num_degrees, axis, dim=0)[source]

Build the rotation of num_degrees about axis, right-handed.

Every call site applies the result to row vectors – basis @ R, (location - about) @ R – so the matrix returned is the transpose of the Rodrigues form written below, and v @ R turns v counter- clockwise seen from the tip of axis.

That transpose is the whole of Algan’s rotation handedness, and it is tied to OUTWARD being +z: with a right-handed world basis, rotate(90, OUTWARD) has to take RIGHT to UP, and a row-vector product against an untransposed Rodrigues matrix takes it to DOWN instead. get_rotation_between_3d_vectors() pairs with this – it returns cross(v1, v2) unnegated for the same reason.

get_rotation_between_3d_vectors(vector1, vector2, dim=-1)[source]
get_rotation_between_bases(basis1, basis2)[source]

Return the right-side transform taking row basis1 to row basis2.

That is, basis1 @ get_rotation_between_bases(basis1, basis2) == basis2.

This has to be the exact inverse of basis1, not the transpose of its normalized form. Mob.basis’s setter turns an absolute basis into a relative change through this function and applies it with a right-multiply (so that concurrent writers compose), which means any error here is re-applied on top of the value it was measured from. On a sheared basis – where the rows are not orthogonal – the normalized transpose left a residual that the round-trip amplified roughly threefold, so float-noise shear grew into total collapse over a couple of dozen assignments (each detach_history clone performs one). The two forms agree exactly for an orthogonal basis.

get_rotation_between_orthonormal_bases(basis1, basis2)[source]
intersect_line_with_plane(line_direction, plane_point, plane_normal, line_point=0, dim=-1, memory=None)[source]
intersect_line_with_plane_colinear(line_direction, plane_point, plane_co1, plane_co2, line_point=0)[source]
invert_row_basis(basis)[source]

Invert a batch of row-major 3x3 bases, shape (*, 3, 3).

Built from the adjugate (three cross products and a determinant) rather than torch.linalg.inv: at this size it is cheaper, it batches without a LAPACK call, and it lets a degenerate basis be handled rather than raised on.

A basis whose rows are coplanar – a Mob scaled flat along an axis, say – has no inverse at all. Those return the identity, i.e. “no change”, so that a basis assignment leaves such a Mob as it is instead of turning its geometry into inf or NaN.

map_global_to_local_coords(location, basis, global_coords)[source]
map_local_to_global_coords(location, basis, local_coords)[source]
normalize(x, dim=-1, p=2, memory=None)[source]
project_onto_basis(vector, basis)[source]
project_point_onto_line(point, line_direction, line_start=0, dim=-1)[source]

Projects point x to the closest point on a line defined by a starting point and a direction

project_point_onto_line_segment(point, line_start, line_end, dim=-1, memory=None)[source]

Projects point x to the closest point on a line segment defined by its start and end points.

project_point_onto_plane(point, plane_normal, plane_point=0, dim=-1)[source]

Projects point x onto a plane defined by a point and normal direction

rotate_basis_to_direction(basis, direction, axis=-1, dim=-1)[source]
rotate_vector_around_axis(vector, num_degrees, axis, dim=0)[source]