# ----------------------------------------------------------------------------# SymForce - Copyright 2022, Skydio, Inc.# This source code is under the Apache 2.0 license found in the LICENSE file.# ----------------------------------------------------------------------------from__future__importannotationsimportsymforce.internal.symbolicassffromsymforceimporttypingasTfromsymforce.ops.interfacesimportLieGroupfrom.matriximportMatrixfrom.matriximportMatrix24from.matriximportMatrix42from.matriximportVector3from.rot3importRot3
[docs]classUnit3(LieGroup):""" Direction in R^3, represented as a :class:`Rot3 <symforce.geo.rot3.Rot3>` that transforms [0, 0, 1] to the desired direction. The storage is therefore a quaternion and the tangent space is 2 dimensional. Most operations are implemented using operations from :class:`Rot3 <symforce.geo.rot3.Rot3>`. Note: an alternative implementation could directly store a unit vector and define its boxplus manifold as described in Appendix B.2 of [Hertzberg 2013]. This can be done by finding the Householder reflector of x and use it to transform the exponential map of delta, which is a small perturbation in the tangent space (R^2). Namely:: x.retract(delta) = x [+] delta = Rx * Exp(delta), where Exp(delta) = [sinc(||delta||) * delta, cos(||delta||)], and Rx = (I - 2 vv^T / (v^Tv))X, v = x - e_z != 0, X is a matrix negating 2nd vector component = I , x = e_z [Hertzberg 2013] Integrating Generic Sensor Fusion Algorithms with Sound State Representations through Encapsulation of Manifolds """E_Z=Vector3.unit_z()def__init__(self,rot3:T.Optional[Rot3]=None)->None:""" Construct from a :class:`Rot3 <symforce.geo.rot3.Rot3>`, or identity if none provided. """self.rot3=rot3ifrot3isnotNoneelseRot3.identity()assertisinstance(self.rot3,Rot3)# -------------------------------------------------------------------------# Storage concept - see symforce.ops.storage_ops# -------------------------------------------------------------------------def__repr__(self)->str:xyz=self.to_unit_vector()return"<Unit3 xyz=[{}, {}, {}]>".format(repr(xyz[0]),repr(xyz[1]),repr(xyz[2]))
# -------------------------------------------------------------------------# Group concept - see symforce.ops.group_ops# -------------------------------------------------------------------------
[docs]@classmethoddeffrom_vector(cls,a:Vector3,epsilon:T.Scalar=sf.epsilon())->Unit3:""" Return a :class:`Unit3` that points along the direction of vector ``a`` ``a`` does not have to be a unit vector. """u=a.normalized(epsilon=epsilon)returncls(Rot3.from_two_unit_vectors(cls.E_Z,u,epsilon=epsilon))
[docs]@classmethoddefrandom(cls,epsilon:T.Scalar=sf.epsilon())->Unit3:""" Generate a random element of :class:`Unit3`, by generating a random rotation first and then rotating ``e_z`` to get a random direction. """returncls.from_vector(Rot3.random()*cls.E_Z,epsilon=epsilon)