se3 API
July 8, 2026 · View on GitHub
This document is the source of truth for the extension API.
Types
vec3:STRUCT(x DOUBLE, y DOUBLE, z DOUBLE)quat:STRUCT(w DOUBLE, x DOUBLE, y DOUBLE, z DOUBLE)vec4:STRUCT(w DOUBLE, x DOUBLE, y DOUBLE, z DOUBLE)(same physical layout asquat)W:STRUCT(t vec3, q quat)
vec4 and quat intentionally share the same structure, so DuckDB can accept either where that shape is expected. This is for convenience, not semantic equivalence: use vector functions for vectors and quaternion functions for rotations.
Convention
W(p) = R_q(p + t)
A point is translated by t first, then rotated by quaternion q. Quaternions are assumed to be unit length. Axis-angle constructors normalize the supplied axis; if the axis norm is zero they return NULL.
Behavioral Notes
- Except for
se3_identity(), all functions propagateNULLif any required input or required struct field isNULL. - Vector math functions follow floating-point semantics and do not add explicit zero-denominator guards.
- Zero denominators and non-finite inputs can produce
NaN/Inf. vcos_anglereturns the raw ratiovdot(a,b)/(vnorm(a)*vnorm(b))without clamping.
Functions
vvec(...) -> ...
Constructs a vector.
Signatures:
vvec(x: DOUBLE, y: DOUBLE, z: DOUBLE) -> vec3vvec(w: DOUBLE, x: DOUBLE, y: DOUBLE, z: DOUBLE) -> vec4
Constructs either a vec3 (STRUCT(x DOUBLE, y DOUBLE, z DOUBLE)) or a 4D vector
(STRUCT(w DOUBLE, x DOUBLE, y DOUBLE, z DOUBLE), same layout as quat).
vadd(a, b) -> a
Element-wise addition.
Signatures:
vadd(a: vec3, b: vec3) -> vec3vadd(a: vec4, b: vec4) -> vec4
vsub(a, b) -> a
Element-wise subtraction (a - b).
Signatures:
vsub(a: vec3, b: vec3) -> vec3vsub(a: vec4, b: vec4) -> vec4
vscale(a, s) -> a
Scalar multiplication (a * s).
Signatures:
vscale(a: vec3, s: DOUBLE) -> vec3vscale(a: vec4, s: DOUBLE) -> vec4
vdot(a, b) -> DOUBLE
Dot product.
Signatures:
vdot(a: vec3, b: vec3) -> DOUBLEvdot(a: vec4, b: vec4) -> DOUBLE
vnorm2(a) -> DOUBLE
Squared Euclidean norm.
Signatures:
vnorm2(a: vec3) -> DOUBLEvnorm2(a: vec4) -> DOUBLE
vnorm(a) -> DOUBLE
Euclidean norm.
Signatures:
vnorm(a: vec3) -> DOUBLEvnorm(a: vec4) -> DOUBLE
vnormalize(a) -> a
Unit normalization (a / vnorm(a)).
Signatures:
vnormalize(a: vec3) -> vec3vnormalize(a: vec4) -> vec4
vcross(a: vec3, b: vec3) -> vec3
3D cross product (a × b).
vcos_angle(a, b) -> DOUBLE
Cosine of the angle between vectors:
vdot(a,b) / (vnorm(a) * vnorm(b)).
No clamping is applied.
Signatures:
vcos_angle(a: vec3, b: vec3) -> DOUBLEvcos_angle(a: vec4, b: vec4) -> DOUBLE
vangle(a, b) -> DOUBLE
Angle in radians between vectors.
Signatures:
vangle(a: vec3, b: vec3) -> DOUBLEvangle(a: vec4, b: vec4) -> DOUBLE
vproj(a, b) -> b
Vector projection of a onto b.
Signatures:
vproj(a: vec3, b: vec3) -> vec3vproj(a: vec4, b: vec4) -> vec4
vrej(a, b) -> a
Vector rejection of a from b:
a - vproj(a, b).
Signatures:
vrej(a: vec3, b: vec3) -> vec3vrej(a: vec4, b: vec4) -> vec4
quat_from_axis_angle(axis: vec3, th: DOUBLE) -> quat
Creates a quaternion representing a rotation of th radians about an axis.
The axis is normalized internally. If the axis norm is zero, returns NULL.
Example:
SELECT quat_from_axis_angle(
struct_pack(x:=0.0, y:=0.0, z:=1.0),
pi()/2.0
);
qmul(qA: quat, qB: quat) -> quat
Quaternion multiplication (qA ⊗ qB).
Example:
SELECT qmul(
quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0),
quat_from_axis_angle(struct_pack(x:=0.0, y:=1.0, z:=0.0), pi()/2.0)
);
qconj(q: quat) -> quat
Quaternion conjugate (negates vector part).
Example:
SELECT qconj(struct_pack(w:=1.0, x:=2.0, y:=3.0, z:=4.0));
qnorm2(q: quat) -> DOUBLE
Squared norm of a quaternion. For unit quaternions, this should be 1.0.
Example:
SELECT qnorm2(quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0));
se3_identity() -> W
Returns the identity transform (t = 0, q = (1,0,0,0)).
Example:
SELECT se3_identity();
se3_make(t: vec3, q: quat) -> W
Constructs a transform from a translation and quaternion.
Example:
SELECT se3_make(
struct_pack(x:=1.0, y:=2.0, z:=3.0),
quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0)
);
se3_from_axis_angle(t: vec3, axis: vec3, th: DOUBLE) -> W
Constructs a transform from translation and axis-angle rotation.
The axis is normalized internally. If the axis norm is zero, returns NULL.
Example:
SELECT se3_from_axis_angle(
struct_pack(x:=1.0, y:=0.0, z:=0.0),
struct_pack(x:=0.0, y:=0.0, z:=1.0),
pi()/2.0
);
se3_apply(x, p) -> vec3
Applies a transformation, translation, or rotation to a point.
Signatures:
se3_apply(W: W, p: vec3) -> vec3
Apply full transform.se3_apply(t: vec3, p: vec3) -> vec3
Translate:p + t.se3_apply(q: quat, p: vec3) -> vec3
Rotate:R_q(p).
Example (rotate + translate):
SELECT se3_apply(
se3_from_axis_angle(
struct_pack(x:=1.0, y:=0.0, z:=0.0),
struct_pack(x:=0.0, y:=0.0, z:=1.0),
pi()/2.0
),
struct_pack(x:=1.0, y:=0.0, z:=0.0)
);
SELECT se3_apply(
struct_pack(x:=1.0, y:=2.0, z:=3.0),
struct_pack(x:=4.0, y:=5.0, z:=6.0)
);
SELECT se3_apply(
quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0),
struct_pack(x:=1.0, y:=0.0, z:=0.0)
);
se3_inv(x) -> x
Inverse of a translation, rotation, or transform.
Signatures:
se3_inv(t: vec3) -> vec3
Returns-t.se3_inv(q: quat) -> quat
Returns the conjugateq*(inverse for unit quaternions).se3_inv(W: W) -> W
Returns the inverse transform such thatse3_apply(se3_inv(W), se3_apply(W, p)) = p.
Examples:
SELECT se3_inv(struct_pack(x:=1.0, y:=2.0, z:=3.0));
SELECT se3_inv(quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0));
SELECT se3_inv(se3_from_axis_angle(struct_pack(x:=1.0, y:=0.0, z:=0.0), struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0));
se3_compose(A, B) -> ...
Composition operator. Semantics: apply B first, then A.
Signatures:
se3_compose(W2: W, W1: W) -> W
Composition of transforms.se3_compose(t2: vec3, t1: vec3) -> vec3
Translation addition (t1 + t2).se3_compose(q2: quat, q1: quat) -> quat
Quaternion multiplication (q2 ⊗ q1).se3_compose(q: quat, t: vec3) -> W
Apply translation then rotation (tthenq).se3_compose(t: vec3, q: quat) -> W
Apply rotation then translation (internally converted to ourWconvention).se3_compose(W: W, q: quat) -> W
ApplyqthenW.se3_compose(q: quat, W: W) -> W
ApplyWthenq.se3_compose(W: W, t: vec3) -> W
ApplytthenW.se3_compose(t: vec3, W: W) -> W
ApplyWthent.
Examples:
-- Compose two transforms (apply W1 then W2)
SELECT se3_compose(W2, W1);
-- Translation then rotation
SELECT se3_compose(
quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0),
struct_pack(x:=1.0, y:=0.0, z:=0.0)
);
-- Rotation then translation
SELECT se3_compose(
struct_pack(x:=1.0, y:=0.0, z:=0.0),
quat_from_axis_angle(struct_pack(x:=0.0, y:=0.0, z:=1.0), pi()/2.0)
);