% \iffalse meta-comment % % File: siunitx-angle.dtx Copyright (C) 2016-2019,2021-2024 Joseph Wright % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "siunitx bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % The released version of this bundle is available from CTAN. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/josephwright/siunitx % % for those people who are interested. % % ----------------------------------------------------------------------- % %<*driver> \documentclass{l3doc} % Additional commands needed in this source \ProvideDocumentCommand\email{m}{\href{mailto:#1}{\nolinkurl{#1}}} \ProvideDocumentCommand\foreign{m}{\textit{#1}} % The next line is needed so that \GetFileInfo will be able to pick up % version data \usepackage{siunitx} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \GetFileInfo{siunitx.sty} % % \title{^^A % \pkg{siunitx-angle} -- Formatting angles^^A % \thanks{This file describes \fileversion, % last revised \filedate.}^^A % } % % \author{^^A % Joseph Wright^^A % \thanks{^^A % E-mail: % \email{joseph@texdev.net}^^A % }^^A % } % % \date{Released \filedate} % % \maketitle % % \begin{documentation} % % \section{Formatting angles} % % \begin{function} % { % \siunitx_angle:n, \siunitx_angle:e, % \siunitx_angle:nnn, \siunitx_angle:eee % } % \begin{syntax} % \cs{siunitx_angle:n} \Arg{angle} % \cs{siunitx_angle:nnn} \Arg{degrees} \Arg{minutes} \Arg{seconds} % \end{syntax} % Typeset the \meta{angle} (which may be given as separate \meta{degree}, % \meta{minute} and \meta{second} components). The \meta{angle} (or components) % may be given as expressions. The \meta{angle} should be a number as understood % by \cs{siunitx_format_number:nN}, with no uncertainty, exponent or imaginary % part. The unit symbols for degrees, minutes and seconds are |\degree|, % |\arcminute| and |\arcsecond|, respectively. % \end{function} % % \subsection{Key--value options} % % The options defined by this submodule are available within the \pkg{l3keys} % |siunitx| tree. % % \begin{function}{angle-mode} % \begin{syntax} % |angle-mode| = \meta{choice} % \end{syntax} % Selects how angles are formatted: a choice from % the options |arc|, |decimal| and |input|. The option |arc| means that angles % will always be typeset in arc (degree, minute, second) format, whilst % |decimal| means that angles are typeset as a single decimal value. The % |input| setting means that the input format (\foreign{i.e.}~difference % between \cs{siunitx_angle:n} and \cs{siunitx_angle:nnn}) is maintained. The % standard setting is |input|. % \end{function} % % \begin{function} % { % angle-symbol-degree , % angle-symbol-minute , % angle-symbol-second % } % \begin{syntax} % |angle-symbol-degree| = \meta{symbol} % \end{syntax} % Sets the symbol used for arc degrees, minutes or seconds, respectively. % \end{function} % % \begin{function}{angle-symbol-over-decimal} % \begin{syntax} % |angle-symbol-over-decimal| = |true|\verb"|"|false| % \end{syntax} % Determines if the arc separator is printed over the decimal marker, a % format used in astronomy. The standard setting is |false|. % \end{function} % % \begin{function}{arc-separator} % \begin{syntax} % |arc-separator| = \meta{separator} % \end{syntax} % Inserted between arc parts (degree, minute and second components). % The standard setting is |\,|. % \end{function} % % \begin{function}{fill-angle-degrees} % \begin{syntax} % |fill-arc-degrees| = |true|\verb"|"|false| % \end{syntax} % Determines whether a missing degrees part is zero-filled when printing an % arc. The standard setting is |false|. % \end{function} % % \begin{function}{fill-angle-minutes} % \begin{syntax} % |fill-arc-minutes| = |true|\verb"|"|false| % \end{syntax} % Determines whether a missing minutes part is zero-filled when printing an % arc. The standard setting is |false|. % \end{function} % % \begin{function}{fill-angle-seconds} % \begin{syntax} % |fill-arc-seconds| = |true|\verb"|"|false| % \end{syntax} % Determines whether a missing seconds part is zero-filled when printing an % arc. The standard setting is |false|. % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{siunitx-angle} implementation} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*package> % \end{macrocode} % % Identify the internal prefix. % \begin{macrocode} %<@@=siunitx_angle> % \end{macrocode} % % \begin{variable}{\l_@@_tmp_bool, \l_@@_tmp_dim, \l_@@_tmp_tl} % Scratch space. % \begin{macrocode} \bool_new:N \l_@@_tmp_bool \dim_new:N \l_@@_tmp_dim \tl_new:N \l_@@_tmp_tl % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_symbol_degree_tl , % \l_@@_symbol_minute_tl , % \l_@@_symbol_second_tl , % \l_@@_force_arc_bool , % \l_@@_force_decimal_bool , % \l_@@_astronomy_bool , % \l_@@_separator_tl , % \l_@@_fill_degrees_bool , % \l_@@_fill_minutes_bool , % \l_@@_fill_seconds_bool % } % \begin{macrocode} \keys_define:nn { siunitx } { angle-mode .choice: , angle-mode / arc .code:n = { \bool_set_true:N \l_@@_force_arc_bool \bool_set_false:N \l_@@_force_decimal_bool } , angle-mode / decimal .code:n = { \bool_set_false:N \l_@@_force_arc_bool \bool_set_true:N \l_@@_force_decimal_bool } , angle-mode / input .code:n = { \bool_set_false:N \l_@@_force_arc_bool \bool_set_false:N \l_@@_force_decimal_bool } , angle-symbol-degree .tl_set:N = \l_@@_symbol_degree_tl , angle-symbol-minute .tl_set:N = \l_@@_symbol_minute_tl , angle-symbol-second .tl_set:N = \l_@@_symbol_second_tl , angle-symbol-over-decimal .bool_set:N = \l_@@_astronomy_bool , angle-separator .tl_set:N = \l_@@_separator_tl , fill-angle-degrees .bool_set:N = \l_@@_fill_degrees_bool , fill-angle-minutes .bool_set:N = \l_@@_fill_minutes_bool , fill-angle-seconds .bool_set:N = \l_@@_fill_seconds_bool , } \bool_new:N \l_@@_force_arc_bool \bool_new:N \l_@@_force_decimal_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\siunitx_angle:n, \siunitx_angle:e, \@@_angle:n} % \begin{macro}{\siunitx_angle:nnn, \siunitx_angle:eee} % \begin{macro}{\@@_arc_convert:n, \@@_arc_convert:e} % \begin{macro}{\@@_arc_convert:w} % The first step here is to force format conversion if required. Going to % a decimal is easy, going to arc format is a bit more painful: avoid % repeating calculations mainly for code readability. % \begin{macrocode} \cs_new_protected:Npn \siunitx_angle:n #1 { \bool_if:NTF \l_siunitx_number_parse_bool { \@@_angle:n {#1} } { \tl_if_blank:nF {#1} { \tl_set:Nn \l_@@_degrees_tl { \ensuremath {#1} } \@@_arc_print:VVV \l_@@_degrees_tl \c_empty_tl \c_empty_tl } } } \cs_generate_variant:Nn \siunitx_angle:n { e , x } \cs_new_protected:Npn \@@_angle:n #1 { \bool_if:NTF \l_@@_force_arc_bool { \@@_arc_convert:e { \fp_eval:n {#1} } } { \siunitx_number_parse:nN {#1} \l_@@_degrees_tl \siunitx_number_process:NN \l_@@_degrees_tl \l_@@_degrees_tl \tl_set:Nx \l_@@_degrees_tl { \siunitx_number_output:NN \l_@@_degrees_tl \q_nil } \@@_arc_print:VVV \l_@@_degrees_tl \c_empty_tl \c_empty_tl } } \cs_new_protected:Npn \siunitx_angle:nnn #1#2#3 { \bool_if:NTF \l_siunitx_number_parse_bool { \bool_if:NTF \l_@@_force_decimal_bool { \siunitx_angle:e { \fp_eval:n { #1 + ( 0 #2 ) / 60 + ( 0 #3 ) / 3600 } } } { \@@_arc_sign:nnn {#1} {#2} {#3} } } { \tl_set:Nx \l_@@_degrees_tl { \tl_if_blank:nF {#1} { \exp_not:n { \ensuremath {#1} } } } \tl_set:Nx \l_@@_minutes_tl { \tl_if_blank:nF {#2} { \exp_not:n { \ensuremath {#2} } } } \tl_set:Nx \l_@@_seconds_tl { \tl_if_blank:nF {#3} { \exp_not:n { \ensuremath {#3} } } } \@@_arc_print:VVV \l_@@_degrees_tl \l_@@_minutes_tl \l_@@_seconds_tl } } \cs_generate_variant:Nn \siunitx_angle:nnn { eee , xxx } % \end{macrocode} % Here, the need for absolute values is to handle conversion of negative % values: the result should be exactly one sign in the integer part. For % integer input, we can control whether there are minute or second parts % at all. % \begin{macrocode} \cs_new_protected:Npn \@@_arc_convert:n #1 { \@@_arc_convert:w #1 . \q_nil . \q_stop } \cs_generate_variant:Nn \@@_arc_convert:n { e } \cs_new_protected:Npn \@@_arc_convert:w #1 . #2 . #3 \q_stop { \quark_if_nil:nTF {#2} { \siunitx_angle:eee { \fp_eval:n { trunc(#1,0) } } { \bool_if:NT \l_@@_fill_minutes_bool { 0 } } { \bool_if:NT \l_@@_fill_seconds_bool { 0 } } } { \siunitx_angle:eee { \fp_eval:n { trunc(#1.#2,0) } } { \fp_eval:n { abs(trunc((#1.#2 - trunc(#1,0)) * 60,0)) } } { \fp_eval:n { abs ( (#1.#2 - trunc(#1.#2,0)) * 60 - trunc((#1.#2 - trunc(#1.#2,0)) * 60,0) ) * 60 } } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{variable}{\c_@@_parts_tl} % For mappings. % \begin{macrocode} \clist_const:Nn \c_@@_parts_tl { degrees , minutes , seconds } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_degrees_tl, \l_@@_minutes_tl, \l_@@_seconds_tl} % Space for formatting parsed numbers. % \begin{macrocode} \tl_new:N \l_@@_degrees_tl \tl_new:N \l_@@_minutes_tl \tl_new:N \l_@@_seconds_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_sign_tl} % For the \enquote{sign shuffle}. % \begin{macrocode} \tl_new:N \l_@@_sign_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_signed_bool} % To check that only one sign is seen in arc mode. % \begin{macrocode} \tl_new:N \l_@@_signed_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_suppress_comp:, \@@_suppress_exp:, \@@_suppress_uncert:,} % Pre-compilation of keys to suppress uncertainties and exponents, % and % \begin{macrocode} \keys_precompile:nnN { siunitx } { input-comparators = } \l_@@_tmp_tl \cs_set_protected:Npx \@@_suppress_comp: { \exp_not:V \l_@@_tmp_tl } \keys_precompile:nnN { siunitx } { input-exponent-markers = } \l_@@_tmp_tl \cs_set_protected:Npx \@@_suppress_exp: { \exp_not:V \l_@@_tmp_tl } \keys_precompile:nnN { siunitx } { input-close-uncertainty = , input-open-uncertainty = , input-uncertainty-signs = } \l_@@_tmp_tl \cs_set_protected:Npx \@@_suppress_uncert: { \exp_not:V \l_@@_tmp_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_arc_sign:nnn} % \begin{macro}{\@@_arc_sign:nn} % \begin{macro}{\@@_arc_sign_auxi:, \@@_arc_sign_auxii:} % \begin{macro}{\@@_extract_sign:nnnnnnnn} % \begin{macro}[EXP]{\@@_sign:nnnnnnn} % To get the sign in the right place whilst dealing with zero filling % means doing some shuffling. That means doing processing of each number % manually. For degrees, auto-conversion can give |-0|, which needs to be % picked up early to set the sign. A bit of shuffling is needed as only % the seconds argument is permitted to have an uncertainty. % \begin{macrocode} \cs_new_protected:Npn \@@_arc_sign:nnn #1#2#3 { \group_begin: \@@_suppress_exp: \tl_clear:N \l_@@_sign_tl \bool_set_false:N \l_@@_signed_bool \group_begin: \@@_suppress_uncert: \str_if_eq:nnTF {#1} { -0 } { \@@_arc_sign:nn { } { degrees } \@@_suppress_comp: \tl_set:Nn \l_@@_sign_tl { - } \bool_set_true:N \l_@@_signed_bool } { \@@_arc_sign:nn {#1} { degrees } } \@@_arc_sign:nn {#2} { minutes } \tl_set:Nx \l_@@_tmp_tl { \tl_set:Nn \exp_not:N \l_@@_sign_tl { \exp_not:V \l_@@_sign_tl } \tl_set:Nn \exp_not:N \l_@@_degrees_tl { \exp_not:V \l_@@_degrees_tl } \tl_set:Nn \exp_not:N \l_@@_minutes_tl { \exp_not:V \l_@@_minutes_tl } \exp_not:c { bool_set_ \bool_if:NTF \l_@@_signed_bool { true } { false } :N } \exp_not:N \l_@@_signed_bool } \exp_after:wN \group_end: \l_@@_tmp_tl \@@_arc_sign:nn {#3} { seconds } \tl_if_empty:NF \l_@@_sign_tl { \clist_map_function:NN \c_@@_parts_tl \@@_arc_sign_auxi:n } \clist_map_function:NN \c_@@_parts_tl \@@_arc_sign_auxii:n \@@_arc_print:VVV \l_@@_degrees_tl \l_@@_minutes_tl \l_@@_seconds_tl \group_end: } \cs_new_protected:Npn \@@_arc_sign:nn #1#2 { \tl_if_blank:nTF {#1} { \bool_if:cTF { l_@@_fill_ #2 _bool } { \tl_set:cn { l_@@_ #2 _tl } { { } { } { 0 } { } { } { } { 0 } } } { \tl_clear:c { l_@@_ #2 _tl } } } { \siunitx_number_parse:nN {#1} \l_@@_tmp_tl \tl_if_empty:NF \l_@@_tmp_tl { \exp_after:wN \@@_extract_sign:nnnnnnnn \l_@@_tmp_tl {#2} \bool_set_true:N \l_@@_signed_bool } } } \cs_new_protected:Npn \@@_arc_sign_auxi:n #1 { \tl_if_empty:cF { l_@@_ #1 _tl } { \tl_set:cx { l_@@_ #1 _tl } { { } { \exp_not:V \l_@@_sign_tl } \exp_after:wN \exp_after:wN \exp_after:wN \@@_sign:nnnnnnn \cs:w l_@@_ #1 _tl \cs_end: } \clist_map_break: } } \cs_new_protected:Npn \@@_arc_sign_auxii:n #1 { \tl_if_empty:cF { l_@@_ #1 _tl } { \bool_lazy_and:nnF { \l_@@_force_arc_bool } { ! \str_if_eq_p:nn {#1} { seconds } } { \siunitx_number_process:cc { l_@@_ #1 _tl } { l_@@_ #1 _tl } } \tl_set:cx { l_@@_ #1 _tl } { \siunitx_number_output:cN { l_@@_ #1 _tl } \q_nil } } } \cs_new_protected:Npn \@@_extract_sign:nnnnnnnn #1#2#3#4#5#6#7#8 { \tl_if_blank:nTF {#2} { \tl_set_eq:cN { l_@@_ #8 _tl } \l_@@_tmp_tl } { \bool_if:NTF \l_@@_signed_bool { \msg_error:nnn { siunitx } { arc-multi-sign } } { \tl_set:Nn \l_@@_sign_tl {#2} } \tl_set:cn { l_@@_ #8 _tl } { {#1} { } {#3} {#4} {#5} {#6} {#7} } \@@_suppress_comp: } } \cs_new:Npn \@@_sign:nnnnnnn #1#2#3#4#5#6#7 { \exp_not:n { {#3} {#4} {#5} {#6} {#7} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{variable}{\l_@@_marker_box, \l_@@_unit_box} % For \enquote{astronomy style} angles. % \begin{macrocode} \box_new:N \l_@@_marker_box \box_new:N \l_@@_unit_box % \end{macrocode} % \end{variable} % % \begin{macro} % { % \@@_arc_print:nnn, \@@_arc_print:VVV, % \@@_arc_print_auxi:nnn, \@@_arc_print_auxi:nVn % } % \begin{macro}{\@@_arc_print_auxii:w} % \begin{macro}{\@@_arc_print_auxiii:n} % \begin{macro}{\@@_arc_print_auxiv:NN} % \begin{macro}{\@@_arc_print_auxv:w} % \begin{macro}{\@@_arc_print_auxvi:n} % The final stage of printing an angle is to put together the three parts: % this works even for decimal angles as they will blank arguments for the % other two parts The need to handle astronomy-style formatting means that % the number has to be decomposed into parts. % \begin{macrocode} \cs_new_protected:Npn \@@_arc_print:nnn #1#2#3 { \@@_arc_print_auxi:nVn {#1} \l_@@_symbol_degree_tl {#2#3} \@@_arc_print_auxi:nVn {#2} \l_@@_symbol_minute_tl {#3} \@@_arc_print_auxi:nVn {#3} \l_@@_symbol_second_tl { } } \cs_generate_variant:Nn \@@_arc_print:nnn { VVV } \cs_new_protected:Npn \@@_arc_print_auxi:nnn #1#2#3 { \tl_if_blank:nF {#1} { \bool_if:NTF \l_siunitx_number_parse_bool { \bool_if:NTF \l_@@_astronomy_bool { \@@_arc_print_auxii:nw {#2} #1 \q_stop } { \@@_arc_print_auxv:w #1 \q_stop \@@_arc_print_auxvi:n {#2} } } { \siunitx_print_number:n {#1} \@@_arc_print_auxvi:n {#2} } \tl_if_blank:nF {#3} { \nobreak \l_@@_separator_tl } } } \cs_generate_variant:Nn \@@_arc_print_auxi:nnn { nV } % \end{macrocode} % To align the two parts of the astronomy-style marker, we need to allow % for the |\scriptspace|. % \begin{macrocode} \cs_new_protected:Npn \@@_arc_print_auxii:nw #1#2 \q_nil #3 \q_nil #4 \q_nil #5 \q_nil #6 \q_nil #7 \q_nil #8 \q_stop { \mode_if_math:TF { \bool_set_true:N \l_@@_tmp_bool } { \bool_set_false:N \l_@@_tmp_bool } \siunitx_print_number:n {#2#3#4} \tl_if_blank:nTF {#6} { \@@_arc_print_auxvi:n {#1} } { \hbox_set:Nn \l_@@_marker_box { \@@_arc_print_auxiii:n { \siunitx_print_number:n {#5} } } \hbox_set:Nn \l_@@_unit_box { \@@_arc_print_auxiii:n { \siunitx_unit_format:nN {#1} \l_@@_tmp_tl \siunitx_print_unit:V \l_@@_tmp_tl \skip_horizontal:n { -\scriptspace } } } \dim_compare:nNnTF { \box_wd:N \l_@@_marker_box } > { \box_wd:N \l_@@_unit_box } { \@@_arc_print_auxiv:NN \l_@@_marker_box \l_@@_unit_box } { \@@_arc_print_auxiv:NN \l_@@_unit_box \l_@@_marker_box } \hbox_set_to_wd:Nnn \l_@@_marker_box \l_@@_tmp_dim { \hbox_overlap_right:n { \box_use_drop:N \l_@@_marker_box } \hbox_overlap_right:n { \box_use_drop:N \l_@@_unit_box } \tex_hfil:D } \box_use:N \l_@@_marker_box \skip_horizontal:N \scriptspace \siunitx_print_number:n {#6} } } \cs_new_protected:Npn \@@_arc_print_auxiii:n #1 { \bool_if:NTF \l_@@_tmp_bool { \ensuremath } { \use:n } {#1} } \cs_new_protected:Npn \@@_arc_print_auxiv:NN #1#2 { \dim_set:Nn \l_@@_tmp_dim { \box_wd:N #1 } \hbox_set_to_wd:Nnn #2 \l_@@_tmp_dim { \tex_hss:D \hbox_unpack_drop:N #2 \tex_hss:D } } \cs_new_protected:Npn \@@_arc_print_auxv:w #1 \q_nil #2 \q_nil #3 \q_nil #4 \q_nil #5 \q_nil #6 \q_stop { \siunitx_print_number:n {#1#2#3#4#5} } \cs_new_protected:Npn \@@_arc_print_auxvi:n #1 { \group_begin: \siunitx_unit_options_apply:n {#1} \siunitx_unit_format:nN {#1} \l_@@_tmp_tl \siunitx_quantity_print:nV { } \l_@@_tmp_tl \group_end: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macrocode} \msg_new:nnnn { siunitx } { arc-multi-sign } { Multiple~signs~given~for~arc~angle! } { An~angle~given~as~an~arc~should~have~at~most~one~sign:~ only~the~first~sign~will~be~used. } % \end{macrocode} % % \subsection{Deprecated options} % % \begin{macrocode} \msg_new:nnn { siunitx } { angle-option-deprecated } { Option~"#1"~has~been~deprecated~in~this~release.\\ \\ Set~the~"quantity-product"~option~for~the~units~ \token_to_str:N \arcminute,~\arcsecond\ and~\degree\ instead. } \keys_define:nn { siunitx } { number-angle-product .code:n = { \msg_info:nnn { siunitx } { angle-option-deprecated } { number-angle-product } \siunitx_unit_options_declare:Nn \arcminute { quantity-product = {#1} } \siunitx_unit_options_declare:Nn \arcsecond { quantity-product = {#1} } \siunitx_unit_options_declare:Nn \degree { quantity-product = {#1} } } } % \end{macrocode} % % \subsection{Standard settings for module options} % % \begin{macrocode} \keys_set:nn { siunitx } { angle-mode = input , angle-symbol-degree = \degree , angle-symbol-minute = \arcminute , angle-symbol-over-decimal = false , angle-symbol-second = \arcsecond , angle-separator = , fill-angle-degrees = false , fill-angle-minutes = false , fill-angle-seconds = false } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex