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
- 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, viaCylinder._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_degreesaboutaxis, 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, andv @ Rturnsvcounter- clockwise seen from the tip ofaxis.That transpose is the whole of Algan’s rotation handedness, and it is tied to
OUTWARDbeing+z: with a right-handed world basis,rotate(90, OUTWARD)has to takeRIGHTtoUP, and a row-vector product against an untransposed Rodrigues matrix takes it toDOWNinstead.get_rotation_between_3d_vectors()pairs with this – it returnscross(v1, v2)unnegated for the same reason.
- 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 (eachdetach_historyclone performs one). The two forms agree exactly for an orthogonal basis.
- 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.
- 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.