R3::basic_rotation

namespace nin::R3 {
    template <std::floating_point T>
    class basic_rotation;

    using rotation = basic_rotation<double>;
}

A 3D rotation transformation. Parameterised as an axis-angle pair (with the angle including any accumulated revolutions), it is composable, invertible, and callable on R3::basic_position_qty, R3::basic_orientation_qty, and on plain fixed-size containers of three position coordinates.

Rotations differ from orientations: * Conceptually, they are transformations as opposed to coordinates in SO(3). * Numerically, they retain the number of full revolutions taken to reach the angle. See basic_orientation_qty for the coordinate quantity counterpart.

The type alias rotation is provided for basic_rotation<double>.

Nested types

Type Definition

value_type

T

orientation_qty

basic_orientation_qty<T>

Member functions

(Constructor)

basic_rotation()

(1)

basic_rotation(copy)

(2)

basic_rotation(matrix, order, revolutions = 0)

(3)

basic_rotation(TA const& axis, units::angle_v<T> angle)

(4)

basic_rotation(reference, target, revolutions = 0)

(5)

  • (1) The default constructor is the identity rotation.

  • (2) Copy constructor from a different floating-point precision.

  • (3) Constructs a rotation from a 3×3 rotation matrix in the specified matrix ordering (type matrix_ordering), plus an optional integer number of full revolutions.

  • (4) Constructor from an axis-angle pair. Throws if the axis is zero and the angle is non-zero simultaneously.

  • (5) Constructs the rotation that takes reference to target, both of type R3::orientation_qty, plus an optional number of full revolutions.

Example
import ninbot;
using namespace nin;
int main() {
    R3::rotation r0;                                            // identity
    R3::rotation r1 { std::array<double,3>{0, 0, 1}, 90_deg };  // 90° about Z
}

invert

inverts the rotation in place

basic_rotation & invert()

(1)

Negates the angle (the axis is left unchanged). Returns a reference to *this to allow chaining.

axis, set_axis

access the rotation axis

std::array<T,3> const& axis() const

(1)

template <fixed_container<T,3> TM> void set_axis(TM const&)

(2)

  • (1) Returns a const reference to the axis. The axis is not necessarily unit-length; it is normalised internally when the rotation is applied.

  • (2) Replaces the axis, re-validating the rotation. Throws std::domain_error if the new axis is zero and the angle is non-zero.

Direct mutable access to axis() is intentionally not provided: the rotation’s invariants cannot be checked after an unsupervised mutation.

angle

access the rotation angle

units::angle_v<T> & angle()

(2)

  • (1) Returns the angle (including any accumulated revolutions).

  • (2) Returns a mutable reference for in-place modification.

operator()

applies the rotation

template <fixed_container<T,3> TM> TM operator () (TM const& vec)

(1)

orientation operator () (orientation const& orient = {})

(2)

basic_position_qty<T> operator () (basic_position_qty<T> const&)

(3)

  • (1) Rotates a raw 3-component vector and returns a value of the same container type.

  • (2) Rotates an orientation quantity.

  • (3) Rotates a point quantity.

Example
import ninbot;
using namespace nin;
int main() {
    R3::rotation        r { std::array<double,3>{0, 0, 1}, 90_deg };
    R3::point_qty       p { 1_m, 0_m, 0_m };
    R3::point_qty       q = r(p);     // q ≈ (0, 1, 0) (m)
    R3::orientation_qty o = r();      // identity rotated by 90° about Z
}

Non-member functions

inv

returns the inverse rotation

basic_rotation<T> inv(basic_rotation<T> R)

(1)

Returns a rotation about the same axis with the angle negated.

compose

composes two rotations

basic_rotation<T> compose(basic_rotation<T> const& left_evaluates_after, basic_rotation<T> const& right_evaluates_first, int revolutions = 0)

(1)

Returns a rotation equivalent to applying right_evaluates_first and then left_evaluates_after. The first argument is the rotation that evaluates after the second.

The number of full revolutions of each input is discarded during composition; pass revolutions to set the revolution count of the result explicitly.
Example
import ninbot;
using namespace nin;
int main() {
    R3::rotation rx { std::array<double,3>{1, 0, 0}, 90_deg };
    R3::rotation rz { std::array<double,3>{0, 0, 1}, 90_deg };
    R3::rotation r  = compose(rz, rx);     // rx first, then rz
}

std::formatter<>

STL overload for the std::format and std::print families.

std::format("{:<format-spec>}", r)

(1)

Adds a specialization of std::formatter that formats the rotation in axis-angle form, displaying the three axis components followed by the angle. By default the output is wrapped in parentheses and reads "(ax, ay, az; θ rad)".

The <format-spec> syntax is

[<float-spec>] [ ":" <repr> [ "|" <axis-sep> [ "|" <axis-angle-sep> ] ] ]

with the following rules:

  • <float-spec> — anything before the first : is forwarded verbatim to the STL formatter for floating points and applied to every value (the three axis components and the angle).

  • <repr> — representation character:

    • x (default) — angle in radians; unit label ` rad` is appended.

    • X — angle in degrees; unit label ° is appended (no leading space).

  • Parentheses — when the format-spec contains no | separator, the output is wrapped in parentheses.

  • Separators — the text after the first | is placed between consecutive axis components. If a second | is present, the text after it becomes the separator between the axis triple and the angle (the unit label still follows the angle). When only one separator is given, it is used for both positions.

  • Defaults — when no separators are given, <axis-sep> defaults to ", " and <axis-angle-sep> to "; ".

Example
import ninbot;
#include <print>
int main() {
    nin::R3::rotation rz90 { std::array<double,3>{0, 0, 1}, 90_deg };
    nin::R3::rotation ident;
    std::println("{}",               ident);   // "(1, 0, 0; 0 rad)"
    std::println("{:.1f}",           rz90);    // "(0.0, 0.0, 1.0; 1.6 rad)"
    std::println("{:.6f:x}",         rz90);    // "(0.000000, 0.000000, 1.000000; 1.570796 rad)"
    std::println("{:.1f:X}",         rz90);    // "(0.0, 0.0, 1.0; 90.0°)"
    std::println("{:.2f:x|, | -- }", rz90);    // "0.00, 0.00, 1.00 -- 1.57 rad"
    std::println("{:.1f:x| / }",     rz90);    // "0.0 / 0.0 / 1.0 / 1.6 rad"
}