diff --git a/docs/pyttadoc/ambisonics.rst b/docs/pyttadoc/ambisonics.rst new file mode 100644 index 0000000..4585292 --- /dev/null +++ b/docs/pyttadoc/ambisonics.rst @@ -0,0 +1,7 @@ +.. currentmodule:: pytta + +Ambisonics +========== + +.. automodule:: pytta.ambisonics + :members: diff --git a/docs/pyttadoc/index.rst b/docs/pyttadoc/index.rst index ca3da8d..ba9cc57 100644 --- a/docs/pyttadoc/index.rst +++ b/docs/pyttadoc/index.rst @@ -57,6 +57,12 @@ Modules: * pytta.iso3741: calculations according to the standard; + * pytta.vbap: + vector base amplitude panning for 2D and 3D loudspeaker layouts; + + * pytta.ambisonics: + higher-order Ambisonics encoding, decoding and rotation; + .. toctree:: classes/index utils/index @@ -64,4 +70,6 @@ Modules: functions generate rooms + vbap + ambisonics properties diff --git a/docs/pyttadoc/vbap.rst b/docs/pyttadoc/vbap.rst new file mode 100644 index 0000000..3d1b0d0 --- /dev/null +++ b/docs/pyttadoc/vbap.rst @@ -0,0 +1,7 @@ +.. currentmodule:: pytta + +VBAP +==== + +.. automodule:: pytta.vbap + :members: diff --git a/pytta/__init__.py b/pytta/__init__.py index d8740aa..7e39efd 100644 --- a/pytta/__init__.py +++ b/pytta/__init__.py @@ -72,6 +72,12 @@ * pytta.iso3741: calculations according to the standard; + * pytta.vbap: + vector base amplitude panning for 2D and 3D loudspeaker layouts; + + * pytta.ambisonics: + higher-order Ambisonics encoding, decoding and rotation; + You can find usage examples in the toolbox's examples folder. @@ -108,6 +114,8 @@ from . import utils from . import rooms from . import iso3741 +from . import vbap +from . import ambisonics from .functions import read_wav, write_wav, merge, split, \ list_devices, print_devices, get_device_from_user,\ @@ -130,6 +138,8 @@ 'generate', 'utils', 'iso3741', + 'vbap', + 'ambisonics', 'rooms', # Functions diff --git a/pytta/_tdesigns.py b/pytta/_tdesigns.py new file mode 100644 index 0000000..578a145 --- /dev/null +++ b/pytta/_tdesigns.py @@ -0,0 +1,1903 @@ +""" +Spherical t-designs of degree 1 to 21 on the unit sphere, as unit vectors. + +A t-design of degree t is a set of points whose mean of any polynomial of +degree t or less on the sphere equals the mean over the sphere. The designs +are those of Hardin and Sloane, as stored in t_designs_1_21.mat of the +Spherical-Harmonic-Transform library of Archontis Politis +(https://github.com/polarch/Spherical-Harmonic-Transform), BSD 3-Clause +licence, reproduced in pytta/ambisonics.py. + +Hardin, R. H., & Sloane, N. J. A. (1996). McLaren's improved snub cube and +other new spherical designs in three dimensions. Discrete & Computational +Geometry, 15(4), 429-441. +""" + +T_DESIGNS = { + 1: [ + (1.0, 0.0, 0.0), + (-1.0, 0.0, 0.0), + ], + 2: [ + (0.5773502691896257, 0.5773502691896257, 0.5773502691896257), + (0.5773502691896257, -0.5773502691896257, -0.5773502691896257), + (-0.5773502691896257, 0.5773502691896257, -0.5773502691896257), + (-0.5773502691896257, -0.5773502691896257, 0.5773502691896257), + ], + 3: [ + (1.0, 0.0, 0.0), + (-1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (0.0, -1.0, 0.0), + (0.0, 0.0, 1.0), + (0.0, 0.0, -1.0), + ], + 4: [ + (0.850650808352, 0.0, -0.525731112119), + (0.525731112119, -0.850650808352, 0.0), + (0.0, -0.525731112119, 0.850650808352), + (0.850650808352, 0.0, 0.525731112119), + (-0.525731112119, -0.850650808352, 0.0), + (0.0, 0.525731112119, -0.850650808352), + (-0.850650808352, 0.0, -0.525731112119), + (-0.525731112119, 0.850650808352, 0.0), + (0.0, 0.525731112119, 0.850650808352), + (-0.850650808352, 0.0, 0.525731112119), + (0.525731112119, 0.850650808352, 0.0), + (0.0, -0.525731112119, -0.850650808352), + ], + 5: [ + (0.850650808352, 0.0, -0.525731112119), + (0.525731112119, -0.850650808352, 0.0), + (0.0, -0.525731112119, 0.850650808352), + (0.850650808352, 0.0, 0.525731112119), + (-0.525731112119, -0.850650808352, 0.0), + (0.0, 0.525731112119, -0.850650808352), + (-0.850650808352, 0.0, -0.525731112119), + (-0.525731112119, 0.850650808352, 0.0), + (0.0, 0.525731112119, 0.850650808352), + (-0.850650808352, 0.0, 0.525731112119), + (0.525731112119, 0.850650808352, 0.0), + (0.0, -0.525731112119, -0.850650808352), + ], + 6: [ + (0.8662468181078206, 0.42251865376111153, 0.26663540151670473), + (0.8662468181078206, -0.42251865376111153, -0.26663540151670473), + (0.8662468181078206, 0.26663540151670473, -0.42251865376111153), + (0.8662468181078206, -0.26663540151670473, 0.42251865376111153), + (-0.8662468181078206, 0.42251865376111153, -0.26663540151670473), + (-0.8662468181078206, -0.42251865376111153, 0.26663540151670473), + (-0.8662468181078206, 0.26663540151670473, 0.42251865376111153), + (-0.8662468181078206, -0.26663540151670473, -0.42251865376111153), + (0.26663540151670473, 0.8662468181078206, 0.42251865376111153), + (-0.26663540151670473, 0.8662468181078206, -0.42251865376111153), + (-0.42251865376111153, 0.8662468181078206, 0.26663540151670473), + (0.42251865376111153, 0.8662468181078206, -0.26663540151670473), + (-0.26663540151670473, -0.8662468181078206, 0.42251865376111153), + (0.26663540151670473, -0.8662468181078206, -0.42251865376111153), + (0.42251865376111153, -0.8662468181078206, 0.26663540151670473), + (-0.42251865376111153, -0.8662468181078206, -0.26663540151670473), + (0.42251865376111153, 0.26663540151670473, 0.8662468181078206), + (-0.42251865376111153, -0.26663540151670473, 0.8662468181078206), + (0.26663540151670473, -0.42251865376111153, 0.8662468181078206), + (-0.26663540151670473, 0.42251865376111153, 0.8662468181078206), + (0.42251865376111153, -0.26663540151670473, -0.8662468181078206), + (-0.42251865376111153, 0.26663540151670473, -0.8662468181078206), + (0.26663540151670473, 0.42251865376111153, -0.8662468181078206), + (-0.26663540151670473, -0.42251865376111153, -0.8662468181078206), + ], + 7: [ + (0.8662468181078206, 0.42251865376111153, 0.26663540151670473), + (0.8662468181078206, -0.42251865376111153, -0.26663540151670473), + (0.8662468181078206, 0.26663540151670473, -0.42251865376111153), + (0.8662468181078206, -0.26663540151670473, 0.42251865376111153), + (-0.8662468181078206, 0.42251865376111153, -0.26663540151670473), + (-0.8662468181078206, -0.42251865376111153, 0.26663540151670473), + (-0.8662468181078206, 0.26663540151670473, 0.42251865376111153), + (-0.8662468181078206, -0.26663540151670473, -0.42251865376111153), + (0.26663540151670473, 0.8662468181078206, 0.42251865376111153), + (-0.26663540151670473, 0.8662468181078206, -0.42251865376111153), + (-0.42251865376111153, 0.8662468181078206, 0.26663540151670473), + (0.42251865376111153, 0.8662468181078206, -0.26663540151670473), + (-0.26663540151670473, -0.8662468181078206, 0.42251865376111153), + (0.26663540151670473, -0.8662468181078206, -0.42251865376111153), + (0.42251865376111153, -0.8662468181078206, 0.26663540151670473), + (-0.42251865376111153, -0.8662468181078206, -0.26663540151670473), + (0.42251865376111153, 0.26663540151670473, 0.8662468181078206), + (-0.42251865376111153, -0.26663540151670473, 0.8662468181078206), + (0.26663540151670473, -0.42251865376111153, 0.8662468181078206), + (-0.26663540151670473, 0.42251865376111153, 0.8662468181078206), + (0.42251865376111153, -0.26663540151670473, -0.8662468181078206), + (-0.42251865376111153, 0.26663540151670473, -0.8662468181078206), + (0.26663540151670473, 0.42251865376111153, -0.8662468181078206), + (-0.26663540151670473, -0.42251865376111153, -0.8662468181078206), + ], + 8: [ + (0.507475446410817, -0.306200013239571, 0.805425492011663), + (-0.306200013239569, 0.805425492011663, 0.507475446410817), + (-0.507475446410817, 0.30620001323957, 0.805425492011663), + (0.805425492011663, 0.507475446410817, -0.306200013239569), + (0.306200013239569, 0.805425492011664, -0.507475446410817), + (0.805425492011663, -0.507475446410817, 0.306200013239569), + (0.306200013239569, -0.805425492011663, 0.507475446410816), + (-0.805425492011663, -0.507475446410817, -0.306200013239569), + (-0.30620001323957, -0.805425492011664, -0.507475446410816), + (-0.805425492011663, 0.507475446410818, 0.306200013239569), + (0.507475446410817, 0.30620001323957, -0.805425492011663), + (-0.507475446410817, -0.30620001323957, -0.805425492011663), + (0.626363670265271, -0.243527775409194, -0.74051520928072), + (-0.243527775409195, -0.74051520928072, 0.626363670265271), + (-0.626363670265271, 0.243527775409194, -0.74051520928072), + (-0.74051520928072, 0.62636367026527, -0.243527775409195), + (0.243527775409195, -0.740515209280719, -0.626363670265271), + (-0.74051520928072, -0.62636367026527, 0.243527775409195), + (0.243527775409195, 0.740515209280719, 0.626363670265271), + (0.74051520928072, -0.62636367026527, -0.243527775409195), + (-0.243527775409195, 0.74051520928072, -0.626363670265271), + (0.74051520928072, 0.62636367026527, 0.243527775409195), + (0.626363670265271, 0.243527775409194, 0.74051520928072), + (-0.626363670265271, -0.243527775409194, 0.74051520928072), + (-0.286248723426035, 0.957120327092458, -0.044523564585421), + (0.957120327092458, -0.04452356458542, -0.286248723426035), + (0.286248723426035, -0.957120327092458, -0.044523564585421), + (-0.04452356458542, -0.286248723426035, 0.957120327092458), + (-0.957120327092458, -0.044523564585419, 0.286248723426035), + (-0.044523564585421, 0.286248723426034, -0.957120327092458), + (-0.957120327092458, 0.04452356458542, -0.286248723426034), + (0.044523564585421, 0.286248723426034, 0.957120327092458), + (0.957120327092458, 0.04452356458542, 0.286248723426034), + (0.044523564585421, -0.286248723426034, -0.957120327092458), + (-0.286248723426034, -0.957120327092458, 0.044523564585421), + (0.286248723426035, 0.957120327092458, 0.044523564585421), + ], + 9: [ + (0.9333646931993099, 0.353542188921472, -0.06195377423185971), + (0.9333646931993099, -0.353542188921472, 0.06195377423185971), + (0.9333646931993099, -0.06195377423185971, -0.353542188921472), + (0.9333646931993099, 0.06195377423185971, 0.353542188921472), + (-0.9333646931993099, 0.353542188921472, 0.06195377423185971), + (-0.9333646931993099, -0.353542188921472, -0.06195377423185971), + (-0.9333646931993099, -0.06195377423185971, 0.353542188921472), + (-0.9333646931993099, 0.06195377423185971, -0.353542188921472), + (-0.06195377423185971, 0.9333646931993099, 0.353542188921472), + (0.06195377423185971, 0.9333646931993099, -0.353542188921472), + (-0.353542188921472, 0.9333646931993099, -0.06195377423185971), + (0.353542188921472, 0.9333646931993099, 0.06195377423185971), + (0.06195377423185971, -0.9333646931993099, 0.353542188921472), + (-0.06195377423185971, -0.9333646931993099, -0.353542188921472), + (0.353542188921472, -0.9333646931993099, -0.06195377423185971), + (-0.353542188921472, -0.9333646931993099, 0.06195377423185971), + (0.353542188921472, -0.06195377423185971, 0.9333646931993099), + (-0.353542188921472, 0.06195377423185971, 0.9333646931993099), + (-0.06195377423185971, -0.353542188921472, 0.9333646931993099), + (0.06195377423185971, 0.353542188921472, 0.9333646931993099), + (0.353542188921472, 0.06195377423185971, -0.9333646931993099), + (-0.353542188921472, -0.06195377423185971, -0.9333646931993099), + (-0.06195377423185971, 0.353542188921472, -0.9333646931993099), + (0.06195377423185971, -0.353542188921472, -0.9333646931993099), + (0.7068416977125503, 0.639740098619792, 0.3018400579657685), + (0.7068416977125503, -0.639740098619792, -0.3018400579657685), + (0.7068416977125503, 0.3018400579657685, -0.639740098619792), + (0.7068416977125503, -0.3018400579657685, 0.639740098619792), + (-0.7068416977125503, 0.639740098619792, -0.3018400579657685), + (-0.7068416977125503, -0.639740098619792, 0.3018400579657685), + (-0.7068416977125503, 0.3018400579657685, 0.639740098619792), + (-0.7068416977125503, -0.3018400579657685, -0.639740098619792), + (0.3018400579657685, 0.7068416977125503, 0.639740098619792), + (-0.3018400579657685, 0.7068416977125503, -0.639740098619792), + (-0.639740098619792, 0.7068416977125503, 0.3018400579657685), + (0.639740098619792, 0.7068416977125503, -0.3018400579657685), + (-0.3018400579657685, -0.7068416977125503, 0.639740098619792), + (0.3018400579657685, -0.7068416977125503, -0.639740098619792), + (0.639740098619792, -0.7068416977125503, 0.3018400579657685), + (-0.639740098619792, -0.7068416977125503, -0.3018400579657685), + (0.639740098619792, 0.3018400579657685, 0.7068416977125503), + (-0.639740098619792, -0.3018400579657685, 0.7068416977125503), + (0.3018400579657685, -0.639740098619792, 0.7068416977125503), + (-0.3018400579657685, 0.639740098619792, 0.7068416977125503), + (0.639740098619792, -0.3018400579657685, -0.7068416977125503), + (-0.639740098619792, 0.3018400579657685, -0.7068416977125503), + (0.3018400579657685, 0.639740098619792, -0.7068416977125503), + (-0.3018400579657685, -0.639740098619792, -0.7068416977125503), + ], + 10: [ + (-0.753828667197017, 0.54595190806126, -0.365621190026287), + (0.545951908061258, -0.36562119002629, -0.753828667197017), + (0.753828667197016, -0.545951908061261, -0.365621190026288), + (-0.365621190026289, -0.753828667197017, 0.545951908061259), + (-0.545951908061258, -0.365621190026288, 0.753828667197018), + (-0.365621190026289, 0.753828667197017, -0.545951908061259), + (-0.545951908061258, 0.365621190026289, -0.753828667197017), + (0.365621190026287, 0.753828667197017, 0.54595190806126), + (0.545951908061259, 0.365621190026289, 0.753828667197017), + (0.365621190026287, -0.753828667197018, -0.545951908061259), + (-0.753828667197017, -0.545951908061261, 0.365621190026288), + (0.753828667197016, 0.545951908061261, 0.365621190026287), + (0.70018101936373, -0.713151065847793, 0.034089549761256), + (-0.713151065847794, 0.034089549761254, 0.700181019363729), + (-0.70018101936373, 0.713151065847793, 0.034089549761256), + (0.034089549761255, 0.70018101936373, -0.713151065847793), + (0.713151065847793, 0.034089549761254, -0.70018101936373), + (0.034089549761257, -0.700181019363729, 0.713151065847794), + (0.713151065847794, -0.034089549761255, 0.700181019363728), + (-0.034089549761256, -0.700181019363729, -0.713151065847794), + (-0.713151065847794, -0.034089549761254, -0.700181019363729), + (-0.034089549761257, 0.700181019363729, 0.713151065847794), + (0.70018101936373, 0.713151065847793, -0.034089549761257), + (-0.700181019363729, -0.713151065847794, -0.034089549761257), + (0.276230218261792, 0.077050720725736, -0.957997939953259), + (0.077050720725735, -0.957997939953258, 0.276230218261793), + (-0.276230218261792, -0.077050720725734, -0.957997939953259), + (-0.957997939953259, 0.276230218261791, 0.077050720725738), + (-0.077050720725735, -0.957997939953259, -0.276230218261792), + (-0.957997939953258, -0.276230218261793, -0.077050720725736), + (-0.077050720725736, 0.957997939953258, 0.276230218261794), + (0.957997939953259, -0.27623021826179, 0.077050720725737), + (0.077050720725734, 0.957997939953259, -0.276230218261792), + (0.957997939953258, 0.276230218261793, -0.077050720725738), + (0.276230218261793, -0.077050720725736, 0.957997939953258), + (-0.276230218261791, 0.077050720725735, 0.957997939953259), + (0.451819102555243, -0.783355937521819, 0.42686411621907), + (-0.783355937521818, 0.426864116219071, 0.451819102555243), + (-0.451819102555243, 0.783355937521819, 0.42686411621907), + (0.426864116219071, 0.451819102555242, -0.783355937521819), + (0.783355937521818, 0.42686411621907, -0.451819102555244), + (0.426864116219072, -0.451819102555242, 0.783355937521818), + (0.783355937521819, -0.42686411621907, 0.451819102555242), + (-0.426864116219072, -0.451819102555241, -0.783355937521819), + (-0.783355937521818, -0.42686411621907, -0.451819102555243), + (-0.426864116219072, 0.451819102555241, 0.783355937521819), + (0.451819102555243, 0.783355937521818, -0.426864116219071), + (-0.451819102555242, -0.783355937521819, -0.426864116219071), + (-0.33858435995926, -0.933210037239527, 0.120331448866784), + (-0.933210037239526, 0.120331448866787, -0.33858435995926), + (0.338584359959261, 0.933210037239526, 0.120331448866786), + (0.120331448866785, -0.338584359959261, -0.933210037239526), + (0.933210037239526, 0.120331448866789, 0.33858435995926), + (0.120331448866785, 0.338584359959261, 0.933210037239526), + (0.933210037239526, -0.120331448866787, -0.338584359959262), + (-0.120331448866784, 0.338584359959262, -0.933210037239526), + (-0.933210037239526, -0.120331448866787, 0.338584359959261), + (-0.120331448866784, -0.338584359959262, 0.933210037239526), + (-0.338584359959262, 0.933210037239526, -0.120331448866784), + (0.338584359959261, -0.933210037239527, -0.120331448866783), + ], + 11: [ + (-0.674940520480437, 0.725629052064501, 0.133857284499464), + (0.09672433446143, -0.910327382989987, -0.402428203412229), + (0.906960315916358, 0.135127022135053, 0.398953221871704), + (-0.132758704758026, -0.307658524060733, 0.942189661842955), + (-0.226055801127587, -0.958831174708704, -0.171876563798827), + (0.275738264019853, -0.180692733507538, -0.944096682449892), + (0.830881650513589, 0.333278644528177, -0.445601871563928), + (-0.616471328612787, -0.2675443371664, 0.740528951931372), + (0.430277293287436, -0.892644471615357, -0.13434023290057), + (-0.690987198523076, 0.175109339053207, 0.701336874015319), + (0.810517041535507, -0.381449337547215, 0.444475565431127), + (-0.086734443854626, -0.706008517835924, -0.702872043114784), + (0.871320852056737, 0.46045780600396, 0.169642511361809), + (-0.600735266749549, 0.303266118552509, -0.739693720820614), + (-0.899100947083419, -0.418081246639828, 0.12971336924846), + (0.896927087079571, -0.188066327344843, -0.400191025613991), + (0.150494960966991, 0.903072153139254, 0.402258564791324), + (0.248601716402621, -0.224283612281953, 0.94228129975259), + (0.842584674708423, -0.510756382085546, -0.1708185707275), + (0.260034500418337, 0.209356489957684, -0.942630319215749), + (-0.058802461572434, 0.894595213188746, -0.442991732488095), + (0.061611769180132, -0.671290108790159, 0.738629528071408), + (0.982337536097614, 0.133784014710179, -0.130823555148513), + (-0.382277582532576, -0.605243847900137, 0.698243320392029), + (0.611839278216357, 0.651571608497249, 0.448449703569971), + (0.646865348569582, -0.298464129297652, -0.701772316597447), + (-0.169201016881282, 0.970430912746818, 0.172147783812972), + (-0.471725450862325, -0.47529570366279, -0.742676977621112), + (0.119369755955723, -0.984692604411347, 0.127009197228668), + (0.457289212231729, 0.796155990558714, -0.396260287026038), + (-0.813631436350979, 0.420942272793499, 0.40101307803722), + (0.287154555386871, 0.16417332397066, 0.943710432821951), + (0.746667577045155, 0.644035989066398, -0.166448713352744), + (-0.115779644740906, 0.314952464646105, -0.942019118105898), + (-0.867579212111466, 0.221916315040665, -0.445038717226738), + (0.655140022433912, -0.151162631680508, 0.740230646345257), + (0.176736512358047, 0.976002671721061, -0.12721238144483), + (0.455284607701078, -0.55278635410423, 0.697956426080188), + (-0.432023930219742, 0.781838026058859, 0.449538234998843), + (0.485961267092557, 0.525163287076294, -0.698602296584415), + (-0.975758639968897, 0.138431863354196, 0.1695042646494), + (0.308602378401872, -0.593188152818631, -0.743567338847214), + (0.972979693579006, -0.191167383224118, 0.129481842256537), + (-0.614624689780931, 0.68217777986423, -0.39606813475866), + (-0.653028964396532, -0.644975511259979, 0.396937981974668), + (-0.070378900922493, 0.320878001965403, 0.944502047726543), + (-0.381252925250545, 0.909662131759037, -0.164805986030565), + (-0.332341796304234, -0.009834857390798, -0.943107738283054), + (-0.477746621168896, -0.755138676192789, -0.448913962446598), + (0.343877558432071, 0.574039599276676, 0.743119615720828), + (-0.873212544495548, 0.47009394139203, -0.128497231106812), + (0.664216892966437, 0.259987346974329, 0.700872669256879), + (-0.878489109322641, -0.170673846340671, 0.446236846278739), + (-0.347082716608212, 0.626648635925969, -0.697742842975825), + (-0.433716795977713, -0.885744934523588, 0.165365207503367), + (0.661861683362982, 0.112512128799614, -0.741134355544863), + (0.482068945127674, 0.865869532174741, 0.133714192945202), + (-0.8374660393934, -0.372486946227971, -0.399880116725617), + (0.410355219266256, -0.82161905066793, 0.39566492086167), + (-0.329899568015879, 0.02926988290883, 0.943562159572669), + (-0.982429034616553, -0.080964254903198, -0.168160582094488), + (-0.090370421487683, -0.316160436207578, -0.944391743662116), + (0.571959920493404, -0.686312971502271, -0.449262010965652), + (-0.442021476996821, 0.502111749619808, 0.743303978710785), + (-0.716515724344093, -0.684793506171761, -0.13290248557738), + (-0.044218043628816, 0.709851625611568, 0.702961900983442), + (-0.110556556362806, -0.889624975730714, 0.443107944412334), + (-0.701028131184281, -0.134257385503649, -0.700381691455451), + (0.707841110014082, -0.686721956709281, 0.165450648676302), + (0.099860111408803, 0.666551337869757, -0.738740327945793), + ], + 12: [ + (-0.893804977761136, -0.426862191124497, 0.137482113446834), + (-0.426862191241092, 0.137482113445288, -0.893804977705691), + (0.893804977770157, 0.426862191128157, 0.137482113376823), + (0.1374821132964, -0.893804977739491, -0.426862191218271), + (0.426862191272731, 0.137482113377345, 0.893804977701032), + (0.137482113529033, 0.893804977707775, 0.426862191209756), + (0.426862191185983, -0.137482113474993, -0.893804977727441), + (-0.137482113324291, 0.893804977725279, -0.426862191239047), + (-0.426862191217414, -0.137482113347288, 0.893804977732073), + (-0.137482113501071, -0.893804977693655, 0.426862191248328), + (-0.893804977672548, 0.426862191328071, -0.137482113390703), + (0.893804977663553, -0.42686219133326, -0.137482113433065), + (0.983086600385574, 0.022300380107522, -0.181778516853323), + (0.022300380232394, -0.181778516808726, 0.983086600390988), + (-0.983086600396613, -0.022300380113323, -0.181778516792915), + (-0.181778516710471, 0.983086600409631, 0.022300380211455), + (-0.022300380272854, -0.181778516836686, -0.9830866003849), + (-0.18177851693601, -0.983086600368179, -0.022300380200376), + (-0.0223003801708, 0.181778516841875, 0.983086600386256), + (0.181778516710979, -0.983086600409044, 0.022300380233212), + (0.022300380212558, 0.181778516804081, -0.983086600392297), + (0.181778516934384, 0.983086600367503, -0.022300380243431), + (0.983086600391629, -0.022300380332372, 0.181778516792996), + (-0.98308660038057, 0.022300380337865, 0.181778516852128), + (-0.897951986971875, 0.376695603035365, 0.227558018419664), + (0.376695602927528, 0.227558018339206, -0.897951987037503), + (0.897951986986053, -0.376695603028569, 0.227558018374966), + (0.227558018305554, -0.897951987041904, 0.376695602937366), + (-0.376695602875261, 0.227558018455254, 0.89795198703002), + (0.227558018486567, 0.89795198699048, -0.3766956029506), + (-0.376695602982511, -0.22755801836891, -0.89795198700691), + (-0.227558018280939, 0.897951987054767, 0.376695602921573), + (0.376695602931437, -0.22755801842558, 0.897951987013974), + (-0.227558018511349, -0.897951987002348, -0.376695602907339), + (-0.897951987072194, -0.376695602830637, -0.227558018362707), + (0.897951987057819, 0.376695602823051, -0.227558018431989), + (-0.171330151245221, 0.459786194953055, -0.871345301361568), + (0.459786194843117, -0.871345301414649, -0.171330151270292), + (0.171330151191219, -0.459786194982334, -0.871345301356736), + (-0.871345301364754, -0.171330151162981, 0.459786194977662), + (-0.459786195042432, -0.871345301303738, 0.171330151299472), + (-0.871345301353407, 0.171330151362727, -0.459786194924734), + (-0.459786194855202, 0.87134530140841, -0.171330151269592), + (0.871345301392835, 0.171330151178183, 0.45978619491878), + (0.459786195054412, 0.871345301309038, 0.171330151240368), + (0.871345301325486, -0.171330151377355, -0.459786194972196), + (-0.17133015129661, -0.459786194913003, 0.871345301372597), + (0.171330151350736, 0.459786194942983, 0.871345301346135), + (-0.397191702297223, -0.548095590649226, -0.736091010091219), + (-0.548095590778902, -0.736091010056557, -0.397191702182515), + (0.397191702250221, 0.548095590625205, -0.736091010134467), + (-0.736091010174764, -0.397191702137083, -0.548095590653075), + (0.548095590610212, -0.736091010169131, 0.397191702206669), + (-0.736091010049194, 0.397191702305889, 0.548095590699385), + (0.548095590752529, 0.736091010044117, -0.397191702241962), + (0.736091010139925, 0.397191702119602, -0.548095590712531), + (-0.548095590584386, 0.736091010182625, 0.3971917022173), + (0.736091010083782, -0.39719170228798, 0.548095590665912), + (-0.39719170212526, 0.548095590740419, 0.736091010116106), + (0.397191702171386, -0.548095590716295, 0.736091010109179), + (0.379474725534956, 0.69627727809449, 0.609259291836815), + (0.696277278210441, 0.609259291787114, 0.379474725402001), + (-0.379474725495576, -0.696277278074161, 0.609259291884576), + (0.609259291925953, 0.379474725376213, 0.696277278103008), + (-0.696277278071056, 0.609259291933888, -0.379474725422102), + (0.60925929179591, -0.379474725515542, -0.696277278140864), + (-0.696277278185906, -0.609259291774849, 0.379474725466713), + (-0.609259291882878, -0.379474725353089, 0.696277278153303), + (0.696277278046548, -0.609259291946589, -0.379474725446676), + (-0.609259291838737, 0.379474725493095, -0.696277278115623), + (0.37947472533629, -0.696277278181595, -0.609259291861008), + (-0.379474725375237, 0.696277278161216, -0.609259291860039), + (-0.678701446470328, 0.729764213479081, 0.082513873284097), + (0.729764213389772, 0.082513873179234, -0.678701446579104), + (0.678701446474772, -0.72976421347722, 0.082513873263995), + (0.082513873217671, -0.678701446552547, 0.729764213410125), + (-0.729764213370974, 0.082513873368402, 0.678701446576318), + (0.082513873326892, 0.678701446534692, -0.729764213414381), + (-0.729764213431284, -0.082513873201736, -0.678701446531733), + (-0.082513873171694, 0.678701446577399, 0.72976421339221), + (0.729764213412797, -0.08251387334668, 0.67870144653399), + (-0.082513873373655, -0.678701446558336, -0.729764213387104), + (-0.678701446641541, -0.729764213324827, -0.082513873240061), + (0.678701446637016, 0.729764213321344, -0.082513873308075), + ], + 13: [ + (0.276790129286922, -0.235256466916603, 0.931687511509759), + (0.198886780634501, 0.360548603139528, 0.911289609983006), + (-0.258871339062373, 0.204230077441409, 0.944073993540935), + (-0.20028291392731, -0.228346161950354, 0.952756414153864), + (-0.883545166667525, -0.414277696639041, -0.218453492821483), + (0.397750057908559, -0.901619535998689, -0.16993264471327), + (0.876539487069282, 0.434392104192327, -0.207321073274483), + (-0.411742357517625, 0.88489597883979, -0.217778184534166), + (0.501114093867204, 0.377868932752059, 0.778524074507957), + (-0.394238847790386, 0.473687133880952, 0.787525383774109), + (-0.495364292002136, -0.406429808740612, 0.767742814213388), + (0.370186583802172, -0.559306270968252, 0.741713144300723), + (0.411742357517961, -0.884895978839253, 0.217778184535715), + (0.883545166668397, 0.414277696639157, 0.218453492817737), + (-0.39775005791059, 0.901619535997218, 0.169932644716324), + (-0.876539487069878, -0.434392104191278, 0.20732107327416), + (-0.69101430131565, -0.702815226887987, -0.168967429499392), + (0.684400344460127, -0.714441044251654, -0.145499700314004), + (0.660710489482765, 0.731357715035063, -0.169048932993191), + (-0.773611287956309, 0.615222357857778, -0.151746583284428), + (0.683629784686022, -0.21996733132084, -0.695891292258878), + (0.256574099503526, 0.681472791071418, -0.685393730999406), + (-0.644474509637892, 0.354062227985534, -0.677711254990588), + (-0.220535080416141, -0.731547754140859, -0.645137320046912), + (0.394238847792041, -0.473687133882522, -0.787525383772336), + (0.495364292000968, 0.406429808741285, -0.767742814213785), + (-0.370186583802439, 0.559306270970003, -0.74171314429927), + (-0.50111409386464, -0.377868932752239, -0.77852407450952), + (-0.488574873968534, -0.006884557978444, -0.872494811095214), + (0.055542048727444, -0.584131720249991, -0.809756268404849), + (0.526812107464791, 0.049707819617671, -0.848527039107984), + (0.004245864108125, 0.4886546223943, -0.872466980836902), + (-0.710317361514613, -0.479530914625401, 0.515266288291253), + (0.521404384476562, -0.728039165451723, 0.445080264016476), + (0.738099355388852, 0.407803273205931, 0.53749961110413), + (-0.496057991262554, 0.699670113703365, 0.514186932248264), + (-0.973220809307327, 0.194260751789571, -0.122898399685852), + (-0.376203572666605, -0.908865964003535, -0.180093118660339), + (0.914477900370762, -0.368657988534049, -0.166797653531193), + (0.28746218785413, 0.946817914340553, -0.144572914606861), + (-0.098900669929334, 0.99509705928004, 0.000707177308311), + (-0.986068201425202, -0.161328561779779, 0.04052896855503), + (0.098900669927371, -0.995097059280236, -0.000707177307522), + (0.98606820142538, 0.161328561777872, -0.040528968558297), + (0.815232440848265, 0.131832381174928, -0.563929331266187), + (-0.113644567080339, 0.787251605615581, -0.606069155978764), + (-0.76050444170531, -0.010569874890521, -0.649246695710724), + (0.179848227912241, -0.83248278540524, -0.52404868754798), + (-0.92768989180951, -0.047241289482188, -0.370350813692261), + (-0.062273759773745, -0.944434685686409, -0.322746190242513), + (0.939840260740896, -0.044569841802216, -0.33869427732427), + (-0.00824273878155, 0.93408705946015, -0.356950168239866), + (-0.287462187854123, -0.94681791433958, 0.144572914613243), + (0.973220809307003, -0.194260751794934, 0.122898399679932), + (0.376203572664097, 0.908865964003331, 0.180093118666611), + (-0.914477900372906, 0.368657988531957, 0.16679765352406), + (-0.198886780630987, -0.360548603140065, -0.91128960998356), + (0.258871339064112, -0.204230077444254, -0.944073993539843), + (0.200282913924527, 0.228346161951352, -0.952756414154209), + (-0.276790129284401, 0.235256466920766, -0.931687511509456), + (0.496057991258595, -0.69967011370777, -0.514186932246089), + (0.710317361512836, 0.479530914628761, -0.515266288290576), + (-0.521404384476695, 0.728039165452453, -0.445080264015126), + (-0.738099355384499, -0.407803273209131, -0.537499611107679), + (-0.815232440849446, -0.131832381170712, 0.563929331265466), + (0.113644567080183, -0.787251605613717, 0.606069155981215), + (0.760504441709935, 0.010569874889864, 0.649246695705317), + (-0.179848227916839, 0.832482785402468, 0.524048687550806), + (0.644474509638734, -0.354062227985804, 0.677711254989647), + (0.220535080413518, 0.73154775414237, 0.645137320046095), + (-0.683629784685343, 0.219967331325312, 0.695891292258132), + (-0.256574099500943, -0.681472791069379, 0.6853937310024), + (0.00824273878347, -0.934087059459458, 0.356950168241634), + (0.927689891812602, 0.047241289479133, 0.370350813684907), + (0.062273759768788, 0.944434685686016, 0.322746190244617), + (-0.939840260741931, 0.04456984180196, 0.338694277321433), + (-0.684400344460716, 0.714441044251305, 0.145499700312953), + (-0.660710489482671, -0.731357715034246, 0.169048932997096), + (0.773611287955743, -0.615222357858877, 0.151746583282855), + (0.691014301313431, 0.702815226889319, 0.168967429502926), + (0.823586023578098, -0.394634588904438, 0.407393670798948), + (0.494068620358303, 0.708839608416629, 0.503430837272612), + (-0.75887513050105, 0.450605887274021, 0.470173234734822), + (-0.431499072601357, -0.787935048711447, 0.439279989706176), + (0.488574873974618, 0.006884557979146, 0.872494811091801), + (-0.055542048725634, 0.584131720247444, 0.80975626840681), + (-0.52681210746758, -0.049707819615904, 0.848527039106356), + (-0.004245864106237, -0.488654622389235, 0.872466980839748), + (-0.49406862035774, -0.708839608420214, -0.503430837268118), + (0.758875130496518, -0.450605887275878, -0.470173234740358), + (0.431499072601226, 0.787935048714215, -0.43927998970134), + (-0.823586023577754, 0.394634588903444, -0.407393670800605), + (-0.05223814787874, -0.056184830047506, -0.997052877624217), + (0.052238147881538, 0.05618483004769, 0.99705287762406), + ], + 14: [ + (-0.625520988160254, -0.7673610045544, 0.14099851793647), + (-0.76724274137005, 0.141111638293461, -0.625640536852518), + (0.625492928633992, 0.767336602497947, 0.141255565185161), + (0.141259978285753, -0.625497336538309, -0.767332196977417), + (0.767217177597722, 0.141142445065633, 0.625664936367606), + (0.140994104732436, 0.625522897885037, 0.76736025871308), + (0.767367121351956, -0.141003470846495, -0.625512367805189), + (-0.141107421738627, 0.625630007825311, -0.767252102531351), + (-0.767341557579575, -0.14125061251247, 0.625487968290521), + (-0.141146661279494, -0.625655569171042, 0.767224040795719), + (-0.625631916042134, 0.767247698300479, -0.141122907715456), + (0.625659975568208, -0.76722329624468, -0.141131175405856), + (0.557188048071509, -0.044753456478336, -0.829179478291342), + (-0.044855878114542, -0.829283214592494, 0.55702540354432), + (-0.557023176560509, 0.04489683751235, -0.829282493940292), + (-0.82927698135379, 0.557030331904205, -0.044909882380585), + (0.04500608513203, -0.829178759033645, -0.557168769645708), + (-0.829184990273806, -0.557180524667655, 0.044744997884746), + (0.044745112627007, 0.829186887034329, 0.557177692721376), + (0.829285903726696, -0.557022572534015, -0.044841315410966), + (-0.044895319644503, 0.829275086591665, -0.557034326619468), + (0.829176067900875, 0.557172765298515, 0.045006199906779), + (0.55701504778817, 0.04485436035495, 0.829290252501915), + (-0.55717991929916, -0.044997741388974, 0.829171719729799), + (-0.256065565410913, 0.860770382492113, 0.439891776275906), + (0.860817193749452, 0.439942893453515, -0.255820267854343), + (0.255978113844099, -0.860846435024418, 0.439793838677361), + (0.439780410927513, -0.2559784640674, 0.860853190792787), + (-0.86089686693789, 0.439742722239698, 0.255896312466096), + (0.439905203705212, 0.256058129693811, -0.860765732339981), + (-0.860766305814323, -0.439898638597135, -0.256067480432698), + (-0.439951565414546, 0.255829619022141, 0.860809982586329), + (0.860845979002674, -0.439786977094207, 0.255991435820162), + (-0.439734049218326, -0.255909284650278, -0.860897441039197), + (-0.255822182689152, -0.860816739802146, -0.439942668220034), + (0.255909634255987, 0.860892792334745, -0.43974294673258), + (-0.214847470746312, -0.032398468989078, 0.976110087808274), + (-0.032361689068532, 0.976149872834738, -0.214672184610297), + (0.214653391258715, 0.032229687143749, 0.976158372851326), + (0.976156890684175, -0.214662164023714, -0.032216146673044), + (0.032184871976157, 0.976118589466139, 0.214840948877335), + (0.976111569264229, 0.214838964338531, 0.032410241444204), + (0.032404387317597, -0.976112740167589, -0.214834527404447), + (-0.97615046971745, 0.214667748038907, -0.032373112644706), + (-0.032227570225248, -0.976155722133346, 0.214665763138194), + (-0.976117990230955, -0.214844548351237, 0.032179017872419), + (-0.214659241112501, 0.032359572226461, -0.976152789418913), + (0.21485332060012, -0.032190790383644, -0.976115671240647), + (-0.531657953418075, -0.827333953094149, -0.181268724894593), + (-0.827232187812406, -0.181173291587112, -0.531848799813059), + (0.531693969367769, 0.827365274479422, -0.181019958909332), + (-0.181013937184585, -0.531695771783126, -0.827365433658478), + (0.82726500032424, -0.181115392520565, 0.53181748168959), + (-0.181274746488222, 0.531662962384716, 0.827329414872902), + (0.827337912964399, 0.181265235803751, -0.531652980863198), + (0.181178425057189, 0.531838819184424, -0.827237480233043), + (-0.827370725476244, 0.181023448305968, 0.53168429898609), + (0.181110258615433, -0.531806009787378, 0.82727349901848), + (-0.531843826870477, 0.827237640803175, 0.181162991414264), + (0.531807810920692, -0.827268962188108, 0.181125692390543), + (-0.660052978431453, -0.64107030142389, -0.391610692264717), + (-0.640943278162024, -0.391490626360013, -0.660247532105318), + (0.660130816198753, 0.641137993287446, -0.391368597447617), + (-0.391366127194665, -0.660129990434437, -0.641140351415881), + (0.64101419265168, -0.391488664009925, 0.660179847292265), + (-0.391613162232706, 0.660059082674287, 0.641062507518011), + (0.641074532205542, 0.391604771858404, -0.660052381872206), + (0.391493582576226, 0.660240832413506, -0.64094837390819), + (-0.641145446695277, 0.391374518513393, 0.660120066685087), + (0.391485706851, -0.660169924653076, 0.641026217806203), + (-0.660246935065265, 0.640950733115446, 0.391479427883123), + (0.660169097297863, -0.641018424977353, 0.391499861829449), + (-0.887809544786451, -0.296234001309576, 0.352192601646022), + (-0.296066792023988, 0.352356402512996, -0.887800326801429), + (0.88773949759831, 0.296173084548554, 0.352420329142482), + (0.352416311777685, -0.887743835667625, -0.296164861904896), + (0.296002975181616, 0.352256528867817, 0.887861237217634), + (0.352196618754376, 0.887807646454613, 0.296234914611202), + (0.29624330876316, -0.35220289417087, -0.887802356017778), + (-0.352357435214616, 0.887795037857324, -0.296081422255585), + (-0.296179491920463, -0.352410037210515, 0.887741445601714), + (-0.352255495317536, -0.887858848644387, 0.296011369549315), + (-0.887793137385775, 0.296073200109863, -0.352369132285204), + (0.887863184573985, -0.29601228334985, -0.352243798503465), + (-0.26223504413332, -0.963196832316083, -0.059030871962556), + (-0.963146753177879, -0.05898607663367, -0.262428989645347), + (0.262246759182379, 0.963207020345643, -0.058812186281487), + (-0.058802018270752, -0.262250202293796, -0.963206703695603), + (0.963157426232092, -0.058856981708733, 0.262418802676392), + (-0.059041039930677, 0.26223953025181, 0.9631949877243), + (0.963198910461706, 0.05903143538266, -0.262227284091577), + (0.058993521775316, 0.262416743924601, -0.963149633700058), + (-0.963209583515922, 0.058811622961071, 0.262237471059664), + (0.058849536426177, -0.26240607188442, 0.963161349671286), + (-0.262421229412009, 0.963149318669471, 0.058978710569357), + (0.262409514362814, -0.96315950669899, 0.058864347675232), + (-0.715507563967586, -0.551203770138786, 0.429212452859839), + (-0.551069607492995, 0.429343180584727, -0.715532473744488), + (0.715422202362423, 0.551129535145429, 0.429450006237378), + (0.429450819917231, -0.715428271411138, -0.551121022769127), + (0.5509918383738, 0.429319279234025, 0.715606701005125), + (0.429211638866897, 0.7155060331503, 0.551206391097486), + (0.551211869430264, -0.429219462039715, -0.715497119774448), + (-0.429341247317559, 0.715523561571074, -0.551082685436994), + (-0.551134100310698, -0.42944299777979, 0.715422892513669), + (-0.429321211466422, -0.715601323311163, 0.550997317108093), + (-0.715522029029489, 0.551074173985694, -0.429354726001132), + (0.71560739063481, -0.550999938995098, -0.429307733096245), + ], + 15: [ + (0.854403279867469, -0.505354134007206, 0.120881076242474), + (-0.50543491755569, 0.120816219805996, 0.85436466754382), + (-0.854386776665562, 0.505324765203946, 0.121120260611542), + (0.120833358636621, 0.854397789834015, -0.505374827397788), + (0.505397909754575, 0.121184507524897, -0.854334400543285), + (0.121167891781777, -0.854359592169892, 0.505359307095908), + (0.505550243990606, -0.121029099883223, 0.85426629793203), + (-0.120901230058257, -0.8542712308899, -0.505572503845152), + (-0.505512743080475, -0.120971801893292, -0.854296605243135), + (-0.121100221709937, 0.854233310670545, 0.505588950870808), + (0.854228086018077, 0.50562286374044, -0.120995440909188), + (-0.854244251915001, -0.505593339178042, -0.121004683582819), + (-0.264987898778375, 0.883813698575362, -0.385557725524417), + (0.883849543661418, -0.385514772323787, -0.264930829632268), + (0.26493557758679, -0.883717531264112, -0.385814028600849), + (-0.385585969828214, -0.26499188213212, 0.883800182323873), + (-0.883729220902574, -0.385857204128221, 0.264833687708496), + (-0.385785570894326, 0.264874341279828, -0.883748310675226), + (-0.88388533425828, 0.385579930632135, -0.264716514363662), + (0.385705565188833, 0.2647713629504, 0.883814088111154), + (0.883764779677347, 0.385791993289806, 0.26481003023928), + (0.385667159435638, -0.264653128949093, -0.883866258814251), + (-0.26465693400637, -0.883897033692944, 0.385594010730408), + (0.264708980625706, 0.88380076582133, 0.385778902883153), + (-0.973352164893031, 0.228026239253951, 0.024281624940474), + (0.228112345886926, 0.024344549147457, -0.973330416960638), + (0.973355442809202, -0.228032180463189, 0.024093705130315), + (0.02435678809852, -0.973342833359315, 0.228058053182924), + (-0.228119646411613, 0.024031592823588, 0.973336483168797), + (0.024019836271027, 0.973350319061891, -0.228061842156086), + (-0.228243300844141, -0.024101340294447, -0.97330576953791), + (-0.024325527828938, 0.973285859331229, 0.228304412400888), + (0.228251100231509, -0.024273911824481, 0.973299651930403), + (-0.024050630999231, -0.973293331950969, -0.228301680082122), + (-0.973284068418876, -0.228330589256741, -0.024150862752081), + (0.973280766897442, 0.228336792285628, -0.024225153791815), + (0.176494164360597, -0.643915961757687, 0.744460908403072), + (-0.643955474734765, 0.744429879896195, 0.176480878502065), + (-0.176394545795758, 0.643730246363376, 0.744645106161623), + (0.744482176984942, 0.176540768465681, -0.643878595094843), + (0.643722173544997, 0.744675565408302, -0.176295392935637), + (0.7446232602985, -0.176308754645389, 0.643779017410342), + (0.643979646991902, -0.744473128172097, 0.176210032886433), + (-0.744568815199564, -0.17638194509082, -0.643821938798528), + (-0.643745300418696, -0.744632317149074, -0.176393595252333), + (-0.744536607308663, 0.176149174255325, 0.643922905933989), + (0.17619570702358, 0.643971118562494, -0.744483895919737), + (-0.176295790346183, -0.643784799068587, -0.744621331143847), + (-0.538268091669357, -0.714443097207928, 0.447033021534844), + (-0.714408571342234, 0.447040121797618, -0.538308018420605), + (0.538328211219438, 0.714331668550684, 0.447138685768607), + (0.447120197809599, -0.538252620284606, -0.714400199795228), + (0.714269338389924, 0.44713116789972, 0.538417153263762), + (0.447050760030329, 0.538390645652783, 0.714339646547694), + (0.714335378036911, -0.44690804426239, -0.538514779424324), + (-0.44721697306067, 0.538430741435079, -0.714205373603506), + (-0.714197054202002, -0.447264479166707, 0.53840231560137), + (-0.446955147518652, -0.538568616287532, 0.714265316011294), + (-0.53855313587161, 0.7142734853459, -0.446960745451628), + (0.538492423492547, -0.714162749879013, -0.44721077416177), + (-0.854262559171519, -0.121196786481334, 0.505516388403308), + (-0.121135225917774, 0.505562840819898, -0.854243800693903), + (0.854330941426722, 0.121071937454801, 0.505430735592792), + (0.50560092757097, -0.854220500665318, -0.12114057240441), + (0.120980483865727, 0.505385380088754, 0.854370727562784), + (0.505347417325379, 0.854380006035553, 0.121073502837149), + (0.121020187472529, -0.505348640639146, -0.854386836057462), + (-0.505616684568734, 0.854250931602746, -0.120859894677939), + (-0.120865731027902, -0.505598482368904, 0.854260879175297), + (-0.50533076030876, -0.85441039802093, 0.120928468275617), + (-0.854368086396689, 0.120928425096259, -0.505402304061426), + (0.854300186149896, -0.120804448918389, -0.50554671106217), + (0.744463297304691, 0.643945879165515, 0.176374895243003), + (0.643874092586096, 0.176352654639277, 0.744530653565125), + (-0.744439438351756, -0.643989673955645, 0.176315689786883), + (0.176272538481235, 0.744499406704124, 0.643932159155441), + (-0.643927227994187, 0.176337348613599, -0.744488323972679), + (0.176417156092134, -0.744445359352701, -0.643955040336351), + (-0.643773276436742, -0.176537581494637, 0.74457400630557), + (-0.176186289075531, -0.744659935538757, 0.643770123526409), + (0.643827610309512, -0.176153056179147, -0.744618096074686), + (-0.176503857743247, 0.744606426388638, -0.643745025598251), + (0.744642563797528, -0.643711305756388, -0.176474380640507), + (-0.744666089545738, 0.643755051780831, -0.176215346628265), + (-0.228336071531986, 0.973285242484899, -0.024051511354805), + (0.97330645157172, -0.02400727784605, -0.228250305452786), + (0.228333198106367, -0.973279105669999, -0.024325564920999), + (-0.024087022235214, -0.228306334617004, 0.973291340212985), + (-0.973298936142401, -0.024368394387641, 0.228244084828147), + (-0.024288551979359, 0.228299553239789, -0.97328792257649), + (-0.973337174231582, 0.024064219708824, -0.228113258248362), + (0.024218299052141, 0.228063963157171, 0.973344904286279), + (0.973329707418057, 0.024311563193311, 0.228118891266323), + (0.024157446706033, -0.228056100974521, -0.97334825862943), + (-0.228026586903388, -0.973357047279715, 0.0240818225246), + (0.228030283046768, 0.973350913461235, 0.024293811512186), + (0.714188221641478, 0.538577770578359, -0.447067298186109), + (0.538527478643698, -0.447091804432198, 0.714210804423472), + (-0.714248768779612, -0.538467220657919, -0.447103733571689), + (-0.447161335031565, 0.714165750281327, 0.538529499264337), + (-0.538389584312319, -0.447080411787951, -0.714321888856505), + (-0.447010969457209, -0.714305113088417, -0.538469496444014), + (-0.538432015415055, 0.446913681352591, 0.714394237235966), + (0.447232748299163, -0.714300498031757, 0.538291433482237), + (0.538293039926151, 0.447257878415203, -0.714283552493402), + (0.446938450981038, 0.714439434305571, -0.538351479745163), + (0.714416613789271, -0.538354159540915, 0.44697170027516), + (-0.714357130519037, 0.538243224338143, 0.447200314770336), + (-0.88382262479637, 0.264636203965444, 0.385778754532718), + (0.264703577640278, 0.38583706665855, -0.88377699335113), + (0.883874390523727, -0.264731681104579, 0.385594604209984), + (0.385842662030848, -0.883779863837697, 0.264685837233447), + (-0.26482277877838, 0.385535295796556, 0.883872972510847), + (0.385530295763018, 0.883900556837248, -0.264737977388365), + (-0.264823043649479, -0.385616295049092, -0.883837557781314), + (-0.385806659371906, 0.883713819704025, 0.264958688191976), + (0.264941401779006, -0.385755452567231, 0.883741356075422), + (-0.385565266715756, -0.883834666939721, -0.264906977291953), + (-0.883791950673863, -0.264913750483099, -0.38565851828926), + (0.883739852971877, 0.26500888802554, -0.385712537437807), + ], + 16: [ + (0.938311825813856, -0.17507925577492, -0.298191501782276), + (-0.175109632245629, -0.298282531121024, 0.938277223598034), + (-0.938311652301346, 0.175147761450008, -0.298151815044902), + (-0.298182757815715, 0.938327057553728, -0.175012502421904), + (0.175097712410131, -0.298058347845738, -0.938350687316958), + (-0.298185477757762, -0.938323612741539, 0.175026336949732), + (0.175121225661409, 0.298070999742225, 0.938342280532811), + (0.298159022282375, -0.938297484887434, -0.175211378870018), + (-0.175136638135111, 0.298288500226525, -0.938270285480331), + (0.298175056505462, 0.938292628074833, 0.175210101816042), + (0.938309721676758, 0.175091137054814, 0.298191146635404), + (-0.938307020714082, -0.175144295988174, 0.298168426332282), + (0.318319389865683, -0.189552295411868, 0.928839433561922), + (-0.189466106261457, 0.928833946336168, 0.318386706242113), + (-0.318293314473071, 0.18936285961738, 0.928887007853633), + (0.928852943553566, 0.318350700348959, -0.189433473386317), + (0.189441607397533, 0.928892798895752, -0.318229548512164), + (0.928866264406345, -0.318313837307129, 0.189430102746667), + (0.18945182591494, -0.928887156552102, 0.318239934719146), + (-0.928865750332054, -0.318289122686796, -0.189474146625178), + (-0.189481041982253, -0.928834132900175, -0.318377273511944), + (-0.928863874908086, 0.318277395441538, 0.189503038080361), + (0.318275484124591, 0.18957282380822, -0.92885028970154), + (-0.318345902583112, -0.189353418017315, -0.928870911049379), + (0.415270907116288, 0.626546860524453, 0.659537038588256), + (0.626612654947257, 0.659451415891007, 0.415307609777736), + (-0.415241828112963, -0.626676394380167, 0.659432271664102), + (0.659494217922308, 0.41519684716212, 0.626641009377521), + (-0.626618996427069, 0.659521812332477, -0.415186238180433), + (0.659478785687794, -0.415192215022902, -0.626660319321504), + (-0.626602233185435, -0.65952877581014, 0.415200475969626), + (-0.659472693683341, -0.415326178073293, 0.626577953724091), + (0.626606052873236, -0.65944383659479, -0.415329605108723), + (-0.659498633823103, 0.415315781516604, -0.626557542136963), + (0.415250963158486, -0.626542854390271, -0.659553401331872), + (-0.415267233073285, 0.626674158557439, -0.659418398387537), + (0.081476869754028, 0.884767493032223, 0.458855100188022), + (0.88480215017059, 0.458780629597686, 0.081519868495058), + (-0.08148097265168, -0.88484396510395, 0.458706887363658), + (0.458778051156021, 0.08139667888042, 0.884814828336823), + (-0.884809515892886, 0.45878451702782, -0.081417980329578), + (0.458732327572868, -0.081386172952098, -0.884839500978449), + (-0.884806469025575, -0.458784799888689, 0.081449492089205), + (-0.458770768146743, -0.081567624478124, 0.884802862185155), + (0.884821176813587, -0.458741101923224, -0.081535798692882), + (-0.458810899116744, 0.081573887356361, -0.884781475706435), + (0.081470600041761, -0.884777903494754, -0.458836139396478), + (-0.081497545017818, 0.88485010959699, -0.458692090298344), + (-0.722581612146772, 0.69116944690793, -0.012673178305347), + (0.691146231887784, -0.012722477090735, -0.722602950951623), + (0.722589739174094, -0.691157232223568, -0.012874361552029), + (-0.012719991090033, -0.722649829139429, 0.691097262526357), + (-0.6911640879369, -0.012832809701898, 0.722583920760425), + (-0.012740894622282, 0.722658126679523, -0.691088200990487), + (-0.691184825451665, 0.012806932405418, -0.722564543516851), + (0.01278690708865, 0.722509435119358, 0.69124280189425), + (0.691166758903022, 0.012679269543203, 0.722584076430794), + (0.012798402734516, -0.722517774658893, -0.691233872281593), + (-0.722587198973115, -0.691163495604889, 0.01267920436853), + (0.722578352800658, 0.691170335944389, 0.012809792129789), + (0.560117573995459, 0.806868022890413, 0.187702682288658), + (0.806883478716379, 0.18757144265397, 0.560139273462648), + (-0.560134093540899, -0.806891631206385, 0.18755184014617), + (0.187652131237362, 0.560025149416763, 0.806943932168034), + (-0.806885441512999, 0.18768574188158, -0.560098157976558), + (0.187630222901067, -0.560004720839195, -0.806963203679022), + (-0.806874677594158, -0.187697516958668, 0.560109718523856), + (-0.187614808802038, -0.560215760321792, 0.806820293129301), + (0.806892320702248, -0.18757613331143, -0.560124965524367), + (-0.187636487681617, 0.56022497423671, -0.806808853900342), + (0.56009182108872, -0.806880865199227, -0.18772432267788), + (-0.560129384097476, 0.806896245083186, -0.187546054987136), + (-0.099485634221032, -0.358895129517995, -0.928060824834181), + (-0.359050794288811, -0.927994608087772, -0.099541621850345), + (0.099434389660615, 0.359143761945999, -0.927970129049474), + (-0.928019026720099, -0.09942019096838, -0.359021324816913), + (0.358990815531993, -0.928035748444477, 0.099374262124424), + (-0.928007207203491, 0.099420259668564, 0.359051856067911), + (0.359002982562248, 0.928031348467288, -0.099371398165657), + (0.928017938922059, 0.099510949379702, -0.358998991631458), + (-0.359042863742385, 0.92799619207621, 0.099555459356689), + (0.928013665632084, -0.099489549105096, 0.359015969030581), + (-0.099451875312545, 0.358926751348054, 0.928052213867059), + (0.099465503317397, -0.359120063291987, 0.927975966170987), + (0.787833199437607, 0.557450082325166, -0.261855409681697), + (0.557405388687852, -0.261977292048617, 0.787824302184578), + (-0.787861477876718, -0.557364111687839, -0.26195331393273), + (-0.261861028070608, 0.787802657602316, 0.557490604990374), + (-0.557427204478003, -0.261835304855293, -0.787856068605919), + (-0.261850091868655, -0.787804146924511, -0.557493637162722), + (-0.557398047481063, 0.261806624190095, 0.787886227950765), + (0.26192814680606, -0.787893374500188, 0.557330849971047), + (0.557399834363592, 0.261935778537884, -0.787842035292097), + (0.261909535328364, 0.787908371337394, -0.55731839524686), + (0.787858967733566, -0.557444321449493, 0.261790136264747), + (-0.787856023927293, 0.557369329488324, 0.261958615256708), + (-0.507282732168614, -0.717049946047353, -0.478020506377115), + (-0.71706431400176, -0.477906271006066, -0.507370048109131), + (0.507331753192767, 0.71711626280308, -0.477868975583995), + (-0.477891616916408, -0.50725750267016, -0.717153699332196), + (0.717108744361459, -0.47798851765986, 0.507229756368514), + (-0.477913676926975, 0.507235340412842, 0.717154674280526), + (0.717103637758922, 0.477942092943937, -0.507280719627002), + (0.477949791330649, 0.507362311781387, -0.7170407809538), + (-0.717073605236621, 0.477889924387354, 0.507372313830785), + (0.477966885504482, -0.507396895429057, 0.717004914118516), + (-0.507289494490155, 0.717039874321013, 0.478028437871252), + (0.507342973335893, -0.717147616692481, 0.47781000751239), + (-0.469705390085658, -0.33624876406351, 0.816280353304085), + (-0.336180458859188, 0.816354017519737, -0.4696262526314), + (0.469729267279509, 0.336087427571651, 0.816333054879763), + (0.816299320102214, -0.469688480988201, -0.336226338688182), + (0.336166188592078, 0.816261044646191, 0.469798042397566), + (0.816308187841148, 0.469684487990511, 0.336210386818421), + (0.336161196424763, -0.816254520485116, -0.469812949806501), + (-0.81631474754769, 0.469749196906201, -0.336104038866138), + (-0.336166711539314, -0.816355082377068, 0.469634242288587), + (-0.816302029136435, -0.469752338316787, 0.33613053695499), + (-0.469725914764869, 0.336254309274991, -0.816266258332602), + (0.469715709020586, -0.336082571137018, -0.816342855715183), + (0.220975783117544, 0.56198189964132, -0.797085972622227), + (0.56189854338611, -0.797188442616427, 0.220818056099052), + (-0.22090980871236, -0.561819935638318, -0.79721842448229), + (-0.7971433029262, 0.220906560624346, 0.561927794358875), + (-0.561911046458035, -0.797113560704263, -0.221056434445611), + (-0.797166608166814, -0.22092145416411, -0.561888876837612), + (-0.561903189214556, 0.797117195899141, 0.221063298519679), + (0.797149071206196, -0.221019917708182, 0.56187503437274), + (0.56187154518738, 0.797190222992272, -0.220880318440273), + (0.797151311779493, 0.220966501329483, -0.561892864715723), + (0.220989674227739, -0.56195922843892, 0.79709810529009), + (-0.220934514736207, 0.561821479644177, 0.797210489901321), + (-0.025586321091663, 0.991400659992677, -0.128335776535923), + (0.991391023154192, -0.128410509654448, -0.025584765380375), + (0.0255553186148, -0.991378867053065, -0.128510185009118), + (-0.128427355734578, -0.025687167640031, 0.991386193023514), + (-0.99138841235829, -0.128432289728374, 0.02557660643704), + (-0.128471046150121, 0.025696657527584, -0.991380286314492), + (-0.991388770492313, 0.128433611757029, -0.025556077805202), + (0.128434643068809, 0.02546732329508, 0.991390920829907), + (0.991386054149539, 0.128448345934336, 0.025587380962899), + (0.128392989158359, -0.02544975216483, -0.991396767419448), + (-0.025589705051665, -0.991398220731893, 0.128353943940207), + (0.025571746935955, 0.991376476866512, 0.128525354986419), + ], + 17: [ + (-0.053895316433783, -0.14060350667641, -0.988597971258691), + (-0.140602010826056, -0.988598302765153, -0.053893137981829), + (0.05389273741214, 0.140602992486377, -0.988598184986247), + (-0.988598098647216, -0.053895299659618, -0.140602617421263), + (0.140604478516356, -0.988597884351918, 0.053894375180116), + (-0.98859806586327, 0.053892813420617, 0.140603800919506), + (0.14060181241573, 0.988598276619905, -0.053894135205652), + (0.988598002509875, 0.053895652551635, -0.140603158106489), + (-0.140604846635692, 0.988597850035353, 0.053894044272357), + (0.988598167360928, -0.0538924507278, 0.140603226297146), + (-0.053892835910884, 0.140602751584219, 0.988598213878838), + (0.053896097450443, -0.140603793852246, 0.988597887836084), + (-0.712137820619482, 0.484725955627139, 0.50783902211694), + (0.484727142303201, 0.507838589157962, -0.71213732164283), + (0.712137878427749, -0.484726895412166, 0.507838044038163), + (0.507839760738435, -0.712137969376798, 0.484724963236905), + (-0.484727642870466, 0.507838390853387, 0.712137122338588), + (0.507839607814555, 0.712136191976364, -0.48472773472555), + (-0.484726768067281, -0.507839236872381, -0.712137114474401), + (-0.507840112632748, 0.712136257912531, 0.48472710896699), + (0.48472854912246, -0.507840095411427, 0.712135289926112), + (-0.507838861015403, -0.712137466171353, -0.484726645149226), + (-0.712136671868904, -0.484727632936327, -0.507839032024349), + (0.712137765857364, 0.484726401555971, -0.507838673275561), + (-0.703005448039525, 0.261790111709517, 0.66124827216248), + (0.26179085361446, 0.661247136036676, -0.7030062404041), + (0.703006433944545, -0.261790569085573, 0.661247042919986), + (0.661249487589413, -0.70300443378585, 0.261789765346499), + (-0.261791711051733, 0.661247232073399, 0.703005830772316), + (0.661247423359215, 0.703005908959219, -0.261791017930756), + (-0.261791042135151, -0.661248438610085, -0.703004944999334), + (-0.661249044674904, 0.703004424763402, 0.261790908321135), + (0.26179224626734, -0.661248916662998, 0.703004046934519), + (-0.661247254530942, -0.703006260525483, -0.261790500280794), + (-0.70300546948428, -0.261791326803081, -0.66124776830313), + (0.703005158463527, 0.261791025358218, -0.661248218308045), + (0.062800447246381, 0.786218819998244, -0.614748786827777), + (0.786220043108977, -0.614747449388309, 0.062798226760693), + (-0.062799502252198, -0.786219239565021, -0.614748346768559), + (-0.61474770709614, 0.062799571514381, 0.786219734194995), + (-0.786218534519124, -0.614749234835089, -0.062799635733612), + (-0.61474933069654, -0.062799956628617, -0.786218433932708), + (-0.786219538571286, 0.614747943528109, 0.062799706183351), + (0.614749395150454, -0.062799770458141, 0.786218398406292), + (0.786217798458002, 0.614750179051967, -0.062799607828601), + (0.614747800214019, 0.062800058129605, -0.786219622517107), + (0.062800526363459, -0.786218909415802, 0.614748664386918), + (-0.062801412397757, 0.786218712810953, 0.614748825315458), + (0.829543607739232, 0.321465368220585, 0.456637076783941), + (0.321463595502047, 0.456637380632479, 0.829544127443504), + (-0.82954344503853, -0.321464459743646, 0.456638011903666), + (0.456635000556537, 0.829545159774775, 0.321464312422039), + (-0.32146420025779, 0.456637459573867, -0.829543849634571), + (0.456637068558535, -0.829544000114897, -0.321464367374743), + (-0.321462954195433, -0.456636421102337, 0.829544904150941), + (-0.456636713034899, -0.829544190971737, 0.321464379883261), + (0.321462955106589, -0.456636688799517, -0.82954475643955), + (-0.456637112396323, 0.829544098578271, -0.321464051017078), + (0.829544701976578, -0.321462298506758, -0.45663724997129), + (-0.829544861446795, 0.321463589390512, -0.456636051515194), + (-0.249500423448462, 0.954025094362385, -0.166089307379737), + (0.954025470855406, -0.166087567010738, -0.249500142371853), + (0.249500943484422, -0.954025029612664, -0.166088898102612), + (-0.166086877408662, -0.249500137449683, 0.954025592196158), + (-0.954024855383494, -0.166089937003122, 0.249500918108135), + (-0.166090151998567, 0.249500107118379, -0.954025030047436), + (-0.954025593688894, 0.166087862658579, -0.249499475879328), + (0.166089692874499, 0.249499687822531, 0.954025219633797), + (0.954024931419817, 0.166090647913648, 0.249500154118264), + (0.166087956122076, -0.249500002352048, -0.954025439732882), + (-0.249499759982538, -0.954025225930409, 0.166089548307795), + (0.249498374708179, 0.954025720257113, 0.166088789826025), + (0.860787215766444, 0.418630333044569, -0.289471956203095), + (0.418631425510959, -0.289473932939102, 0.860786019707239), + (-0.860786771736426, -0.418630687137034, -0.289472764506019), + (-0.289474446503673, 0.860786651964262, 0.418629770347917), + (-0.418629889262302, -0.289472838226515, -0.860787134978979), + (-0.289472399693171, -0.860787556030986, -0.418629326729602), + (-0.418629257388446, 0.289472594933548, 0.86078752409688), + (0.289473185156189, -0.860787817544824, 0.418628245872098), + (0.418628942424652, 0.289472756316209, -0.860787623002977), + (0.289473542762772, 0.860786603163078, -0.418630495610795), + (0.860788012261758, -0.418628676927689, 0.289471982755196), + (-0.860787839361109, 0.418628479474405, 0.289472782452827), + (-0.16910412959425, -0.878917391692094, 0.445991044680649), + (-0.878918175066698, 0.445989574417742, -0.169103935637546), + (0.169102333488308, 0.878918206143462, 0.445990120651083), + (0.445989469034572, -0.169103982598388, -0.878918219506016), + (0.878916540171611, 0.445992443170219, 0.169104867014003), + (0.445990224108014, 0.169104198005711, 0.87891779491425), + (0.878918285318947, -0.445988968934561, -0.169104959479873), + (-0.445991404870479, 0.169104651147508, -0.87891710857278), + (-0.878917501870672, -0.445990557689425, 0.169104841318319), + (-0.4459886381463, -0.169104239878744, 0.878918591622365), + (-0.16910368279187, 0.878918081785917, -0.445989854117772), + (0.169104218306708, -0.878917936541782, -0.445989937303537), + (0.699159749436449, 0.682605593469953, 0.2126622875159), + (0.682603600110598, 0.212662432840561, 0.699161651389995), + (-0.699161274801242, -0.682604056820824, 0.21266220498729), + (0.212660843412531, 0.699162101347243, 0.68260363441662), + (-0.682604762820295, 0.212661985195386, -0.699160652373835), + (0.212662594223091, -0.699161699678606, -0.682603500372528), + (-0.682602562402764, -0.212662368159073, 0.69916268419457), + (-0.212661230546804, -0.699160950060591, 0.682604693019826), + (0.682603395227417, -0.21266176997579, -0.699162053042617), + (-0.212661876797938, 0.699162212981133, -0.682603198129121), + (0.699162077611852, -0.682602648532129, -0.212664085934605), + (-0.69916198280867, 0.682603270588311, -0.212662400948523), + (-0.893254372981228, -0.172342415041176, -0.415204428116666), + (-0.17234169138316, -0.415205105182347, -0.893254197886418), + (0.893254479512939, 0.172341865725213, -0.415204426937409), + (-0.415203760144359, -0.893254961141014, -0.172340975855865), + (0.172343621116966, -0.415205895670259, 0.893253458129858), + (-0.41520633444977, 0.89325370222595, 0.172341298859036), + (0.172340599563611, 0.415204277853847, -0.893254793098767), + (0.415204013461881, 0.893254652987798, -0.172341962739188), + (-0.17234119462921, 0.415204328749048, 0.893254654632054), + (0.415206142771325, -0.893254015914866, 0.172340134782712), + (-0.89325441464337, 0.172340274858685, 0.415205226823752), + (0.893254705389659, -0.172340550786628, 0.415204486793911), + (-0.030119107290242, 0.538031004327585, -0.842386774444073), + (0.538032715913301, -0.84238566918646, -0.030119444819523), + (0.030118087641353, -0.538031590262412, -0.842386436664628), + (-0.842386183209587, -0.030119347292783, 0.538031916577671), + (-0.538030304105545, -0.842387233636645, 0.030118772718924), + (-0.842387312723823, 0.030117901022641, -0.538030229076328), + (-0.538031723308682, 0.842386324999934, -0.030118834084264), + (0.842387103098144, 0.030119789658303, 0.53803045155907), + (0.538029173032331, 0.842387968746045, 0.030118417588877), + (0.842386330532407, -0.030117441179441, -0.538031792619125), + (-0.030117059116644, -0.538030739137179, 0.842387017049566), + (0.030118346812524, 0.538030710181824, 0.84238698950454), + (0.951905881051384, -0.301774121097739, 0.052986540323701), + (-0.301774405343499, 0.052986798530194, 0.951905776566724), + (-0.951905698855431, 0.3017745999, 0.05298708655653), + (0.052987612958423, 0.951905238066977, -0.301775960960479), + (0.301774562398047, 0.052986834212903, -0.951905724790833), + (0.052986766829206, -0.951905252173644, 0.301776065030379), + (0.301777293645336, -0.052987994727859, 0.951904794322844), + (-0.052986574701187, -0.951905591847301, -0.301775027315507), + (-0.301776941841401, -0.052986526316734, -0.951904987591586), + (-0.052988794896112, 0.951905240176443, 0.301775746772478), + (0.95190486181224, 0.301777462059556, -0.052985823114433), + (-0.951905018594348, -0.301776824324304, -0.052986638651951), + (0.553606146300219, 0.45440115669048, 0.697882385203248), + (0.454399619298559, 0.697882738116233, 0.55360696330584), + (-0.553605814018882, -0.454401128197097, 0.697882667342941), + (0.697880969772289, 0.55360789125326, 0.454401204632875), + (-0.454400796454347, 0.697882268834231, -0.553606588678677), + (0.697882520653254, -0.553605103541103, -0.454402219074583), + (-0.454400180900896, -0.697882141032518, 0.553607255032936), + (-0.697881635390884, -0.553607048847703, 0.454401208680482), + (0.454401271818775, -0.69788172578063, -0.553606883077631), + (-0.69788232191633, 0.553606343141444, -0.454401014072625), + (0.553606029480292, -0.454400306845994, -0.697883031217504), + (-0.553606960810672, 0.454400842716203, -0.697881943512493), + ], + 18: [ + (-0.866376343641697, 0.223696804580225, 0.446488265017841), + (0.223696806212017, 0.446488265347841, -0.866376343050305), + (0.866376343115579, -0.223696806225293, 0.44648826521453), + (0.44648826367979, -0.866376344067145, 0.223696805603153), + (-0.223696804286002, 0.446488265023544, 0.866376343714725), + (0.446488262849567, 0.866376344947941, -0.22369680384892), + (-0.2236968055886, -0.446488263582537, -0.866376344121022), + (-0.446488264810465, 0.866376343829741, 0.223696804265844), + (0.223696803801399, -0.446488262808774, 0.866376344981234), + (-0.446488265014924, -0.866376343219064, -0.223696806222901), + (-0.8663763449212, -0.223696804074408, -0.446488262788483), + (0.866376344172558, 0.223696805482214, -0.446488263535836), + (-0.806844783933568, -0.461758079243128, -0.368484695601989), + (-0.461758081774945, -0.368484698390835, -0.806844781210945), + (0.80684478133506, 0.461758081613586, -0.368484698321273), + (-0.368484697968357, -0.806844781706494, -0.461758081246195), + (0.461758078945765, -0.368484695716793, 0.806844784051319), + (-0.368484697702554, 0.806844783105505, 0.461758079013772), + (0.461758081217295, 0.368484698443883, -0.806844781505862), + (0.368484695481328, 0.806844784182969, -0.461758078903629), + (-0.461758078967836, 0.36848469793606, 0.806844783025151), + (0.36848469846061, -0.806844781213308, 0.461758081715136), + (-0.806844782774103, 0.461758079314706, 0.368484698051091), + (0.806844781709987, -0.461758081098712, 0.368484698145526), + (-0.134842418858112, -0.040021669507572, 0.990058477084218), + (-0.040021669975618, 0.990058477016276, -0.134842419218046), + (0.134842418981357, 0.040021669788942, 0.990058477056058), + (0.990058476924117, -0.134842420436143, -0.040021668151402), + (0.040021669677461, 0.990058477116921, 0.13484241856757), + (0.990058477286021, 0.134842417855397, 0.040021667893743), + (0.040021668037836, -0.990058476927568, -0.134842420444507), + (-0.990058477115239, 0.134842418635191, -0.040021669491235), + (-0.040021667837798, -0.990058477270892, 0.134842417983082), + (-0.990058477042031, -0.134842419087429, 0.040021669778575), + (-0.134842418122745, 0.040021667670891, -0.990058477258617), + (0.134842420378113, -0.040021667867212, -0.990058476943508), + (0.049794077313207, -0.279738156561879, -0.958784185115654), + (-0.279738157129975, -0.958784185068512, 0.049794075029415), + (-0.049794075085005, 0.279738157111834, -0.958784185070918), + (-0.958784184460955, 0.049794077429761, -0.279738158785068), + (0.279738156684233, -0.958784185083851, -0.049794077238191), + (-0.958784184306963, -0.049794076856858, 0.279738159414846), + (0.279738159012938, 0.958784184390379, 0.049794077508567), + (0.958784185034086, -0.049794077337113, -0.279738156837192), + (-0.279738159575992, 0.95878418424315, -0.049794077180261), + (0.958784185016722, 0.049794074909289, 0.279738157328865), + (0.049794077031905, 0.279738159517178, 0.958784184268015), + (-0.049794077785621, -0.279738158888691, 0.958784184412241), + (0.205470768670777, -0.192901743072287, 0.959463746444603), + (-0.192901744385898, 0.959463746331714, 0.205470767964668), + (-0.205470768086678, 0.19290174463045, 0.959463746256418), + (0.959463745738735, 0.205470770340502, -0.192901744804646), + (0.19290174290288, 0.959463746447685, -0.205470768815433), + (0.95946374536634, -0.205470771694041, 0.192901745215149), + (0.192901744892626, -0.959463745685675, 0.205470770505673), + (-0.959463746372533, -0.205470769064203, -0.192901743011692), + (-0.192901745122065, -0.959463745348892, -0.205470771862908), + (-0.959463746220563, 0.205470768260652, 0.192901744623478), + (0.205470771726444, 0.192901745460598, -0.959463745310053), + (-0.205470770652743, -0.192901744949698, -0.959463745642705), + (-0.278905392074019, 0.772004854137857, -0.571156972696319), + (0.772004854268172, -0.571156972466399, -0.278905392184152), + (0.278905392160238, -0.772004854249339, -0.571156972503532), + (-0.571156971675365, -0.278905392078835, 0.772004854891456), + (-0.772004854266013, -0.571156972533567, 0.278905392052577), + (-0.571156970567139, 0.278905391582234, -0.77200485589077), + (-0.772004855078365, 0.571156971421921, -0.27890539208049), + (0.57115697255647, 0.278905391952095, 0.77200485428537), + (0.772004855995114, 0.571156970376128, 0.278905391684575), + (0.571156972406906, -0.278905392034109, -0.772004854366394), + (-0.278905391724262, -0.772004855939801, 0.571156970431511), + (0.278905392185009, 0.772004854970249, 0.571156971517017), + (0.912363859945553, -0.393198149041577, -0.113962286110494), + (-0.393198146993911, -0.113962287143254, 0.912363860699027), + (-0.912363860783175, 0.393198146824102, -0.113962287055462), + (-0.113962288369946, 0.912363860162702, -0.393198147882843), + (0.393198149035756, -0.113962285681562, -0.912363860001638), + (-0.113962285259825, -0.912363861029922, 0.393198146771995), + (0.393198147677495, 0.113962288066404, 0.912363860289116), + (0.113962286031302, -0.912363859929225, -0.393198149102416), + (-0.39319814691232, 0.113962285090844, -0.912363860990554), + (0.113962287370876, 0.912363860762456, 0.393198146780759), + (0.9123638610767, 0.393198146641003, 0.113962285337288), + (-0.912363860199082, -0.39319814784926, 0.113962288194565), + (0.848662336981788, -0.012909984472825, -0.528777429633226), + (-0.01290998542466, -0.528777432035493, 0.848662335470524), + (-0.848662335552813, 0.012909985214183, -0.528777431908562), + (-0.52877743352546, 0.848662334565693, -0.012909983878239), + (0.012909984639682, -0.528777429281808, -0.848662337198209), + (-0.528777430386149, -0.848662336521067, 0.012909983920382), + (0.012909983944448, 0.52877743344648, 0.848662334613896), + (0.528777429496827, -0.848662337067122, -0.012909984449961), + (-0.012909983871647, 0.528777430419671, -0.848662336500922), + (0.528777432240356, 0.848662335344326, 0.012909985329594), + (0.848662336343559, 0.012909983743557, 0.528777430675359), + (-0.848662334668199, -0.012909983655303, 0.528777433366386), + (-0.69585113208617, 0.211164782101034, 0.686440555892948), + (0.211164781099711, 0.68644055554441, -0.695851132733858), + (0.695851132741401, -0.211164781335084, 0.686440555464357), + (0.686440553889191, -0.695851134384757, 0.211164781040182), + (-0.211164781930503, 0.686440555960218, 0.695851132071559), + (0.686440553598939, 0.695851134525998, -0.21116478151828), + (-0.21116478087036, -0.686440553737906, -0.69585113458553), + (-0.686440555776475, 0.695851132224679, 0.211164782023223), + (0.211164781498505, -0.686440553499572, 0.695851134630023), + (-0.686440555292332, -0.695851132981083, -0.211164781104467), + (-0.695851134744882, -0.211164781531153, -0.686440553373094), + (0.695851134495813, 0.211164781101486, -0.686440553757753), + (-0.261718169263029, -0.581630098396244, 0.770201290908541), + (-0.581630098290833, 0.770201291506502, -0.261718167737572), + (0.261718167857864, 0.581630098126426, 0.770201291589781), + (0.770201292726794, -0.261718168321791, -0.581630096412025), + (0.581630098450626, 0.770201290888376, 0.261718169201518), + (0.770201293263127, 0.261718168077775, 0.581630095811608), + (0.581630096213803, -0.770201292881278, -0.261718168307686), + (-0.770201291051568, 0.26171816913029, -0.581630098266577), + (-0.581630095716607, -0.770201293304276, 0.261718168167806), + (-0.770201291705965, -0.261718167641045, 0.581630098070137), + (-0.261718168076348, 0.581630095637746, -0.770201293394907), + (0.261718168494542, -0.581630096129926, -0.770201292881124), + (0.506136437086844, 0.700992881596967, 0.502428987025446), + (0.700992883568509, 0.502428985302136, 0.506136436066968), + (-0.506136436123196, -0.700992883503112, 0.502428985336736), + (0.502428986281426, 0.506136435764488, 0.700992883085013), + (-0.700992881635171, 0.502428986938925, -0.50613643711982), + (0.502428986199081, -0.506136436342322, -0.700992882726821), + (-0.700992883178434, -0.502428986124795, 0.506136435790584), + (-0.502428987099143, -0.506136437034413, 0.700992881582003), + (0.700992882671006, -0.502428986197914, -0.506136436420782), + (-0.502428985277898, 0.506136435955935, -0.700992883666051), + (0.506136436300189, -0.700992882789867, -0.502428986153563), + (-0.506136435852246, 0.700992882991532, -0.502428986323445), + (-0.440748149182578, 0.602242024157979, 0.665616716534547), + (0.602242022260099, 0.66561671834234, -0.440748149045733), + (0.440748149100016, -0.602242022337998, 0.665616718235914), + (0.665616715634027, -0.440748149390786, 0.602242025000887), + (-0.602242023804998, 0.665616716814167, 0.440748149242614), + (0.665616716586012, 0.440748149783209, -0.602242023661529), + (-0.602242024940208, -0.665616715760932, -0.440748149282046), + (-0.665616716462371, 0.440748149424008, 0.602242024061062), + (0.602242023852026, -0.665616716460744, 0.440748149712092), + (-0.665616718266293, -0.44074814917988, -0.602242022245974), + (-0.440748149655782, -0.602242023883194, -0.66561671646983), + (0.44074814928254, 0.602242025306933, -0.665616715428797), + (-0.89025783677553, -0.293518547758229, 0.348264046639405), + (-0.293518546899673, 0.348264043649922, -0.890257838228066), + (0.890257838178446, 0.293518546762444, 0.348264043892422), + (0.34826404446276, -0.890257837322353, -0.293518548682307), + (0.293518547908785, 0.348264046686625, 0.89025783670742), + (0.348264047178787, 0.890257836270502, 0.293518548650024), + (0.293518548932545, -0.348264044336184, -0.890257837289365), + (-0.348264046901224, 0.890257836626627, -0.29351854789921), + (-0.2935185489462, -0.348264047080228, 0.890257836211408), + (-0.348264043786589, -0.890257838192766, 0.293518546844585), + (-0.890257836357219, 0.293518548692058, -0.348264046921688), + (0.890257837186443, -0.293518548811097, -0.348264044701638), + (0.661971946522154, 0.031389655564508, 0.748871037990662), + (0.03138965429721, 0.748871040172752, 0.661971944113708), + (-0.661971944196008, -0.031389654112142, 0.748871040107759), + (0.748871039164329, 0.661971945218693, 0.031389655052549), + (-0.031389655768972, 0.748871037783183, -0.661971946747175), + (0.748871037422933, -0.661971947171443, -0.031389655416215), + (-0.031389655026768, -0.748871039044161, 0.661971945355858), + (-0.748871037767735, -0.661971946761125, 0.031389655843332), + (0.03138965553856, -0.748871037222178, -0.661971947392751), + (-0.748871040238931, 0.661971944045087, -0.031389654165497), + (0.66197194707148, -0.031389655223358, -0.748871037519379), + (-0.661971945551351, 0.031389654961479, -0.74887103887409), + (-0.125732546862956, -0.877697090664539, -0.462427446956124), + (-0.877697091831705, -0.462427445382079, -0.125732544504481), + (0.125732544403638, 0.877697091976424, -0.46242744513482), + (-0.462427446167756, -0.125732547895101, -0.877697090932044), + (0.877697090790478, -0.462427446687307, 0.125732546972493), + (-0.462427443232932, 0.125732547131528, 0.877697092587683), + (0.87769709111192, 0.462427445862905, -0.12573254776065), + (0.462427446678181, 0.125732547366796, -0.877697090738801), + (-0.87769709250851, 0.462427443357225, 0.125732547227075), + (0.462427444949274, -0.125732544734265, 0.877697092026818), + (-0.125732546895942, 0.877697092616795, 0.462427443241732), + (0.125732547889573, -0.877697091021935, 0.462427445998644), + ], + 19: [ + (0.553035945587524, -0.472050222255944, 0.686527370580538), + (-0.472050227459673, 0.686527365766638, 0.553035947121696), + (-0.55303594558747, 0.472050222505474, 0.686527370409006), + (0.686527372366403, 0.553035941501725, -0.472050224445432), + (0.472050228567412, 0.686527364805305, -0.553035947369552), + (0.68652737203169, -0.553035941518164, 0.472050224912964), + (0.472050228340927, -0.686527365268236, 0.553035946988198), + (-0.686527371732145, -0.553035942965273, -0.47205022365323), + (-0.472050227580466, -0.686527365608527, -0.553035947214868), + (-0.686527371021655, 0.553035942983048, 0.472050224665708), + (0.553035946644886, 0.472050221691609, -0.686527370116806), + (-0.553035947212832, -0.472050222465287, -0.68652736912732), + (0.534151654424436, 0.792082393152326, 0.29544456761586), + (0.792082397489039, 0.295444568376044, 0.534151647573148), + (-0.53415165460592, -0.792082392760173, 0.295444568339099), + (0.295444567949351, 0.534151645341887, 0.792082399152876), + (-0.792082397600766, 0.29544456929757, -0.534151646897765), + (0.295444567829592, -0.534151645364488, -0.792082399182305), + (-0.792082397865911, -0.295444567933543, 0.534151647259045), + (-0.295444567962042, -0.53415164560035, 0.792082398973845), + (0.792082397128777, -0.29544456908432, -0.534151647715618), + (-0.295444567489444, 0.53415164476261, -0.792082399715064), + (0.534151654464793, -0.792082393125927, -0.29544456761367), + (-0.534151654460867, 0.792082392713663, -0.295444568726043), + (-0.987783901989363, -0.008366313346394, -0.155605166275604), + (-0.008366316491905, -0.155605166254194, -0.987783901966094), + (0.987783902042018, 0.008366312354305, -0.155605165994688), + (-0.155605167507252, -0.98778390181532, -0.008366310987655), + (0.008366315777747, -0.155605166766477, 0.987783901891443), + (-0.155605168424393, 0.987783901667492, 0.008366311383278), + (0.008366317026602, 0.155605166706053, -0.987783901890384), + (0.155605166835858, 0.987783901919836, -0.008366311135093), + (-0.008366315838957, 0.155605165685948, 0.98778390206114), + (0.155605167761508, -0.987783901773982, 0.008366311139443), + (-0.98778390211314, 0.008366313179595, 0.155605165498836), + (0.987783902165643, -0.008366312162939, 0.155605165220208), + (0.950764981387945, 0.202727494112491, -0.234408859255043), + (0.202727496789996, -0.234408860732757, 0.950764980452705), + (-0.950764980986237, -0.202727494847485, -0.234408860248721), + (-0.23440885021567, 0.950764983233011, 0.202727495911382), + (-0.20272749729896, -0.234408861592541, -0.950764980132203), + (-0.23440885011577, -0.950764983322899, -0.20272749560533), + (-0.202727496759051, 0.234408860491485, 0.950764980518789), + (0.234408850569327, -0.950764983253747, 0.20272749540521), + (0.202727497565203, 0.234408861679376, -0.950764980054025), + (0.234408850341461, 0.950764983305224, -0.202727495427267), + (0.950764981380539, -0.202727493695606, 0.234408859645621), + (-0.950764980970666, 0.202727494426432, 0.234408860676023), + (0.512072989115983, -0.124051607170076, -0.849936734455185), + (-0.12405160965336, -0.849936734716267, 0.512072988081055), + (-0.51207298893537, 0.124051606674421, -0.849936734636344), + (-0.849936734725645, 0.512072989351902, -0.124051604343177), + (0.124051609706947, -0.849936734419284, -0.512072988561004), + (-0.849936734185619, -0.512072990133951, 0.124051604814925), + (0.124051609905272, 0.849936734159209, 0.512072988944631), + (0.849936734486865, -0.512072989718667, -0.124051604465195), + (-0.124051609776913, 0.849936734911909, -0.512072987726399), + (0.849936733865973, 0.512072990727649, 0.124051604554246), + (0.512072989657044, 0.124051606837459, 0.849936734177751), + (-0.512072989396574, -0.124051606970032, 0.84993673431533), + (0.391883697914976, 0.850423194793585, -0.351009340424947), + (0.850423195330397, -0.351009335923244, 0.39188370078221), + (-0.391883697466306, -0.850423195007668, -0.351009340407185), + (-0.351009335872243, 0.391883705326061, 0.850423193257595), + (-0.850423194593128, -0.351009337444654, -0.391883701019427), + (-0.35100933799945, -0.391883705264188, -0.850423192408108), + (-0.850423194468673, 0.351009337760337, 0.391883701006749), + (0.351009335527498, -0.391883705866852, 0.850423193150685), + (0.850423195361416, 0.351009336170377, -0.391883700493539), + (0.351009336873483, 0.39188370616407, -0.850423192458173), + (0.391883698323181, -0.850423194786778, 0.3510093399857), + (-0.391883698167036, 0.850423194811902, 0.351009340099156), + (-0.637143378120116, -0.628499374133282, 0.446135464216598), + (-0.628499375204954, 0.446135468086576, -0.637143374353178), + (0.63714337757707, 0.628499374826823, 0.446135464015109), + (0.446135466991108, -0.63714337381196, -0.628499376531226), + (0.628499375911897, 0.446135468292267, 0.637143373511799), + (0.446135467311664, 0.637143373480954, 0.628499376639239), + (0.62849937527089, -0.446135468666392, -0.637143373882143), + (-0.446135467006437, 0.637143373286424, -0.628499377053108), + (-0.628499376195147, -0.446135467887251, 0.637143373515989), + (-0.446135467633094, -0.637143373382135, 0.628499376511253), + (-0.637143377816856, 0.628499373935058, -0.446135464928946), + (0.637143377542956, -0.62849937478419, -0.446135464123887), + (-0.420378708184596, 0.903565957647232, -0.082766550526719), + (0.903565960547129, -0.082766548074817, -0.420378702434272), + (0.42037870768752, -0.903565957904322, -0.082766550244743), + (-0.08276654593922, -0.420378701283585, 0.9035659612781), + (-0.903565960760554, -0.082766547146253, 0.420378702158358), + (-0.082766545039106, 0.420378701254078, -0.903565961374279), + (-0.903565960509685, 0.082766547722836, -0.420378702584056), + (0.082766546052241, 0.420378700439882, 0.903565961660275), + (0.90356596090935, 0.082766547862683, 0.420378701697478), + (0.082766545679396, -0.420378701270528, -0.903565961307975), + (-0.420378707505505, -0.903565957945495, 0.082766550719722), + (0.420378706956033, 0.903565958233438, 0.082766550367062), + (0.491848298473796, 0.355367007972287, 0.794858189196825), + (0.355367012626596, 0.794858187708896, 0.491848297515583), + (-0.491848298344631, -0.355367008156744, 0.794858189194284), + (0.794858192062911, 0.491848294626225, 0.355367006886901), + (-0.355367012548889, 0.794858187634159, -0.491848297692508), + (0.794858192091183, -0.491848294618182, -0.355367006834796), + (-0.355367012605403, -0.79485818761909, 0.491848297676028), + (-0.79485819260841, -0.491848293926967, 0.355367006634583), + (0.35536701250799, -0.794858187986535, -0.491848297152596), + (-0.794858192358054, 0.491848294578517, -0.355367006292778), + (0.491848297979809, -0.355367007868558, -0.794858189548874), + (-0.491848297571808, 0.355367007417215, -0.794858190003127), + (0.060667255805915, 0.97798263888706, 0.199673338501868), + (0.977982638810576, 0.199673341482313, 0.060667247229371), + (-0.06066725576913, -0.977982638936182, 0.199673338272451), + (0.19967333790937, 0.060667250976018, 0.977982639307643), + (-0.977982639072168, 0.199673340242081, -0.060667247094362), + (0.199673337373138, -0.060667251086811, -0.977982639410252), + (-0.977982638978921, -0.199673340702871, 0.060667247080943), + (-0.199673337990036, -0.060667251306886, 0.977982639270649), + (0.977982638897865, -0.199673341052594, -0.060667247236562), + (-0.199673337084201, 0.060667250789575, -0.977982639487682), + (0.06066725570001, -0.977982638898456, -0.199673338478232), + (-0.060667256074209, 0.977982638961939, -0.199673338053604), + (-0.708312961873346, 0.702414591990534, 0.070046334671986), + (0.702414584158394, 0.070046328146925, -0.70831297028554), + (0.70831296180624, -0.702414591950002, 0.070046335757007), + (0.070046325730793, -0.7083129711293, 0.702414583548491), + (-0.702414584241602, 0.070046328819927, 0.70831297013647), + (0.070046325138075, 0.708312971393231, -0.70241458334145), + (-0.702414584340882, -0.070046327329757, -0.708312970185382), + (-0.070046326094986, 0.708312970542407, 0.702414584103993), + (0.702414584126282, -0.070046328999645, 0.708312970233058), + (-0.07004632593766, -0.708312970292399, -0.70241458437179), + (-0.70831296129047, -0.702414592488956, -0.070046335567964), + (0.708312961059513, 0.702414592640383, -0.070046336384914), + (-0.608778246497891, -0.729529462544733, -0.311730348009535), + (-0.729529461162802, -0.311730341531525, -0.608778251471052), + (0.608778246679673, 0.729529462023489, -0.31173034887438), + (-0.311730343069402, -0.608778253416134, -0.729529458882528), + (0.729529460955067, -0.311730341992774, 0.608778251483804), + (-0.311730342453046, 0.608778253837742, 0.729529458794075), + (0.729529461285603, 0.311730341286902, -0.608778251449154), + (0.311730342676067, 0.608778254584565, -0.729529458075568), + (-0.729529460737167, 0.311730342625706, 0.608778251420826), + (0.311730342500045, -0.608778254449614, 0.729529458263397), + (-0.608778247292532, 0.72952946202083, 0.31173034768375), + (0.608778247330452, -0.729529461617846, 0.311730348552781), + (0.230102774190651, -0.807756554170623, 0.542754145543051), + (-0.807756552084345, 0.542754149424728, 0.230102772358463), + (-0.230102773683601, 0.807756554197333, 0.542754145718266), + (0.542754144206019, 0.230102772513564, -0.807756555546758), + (0.807756552132751, 0.542754149180793, -0.230102772763921), + (0.54275414387689, -0.230102773432955, 0.807756555506005), + (0.807756552229309, -0.542754148882616, 0.230102773128283), + (-0.542754145084005, -0.230102772500065, -0.80775655496066), + (-0.807756552237738, -0.542754149346909, -0.230102772003543), + (-0.542754144288786, 0.230102773227955, 0.807756555287639), + (0.230102774097675, 0.807756554025896, -0.542754145797859), + (-0.230102773562357, -0.807756553761761, -0.542754146417909), + (-0.496383809474105, -0.862518230775131, -0.098312843883766), + (-0.862518224287333, -0.098312838975785, -0.49638382171939), + (0.496383809596231, 0.862518230686221, -0.098312844047173), + (-0.098312839350041, -0.496383823019562, -0.862518223496418), + (0.862518224300261, -0.098312838333147, 0.496383821824206), + (-0.098312838299782, 0.496383823078636, 0.862518223582133), + (0.862518224470515, 0.098312838524506, -0.496383821490472), + (0.09831283917121, 0.496383824314041, -0.862518222771822), + (-0.862518224078588, 0.098312839378387, 0.496383822002367), + (0.098312838470056, -0.49638382381015, 0.862518223141735), + (-0.496383810069422, 0.862518230414379, 0.098312844042943), + (0.496383810403814, -0.862518230215463, 0.098312844099726), + (0.278692551327958, 0.919313188465131, 0.277837584477674), + (0.919313191744972, 0.277837581526559, 0.278692543450923), + (-0.278692551566547, -0.91931318841363, 0.277837584408758), + (0.277837583051005, 0.278692544908351, 0.919313190842426), + (-0.919313192180326, 0.277837580345951, -0.278692543191822), + (0.277837582008532, -0.278692545046071, -0.919313191115735), + (-0.919313192196504, -0.277837580255645, 0.278692543228489), + (-0.277837582825575, -0.278692545265575, 0.919313190802263), + (0.919313191814052, -0.277837581086655, -0.278692543661607), + (-0.277837581528602, 0.278692544535811, -0.919313191415468), + (0.278692551299389, -0.91931318860489, -0.277837584043894), + (-0.278692551719555, 0.919313188501633, -0.277837583964092), + (0.711723818982073, -0.147355178359107, -0.686830151423428), + (-0.14735518004562, -0.686830151696651, 0.711723818369232), + (-0.711723818994987, 0.147355179083635, -0.686830151254603), + (-0.686830156031755, 0.711723816221312, -0.147355170213896), + (0.147355179878181, -0.68683015150873, -0.711723818585246), + (-0.686830155899656, -0.711723816480405, 0.147355169578202), + (0.147355179832049, 0.686830151151262, 0.711723818939762), + (0.686830156307707, -0.711723816117428, -0.147355169429431), + (-0.147355180410728, 0.686830151596083, -0.711723818390689), + (0.686830155813336, 0.711723816667769, 0.147355169075579), + (0.711723819167954, 0.147355177853636, 0.686830151339256), + (-0.711723818958232, -0.147355177932743, 0.686830151539607), + (0.910866815770901, -0.407547474081887, 0.065013077890936), + (-0.407547470055014, 0.06501307469253, 0.910866817800923), + (-0.91086681602966, 0.40754747351676, 0.065013077808199), + (0.065013071417123, 0.910866817243773, -0.407547471822745), + (0.407547469547224, 0.065013074424327, -0.910866818047266), + (0.065013071503944, -0.910866817193855, 0.407547471920462), + (0.407547469994702, -0.065013074730498, 0.910866817825199), + (-0.065013071237167, -0.910866817002829, -0.407547472389962), + (-0.407547469492954, -0.065013074760909, -0.910866818047525), + (-0.065013070894069, 0.910866817046896, 0.407547472346204), + (0.910866815571027, 0.407547474607393, -0.065013077397032), + (-0.910866815826998, -0.407547474069762, -0.065013077180997), + ], + 20: [ + (-0.251581299355938, 0.965702462813156, -0.064230858090044), + (0.965702462812973, -0.064230858090163, -0.251581299356609), + (0.25158129935621, -0.965702462813076, -0.064230858090184), + (-0.064230858090037, -0.251581299356469, 0.965702462813018), + (-0.965702462812988, -0.064230858090212, 0.25158129935654), + (-0.064230858090283, 0.251581299356213, -0.965702462813068), + (-0.965702462813129, 0.06423085809035, -0.251581299355962), + (0.064230858090209, 0.251581299356322, 0.965702462813045), + (0.96570246281309, 0.064230858089911, 0.251581299356226), + (0.0642308580902, -0.2515812993563, -0.965702462813051), + (-0.2515812993566, -0.965702462812992, 0.064230858089919), + (0.251581299356516, 0.965702462812981, 0.064230858090402), + (-0.774265533845772, 0.381515182343397, -0.504934697500583), + (0.381515182343197, -0.504934697500657, -0.774265533845823), + (0.774265533845583, -0.381515182343386, -0.504934697500883), + (-0.504934697500797, -0.774265533845681, 0.3815151823433), + (-0.381515182343153, -0.504934697500805, 0.774265533845748), + (-0.504934697500622, 0.774265533845887, -0.381515182343114), + (-0.381515182343272, 0.504934697500883, -0.774265533845639), + (0.504934697500808, 0.774265533845615, 0.381515182343419), + (0.38151518234349, 0.504934697500621, 0.774265533845703), + (0.50493469750058, -0.774265533845806, -0.381515182343333), + (-0.774265533845719, -0.381515182343321, 0.504934697500723), + (0.774265533845894, 0.38151518234298, 0.504934697500711), + (0.621892089865857, 0.451716799694261, -0.639689113113747), + (0.451716799694191, -0.639689113113918, 0.621892089865731), + (-0.621892089865648, -0.451716799694225, -0.639689113113976), + (-0.639689113113901, 0.621892089865499, 0.451716799694535), + (-0.451716799694008, -0.6396891131138, -0.621892089865986), + (-0.639689113113879, -0.621892089865655, -0.451716799694351), + (-0.451716799694347, 0.639689113113675, 0.621892089865869), + (0.639689113113788, -0.621892089865995, 0.451716799694013), + (0.451716799694587, 0.639689113113955, -0.621892089865406), + (0.639689113114061, 0.6218920898659, -0.451716799693757), + (0.621892089865889, -0.451716799694281, 0.639689113113701), + (-0.621892089865898, 0.451716799693713, 0.639689113114094), + (0.281811042675091, 0.858047847696197, -0.429344182783814), + (0.858047847696408, -0.429344182783659, 0.281811042674688), + (-0.281811042675114, -0.858047847696306, -0.429344182783581), + (-0.429344182783315, 0.281811042674947, 0.858047847696495), + (-0.858047847696386, -0.429344182783329, -0.281811042675257), + (-0.429344182783979, -0.281811042674793, -0.858047847696213), + (-0.858047847696136, 0.429344182783948, 0.281811042675075), + (0.429344182783574, -0.281811042675002, 0.858047847696347), + (0.85804784769643, 0.429344182783432, -0.281811042674964), + (0.429344182783407, 0.2818110426754, -0.8580478476963), + (0.28181104267478, -0.858047847696515, 0.429344182783383), + (-0.281811042675193, 0.858047847696227, 0.429344182783688), + (-0.649612004107369, -0.615311084069471, 0.44653836782617), + (-0.615311084069575, 0.446538367826544, -0.649612004107014), + (0.649612004107338, 0.615311084069274, 0.446538367826487), + (0.44653836782629, -0.649612004107234, -0.615311084069526), + (0.615311084069631, 0.446538367826189, 0.649612004107205), + (0.4465383678263, 0.649612004107223, 0.615311084069531), + (0.615311084069337, -0.44653836782627, -0.649612004107428), + (-0.446538367826248, 0.649612004107346, -0.615311084069439), + (-0.615311084069373, -0.446538367826536, 0.649612004107211), + (-0.446538367826286, -0.649612004107303, 0.615311084069457), + (-0.649612004107121, 0.615311084069723, -0.446538367826183), + (0.649612004107125, -0.615311084069551, -0.446538367826415), + (0.993363116319503, -0.113468728148246, -0.018829946054775), + (-0.113468728148035, -0.018829946054639, 0.993363116319529), + (-0.993363116319523, 0.113468728148204, -0.018829946053964), + (-0.018829946053903, 0.993363116319554, -0.113468728147943), + (0.113468728148066, -0.018829946054323, -0.993363116319532), + (-0.018829946054743, -0.993363116319533, 0.113468728147986), + (0.113468728148219, 0.018829946054485, 0.993363116319511), + (0.018829946054344, -0.99336311631951, -0.113468728148254), + (-0.113468728148178, 0.018829946054246, -0.993363116319521), + (0.018829946054485, 0.993363116319503, 0.113468728148287), + (0.99336311631954, 0.113468728147985, 0.018829946054382), + (-0.993363116319531, -0.113468728148037, 0.018829946054542), + (0.246398885891569, -0.720801569649804, 0.647867799957501), + (-0.720801569649392, 0.647867799957886, 0.246398885891762), + (-0.246398885891682, 0.720801569649632, 0.647867799957649), + (0.647867799957437, 0.246398885891663, -0.720801569649829), + (0.720801569649864, 0.647867799957577, -0.246398885891192), + (0.647867799957658, -0.246398885891679, 0.720801569649625), + (0.720801569649656, -0.647867799957734, 0.246398885891389), + (-0.647867799957904, -0.246398885891433, -0.720801569649489), + (-0.720801569649865, -0.647867799957373, -0.246398885891727), + (-0.647867799957474, 0.246398885891166, 0.720801569649966), + (0.246398885891794, 0.720801569649507, -0.647867799957745), + (-0.246398885891456, -0.720801569649666, -0.647867799957697), + (-0.793544204802179, -0.387628773401269, -0.469075184865183), + (-0.387628773401353, -0.469075184864794, -0.793544204802368), + (0.793544204802171, 0.387628773401536, -0.469075184864975), + (-0.469075184865097, -0.793544204802034, -0.387628773401668), + (0.38762877340168, -0.469075184864988, 0.793544204802093), + (-0.46907518486511, 0.793544204802104, 0.387628773401512), + (0.387628773401425, 0.469075184865298, -0.793544204802035), + (0.469075184865068, 0.793544204802337, -0.387628773401084), + (-0.387628773401491, 0.469075184864931, 0.793544204802219), + (0.469075184864784, -0.793544204802296, 0.387628773401512), + (-0.793544204802265, 0.387628773401224, 0.469075184865075), + (0.793544204802185, -0.387628773401823, 0.469075184864715), + (0.164945057653003, -0.958376909717154, 0.233038251960587), + (-0.958376909716935, 0.233038251961126, 0.164945057653512), + (-0.164945057653238, 0.958376909717001, 0.233038251961048), + (0.233038251960668, 0.164945057653504, -0.958376909717048), + (0.958376909717102, 0.233038251960514, -0.164945057653409), + (0.233038251960742, -0.164945057653288, 0.958376909717067), + (0.958376909717099, -0.233038251960827, 0.164945057652982), + (-0.233038251961122, -0.164945057653226, -0.958376909716986), + (-0.958376909717093, -0.233038251960632, -0.164945057653293), + (-0.233038251960434, 0.164945057653261, 0.958376909717147), + (0.164945057653494, 0.958376909716965, -0.233038251961015), + (-0.164945057653458, -0.958376909717031, -0.233038251960769), + (0.560484250466976, 0.813252649483695, -0.156452974040834), + (0.81325264948369, -0.156452974041446, 0.560484250466813), + (-0.56048425046724, -0.813252649483431, -0.156452974041263), + (-0.15645297404103, 0.560484250467047, 0.813252649483609), + (-0.81325264948382, -0.156452974040726, -0.560484250466826), + (-0.156452974041097, -0.560484250466778, -0.813252649483781), + (-0.81325264948363, 0.156452974040967, 0.560484250467035), + (0.156452974041285, -0.560484250467053, 0.813252649483555), + (0.813252649483481, 0.156452974041151, -0.560484250467199), + (0.156452974040881, 0.560484250466996, -0.813252649483672), + (0.560484250466836, -0.813252649483737, 0.156452974041122), + (-0.56048425046674, 0.813252649483823, 0.156452974041018), + (0.366630058651312, 0.922018832550933, -0.124353015704282), + (0.92201883255088, -0.124353015704762, 0.366630058651284), + (-0.366630058651761, -0.922018832550708, -0.124353015704629), + (-0.124353015704377, 0.366630058651577, 0.922018832550815), + (-0.922018832550933, -0.124353015704203, -0.366630058651341), + (-0.124353015704534, -0.366630058651111, -0.922018832550979), + (-0.922018832550883, 0.124353015704478, 0.366630058651372), + (0.12435301570463, -0.366630058651537, 0.922018832550797), + (0.922018832550745, 0.124353015704463, -0.366630058651723), + (0.124353015704299, 0.366630058651563, -0.922018832550831), + (0.366630058651286, -0.922018832550923, 0.124353015704438), + (-0.366630058651229, 0.922018832550938, 0.124353015704492), + (-0.804671953651735, -0.070836250755727, 0.589478814365005), + (-0.070836250756058, 0.589478814365003, -0.804671953651707), + (0.804671953651921, 0.070836250755383, 0.589478814364792), + (0.589478814364726, -0.804671953651941, -0.070836250755714), + (0.070836250755939, 0.589478814364776, 0.804671953651884), + (0.589478814365018, 0.804671953651715, 0.070836250755846), + (0.070836250755601, -0.589478814364811, -0.804671953651888), + (-0.589478814364784, 0.804671953651884, -0.070836250755875), + (-0.070836250755551, -0.589478814364944, 0.804671953651795), + (-0.589478814364978, -0.804671953651759, 0.07083625075567), + (-0.804671953651836, 0.070836250756193, -0.589478814364811), + (0.804671953651731, -0.070836250755764, -0.589478814365006), + (-0.830597137771463, -0.481356221636722, 0.280008183125909), + (-0.481356221636763, 0.280008183126324, -0.830597137771299), + (0.830597137771467, 0.481356221636628, 0.280008183126056), + (0.280008183125864, -0.830597137771343, -0.481356221636956), + (0.481356221637075, 0.280008183125899, 0.830597137771262), + (0.280008183126004, 0.830597137771351, 0.481356221636859), + (0.481356221636653, -0.280008183125859, -0.83059713777152), + (-0.280008183126012, 0.83059713777152, -0.481356221636564), + (-0.481356221636741, -0.280008183126112, 0.830597137771384), + (-0.280008183126053, -0.830597137771314, 0.481356221636894), + (-0.830597137771366, 0.48135622163684, -0.280008183125994), + (0.830597137771194, -0.481356221637029, -0.280008183126178), + (0.622576105404642, 0.027441908430236, -0.782077959439399), + (0.027441908430276, -0.782077959439431, 0.622576105404601), + (-0.622576105404963, -0.027441908430045, -0.78207795943915), + (-0.782077959439118, 0.622576105404988, 0.027441908430397), + (-0.027441908430201, -0.782077959439296, -0.622576105404774), + (-0.782077959439408, -0.622576105404628, -0.027441908430289), + (-0.027441908430238, 0.782077959439221, 0.622576105404866), + (0.782077959439263, -0.62257610540482, 0.027441908430083), + (0.027441908430419, 0.782077959439269, -0.622576105404798), + (0.782077959439451, 0.622576105404591, -0.027441908429928), + (0.622576105404788, -0.02744190843038, 0.782077959439278), + (-0.622576105404572, 0.027441908429868, 0.782077959439468), + (-0.93186959347387, 0.318712863282032, -0.173323891998229), + (0.318712863281944, -0.173323891998258, -0.931869593473894), + (0.931869593473744, -0.318712863282051, -0.173323891998871), + (-0.173323891998841, -0.931869593473836, 0.318712863281799), + (-0.318712863281924, -0.173323891998617, 0.931869593473834), + (-0.173323891998245, 0.931869593473975, -0.318712863281714), + (-0.318712863281997, 0.173323891998515, -0.931869593473828), + (0.173323891998501, 0.931869593473801, 0.318712863282084), + (0.318712863282089, 0.173323891998539, 0.931869593473793), + (0.173323891998443, -0.931869593473824, -0.31871286328205), + (-0.931869593473865, -0.318712863281928, 0.173323891998448), + (0.931869593473897, 0.318712863281802, 0.173323891998503), + (0.883848176852703, 0.201423804475213, 0.422185801827685), + (0.201423804475703, 0.422185801827661, 0.883848176852602), + (-0.883848176852534, -0.201423804475554, 0.422185801827875), + (0.42218580182791, 0.883848176852484, 0.201423804475701), + (-0.201423804475472, 0.422185801827744, -0.883848176852615), + (0.422185801827623, -0.883848176852647, -0.201423804475586), + (-0.201423804475397, -0.422185801827833, 0.88384817685259), + (-0.42218580182793, -0.883848176852523, 0.201423804475489), + (0.201423804475479, -0.422185801827682, -0.883848176852643), + (-0.422185801827514, 0.883848176852769, -0.20142380447528), + (0.883848176852476, -0.201423804475614, -0.422185801827967), + (-0.88384817685271, 0.201423804475563, -0.422185801827502), + (0.204275039956405, 0.718770569884226, 0.664560438123663), + (0.718770569884334, 0.664560438123474, 0.204275039956637), + (-0.20427503995626, -0.718770569884265, 0.664560438123664), + (0.66456043812381, 0.204275039956156, 0.71877056988416), + (-0.718770569884325, 0.664560438123579, -0.204275039956328), + (0.664560438123492, -0.204275039956373, -0.718770569884393), + (-0.718770569884361, -0.664560438123554, 0.20427503995628), + (-0.664560438123554, -0.204275039956662, 0.718770569884254), + (0.71877056988409, -0.664560438123802, -0.204275039956432), + (-0.664560438123505, 0.204275039956682, -0.718770569884293), + (0.204275039956376, -0.718770569884165, -0.664560438123738), + (-0.204275039956367, 0.718770569884538, -0.664560438123337), + (-0.898847927472069, 0.43770082336828, 0.022144807560617), + (0.437700823367923, 0.022144807560963, -0.898847927472234), + (0.898847927472182, -0.437700823368065, 0.02214480756027), + (0.022144807560315, -0.898847927472293, 0.437700823367834), + (-0.437700823367766, 0.022144807560623, 0.898847927472319), + (0.022144807560559, 0.89884792747216, -0.437700823368094), + (-0.437700823368255, -0.022144807560327, -0.898847927472088), + (-0.022144807560661, 0.898847927472103, 0.437700823368207), + (0.43770082336803, -0.022144807560607, 0.898847927472191), + (-0.022144807560733, -0.898847927472195, -0.437700823368015), + (-0.898847927472245, -0.437700823367908, -0.022144807560796), + (0.898847927472313, 0.437700823367778, -0.022144807560634), + ], + 21: [ + (0.892653535762723, 0.412534053657361, -0.181618610454253), + (0.412534053425032, -0.181618610641782, 0.892653535831938), + (-0.892653535806407, -0.412534053627853, -0.181618610306575), + (-0.181618610613849, 0.892653535740475, 0.41253405363524), + (-0.412534053477435, -0.181618610422654, -0.892653535852304), + (-0.181618610451384, -0.892653535762812, -0.412534053658432), + (-0.41253405331709, 0.181618610611827, 0.892653535887918), + (0.181618610400136, -0.8926535358123, 0.412534053573911), + (0.412534053327996, 0.1816186104204, -0.892653535921825), + (0.181618610580789, 0.892653535810904, -0.412534053497399), + (0.892653535867644, -0.412534053472558, 0.181618610358339), + (-0.892653535855064, 0.41253405353516, 0.181618610277971), + (-0.292093742593433, -0.29576702799317, 0.909507070170347), + (-0.295767028026887, 0.90950707008926, -0.292093742811776), + (0.292093742447864, 0.295767028039713, 0.909507070201962), + (0.909507070147612, -0.292093742926721, -0.295767027733934), + (0.295767028145396, 0.909507070084441, 0.292093742706783), + (0.909507070188854, 0.292093742689207, 0.295767027841675), + (0.295767027907311, -0.909507070148419, -0.292093742748651), + (-0.909507070101221, 0.292093743159272, -0.295767027646927), + (-0.295767027835333, -0.909507070047293, 0.292093743136414), + (-0.909507070218591, -0.292093742721776, 0.295767027718069), + (-0.292093742540896, 0.295767027793147, -0.909507070252266), + (0.292093742861938, -0.295767027747614, -0.909507070163969), + (-0.575225718038192, 0.024120572825078, 0.817639022597403), + (0.024120572786144, 0.817639022511238, -0.575225718162301), + (0.575225718116478, -0.024120572979213, 0.817639022537781), + (0.817639022556003, -0.57522571810348, 0.024120572671469), + (-0.024120573041503, 0.817639022440757, 0.575225718251777), + (0.817639022458379, 0.575225718229118, -0.024120572984526), + (-0.024120572818239, -0.81763902258126, -0.575225718061424), + (-0.817639022543578, 0.575225718123882, 0.024120572606111), + (0.02412057271295, -0.817639022527296, 0.575225718142546), + (-0.817639022600495, -0.575225718035174, -0.024120572792228), + (-0.575225717925469, -0.024120572711052, -0.81763902268007), + (0.57522571790823, 0.024120572594155, -0.817639022695646), + (-0.1288331617248, 0.05224764072024, 0.990288947973853), + (0.052247640694409, 0.990288947958895, -0.128833161850251), + (0.128833161840325, -0.052247640320038, 0.990288947979938), + (0.990288947949717, -0.128833161924796, 0.052247640684558), + (-0.05224764038851, 0.990288947967581, 0.128833161907538), + (0.99028894797773, 0.128833161878001, -0.052247640268992), + (-0.052247640390409, -0.99028894796219, -0.128833161948209), + (-0.990288947960626, 0.128833161896649, 0.052247640547187), + (0.052247640527808, -0.990288947953251, 0.1288331619612), + (-0.990288947970868, -0.128833161936205, -0.052247640255526), + (-0.128833161790478, -0.052247640337643, -0.990288947985494), + (0.128833161857416, 0.052247640551545, -0.9902889479655), + (0.71800638603475, 0.657446876255993, -0.228539787596286), + (0.657446876286737, -0.228539787831922, 0.718006385931596), + (-0.718006386109442, -0.657446876171434, -0.228539787604877), + (-0.228539787737219, 0.718006385947422, 0.657446876302374), + (-0.657446876241021, -0.2285397877138, -0.718006386011054), + (-0.228539787678997, -0.718006386031359, -0.657446876230945), + (-0.657446876361185, 0.228539787860549, 0.718006385854315), + (0.228539787703065, -0.718006385857385, 0.657446876412577), + (0.657446876304454, 0.228539787874017, -0.718006385901975), + (0.228539787784967, 0.718006385813853, -0.657446876431648), + (0.71800638588076, -0.657446876363485, 0.228539787770851), + (-0.718006385891018, 0.657446876371558, 0.228539787715401), + (0.863176473117803, 0.468181816653138, 0.189029528940001), + (0.468181816438486, 0.189029529197492, 0.86317647317784), + (-0.863176473194446, -0.46818181657642, 0.189029528780033), + (0.189029529125527, 0.863176473064389, 0.468181816676708), + (-0.468181816392671, 0.189029528897443, -0.863176473268398), + (0.189029528792174, -0.863176473143688, -0.4681818166651), + (-0.468181816411213, -0.189029529128138, 0.863176473207821), + (-0.189029528897852, -0.86317647308972, 0.468181816721931), + (0.468181816508867, -0.189029528930555, -0.863176473198123), + (-0.189029529001823, 0.863176473106659, -0.468181816648722), + (0.863176473135229, -0.468181816648642, -0.189029528871561), + (-0.863176473123334, 0.468181816698762, -0.189029528801744), + (0.772632856847133, -0.51705945069559, 0.368358511462152), + (-0.517059450567132, 0.368358511585515, 0.772632856874286), + (-0.772632856806081, 0.517059450647391, 0.368358511615915), + (0.368358511648001, 0.772632856806054, -0.517059450624573), + (0.517059450494007, 0.368358511816588, -0.772632856813056), + (0.368358511720496, -0.772632856802476, 0.517059450578273), + (0.517059450583445, -0.368358511487117, 0.77263285691028), + (-0.36835851156733, -0.772632856859467, -0.517059450602229), + (-0.517059450502369, -0.368358511665956, -0.772632856879275), + (-0.368358511469803, 0.772632856855651, 0.517059450677412), + (0.772632856934749, 0.517059450691919, -0.368358511283531), + (-0.772632856927485, -0.517059450633778, -0.368358511380378), + (-0.847819231914648, -0.066325775900167, -0.526121128113002), + (-0.066325775913631, -0.526121128257686, -0.847819231823809), + (0.847819231883018, 0.066325775819852, -0.526121128174097), + (-0.526121128348762, -0.847819231766957, -0.06632577591791), + (0.06632577584612, -0.526121128407098, 0.847819231736372), + (-0.52612112845924, 0.84781923170908, 0.066325775781366), + (0.066325775945785, 0.52612112834438, -0.847819231767496), + (0.526121128449532, 0.847819231700692, -0.066325775965613), + (-0.066325775877211, 0.526121128306388, 0.847819231796436), + (0.526121128504669, -0.847819231665213, 0.06632577598176), + (-0.847819231821725, 0.066325775941005, 0.526121128257594), + (0.847819231850264, -0.066325775996655, 0.52612112820459), + (0.00980574322923, 0.942983815842593, 0.332694109443892), + (0.942983815808923, 0.332694109539748, 0.00980574321495), + (-0.00980574337969, -0.942983815787291, 0.332694109596207), + (0.332694109226554, 0.009805743204272, 0.942983815919532), + (-0.94298381577404, 0.332694109635647, -0.009805743315804), + (0.332694109397996, -0.00980574329891, -0.942983815858062), + (-0.942983815776114, -0.332694109630098, 0.009805743304667), + (-0.332694109319027, -0.009805743188507, 0.94298381588707), + (0.942983815775082, -0.332694109635199, -0.009805743230763), + (-0.332694109455765, 0.009805743389762, -0.942983815836735), + (0.00980574330114, -0.942983815752524, -0.332694109697065), + (-0.009805743287713, 0.942983815791379, -0.332694109587331), + (0.785599248371152, -0.405156945312269, -0.467634120465896), + (-0.405156944932125, -0.467634120649859, 0.785599248457698), + (-0.78559924820179, 0.405156945434051, -0.467634120644904), + (-0.467634120611242, 0.785599248334623, -0.405156945215339), + (0.405156945136423, -0.467634120868201, -0.785599248222366), + (-0.467634120811804, -0.785599248145609, 0.405156945350347), + (0.405156944841985, 0.467634120861332, 0.785599248378305), + (0.467634120786726, -0.785599248249857, -0.405156945177156), + (-0.405156944999643, 0.467634120871098, -0.785599248291182), + (0.467634120893713, 0.78559924823424, 0.405156945083953), + (0.785599248313341, 0.405156945117104, 0.467634120732106), + (-0.7855992482811, -0.40515694519737, 0.467634120716727), + (-0.737331999131492, 0.620851501013764, -0.26624225199189), + (0.620851500949186, -0.266242252154895, -0.73733199912701), + (0.737331999060061, -0.620851501088737, -0.266242252014883), + (-0.266242251948631, -0.737331999103255, 0.62085150106585), + (-0.620851501079221, -0.2662422522338, 0.737331998989025), + (-0.266242252011624, 0.737331998996222, -0.620851501165951), + (-0.620851501072124, 0.26624225222256, -0.73733199899906), + (0.266242252113864, 0.737331998832974, 0.620851501315983), + (0.620851501187387, 0.266242252328374, 0.737331998863797), + (0.26624225193225, -0.73733199893899, -0.620851501267959), + (-0.737331998947943, -0.620851501183297, 0.266242252104879), + (0.737331998835007, 0.620851501305786, 0.26624225213201), + (0.726871469165659, -0.027488282350428, -0.686223186468061), + (-0.027488282182755, -0.686223186448325, 0.726871469190633), + (-0.726871469172931, 0.027488282371885, -0.686223186459499), + (-0.686223186449712, 0.726871469185406, -0.027488282286341), + (0.027488282351607, -0.68622318649112, -0.726871469143845), + (-0.686223186545622, -0.726871469089794, 0.027488282420281), + (0.027488282266836, 0.686223186470335, 0.726871469166674), + (0.686223186661183, -0.726871468983422, -0.027488282348185), + (-0.027488282251029, 0.686223186523092, -0.726871469117465), + (0.686223186609112, 0.726871469033498, 0.027488282323948), + (0.726871469070107, 0.02748828233555, 0.686223186569869), + (-0.726871469080183, -0.027488282309716, 0.686223186560232), + (0.665363385720515, 0.580860267739271, 0.468927408352716), + (0.580860267577087, 0.468927408488638, 0.665363385766308), + (-0.66536338567738, -0.580860267719575, 0.468927408438318), + (0.468927408340783, 0.665363385821863, 0.580860267632813), + (-0.580860267528453, 0.468927408678832, -0.665363385674723), + (0.468927408372614, -0.665363385698803, -0.580860267748078), + (-0.580860267640877, -0.468927408552762, 0.665363385665427), + (-0.468927408468336, -0.665363385847947, 0.580860267499961), + (0.580860267386752, -0.468927408654519, -0.665363385815563), + (-0.468927408375699, 0.665363385651356, -0.580860267799938), + (0.665363385651819, -0.580860267791212, -0.46892740838585), + (-0.665363385751734, 0.580860267548017, -0.468927408545326), + (-0.580125367305304, -0.779099597924434, 0.237609710918707), + (-0.779099598053518, 0.237609710909934, -0.580125367135539), + (0.580125367186808, 0.779099597977732, 0.237609711033258), + (0.237609710695932, -0.58012536727611, -0.779099598014114), + (0.779099598064732, 0.23760971114732, 0.58012536702325), + (0.237609710819285, 0.580125367047426, 0.779099598146774), + (0.779099598170224, -0.237609710849642, -0.580125367003499), + (-0.237609710811802, 0.580125367157256, -0.779099598067276), + (-0.779099598074961, -0.237609711045128, 0.580125367051369), + (-0.237609710609253, -0.580125367022359, 0.779099598229495), + (-0.580125367090094, 0.779099598151966, -0.237609710698086), + (0.580125367218411, -0.779099597966716, -0.237609710992215), + (0.9586680253602, 0.101113605900539, -0.265954236389956), + (0.101113605889893, -0.265954236477199, 0.95866802533712), + (-0.95866802532641, -0.101113606095432, -0.26595423643766), + (-0.265954236634179, 0.958668025294555, 0.101113605880558), + (-0.101113606003171, -0.265954236656317, -0.958668025275482), + (-0.265954236715455, -0.958668025246162, -0.101113606125602), + (-0.101113605825438, 0.265954236414664, 0.958668025361267), + (0.265954236286739, -0.958668025393583, 0.101113605855522), + (0.101113605802444, 0.265954236260664, -0.958668025406415), + (0.265954236515854, 0.958668025322577, -0.101113605926106), + (0.9586680254495, -0.101113605909101, 0.265954236064808), + (-0.9586680254786, 0.101113605789497, 0.265954236005386), + (-0.784431814417085, 0.284319025007229, 0.551207239202516), + (0.284319024822848, 0.551207239320709, -0.784431814400862), + (0.784431814443422, -0.284319024888131, 0.551207239226467), + (0.551207239434677, -0.784431814291888, 0.284319024902556), + (-0.284319024640161, 0.551207239347504, 0.784431814448249), + (0.551207239408357, 0.784431814400998, -0.284319024652546), + (-0.28431902471494, -0.551207239160137, -0.784431814552804), + (-0.551207239417649, 0.784431814426743, 0.284319024563503), + (0.284319024477106, -0.551207239394067, 0.784431814474629), + (-0.551207239227164, -0.784431814510832, -0.284319024700797), + (-0.7844318146549, -0.284319024757729, -0.551207238992772), + (0.784431814542139, 0.284319024689884, -0.55120723918824), + (0.166663878535118, 0.97946877886665, 0.113419851953285), + (0.979468778892362, 0.113419852011248, 0.166663878344564), + (-0.166663878322335, -0.979468778877222, 0.113419852174659), + (0.113419851852603, 0.166663878465092, 0.979468778890224), + (-0.979468778908051, 0.113419852233229, -0.166663878101297), + (0.113419852023532, -0.166663878213165, -0.979468778913298), + (-0.979468778891418, -0.113419852088755, 0.166663878297368), + (-0.113419851942299, -0.166663878383785, 0.979468778893673), + (0.979468778887792, -0.113419852252651, -0.166663878207142), + (-0.113419851887333, 0.166663878420061, -0.979468778893865), + (0.166663878513312, -0.97946877885884, -0.113419852052775), + (-0.166663878525992, 0.979468778852403, -0.113419852089727), + (0.90354263539087, 0.099002690679599, 0.416904273507865), + (0.09900269051118, 0.416904273753692, 0.903542635295897), + (-0.903542635383533, -0.099002690647923, 0.416904273531288), + (0.41690427395825, 0.903542635193768, 0.09900269058185), + (-0.099002690414933, 0.416904273699732, -0.903542635331341), + (0.416904273843964, -0.903542635237517, -0.099002690663845), + (-0.099002690464192, -0.416904273937254, 0.903542635216348), + (-0.416904274206036, -0.903542635110147, 0.099002690301575), + (0.099002690128044, -0.41690427406438, -0.903542635194523), + (-0.416904274113744, 0.903542635131386, -0.099002690496392), + (0.903542635279275, -0.099002690467102, -0.416904273800183), + (-0.903542635234399, 0.099002690245829, -0.416904273949988), + (0.278762404536092, 0.349312185537063, -0.894579520698175), + (0.349312185586056, -0.894579520608515, 0.278762404762431), + (-0.278762404540525, -0.349312185503473, -0.89457952070991), + (-0.894579520734144, 0.278762404727917, 0.349312185291866), + (-0.349312185466701, -0.894579520677723, -0.278762404689896), + (-0.894579520788864, -0.278762404658677, -0.349312185206984), + (-0.349312185551041, 0.894579520682798, 0.278762404567923), + (0.894579520785219, -0.278762404680469, 0.349312185198929), + (0.349312185549623, 0.89457952067923, -0.278762404581149), + (0.894579520781805, 0.278762404555908, -0.349312185307075), + (0.27876240443795, -0.3493121855065, 0.894579520740692), + (-0.278762404443259, 0.349312185428787, 0.894579520769382), + (0.555896230179415, -0.676833211736671, 0.48257246581476), + (-0.676833211681567, 0.482572466040116, 0.555896230050876), + (-0.555896230314892, 0.676833211522987, 0.482572465958401), + (0.482572465910283, 0.555896230164672, -0.676833211680673), + (0.676833211457692, 0.482572466092895, -0.555896230277639), + (0.482572465902981, -0.555896230367909, 0.676833211518957), + (0.676833211635592, -0.482572466071981, 0.555896230079191), + (-0.482572466150586, -0.555896230230084, -0.676833211455616), + (-0.676833211438286, -0.482572466327737, -0.5558962300974), + (-0.482572465972373, 0.55589623026777, 0.676833211551727), + (0.555896230192691, 0.676833211589453, -0.482572466005949), + (-0.555896230194338, -0.676833211455537, -0.482572466191875), + ], +} diff --git a/pytta/ambisonics.py b/pytta/ambisonics.py new file mode 100644 index 0000000..7dbc653 --- /dev/null +++ b/pytta/ambisonics.py @@ -0,0 +1,512 @@ +""" +PyTTa Ambisonics: +----------------- + +Higher-order Ambisonics (HOA): real spherical harmonics, encoding, decoding +with five decoders, rotation and the conversions between the usual channel +conventions. + +Available functions: + + >>> Y = pytta.ambisonics.real_sh(order, directions) + >>> hoa = pytta.ambisonics.encode(signals, directions, order) + >>> D, order = pytta.ambisonics.decoder(ls_directions, 'allrad') + >>> ls = pytta.ambisonics.decode(hoa, D) + >>> hoa_rot = pytta.ambisonics.rotate(hoa, yaw, pitch, roll) + +Signals are (samples, channels) arrays. The HOA channels follow ACN ordering, +q = n^2 + n + m (0-based), with orthonormal real spherical harmonics (N3D) +and no Condon-Shortley phase. Directions are (points, 2) arrays of +[azimuth, elevation] in degrees, 0 deg in front and 90 deg to the left. +Conversions to SN3D, FuMa and SID are included, and bformat_fuma gives the +first-order FuMa B-format of Iceberg. + +Origin of the code +------------------ + +This module is a port to Python of the MATLAB library Higher-Order-Ambisonics +by Archontis Politis (2015), https://github.com/polarch/Higher-Order-Ambisonics +(getRSH, encodeHOA_N3D, decodeHOA_N3D, encodeBformat, decodeBformat, +rotateHOA_N3D, rotateBformat, convert_N3D_SN3D, convert_N3D_FuMa, +convert_ACN_SID, getMaxREweights, getLayoutAmbisonicOrder, ambiDecoder, +allrad, csad), with the functions it uses from his Spherical-Harmonic- +Transform library, https://github.com/polarch/Spherical-Harmonic-Transform +(getSHrotMtx, euler2rotationMatrix, getTdesign and its t-designs, in +pytta/_tdesigns.py). Both are under the BSD 3-Clause licence reproduced +below. bformat_fuma follows native_bformat_fuma of Iceberg +(https://github.com/aguirreSL/Iceberg, BSD 3-Clause, Sergio Aguirre). + +References +---------- + +Zotter, F., & Frank, M. (2012). All-round ambisonic panning and decoding. +Journal of the Audio Engineering Society, 60(10), 807-820. + +Zotter, F., Pomberger, H., & Noisternig, M. (2012). Energy-preserving +ambisonic decoding. Acta Acustica united with Acustica, 98(1), 37-47. + +Epain, N., Jin, C. T., & Zotter, F. (2014). Ambisonic decoding with +constant angular spread. Acta Acustica united with Acustica, 100(5), 928-936. + +Ivanic, J., & Ruedenberg, K. (1996). Rotation matrices for real spherical +harmonics. Direct determination by recursion. Journal of Physical Chemistry, +100(15), 6342-6347 (with the corrections of 1998). + +Licence of the ported code (both libraries) +------------------------------------------- + + Copyright (c) 2015, Archontis Politis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +import warnings +from math import factorial + +import numpy as np +from scipy import signal as ss +from scipy.optimize import brentq +from scipy.spatial import ConvexHull +from scipy.special import eval_legendre, lpmv + +from . import vbap +from ._tdesigns import T_DESIGNS + + +def _dirs(directions): + return np.atleast_2d(np.asarray(directions, dtype=float)) + + +def _unit(directions): + d = np.radians(_dirs(directions)) + return np.column_stack([np.cos(d[:, 0]) * np.cos(d[:, 1]), + np.sin(d[:, 0]) * np.cos(d[:, 1]), np.sin(d[:, 1])]) + + +def _directions(vectors): + v = np.asarray(vectors, dtype=float) + return np.degrees(np.column_stack([np.arctan2(v[:, 1], v[:, 0]), + np.arctan2(v[:, 2], np.hypot(v[:, 0], v[:, 1]))])) + + +def _order(channels): + order = int(round(np.sqrt(channels))) - 1 + if (order + 1)**2 != channels: + raise ValueError('HOA signals need (order + 1)^2 channels.') + return order + + +def real_sh(order, directions): + """ + Orthonormal real spherical harmonics, ACN ordering, no Condon-Shortley + phase (getRSH). + + Returns + ------- + numpy.ndarray + ((order + 1)^2, points). + """ + d = np.radians(_dirs(directions)) + azi, x = d[:, 0], np.sin(d[:, 1]) + Y = np.zeros(((order + 1)**2, len(d))) + Y[0] = 1 / np.sqrt(4 * np.pi) + for n in range(1, order + 1): + for m in range(-n, n + 1): + a = abs(m) + norm = np.sqrt((2 * n + 1) * factorial(n - a) / (4 * np.pi * factorial(n + a))) + P = (-1)**a * lpmv(a, n, x) + trig = (np.sqrt(2) * np.cos(m * azi) if m > 0 else + np.sqrt(2) * np.sin(a * azi) if m < 0 else 1.) + Y[n * n + n + m] = norm * P * trig + return Y + + +def t_design(degree): + """ + Spherical t-design of degree 1 to 21 (getTdesign). + + Returns + ------- + (numpy.ndarray, numpy.ndarray) + Unit vectors (points, 3) and their [azimuth, elevation] in degrees. + """ + if not 1 <= degree <= 21: + raise ValueError('t-designs are available for degrees 1 to 21.') + vecs = np.array(T_DESIGNS[degree]) + return vecs, _directions(vecs) + + +def encode(signals, directions, order): + """ + Encode source signals from their directions to HOA, N3D (encodeHOA_N3D). + + signals: (samples, sources); returns (samples, (order + 1)^2). + """ + s = np.asarray(signals, dtype=float) + s = s[:, None] if s.ndim == 1 else s + return s @ real_sh(order, directions).T + + +def _filterbank(sig, cutoffs, fs): + # applyFilterbank: FIR of order 100 (fir1, Hamming), zero phase by + # dropping the 50 samples of group delay. + n_band, half = len(cutoffs) + 1, 50 + pad = np.vstack([sig, np.zeros((half, sig.shape[1]))]) + bands = [] + for b in range(n_band): + if b == 0: + h = ss.firwin(101, cutoffs[0], fs=fs) + elif b == n_band - 1: + h = ss.firwin(101, cutoffs[-1], fs=fs, pass_zero=False) + else: + h = ss.firwin(101, [cutoffs[b - 1], cutoffs[b]], fs=fs, pass_zero=False) + bands.append(ss.lfilter(h, 1, pad, axis=0)[half:]) + return bands + + +def _decode(sig, D, cutoffs, fs, channels): + D = np.asarray(D, dtype=float) + D = D[:, :, None] if D.ndim == 2 else D + n_band = 1 if cutoffs is None else len(cutoffs) + 1 + if D.shape[2] != n_band: + raise ValueError('One decoding matrix is needed for each frequency band.') + if n_band == 1: + return sig @ D[:, :channels, 0].T + return sum(b @ D[:, :channels, k].T + for k, b in enumerate(_filterbank(sig, cutoffs, fs))) + + +def decode(hoa, D, cutoffs=None, fs=None): + """ + Decode HOA signals with a decoding matrix (decodeHOA_N3D). + + D: (loudspeakers, (order + 1)^2), or (loudspeakers, (order + 1)^2, + bands) with cutoffs (bands - 1 frequencies in Hz) and fs for a decoding + in frequency bands, split by FIR filters of order 100. + """ + hoa = np.asarray(hoa, dtype=float) + return _decode(hoa, D, cutoffs, fs, hoa.shape[1]) + + +def encode_bformat(signals, directions): + """First-order FuMa B-format (W/sqrt(2), X, Y, Z) of sources (encodeBformat).""" + s = np.asarray(signals, dtype=float) + s = s[:, None] if s.ndim == 1 else s + u = _unit(directions) + return s @ np.column_stack([np.ones(len(u)) / np.sqrt(2), u]) + + +def decode_bformat(bformat, D, cutoffs=None, fs=None): + """Decode FuMa B-format with the first 4 columns of D (decodeBformat).""" + return _decode(fuma_to_n3d(bformat), D, cutoffs, fs, 4) + + +def n3d_to_sn3d(sig): + """N3D to SN3D normalization (convert_N3D_SN3D, 'n2sn').""" + sig = np.asarray(sig, dtype=float) + n = np.floor(np.sqrt(np.arange(sig.shape[1]))) + return sig / np.sqrt(2 * n + 1) + + +def sn3d_to_n3d(sig): + """SN3D to N3D normalization (convert_N3D_SN3D, 'sn2n').""" + sig = np.asarray(sig, dtype=float) + n = np.floor(np.sqrt(np.arange(sig.shape[1]))) + return sig * np.sqrt(2 * n + 1) + + +_FUMA2ACN = np.array([1, 3, 4, 2, 9, 7, 5, 6, 8, 16, 14, 12, 10, 11, 13, 15]) - 1 +_NORM_FUMA2N3D = np.array([np.sqrt(2), np.sqrt(3), np.sqrt(3), np.sqrt(3), + np.sqrt(15) / 2, np.sqrt(15) / 2, np.sqrt(5), np.sqrt(15) / 2, + np.sqrt(15) / 2, np.sqrt(35 / 8), np.sqrt(35) / 3, + np.sqrt(224 / 45), np.sqrt(7), np.sqrt(224 / 45), + np.sqrt(35) / 3, np.sqrt(35 / 8)]) + + +def _fuma_order(sig): + sig = np.asarray(sig, dtype=float) + order = _order(sig.shape[1]) + if order > 3: + warnings.warn('FuMa is defined up to third order; keeping the first 16 channels.') + sig, order = sig[:, :16], 3 + return sig, (order + 1)**2 + + +def fuma_to_n3d(sig): + """FuMa (up to third order) to N3D, ACN (convert_N3D_FuMa, 'fuma2n').""" + sig, nsh = _fuma_order(sig) + return sig[:, _FUMA2ACN[:nsh]] * _NORM_FUMA2N3D[:nsh] + + +def n3d_to_fuma(sig): + """N3D, ACN to FuMa (up to third order) (convert_N3D_FuMa, 'n2fuma').""" + sig, nsh = _fuma_order(sig) + acn2fuma = np.argsort(_FUMA2ACN) + norm = (1 / _NORM_FUMA2N3D)[acn2fuma] + return sig[:, acn2fuma[:nsh]] * norm[:nsh] + + +def _sid_index(order): + idx = [0] + for n in range(1, order + 1): + ms = [m for k in range(1, n + 1) for m in (n - k + 1, k - n - 1)] + [0] + idx += [n * n + n + m for m in ms] + return np.array(idx) + + +def acn_to_sid(sig): + """ACN to SID channel ordering (convert_ACN_SID, 'acn2sid').""" + sig = np.asarray(sig) + return sig[:, _sid_index(_order(sig.shape[1]))] + + +def sid_to_acn(sig): + """SID to ACN channel ordering (convert_ACN_SID, 'sid2acn').""" + sig = np.asarray(sig) + return sig[:, np.argsort(_sid_index(_order(sig.shape[1])))] + + +def bformat_fuma(sig, convention): + """ + First-order FuMa B-format (W, X, Y, Z) from full-sphere Ambisonics of + any order, as Iceberg's native_bformat_fuma. + + convention: 'fuma', 'ambix' (ACN, SN3D) or 'n3d' (ACN, N3D). The first + four channels hold the first order in all three, so the conversion of + those four is exact. + + Returns + ------- + (numpy.ndarray, int) + (samples, 4) FuMa B-format and the order of the input. + """ + sig = np.asarray(sig, dtype=float) + if sig.ndim != 2 or sig.shape[1] < 4: + raise ValueError('Ambisonics B-format needs (order + 1)^2 channels, 4 or more.') + order = _order(sig.shape[1]) + c = convention.lower() + if c == 'fuma': + return sig[:, :4], order + if c == 'ambix': + return np.column_stack([sig[:, 0] / np.sqrt(2), sig[:, 3], sig[:, 1], sig[:, 2]]), order + if c == 'n3d': + return np.column_stack([sig[:, 0] / np.sqrt(2), sig[:, [3, 1, 2]] / np.sqrt(3)]), order + raise ValueError("convention must be 'fuma', 'ambix' or 'n3d'.") + + +def max_re_weights(order): + """ + Max-rE weights a_n = P_n(cos(137.9 deg / (order + 1.51))), repeated per + channel (getMaxREweights). + """ + x = np.cos(np.radians(137.9) / (order + 1.51)) + return np.concatenate([np.full(2 * n + 1, eval_legendre(n, x)) + for n in range(order + 1)]) + + +def layout_order(ls_directions): + """ + Ambisonic order a 3D loudspeaker layout supports, from the mean energy + spread of VBAP at the centres of its triangles (getLayoutAmbisonicOrder). + """ + ls = _dirs(ls_directions) + U = _unit(ls) + centres = _directions(np.array([U[f].sum(axis=0) for f in ConvexHull(U).simplices])) + groups = vbap.loudspeaker_triplets(ls) + g = vbap.gains(centres, groups, vbap.inverse_matrices(ls, groups)) + rE = np.linalg.norm(g**2 @ U, axis=1) + spread = 2 * np.degrees(np.arccos(np.clip(rE, -1, 1))) + order = int(np.floor(2 * 137.9 / np.mean(spread) - 1.52 + 0.5)) + return max(order, 1) + + +def _fzero(f, x0): + # MATLAB fzero from a starting point: widen [x0 - dx, x0 + dx] by + # sqrt(2) until the sign changes, then solve on that bracket. + dx = x0 / 50 if x0 else 1 / 50 + a = b = x0 + fa = fb = f(x0) + while (fa > 0) == (fb > 0): + dx *= np.sqrt(2) + a, fa = x0 - dx, f(x0 - dx) + if (fa > 0) != (fb > 0): + break + b, fb = x0 + dx, f(x0 + dx) + return brentq(f, min(a, b), max(a, b), xtol=1e-14, rtol=4 * np.finfo(float).eps) + + +def _energy_spread(G, U): + E = np.sum(G**2, axis=1) + rE = np.linalg.norm((G**2 @ U) / E[:, None], axis=1) + return np.degrees(2 * np.arccos(np.clip(2 * rE - 1, -1, 1))) + + +def _csad(ls, order): + U = _unit(ls) + _, tdirs = t_design(4 * order) + azi, ele = np.arange(-180, 181, 5.), np.arange(-90, 91, 5.) + grid = np.column_stack([np.tile(azi, len(ele)), np.repeat(ele, len(azi))]) + groups = vbap.loudspeaker_triplets(ls) + inv = vbap.inverse_matrices(ls, groups) + max_spread = np.max(_energy_spread(vbap.energy_gains(grid, groups, inv), U)) + G = np.zeros((len(ls), len(tdirs))) + for k, d in enumerate(tdirs): + def excess(x): + g = vbap.energy_gains(d, groups, inv, x, 32, 4) + return _energy_spread(g, U)[0] - max_spread + G[:, k] = vbap.energy_gains(d, groups, inv, _fzero(excess, max_spread), 32, 4)[0] + Y = real_sh(order, tdirs) + return G @ Y.T @ np.linalg.inv(Y @ Y.T) + + +def decoder(ls_directions, method='sad', max_re=True, order=None): + """ + Ambisonic decoding matrix for a loudspeaker layout (ambiDecoder). + + Parameters + ---------- + ls_directions : array_like + (loudspeakers, 2) [azimuth, elevation] in degrees. + method : str + 'sad' sampling (4 pi / L Y^T), 'mmd' mode matching (pseudo-inverse), + 'epad' energy preserving, 'allrad' all-round (VBAP of a 21-design, + 3D layouts) or 'csad' constant angular spread (3D layouts). + max_re : bool + Apply the max-rE weights (not for 'csad'). Default True. + order : int, optional + Decoding order; by default the order the layout supports. + + Returns + ------- + (numpy.ndarray, int) + D, (loudspeakers, (order + 1)^2), and the order. + """ + ls = _dirs(ls_directions) + if order is None: + order = layout_order(ls) + L = len(ls) + Y = real_sh(order, ls) + m = method.lower() + if m == 'sad': + D = 4 * np.pi / L * Y.T + elif m == 'mmd': + D = np.linalg.pinv(Y) + elif m == 'epad': + U, _, Vh = np.linalg.svd(Y) + D = 4 * np.pi / L * Vh.T[:, :(order + 1)**2] @ U.T + elif m == 'allrad': + _, tdirs = t_design(21) + groups = vbap.loudspeaker_triplets(ls) + G = vbap.gains(tdirs, groups, vbap.inverse_matrices(ls, groups)).T + D = 4 * np.pi / len(tdirs) * G @ real_sh(order, tdirs).T + elif m == 'csad': + D = _csad(ls, order) + else: + raise ValueError("method must be 'sad', 'mmd', 'epad', 'allrad' or 'csad'.") + if max_re and m != 'csad': + D = D * max_re_weights(order) + return D, order + + +def _euler_zyx(alpha, beta, gamma): + # euler2rotationMatrix(alpha, beta, gamma, 'zyx') = Rx(gamma) Ry(beta) Rz(alpha) + c, s = np.cos, np.sin + Rz = np.array([[c(alpha), s(alpha), 0], [-s(alpha), c(alpha), 0], [0, 0, 1]]) + Ry = np.array([[c(beta), 0, -s(beta)], [0, 1, 0], [s(beta), 0, c(beta)]]) + Rx = np.array([[1, 0, 0], [0, c(gamma), s(gamma)], [0, -s(gamma), c(gamma)]]) + return Rx @ Ry @ Rz + + +def sh_rotation(R, order): + """ + Rotation matrix of real spherical harmonics up to order, from a 3 x 3 + rotation matrix R, by the recursion of Ivanic and Ruedenberg + (getSHrotMtx, real basis). + """ + out = np.zeros(((order + 1)**2,) * 2) + out[0, 0] = 1 + R1 = np.array([[R[1, 1], R[1, 2], R[1, 0]], + [R[2, 1], R[2, 2], R[2, 0]], + [R[0, 1], R[0, 2], R[0, 0]]]) + if order == 0: + return out + out[1:4, 1:4] = R1 + prev = R1 + + def P(i, l, a, b): + if b == -l: + return R1[i + 1, 2] * prev[a + l - 1, 0] + R1[i + 1, 0] * prev[a + l - 1, 2 * l - 2] + if b == l: + return R1[i + 1, 2] * prev[a + l - 1, 2 * l - 2] - R1[i + 1, 0] * prev[a + l - 1, 0] + return R1[i + 1, 1] * prev[a + l - 1, b + l - 1] + + for l in range(2, order + 1): + Rl = np.zeros((2 * l + 1, 2 * l + 1)) + for m in range(-l, l + 1): + for n in range(-l, l + 1): + d = 1 if m == 0 else 0 + denom = (2 * l) * (2 * l - 1) if abs(n) == l else (l * l - n * n) + u = np.sqrt((l * l - m * m) / denom) + v = np.sqrt((1 + d) * (l + abs(m) - 1) * (l + abs(m)) / denom) * (1 - 2 * d) * 0.5 + w = np.sqrt((l - abs(m) - 1) * (l - abs(m)) / denom) * (1 - d) * (-0.5) + if u: + u *= P(0, l, m, n) + if v: + if m == 0: + v *= P(1, l, 1, n) + P(-1, l, -1, n) + elif m > 0: + dm = 1 if m == 1 else 0 + v *= P(1, l, m - 1, n) * np.sqrt(1 + dm) - P(-1, l, -m + 1, n) * (1 - dm) + else: + dm = 1 if m == -1 else 0 + v *= P(1, l, m + 1, n) * (1 - dm) + P(-1, l, -m - 1, n) * np.sqrt(1 + dm) + if w: + w *= (P(1, l, m + 1, n) + P(-1, l, -m - 1, n) if m > 0 else + P(1, l, m - 1, n) - P(-1, l, -m + 1, n)) + Rl[m + l, n + l] = u + v + w + out[l * l:(l + 1)**2, l * l:(l + 1)**2] = Rl + prev = Rl + return out + + +def rotate(hoa, yaw, pitch, roll): + """ + Rotate a HOA sound scene, N3D, by yaw, pitch and roll in degrees + (rotateHOA_N3D). + """ + hoa = np.asarray(hoa, dtype=float) + R = _euler_zyx(-np.radians(yaw), -np.radians(pitch), np.radians(roll)) + return hoa @ sh_rotation(R, _order(hoa.shape[1])).T + + +def rotate_bformat(bformat, yaw, pitch, roll): + """Rotate first-order FuMa B-format by yaw, pitch and roll in degrees (rotateBformat).""" + R = np.eye(4) + R[1:, 1:] = _euler_zyx(-np.radians(yaw), -np.radians(pitch), np.radians(roll)) + return np.asarray(bformat, dtype=float) @ R.T diff --git a/pytta/vbap.py b/pytta/vbap.py new file mode 100644 index 0000000..0cf21a0 --- /dev/null +++ b/pytta/vbap.py @@ -0,0 +1,303 @@ +""" +PyTTa VBAP: +----------- + +Vector base amplitude panning (VBAP) of Pulkki (1997) for 2D and 3D +loudspeaker layouts, and its spread version, multiple-direction amplitude +panning (MDAP). + +Available functions: + + >>> groups = pytta.vbap.loudspeaker_pairs(azimuths) # 2D + >>> groups = pytta.vbap.loudspeaker_triplets(directions) # 3D + >>> inverses = pytta.vbap.inverse_matrices(directions, groups) + >>> G = pytta.vbap.gains(source_directions, groups, inverses, spread=0) + >>> G = pytta.vbap.energy_gains(source_directions, groups, inverses) + +Directions are in degrees: azimuth for 2D layouts, [azimuth, elevation] for +3D ones, with x = cos(azi) cos(elev), y = sin(azi) cos(elev) and z = +sin(elev) (0 deg in front, 90 deg to the left). Loudspeaker indices are +0-based. + +Origin of the code +------------------ + +This module is a port to Python of the MATLAB library +Vector-Base-Amplitude-Panning by Archontis Politis and Ville Pulkki (2015), +https://github.com/polarch/Vector-Base-Amplitude-Panning (findLsPairs.m, +findLsTriplets.m, invertLsMtx.m, vbap.m, vbip.m, getSpreadSrcDirs.m), under the BSD +3-Clause licence reproduced below. The gains follow the MATLAB code +operation by operation. + +References +---------- + +Pulkki, V. (1997). Virtual sound source positioning using vector base +amplitude panning. Journal of the Audio Engineering Society, 45(6), 456-466. + +Pulkki, V. (1999). Uniform spreading of amplitude panned virtual sources. +Proceedings of the IEEE Workshop on Applications of Signal Processing to +Audio and Acoustics, New Paltz, NY. + +Licence of the ported code +-------------------------- + + Copyright (c) 2015, Archontis Politis + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +import numpy as np +from scipy.spatial import ConvexHull + + +def _cartesian(directions): + d = np.radians(np.asarray(directions, dtype=float)) + if d.ndim == 1: + return np.column_stack([np.cos(d), np.sin(d)]) + azi, elev = d[:, 0], d[:, 1] + return np.column_stack([np.cos(azi) * np.cos(elev), + np.sin(azi) * np.cos(elev), np.sin(elev)]) + + +def loudspeaker_pairs(azimuths, max_aperture=None): + """ + Adjacent loudspeaker pairs of a 2D layout (findLsPairs). + + Parameters + ---------- + azimuths : array_like + Loudspeaker azimuths in degrees. + max_aperture : float, optional + Pairs that span this angle in degrees or more are left out. + + Returns + ------- + numpy.ndarray + (pairs, 2) loudspeaker indices, around the circle by azimuth. + """ + azimuths = np.asarray(azimuths, dtype=float) + order = np.argsort(azimuths, kind='stable') + groups = np.column_stack([order, np.roll(order, -1)]) + if max_aperture is not None: + s = azimuths[np.append(order, order[0])] + apertures = np.diff(s) + apertures[-1] = 360 - abs(apertures[-1]) + groups = groups[apertures < max_aperture] + return groups + + +def loudspeaker_triplets(directions, max_aperture=None): + """ + Loudspeaker triplets of a 3D layout (findLsTriplets). + + The triplets are the faces of the convex hull of the loudspeaker + directions, which on the unit sphere is their Delaunay triangulation. + Faces whose outward normal is at 90 deg or more from their centroid are + left out: those are the faces that close a layout with no loudspeakers + below or above, through which no source direction passes. + + Parameters + ---------- + directions : array_like + (loudspeakers, 2) [azimuth, elevation] in degrees. + max_aperture : float, optional + Triplets with a side longer than this angle in degrees are left out. + + Returns + ------- + numpy.ndarray + (triplets, 3) loudspeaker indices, each triplet counter-clockwise + seen from outside and starting at its smallest index, sorted. + """ + U = _cartesian(directions) + faces = [] + for face in ConvexHull(U).simplices: + v = U[face] + n = np.cross(v[1] - v[0], v[2] - v[1]) + if np.dot(n, v.mean(axis=0) - U.mean(axis=0)) < 0: + face, v, n = face[::-1], v[::-1], -n + # MATLAB keeps acos(dot(centroid, n)) < pi/2; a dot product of + # rounding size, as on a flat bottom face, rounds to pi/2 there. + if np.dot(v.mean(axis=0), n) > 1e-12: + faces.append(np.roll(face, -int(np.argmin(face)))) + faces = np.array(faces, dtype=int) + if max_aperture is not None: + sides = np.stack([np.sum(U[faces[:, a]] * U[faces[:, b]], axis=1) + for a, b in ((0, 1), (1, 2), (2, 0))], axis=1) + faces = faces[np.all(np.arccos(np.clip(sides, -1, 1)) + <= np.radians(max_aperture), axis=1)] + return faces[np.lexsort((faces[:, 1], faces[:, 0]))] + + +def inverse_matrices(directions, groups): + """ + Inverse of the matrix of loudspeaker vectors of each group (invertLsMtx). + + Parameters + ---------- + directions : array_like + Loudspeaker azimuths (2D) or [azimuth, elevation] (3D) in degrees. + groups : numpy.ndarray + Pairs or triplets from loudspeaker_pairs or loudspeaker_triplets. + + Returns + ------- + numpy.ndarray + (groups, dim, dim): the inverse of the matrix whose rows are the + unit vectors of the group, so that the gains of a source u are + u @ inverse. + """ + U = _cartesian(directions) + return np.linalg.inv(U[np.asarray(groups)]) + + +def _spread_directions(direction, spread, num_src=8, num_rings=1): + # getSpreadSrcDirs: num_src sources across the spread in 2D; in 3D + # num_rings concentric rings of num_src sources and the source itself. + if np.ndim(direction) == 0: + d = np.radians(np.linspace(direction - spread / 2, direction + spread / 2, + num_src + 1)) + return np.column_stack([np.cos(d), np.sin(d)]) + u = _cartesian(np.atleast_2d(direction))[0] + u_x = np.array([[0, -u[2], u[1]], [u[2], 0, -u[0]], [-u[1], u[0], 0]]) + theta = 2 * np.pi / num_src + R = (np.eye(3) * np.cos(theta) + np.sin(theta) * u_x + + (1 - np.cos(theta)) * np.outer(u, u)) + base = np.zeros((num_src, 3)) + if direction[1] > 89 or direction[1] < -89: + base[0] = [1, 0, 0] + else: + c = np.cross(u, [0, 0, 1]) + base[0] = c / np.sqrt(np.sum(c**2)) + for n in range(1, num_src): + base[n] = R @ base[n - 1] + ring = np.radians(spread / 2) / num_rings + rings = np.vstack([u + base * np.tan(ring * (r + 1)) for r in range(num_rings)]) + rings = rings / np.sqrt(np.sum(rings**2, axis=1))[:, None] + return np.vstack([rings, u]) + + +def _group_gains(u, groups, inverses, first_only, energy=False): + # One direction: the gains of the first (3D) or last (2D) group that + # holds it, normalized in amplitude (VBAP) or in energy (VBIP). + g = np.zeros(int(groups.max()) + 1) + for group, inv in zip(groups, inverses): + g_tmp = u @ inv + if np.min(g_tmp) > -0.001: + g[group] = g_tmp / (np.sum(g_tmp) if energy else np.sqrt(np.sum(g_tmp**2))) + if first_only: + break + return g + + +def gains(directions, groups, inverses, spread=0.): + """ + VBAP gains of a set of source directions (vbap). + + Parameters + ---------- + directions : array_like + Source azimuths (2D) or (sources, 2) [azimuth, elevation] (3D) in + degrees. + groups : numpy.ndarray + Pairs or triplets from loudspeaker_pairs or loudspeaker_triplets. + inverses : numpy.ndarray + From inverse_matrices. + spread : float, optional + Spread in degrees for MDAP: the gains of 9 directions across the + spread (8 on a ring and the source in 3D) are summed. The default is + 0, plain VBAP. + + Returns + ------- + numpy.ndarray + (sources, loudspeakers) gains, non-negative, of norm 1 per source. + A source outside every group (below a hemisphere layout, say) gets + all gains 0, as in the MATLAB code. + """ + groups = np.asarray(groups) + three_d = groups.shape[1] == 3 + directions = np.asarray(directions, dtype=float) + directions = directions.reshape(-1, 2) if three_d else directions.reshape(-1) + out = np.zeros((len(directions), int(groups.max()) + 1)) + for n, d in enumerate(directions): + if spread: + g = sum(_group_gains(u, groups, inverses, three_d) + for u in _spread_directions(d, spread)) + else: + g = _group_gains(_cartesian(np.atleast_2d(d) if three_d else [d])[0], + groups, inverses, three_d) + with np.errstate(invalid='ignore'): + out[n] = np.fmax(0, g / np.sqrt(np.sum(g**2))) + return out + + +def energy_gains(directions, groups, inverses, spread=0., num_src=8, + num_rings=1): + """ + Vector base intensity panning gains (vbip). + + The energies of the loudspeakers of a group, not their amplitudes, + weight the loudspeaker vectors into the source direction; the gains are + their square roots, normalized in energy. + + Parameters + ---------- + directions, groups, inverses + As in gains. + spread : float, optional + Spread in degrees: the energies of num_src directions across it (in + 3D num_rings rings of num_src and the source) are summed. + num_src, num_rings : int, optional + Spread directions, 8 and 1 by default. + + Returns + ------- + numpy.ndarray + (sources, loudspeakers) gains, of norm 1 per source. + """ + groups = np.asarray(groups) + three_d = groups.shape[1] == 3 + directions = np.asarray(directions, dtype=float) + directions = directions.reshape(-1, 2) if three_d else directions.reshape(-1) + out = np.zeros((len(directions), int(groups.max()) + 1)) + for n, d in enumerate(directions): + if spread: + e = sum(_group_gains(u, groups, inverses, three_d, energy=True) + for u in _spread_directions(d, spread, num_src, num_rings)) + else: + e = _group_gains(_cartesian(np.atleast_2d(d) if three_d else [d])[0], + groups, inverses, three_d, energy=True) + with np.errstate(invalid='ignore'): + e = e / np.sum(e) + if three_d: + e[e < 0] = 0 + out[n] = np.fmax(0, np.sqrt(e)) + return out diff --git a/tests/golden/ambisonics_polarch.mat b/tests/golden/ambisonics_polarch.mat new file mode 100644 index 0000000..7cb2a8b Binary files /dev/null and b/tests/golden/ambisonics_polarch.mat differ diff --git a/tests/golden/make_ambisonics_polarch.m b/tests/golden/make_ambisonics_polarch.m new file mode 100644 index 0000000..b12a1e9 --- /dev/null +++ b/tests/golden/make_ambisonics_polarch.m @@ -0,0 +1,44 @@ +% Golden data for tests/test_ambisonics.py: polarch Higher-Order-Ambisonics +% and Vector-Base-Amplitude-Panning (Archontis Politis, BSD 3-Clause), the +% copies that Iceberg installs. Three functions they take from polarch +% Spherical-Harmonic-Transform are replaced by minimal versions: getTdesign +% (the t-designs of pytta/_tdesigns.py), sphDelaunay (the faces of the +% convex hull) and euler2rotationMatrix ('zyx'). rotateHOA_N3D is not here; +% the test checks the rotation by a rule. Run from tests/golden with those +% on the path. +rng(1, 'twister'); +dirs = [rand(20,1)*360-180, asind(rand(20,1)*2-1)]; +Y4 = getRSH(4, dirs); +sig3 = rand(8, 16) - 0.5; % third order, N3D ACN +conv.n2sn = convert_N3D_SN3D(sig3, 'n2sn'); +conv.sn2n = convert_N3D_SN3D(sig3, 'sn2n'); +conv.n2fuma = convert_N3D_FuMa(sig3, 'n2fuma'); +conv.fuma2n = convert_N3D_FuMa(sig3, 'fuma2n'); +conv.acn2sid = convert_ACN_SID(sig3, 'acn2sid'); +conv.sid2acn = convert_ACN_SID(sig3, 'sid2acn'); +maxre = {getMaxREweights(1), getMaxREweights(3), getMaxREweights(5)}; +ls_hemi = [0 0; 90 0; 180 0; -90 0; 45 45; 135 45; -135 45; -45 45; 0 90]; +ls_full = [(0:90:270)' -30*ones(4,1); (0:45:315)' zeros(8,1); (30:60:330)' 45*ones(6,1); 0 90]; +order_hemi = getLayoutAmbisonicOrder(ls_hemi); +order_full = getLayoutAmbisonicOrder(ls_full); +D.sad = ambiDecoder(ls_full, 'sad', 1, 3); +D.mmd = ambiDecoder(ls_full, 'mmd', 1, 3); +D.epad = ambiDecoder(ls_full, 'epad', 0, 3); +D.allrad = ambiDecoder(ls_full, 'allrad', 1, 3); +D.csad = ambiDecoder(ls_full, 'csad', 1, 2); +[D.auto, D.auto_order] = ambiDecoder(ls_full, 'sad'); +src = [30 10; -100 40]; +s2 = rand(16, 2) - 0.5; +enc3 = encodeHOA_N3D(3, s2, src); +rng(2, 'twister'); +hoa2 = rand(400, 9) - 0.5; % numpy: RandomState(2).random_sample((9, 400)).T - 0.5 +Db = cat(3, ambiDecoder(ls_full,'sad',1,2), ambiDecoder(ls_full,'mmd',1,2), ambiDecoder(ls_full,'allrad',1,2)); +dec_bands = decodeHOA_N3D(hoa2, Db, [500 2000], 48000); +bf = encodeBformat(s2, src); +D1 = ambiDecoder(ls_full, 'sad', 1, 1); +bf_dec = decodeBformat(bf, D1); +rotbf = rotateBformat(bf, 30, -20, 10); +save('ambisonics_polarch.mat', 'dirs', 'Y4', 'sig3', 'conv', 'maxre', 'ls_hemi', 'ls_full', ... + 'order_hemi', 'order_full', 'D', 'src', 's2', 'enc3', 'Db', 'dec_bands', ... + 'bf', 'D1', 'bf_dec', 'rotbf'); +fprintf('orders: hemisphere %d, full %d, auto %d\n', order_hemi, order_full, D.auto_order); diff --git a/tests/golden/make_vbap_polarch.m b/tests/golden/make_vbap_polarch.m new file mode 100644 index 0000000..e6abb89 --- /dev/null +++ b/tests/golden/make_vbap_polarch.m @@ -0,0 +1,20 @@ +% Golden data for tests/test_vbap.py: polarch Vector-Base-Amplitude-Panning +% (Archontis Politis, BSD 3-Clause), the copy that Iceberg installs in +% Toolboxes/polarch/vbap. Run from tests/golden with that folder on the path. +% Indices are 1-based. +ls2 = [0 30 -30 110 -110]; % 5.0, 2D +ls3 = [0 0; 90 0; 180 0; -90 0; 45 45; 135 45; -135 45; -45 45; 0 90]; % hemisphere +src2 = (-180:7.5:180)'; +[a, e] = meshgrid(-180:20:160, -10:20:90); src3 = [a(:) e(:)]; +pairs = findLsPairs(ls2); +inv2 = invertLsMtx(ls2, pairs); +g2 = vbap(src2, pairs, inv2); +g2s = vbap(src2, pairs, inv2, 20); +tri = findLsTriplets(ls3); +inv3 = invertLsMtx(ls3, tri); +g3 = vbap(src3, tri, inv3); +g3s = vbap(src3, tri, inv3, 20); +save('vbap_polarch.mat', 'ls2', 'ls3', 'src2', 'src3', 'pairs', 'inv2', 'g2', ... + 'g2s', 'tri', 'inv3', 'g3', 'g3s'); +fprintf('%d pairs, %d triplets\n', size(pairs, 1), size(tri, 1)); +disp(tri) diff --git a/tests/golden/vbap_polarch.mat b/tests/golden/vbap_polarch.mat new file mode 100644 index 0000000..8a568a5 Binary files /dev/null and b/tests/golden/vbap_polarch.mat differ diff --git a/tests/test_ambisonics.py b/tests/test_ambisonics.py new file mode 100644 index 0000000..c95bad4 --- /dev/null +++ b/tests/test_ambisonics.py @@ -0,0 +1,106 @@ +""" +Tests for pytta.ambisonics, a port of the Higher-Order-Ambisonics library +of Archontis Politis. Each test pins one rule. + +The golden data come from golden/make_ambisonics_polarch.m (MATLAB R2026b +with polarch Higher-Order-Ambisonics and Vector-Base-Amplitude-Panning). +""" +import os +import unittest + +import numpy as np +import scipy.io as sio + +from pytta import ambisonics as A + +GOLDEN = os.path.join(os.path.dirname(__file__), 'golden') + + +class TestAmbisonics(unittest.TestCase): + + def test_real_sh_are_orthonormal(self): + # Gauss-Legendre in elevation (12 nodes) and 24 azimuths integrate + # exactly every product of harmonics up to order 10, so + # sum w Y Y^T = I. The 21-design does the same with equal weights, + # (4 pi / N) Y Y^T = I, within the precision of its tabulated points, + # which falls with the order (4e-10 at order 4, 2.5e-6 at order 10). + x, w = np.polynomial.legendre.leggauss(12) + el, az = np.meshgrid(np.degrees(np.arcsin(x)), np.arange(24) * 15., indexing='ij') + Y = A.real_sh(10, np.column_stack([az.ravel(), el.ravel()])) + err_gl = np.abs((Y * np.repeat(w, 24) * np.pi / 12) @ Y.T - np.eye(121)).max() + _, dirs = A.t_design(21) + Y = A.real_sh(6, dirs) + err_td = np.abs(4 * np.pi / len(dirs) * Y @ Y.T - np.eye(49)).max() + print(f'\n Gauss-Legendre, order 10: {err_gl:.1e}; 21-design, order 6: {err_td:.1e}') + self.assertLess(err_gl, 1e-13) + self.assertLess(err_td, 1e-8) + + def test_first_order_is_fuma_bformat(self): + # FuMa B-format is W = s / sqrt(2), X, Y, Z = s times the unit + # vector. The first order of an N3D encoding converts to it, scaled + # by the 1 / sqrt(4 pi) of the orthonormal harmonics, and so does + # Iceberg's bformat_fuma from a third-order AmbiX, N3D or FuMa scene. + rng = np.random.default_rng(4) + s, dirs = rng.uniform(-1, 1, (32, 3)), np.array([[30, 10], [-100, 40], [170, -60]]) + bf = A.encode_bformat(s, dirs) / np.sqrt(4 * np.pi) + hoa = A.encode(s, dirs, 3) + errs = [np.abs(A.n3d_to_fuma(hoa[:, :4]) - bf).max(), + np.abs(A.bformat_fuma(hoa, 'n3d')[0] - bf).max(), + np.abs(A.bformat_fuma(A.n3d_to_sn3d(hoa), 'ambix')[0] - bf).max(), + np.abs(A.bformat_fuma(A.n3d_to_fuma(hoa), 'fuma')[0] - bf).max()] + print('\n ' + ', '.join(f'{e:.1e}' for e in errs)) + self.assertLess(max(errs), 1e-14) + self.assertEqual(A.bformat_fuma(hoa, 'n3d')[1], 3) + + def test_rotation_moves_the_encoded_direction(self): + # Rotating a scene with one source equals encoding the source in + # the rotated direction, M u, with M = Rx(roll) Ry(-pitch) Rz(-yaw); + # a positive yaw adds to the azimuth. + s, u = np.ones((1, 1)), np.array([[25., 15.]]) + yawed = A.rotate(A.encode(s, u, 4), 40, 0, 0) + e_yaw = np.abs(yawed - A.encode(s, [[65., 15.]], 4)).max() + M = A._euler_zyx(-np.radians(30), np.radians(20), np.radians(10)) + moved = A._directions((M @ A._unit(u)[0])[None]) + e_any = np.abs(A.rotate(A.encode(s, u, 4), 30, -20, 10) + - A.encode(s, moved, 4)).max() + print(f'\n yaw only {e_yaw:.1e}, yaw pitch roll {e_any:.1e}') + self.assertLess(max(e_yaw, e_any), 1e-14) + + def test_matches_polarch(self): + d = sio.loadmat(os.path.join(GOLDEN, 'ambisonics_polarch.mat'), + squeeze_me=True, struct_as_record=False) + c, D, L, sig3 = d['conv'], d['D'], d['ls_full'], d['sig3'] + hoa2 = np.random.RandomState(2).random_sample((9, 400)).T - 0.5 + self.assertEqual(A.layout_order(d['ls_hemi']), d['order_hemi']) + self.assertEqual(A.layout_order(L), d['order_full']) + D_auto, order = A.decoder(L, 'sad') + self.assertEqual(order, D.auto_order) + diffs = { + 'real_sh': (A.real_sh(4, d['dirs']), d['Y4']), + 'n3d_to_sn3d': (A.n3d_to_sn3d(sig3), c.n2sn), + 'sn3d_to_n3d': (A.sn3d_to_n3d(sig3), c.sn2n), + 'n3d_to_fuma': (A.n3d_to_fuma(sig3), c.n2fuma), + 'fuma_to_n3d': (A.fuma_to_n3d(sig3), c.fuma2n), + 'acn_to_sid': (A.acn_to_sid(sig3), c.acn2sid), + 'sid_to_acn': (A.sid_to_acn(sig3), c.sid2acn), + 'max_re_weights': (np.concatenate([A.max_re_weights(o) for o in (1, 3, 5)]), + np.concatenate(d['maxre'])), + 'sad': (A.decoder(L, 'sad', True, 3)[0], D.sad), + 'mmd': (A.decoder(L, 'mmd', True, 3)[0], D.mmd), + 'epad': (A.decoder(L, 'epad', False, 3)[0], D.epad), + 'allrad': (A.decoder(L, 'allrad', True, 3)[0], D.allrad), + 'csad': (A.decoder(L, 'csad', True, 2)[0], D.csad), + 'sad, order of the layout': (D_auto, D.auto), + 'encode': (A.encode(d['s2'], d['src'], 3), d['enc3']), + 'decode in 3 bands': (A.decode(hoa2, d['Db'], [500, 2000], 48000), d['dec_bands']), + 'encode_bformat': (A.encode_bformat(d['s2'], d['src']), d['bf']), + 'decode_bformat': (A.decode_bformat(d['bf'], d['D1']), d['bf_dec']), + 'rotate_bformat': (A.rotate_bformat(d['bf'], 30, -20, 10), d['rotbf'])} + worst = {k: np.abs(a - b).max() for k, (a, b) in diffs.items()} + print('\n ' + ', '.join(f'{k} {v:.1e}' for k, v in worst.items())) + for k, v in worst.items(): + self.assertLess(v, 1e-13, k) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_vbap.py b/tests/test_vbap.py new file mode 100644 index 0000000..00e5d13 --- /dev/null +++ b/tests/test_vbap.py @@ -0,0 +1,90 @@ +""" +Tests for pytta.vbap, vector base amplitude panning (Pulkki 1997), a port of +the VBAP library of Archontis Politis. Each test pins one rule. + +The golden data come from golden/make_vbap_polarch.m (MATLAB R2026b with +polarch Vector-Base-Amplitude-Panning, the copy that Iceberg installs); +their loudspeaker indices are 1-based. +""" +import os +import unittest + +import numpy as np +import scipy.io as sio + +from pytta import vbap + +GOLDEN = os.path.join(os.path.dirname(__file__), 'golden') + + +def unit(azi, elev=0.): + a, e = np.radians(azi), np.radians(elev) + return np.array([np.cos(a) * np.cos(e), np.sin(a) * np.cos(e), np.sin(e)]) + + +class TestVBAP(unittest.TestCase): + + def test_stereo_pair_follows_the_tangent_law(self): + # Loudspeakers at +-30 deg: (g1 - g2) / (g1 + g2) = tan(theta) / + # tan(30 deg), with g1^2 + g2^2 = 1 (Pulkki 1997, eq. 4 and 6). + ls = [30, -30] + groups = vbap.loudspeaker_pairs(ls) + inv = vbap.inverse_matrices(ls, groups) + theta = np.linspace(-29, 29, 59) + g = vbap.gains(theta, groups, inv) + law = (g[:, 0] - g[:, 1]) / (g[:, 0] + g[:, 1]) + err = np.max(np.abs(law - np.tan(np.radians(theta)) / np.tan(np.radians(30)))) + print(f'\n max |tangent law error| = {err:.1e}, ' + f'max |norm - 1| = {np.max(np.abs(np.linalg.norm(g, axis=1) - 1)):.1e}') + self.assertLess(err, 1e-12) + np.testing.assert_allclose(np.linalg.norm(g, axis=1), 1, atol=1e-12) + + def test_3d_gains_point_at_the_source(self): + # VBAP definition: the gains of the active triplet weight the + # loudspeaker vectors into a vector along the source direction, all + # gains are non-negative and their norm is 1. A source on a + # loudspeaker gets that loudspeaker alone. + ls = np.array([[0, 0], [90, 0], [180, 0], [-90, 0], [45, 45], + [135, 45], [-135, 45], [-45, 45], [0, 90]]) + groups = vbap.loudspeaker_triplets(ls) + inv = vbap.inverse_matrices(ls, groups) + rng = np.random.default_rng(1) + src = np.column_stack([rng.uniform(-180, 180, 200), rng.uniform(1, 89, 200)]) + g = vbap.gains(src, groups, inv) + L = np.array([unit(*d) for d in ls]) + p = g @ L + u = np.array([unit(*s) for s in src]) + cross = np.max(np.linalg.norm(np.cross(p, u), axis=1)) + print(f'\n max |p x u| = {cross:.1e} over 200 sources') + self.assertLess(cross, 1e-12) + self.assertTrue(np.all(g >= 0)) + np.testing.assert_allclose(np.linalg.norm(g, axis=1), 1, atol=1e-12) + on = vbap.gains(ls[[4]], groups, inv)[0] + self.assertAlmostEqual(on[4], 1, places=12) + self.assertAlmostEqual(np.sum(np.delete(on, 4)**2), 0, places=12) + + def test_matches_polarch(self): + # Same layouts and sources: pairs and triplets (1-based there), the + # inverse matrices (stacked by columns there) and the gains, with and + # without a spread of 20 deg (MDAP). + d = sio.loadmat(os.path.join(GOLDEN, 'vbap_polarch.mat'), squeeze_me=True) + pairs = vbap.loudspeaker_pairs(d['ls2']) + tri = vbap.loudspeaker_triplets(d['ls3']) + np.testing.assert_array_equal(pairs + 1, d['pairs']) + np.testing.assert_array_equal(tri + 1, d['tri']) + inv2 = vbap.inverse_matrices(d['ls2'], pairs) + inv3 = vbap.inverse_matrices(d['ls3'], tri) + diffs = { + 'inv2': np.abs(inv2.transpose(0, 2, 1).reshape(len(pairs), -1) - d['inv2']).max(), + 'inv3': np.abs(inv3.transpose(0, 2, 1).reshape(len(tri), -1) - d['inv3']).max(), + 'g2': np.abs(vbap.gains(d['src2'], pairs, inv2) - d['g2']).max(), + 'g2s': np.abs(vbap.gains(d['src2'], pairs, inv2, 20) - d['g2s']).max(), + 'g3': np.abs(vbap.gains(d['src3'], tri, inv3) - d['g3']).max(), + 'g3s': np.abs(vbap.gains(d['src3'], tri, inv3, 20) - d['g3s']).max()} + print('\n ' + ', '.join(f'{k} {v:.1e}' for k, v in diffs.items())) + for v in diffs.values(): + self.assertLess(v, 1e-12) + + +if __name__ == '__main__': + unittest.main()