Skip to content

Math

mutomMath adds geometry, meshes, event containers, images, transforms, and algorithms to Core. The primary namespace is uLib; a handful of lower-level image APIs live in uLib::Abstract.

Geometry and transforms

The library's common geometry values are Eigen-compatible aliases declared in uLib/Math/Geometry.h: HPoint3f, HVector3f, HMatrix3f, HLine3f, HPlane3f, and their d (double precision) variants. Geometry, LinearGeometry, CylindricalGeometry, SphericalGeometry, and ToroidalGeometry describe coordinate systems.

AffineTransform stores a matrix transform as an editable Core object. TRS stores position, rotation, and scale; the TransformModifier algorithm applies transforms to compatible objects. ClosestApproach provides line/line closest-approach calculations.

#include <Math/Dense.h>
#include <Math/Transform.h>

uLib::HLine3f ray;
ray.origin = uLib::HPoint3f{1.0f, 2.0f, 3.0f};
ray.direction = uLib::HVector3f{0.0f, 0.0f, 1.0f};

uLib::TRS pose;
pose.Position.Set({10.0f, 0.0f, 0.0f});
pose.Rotation.Set({0.0f, 0.0f, 0.5f});  // Euler angles, in radians

Scene objects, meshes, and point data

All of these are uLib::Object descendants and can be placed in an ObjectsContext.

Class Use
ContainerBox, Assembly Structural/grouping objects for a scene or data hierarchy.
Cylinder Editable cylindrical domain object.
PointSet Point coordinates with scalar/vector point attributes.
Polydata General polygonal data and attributes.
TriangleMesh, QuadMesh, TessellatedMesh Surface meshes with triangle, quad, or generic tessellated topology.
StructuredData, Structured2DGrid, Structured4DGrid, StructuredGrid Regular-grid data, geometry, and point/cell arrays.
MultiVoxImage Named collection of voxel images.
#include <Core/ObjectsContext.h>
#include <Math/ContainerBox.h>
#include <Math/PointSet.h>

uLib::ObjectsContext context;
auto *group = context.CreateObject<uLib::ContainerBox>("measurements");
auto *points = context.CreateObject<uLib::PointSet>("hits");
group->AddObject(points);

points->Add(0.0, 0.0);
points->Add(1.0, 0.0);
points->Add(1.0, 1.0);

PointSet, meshes, and grids use Core properties where they need to participate in synchronization. Use their public data APIs for bulk coordinates and attributes, then call the documented update method when the specific type requires one.

Images, voxels, and ray tracing

Abstract::VoxImage is the common image-object base. VoxImage<Voxel> is the templated voxel volume, while VoxChannel represents one channel and MultiVoxImage groups several images. Voxel is supplied for the supported voxel payloads.

VoxRaytracer intersects a ray with a voxel image. Its result contains RayData::Element entries, each reporting the traversed voxel and segment information. VoxImageFilter and the VoxFilterAlgorithm* classes provide reusable filtering pipelines: linear, median, threshold, trimmed, bilateral, Gaussian, and custom filters.

#include <Math/VoxImage.h>
#include <Math/VoxRaytracer.h>

uLib::VoxImage<uLib::Voxel> image{uLib::Vector3i{64, 64, 64}};
image.SetSpacing({2.0f, 2.0f, 2.0f});

uLib::VoxRaytracer tracer{image};
const auto path = tracer.TraceLine(ray);
for (const auto &element : path.Data()) {
    // element.vox_id identifies a crossed voxel; element.L is its segment length.
}

The precise image construction calls vary by voxel payload and version. The catalog links to the owning headers; the VoxImageTest and VoxRaytracerTest test targets are also useful executable references.

Voxels on the GPU

Abstract::VoxImage carries a PreferredDevice property -- Host, or Device for the voxel block to be kept in GPU memory so an algorithm or a Python recipe working there reads it without a copy. It needs no CUDA toolkit (device memory is the driver's job) and it never claims a move it did not make: GetDataLocation() says where the voxels are and why.

image.SetPreferredDevice(uLib::MemoryDevice::VRAM);
uLib::DeviceArrayView view;
if (image.GetDeviceArray(view)) {
    // view.data is a device pointer to the first voxel's Value, one every
    // view.stride bytes -- what a kernel, numba or CuPy is pointed at.
}

A scene to open: assets/examples/math/02_images_on_the_gpu.gcx, written by src/Math/examples/02_images_on_the_gpu.cpp. The whole mechanism is in Data on the GPU.

Histograms and event tables

HistogramAxis describes one binned axis and Histogram stores the resulting counts. EventTable is a tabular, serializable event-data object. They are appropriate when data has a natural analytical representation rather than a scene topology.

#include <Math/Histogram.h>

uLib::HistogramAxis axis{"energy", 100, 0.0, 10.0};
uLib::Histogram histogram{{axis}};
histogram.Fill({4.2});
histogram.Fill({4.2}, 0.5);

Scalar, vector, and tensor fields

Math/Field.h defines a field as a domain and a function that gives every point of the domain a value. The domain, FieldDomain<D>, is an axis-aligned box of any dimension D, sampled on Dims nodes per axis (both ends included, first axis fastest). The value type picks the specialization:

Template Value Concrete, registered classes
ScalarField<D> double ScalarField1d, ScalarField2d, ScalarField3d
VectorField<D, N = D> Eigen::Matrix<double, N, 1> VectorField2d, VectorField3d
TensorField<D, R = D, C = R> Eigen::Matrix<double, R, C> TensorField2d, TensorField3d

Every field is also an Algorithm and a uLib::Function (Core/FunctionObject.h), which maps a point to a value. It has these ports:

Port Direction Carries
Function in What the field computes: a uLib::Function (a CallableFunction, a Geant MagneticField or ElectricField, or another field), or a Recipe whose function named by FunctionName (default evaluate) is called with each point.
Points in (stream) The points to map: a stream of FieldSample items, such as a FieldSampleSet, another field, or a buffer. If nothing is connected, the field maps its domain grid.
Results out A FieldSampleSet that each run fills.
Samples out (stream) The field itself, as a push stream of FieldSample (point and value).

What the field computes is resolved in order: the Function port, then a std::function installed with SetFunction(), then multilinear interpolation between the field's own samples. Run() maps every input point, pushes each sample to the output stream, and adds it to Results. When a consumer reads the Samples stream while nothing is running, the field maps the next BatchSize points on demand, because the field is its own StreamActivator. A chain of fields therefore pulls points through one batch at a time. When the function source, the domain, or the points change, the output stream restarts for its next reader.

The domain is three properties (DomainMin, DomainMax, DomainDims). Samples are cached from the function, or set directly with SetSamples() or SetSample(). Bake() replaces the function with its samples. Saving a field writes its domain, its samples, and its settings. The port wires are saved by the scene document. Differential operators use central differences: Gradient and Laplacian for scalar fields; Jacobian, Divergence, and Curl for vector fields; and Trace, Determinant, and Divergence for tensor fields. AbstractField is the dimension-independent interface. It passes coordinates and values as flat std::vector<double>, with tensor components in row-major order.

#include <Math/Field.h>

uLib::VectorField3d wind;
wind.SetDomain(uLib::Vector3d(-1, -1, -1), uLib::Vector3d(1, 1, 1),
               uLib::Vector3i(21, 21, 21));
wind.SetFunction([](const uLib::Vector3d &p) {
  return uLib::Vector3d(-p.y(), p.x(), 0.1 * p.z());
});
double div = wind.Divergence(uLib::Vector3d(0.2, 0.1, 0.0));   // 0.1
uLib::Vector3d curl = wind.Curl(uLib::Vector3d::Zero());         // (0, 0, 2)

// Or drive the field with a Geant field, and map a set of points through it.
uLib::Geant::MagneticField b("dipole");
b.FieldValue.Set(uLib::Vector3d(0, 0, 1.5));
uLib::VectorField3d map;
map.SetFunctionSource(&b);
uLib::FieldSampleSet probes, results;
probes.AddPoint({0.0, 0.0, 100.0});
map.SetPoints(&probes);
map.SetResults(&results);
map.Run();                     // results.At(0).value == {0, 0, 1.5}

Vtk::Field (Vtk/Math/vtkField.h) draws any field. It samples the field into a vtkImageData. The Field.Backend display property selects the representation for the field's rank:

Field Backends
Scalar Surface, Volume, Isosurface, Points
Vector Topology (vtkVectorFieldTopology: critical points and separatrices), Glyphs, Streamlines, Magnitude
Tensor Ellipsoids, Boxes (vtkTensorGlyph), Norm

A scene to open in gcompose: assets/examples/math/01_vector_fields.gcx, written by src/Math/examples/01_vector_fields.cpp. It contains a cellular flow sampled in C++, and a dipole field whose Function port is a Recipe with a probe line wired to its Points port. Select a field and change Display > Field > Backend. Run main to map the probes.

Statistical helpers

The accumulator types in Accumulator.h support running statistics:

Type Purpose
Accumulator_Mean Mean and standard aggregation.
Accumulator_ABTrim Alpha/beta trimmed aggregation.
Accumulator_ABClip Alpha/beta clipping aggregation.

They are deliberately small value types, suitable for numeric algorithms and filters without requiring an ObjectsContext.

Math class catalog

See the complete Math catalog for every installed Math public class, grouped by header family. The streaming guide shows how Math objects participate in Core algorithms and data pipelines.