How to define a bridge between two coordinate spaces

You have two coordinate spaces — typically with different coordsys_traits — and you want mapCS() to be able to transform a coordinate from one into the other. The piece you need to supply is a bridge: a type that satisfies coordsys_bridge_traits.

A bridge is small. Most useful bridges are a couple of dozen lines. The work is mostly in choosing the conversion.

The recipe

  1. Declare a struct that inherits both from coordsys<traits_A> and from coordsys<traits_B>.

  2. Publish two type aliases: coordsys_traits_A and coordsys_traits_B.

  3. Provide two static convert() overloads, one in each direction.

  4. Add static_assert(nin::coordsys_bridge_traits<my_bridge>) immediately after the struct, so any mistake is reported at the definition site instead of the first call to mapCS().

The concept requires sizeof(Br) == sizeof(coordsys<traits_A>) + sizeof(coordsys<traits_B>), so the bridge cannot have any data members of its own. The convert() overloads describe a pure mapping of representation between the two spaces — they are typically static constexpr noexcept and stateless. Any per-bridge state (calibration offsets, mounting transforms) belongs in one of the inherited coordsys subobjects' backends.

Skeleton

struct my_bridge : public nin::coordsys<traits_A>,
                   public nin::coordsys<traits_B>
{
    using coordsys_traits_A = traits_A;
    using coordsys_traits_B = traits_B;

    static constexpr
    traits_B::quantity_type convert(traits_A::quantity_type const& q_inA) noexcept
    {
        return { /* … project q_inA into space B … */ };
    }

    static constexpr
    traits_A::quantity_type convert(traits_B::quantity_type const& q_inB) noexcept
    {
        return { /* … project q_inB into space A … */ };
    }
};

static_assert(nin::coordsys_bridge_traits<my_bridge>);

If the static_assert fires, work down the requirement list on coordsys_bridge_traits: a missing alias, a missing overload, the wrong return type, or an accidental data member breaking the size constraint.

Using the bridge

A bridge instance occupies a position in the kinematic tree of both spaces, so it is constructed and placed like any other coordinate system. Pass it to mapCS() as an intermediate argument:

my_bridge bridge;                          // sits at WCS in both spaces

nin::coord_tf tf = nin::mapCS(cs_inA, bridge, cs_inB);
auto q_inB = tf(q_inA);

Bridges chain: mapCS(cs_inA, bridge_AB, bridge_BC, cs_inC) crosses two spaces in one call. The traversal walks each subtree as usual and applies convert() at every crossing.

Worked example to copy from

In-tree: bridge_R2_R3 in src/pos/pos.c++. It connects the R2 and R3 position spaces by dropping (or zero-padding) the z component, with both convert() overloads constexpr noexcept, no extra data members, and a static_assert validating the contract.