geomancy: Vectors and matrix manipulation

[ bsd3, graphics, library ] [ Propose Tags ] [ Report a vulnerability ]
Versions [RSS] 0.2.2.3, 0.2.2.4, 0.2.3.0, 0.2.4.0, 0.2.4.1, 0.2.4.2, 0.2.5.0, 0.2.6.0, 0.3.0.0
Change log ChangeLog.md
Dependencies base (>=4.7 && <5), containers, deepseq, gl-block, mono-traversable, ptrdiff, simple-affine-space, webcolor-labels [details]
License BSD-3-Clause
Copyright 2021-2025 IC Rainbow
Author IC Rainbow
Maintainer aenor.realm@gmail.com
Category Graphics
Source repo head: git clone https://gitlab.com/dpwiz/geomancy
Uploaded by AlexanderBondarenko at 2025-11-06T23:21:17Z
Distributions NixOS:0.2.6.0
Reverse Dependencies 8 direct, 0 indirect [details]
Downloads 1040 total (17 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2025-11-06 [all 1 reports]

Readme for geomancy-0.3.0.0

[back to package description]

Geomancy

Linear is nice, but slow. Those are naughty, but a bit faster.

  • All data types are monomorphic, unpacked and specialized.
  • Mat4 and Vec4 are ByteArray#.
  • Mat4xMat4 and Mat4xVec4 is done with SIMD.

The Numbers

Storing a list of 1000 transformations (e.g. rendering instance data):

benchmarking 4x4 poke/1000/geomancy
time                 11.76 μs   (11.66 μs .. 11.92 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)
mean                 11.75 μs   (11.69 μs .. 11.86 μs)
std dev              283.4 ns   (199.0 ns .. 399.0 ns)
variance introduced by outliers: 26% (moderately inflated)

If you're willing to adjust your shaders, it's only 2.4 times slower.

benchmarking 4x4 poke/1000/linear
time                 28.29 μs   (28.21 μs .. 28.38 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 28.40 μs   (28.34 μs .. 28.50 μs)
std dev              267.4 ns   (145.5 ns .. 419.9 ns)

Keeping your shaders straight make the affair 6.1x slower.

benchmarking 4x4 poke/1000/linear/T
time                 73.70 μs   (73.06 μs .. 74.49 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)
mean                 72.77 μs   (72.50 μs .. 73.22 μs)
std dev              1.129 μs   (793.5 ns .. 1.580 μs)

Folding down a gloss-style scene graph is where it is all started:

benchmarking 4x4 multiply/1000/geomancy
time                 20.79 μs   (20.77 μs .. 20.83 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 20.80 μs   (20.78 μs .. 20.83 μs)
std dev              76.71 ns   (60.01 ns .. 99.06 ns)

benchmarking 4x4 multiply/1000/linear
time                 173.9 μs   (173.6 μs .. 174.4 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 173.5 μs   (173.2 μs .. 174.4 μs)
std dev              1.733 μs   (727.8 ns .. 3.422 μs)

Add that time to the poking that'll follow.

Sure, it is in the lower microseconds range, but this budget can be used elsewhere.

Conventions

Matrix layout

Transforms produced, composed, and applied to mimic the GLSL order (col-major):

  • vec4 vPosOut = P * V * M * vPosIn;
  • vPosOut = (p <> v <> m) !* vPosIn

This way you don't have to transpose your transforms or fiddle with layout annotations.

Projections / Views

Geomancy.Vulkan.Projection is using the "reverse-depth" trick that remaps the vulkan default [0; 1] range to [1; 0]. This grants extra precision with one less parameter to specify (you only need "near" now), but makes handedness reasoning tricky. The default depth range the coordinate is left-handed (+X right, +Y down, +Z forward). But after reversing the depth it has to be paired with a right-handed view function like Geomancy.Vulkan.View.lookAtRH.

The intended up vector is still vec3 0 (-1) 0 -- +Y down. Silly as it sounds, this matches the XY plane of the window with XY plane in front of a "first person" camera.

Rotations

Axis rotations (using rotateQ) will appear clockwise when looking along the axis.

Angle rotations follow Tait-Bryan angles (heading/elevation/bank or yaw/pitch/roll) in the y-x-z frame.

  • rotateZ (time * rate) will follow the clock hands in 2D scenes and roll in 3D.
  • rotateX will follow the sun from sunrise to sunset, increasing elevation / pitching UP.
  • rotateY will turn you right, increasing yaw/heading eastwards.

Using Geomancy.Quaternion.intrinsic roll pitch yaw will make a rotation from the 3 angles in one go. You can use it to rotate a point directly (e.g vec3 0 0 1 to get a direction vector from Quaternion) or commit to a matrix using Transform.rotateQ.

yaw-pitch-roll

You're of course free to define your own transforms, just copy the modules and tune to your liking. Just make sure that you use matching row/column constructors and the math layer will do the rest, fast.

GLSL-like functions

To further facilitate conversion between the host and shader code Geomancy.Gl.Funs provides common functions like glFract and smoothstep.