% -------------------------------------------------------------------------- % % the CHEMMACROS package -- module: `redox' % % -------------------------------------------------------------------------- % Clemens Niederberger % -------------------------------------------------------------------------- % https://github.org/cgnieder/chemmacros/ % contact@mychemistry.eu % -------------------------------------------------------------------------- % If you have any ideas, questions, suggestions or bugs to report, please % feel free to contact me. % -------------------------------------------------------------------------- % Copyright 2011--2020 Clemens Niederberger % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Clemens Niederberger. % -------------------------------------------------------------------------- \ChemModule{redox}{2020/02/02 oxidation numbers, redox reactions} \chemmacros_load_modules:n {tikz,xfrac} \RequirePackage{mathtools,relsize} \msg_new:nnn {chemmacros} {redox} { \token_to_str:N #1 \c_space_tl : ~ #2 ~ \msg_line_context: . } % -------------------------------------------------------------------------- % oxidation numbers % \ox{,} \bool_new:N \l__chemmacros_ox_sign_bool \bool_new:N \l__chemmacros_ox_integer_bool \bool_new:N \l__chemmacros_ox_explicit_sign_bool \ChemCompatibilityFrom{5.2} \bool_new:N \l__chemmacros_ox_explicit_zero_sign_bool \EndChemCompatibility \bool_new:N \l__chemmacros_ox_format_roman_bool \bool_new:N \l__chemmacros_ox_decimal_marker_comma_bool \bool_new:N \l__chemmacros_ox_parse_bool \bool_new:N \l__chemmacros_ox_side_bool \bool_new:N \l__chemmacros_ox_super_bool \bool_new:N \l__chemmacros_ox_top_bool \bool_new:N \l__chemmacros_ox_align_center_bool \ChemCompatibilityFrom{5.11} \tl_new:N \l__chemmacros_ox_format_tl \EndChemCompatibility \int_new:N \l__chemmacros_ox_number_int \fp_new:N \l__chemmacros_ox_number_fp \cs_new_protected:Npn \__chemmacros_ox_format:n #1 { \chemmacros_if_compatibility:nnTF {>=} {5.11} { { \l__chemmacros_ox_format_tl {#1} } } { \use:n {#1} } } \cs_new_protected:Npn \__chemmacros_ox_process_number:n #1 { \bool_if:NTF \l__chemmacros_ox_parse_bool { \tl_if_in:nnTF {#1} { / } { \__chemmacros_ox_fraction:w #1 \q_stop } { \__chemmacros_ox_sign:n {#1} \__chemmacros_ox_value:n {#1} } } {#1} } \cs_new_protected:Npn \__chemmacros_ox_fraction:w #1/#2 \q_stop { \group_begin: \bool_set_false:N \l__chemmacros_ox_format_roman_bool \__chemmacros_ox_sign:n {#1} \bool_if:NTF \l__chemmacros_ox_side_bool { \chemmacros_ox_text_frac:nn } { \chemmacros_ox_super_frac:nn } { \__chemmacros_ox_value:n {#1} } {#2} \group_end: } \ChemCompatibilityTo{5.4} \cs_new_protected:Npn \__chemmacros_ox_sign:n #1 { \bool_lazy_and:nnT { \fp_compare_p:n { #1 > 0 } } { \l__chemmacros_ox_explicit_sign_bool } { \c_math_toggle_token + \c_math_toggle_token } \bool_lazy_and:nnT { \fp_compare_p:n { #1 = 0 } } { \l__chemmacros_ox_explicit_sign_bool } { \c_math_toggle_token \pm \c_math_toggle_token } \fp_compare:nT { #1 < 0 } { \c_math_toggle_token - \c_math_toggle_token } } \EndChemCompatibility \ChemCompatibilityFrom{5.4} \cs_new_protected:Npn \__chemmacros_ox_sign:n #1 { \bool_lazy_and:nnT { \fp_compare_p:n { #1 > 0 } } { \l__chemmacros_ox_explicit_sign_bool } { \c_math_toggle_token + \c_math_toggle_token } \bool_lazy_all:nT { { \fp_compare_p:n { #1 = 0 } } { \l__chemmacros_ox_explicit_sign_bool } { \l__chemmacros_ox_explicit_zero_sign_bool } } { \c_math_toggle_token \pm \c_math_toggle_token } \fp_compare:nT { #1 < 0 } { \c_math_toggle_token - \c_math_toggle_token } } \EndChemCompatibility \cs_new_protected:Npn \__chemmacros_ox_value:n #1 { \fp_set:Nn \l__chemmacros_ox_number_fp { abs(#1) } \__chemmacros_ox_is_integer:n { \l__chemmacros_ox_number_fp } \bool_if:NTF \l__chemmacros_ox_format_roman_bool { \__chemmacros_fp_to_Roman:n { \l__chemmacros_ox_number_fp } } { \bool_if:NTF \l__chemmacros_ox_integer_bool { \c_math_toggle_token \__chemmacros_fp_to_arabic:n { \l__chemmacros_ox_number_fp } \c_math_toggle_token } { \c_math_toggle_token \fp_use:N \l__chemmacros_ox_number_fp \c_math_toggle_token } } } \cs_new_protected:Npn \__chemmacros_ox_is_integer:n #1 { \fp_compare:nNnTF {#1} = { round((#1) + 1 , 0 ) - 1 } { \bool_set_true:N \l__chemmacros_ox_integer_bool } { \bool_set_false:N \l__chemmacros_ox_integer_bool \bool_set_false:N \l__chemmacros_ox_format_roman_bool } } \cs_new_protected:Npn \__chemmacros_fp_to_Roman:n #1 { \group_begin: \fp_compare:nNnTF {#1} = { 0 } {0} { \int_to_Roman:n { \fp_to_int:n {#1} } } \group_end: } \cs_new_protected:Npn \__chemmacros_fp_to_arabic:n #1 { \group_begin: \bool_if:NTF \l__chemmacros_ox_decimal_marker_comma_bool { \tl_set:Nx \l__chemmacros_tmpa_tl { \fp_to_decimal:n {#1} } \tl_replace_once:Nnn \l__chemmacros_tmpa_tl {.} { {,} } \tl_use:N \l__chemmacros_tmpa_tl } { \fp_to_decimal:n {#1} } \group_end: } \tl_new:N \l__chemmacros_ox_side_connect_tl \cs_new_protected:Npn \__chemmacros_ox_write:nn #1#2 { \tl_if_blank:nT {#1} { \msg_error:nnn {chemmacros} {redox} {\ox} {oxidation~number~missing} } \tl_if_blank:nT {#2} { \msg_error:nnn {chemmacros} {redox} {\ox} {oxidation-atom~missing} } \hbox_set:Nn \l__chemmacros_tmpa_box { \chemmacros_text:n { \__chemmacros_ox_format:n { \relsize {-3} \__chemmacros_ox_process_number:n {#1} } } } \hbox_set:Nn \l__chemmacros_tmpb_box { \chemmacros_chemformula:n {#2} } \chemmacros_math:n { \bool_if:NT \l__chemmacros_ox_super_bool { \box_use:N \l__chemmacros_tmpb_box \c_math_superscript_token { \box_use:N \l__chemmacros_tmpa_box } } \bool_if:NT \l__chemmacros_ox_side_bool { \chemmacros_text:n { \box_use:N \l__chemmacros_tmpb_box \tl_use:N \l__chemmacros_ox_side_connect_tl ( \__chemmacros_ox_format:n { \__chemmacros_ox_process_number:n {#1} } ) } } \bool_if:NT \l__chemmacros_ox_top_bool { \bool_if:NF \l__chemmacros_ox_align_center_bool { \hbox_set_to_wd:Nnn \l__chemmacros_tmpa_box { \box_wd:N \l__chemmacros_tmpb_box } { \rlap { \hbox_unpack:N \l__chemmacros_tmpa_box } \hfil } } \overset { \bool_if:NTF \l__chemmacros_ox_align_center_bool { \clap } { \use:n } { \box_use:N \l__chemmacros_tmpa_box } } { \box_use:N \l__chemmacros_tmpb_box } } } } \cs_new_protected:Npn \__chemmacros_ox_pos_top: { \bool_set_true:N \l__chemmacros_ox_top_bool \bool_set_false:N \l__chemmacros_ox_super_bool \bool_set_false:N \l__chemmacros_ox_side_bool } \cs_new_protected:Npn \__chemmacros_ox_pos_super: { \bool_set_false:N \l__chemmacros_ox_top_bool \bool_set_true:N \l__chemmacros_ox_super_bool \bool_set_false:N \l__chemmacros_ox_side_bool } \cs_new_protected:Npn \__chemmacros_ox_pos_side: { \bool_set_false:N \l__chemmacros_ox_top_bool \bool_set_false:N \l__chemmacros_ox_super_bool \bool_set_true:N \l__chemmacros_ox_side_bool } \chemmacros_define_keys:nn {redox} { pos .choice: , pos / top .code:n = \__chemmacros_ox_pos_top: , pos / super .code:n = \__chemmacros_ox_pos_super: , pos / side .code:n = \__chemmacros_ox_pos_side: , pos .initial:n = super , side-connect .tl_set:N = \l__chemmacros_ox_side_connect_tl , side-connect .initial:n = \, , text-frac .code:n = \cs_set_protected:Npn \chemmacros_ox_text_frac:nn ##1##2 {#1} , text-frac .initial:n = \chemfrac[text]{#1}{#2} , super-frac .code:n = \cs_set_protected:Npn \chemmacros_ox_super_frac:nn ##1##2 {#1} , super-frac .initial:n = \chemfrac[superscript]{#1}{#2} , roman .bool_set:N = \l__chemmacros_ox_format_roman_bool , roman .default:n = true , roman .initial:n = true , parse .bool_set:N = \l__chemmacros_ox_parse_bool , parse .default:n = true , parse .initial:n = true , explicit-sign .bool_set:N = \l__chemmacros_ox_explicit_sign_bool , explicit-sign .default:n = true , decimal-marker .choice: , decimal-marker / comma .code:n = { \bool_set_true:N \l__chemmacros_ox_decimal_marker_comma_bool } , decimal-marker / point .code:n = { \bool_set_false:N \l__chemmacros_ox_decimal_marker_comma_bool } , decimal-marker .initial:n = point , align .choice: , align / center .code:n = \bool_set_true:N \l__chemmacros_ox_align_center_bool , align / right .code:n = \bool_set_false:N \l__chemmacros_ox_align_center_bool , align .initial:n = center } \ChemCompatibilityFrom{5.4} \chemmacros_define_keys:nn {redox} { explicit-zero-sign .bool_set:N = \l__chemmacros_ox_explicit_zero_sign_bool , explicit-zero-sign .default:n = true } \EndChemCompatibility \ChemCompatibilityFrom{5.11} \chemmacros_define_keys:nn {redox} { format .tl_set:N = \l__chemmacros_ox_format_tl } \EndChemCompatibility % \ox[]{,} % \ox*[]{,} => always number on the top \cs_new_protected:Npn \chemmacros_ox:nnnn #1#2#3#4 { \group_begin: \chemmacros_set_keys:nn {redox} {#2} \tl_if_blank:nF {#1} { \__chemmacros_ox_pos_top: } \__chemmacros_ox_write:nn {#3} {#4} \group_end: } \NewDocumentCommand \ox { sO{}>{\SplitArgument{1}{,}}m } { \IfBooleanTF {#1} { \chemmacros_ox:nnnn {#1} {#2} #3 } { \chemmacros_ox:nnnn { } {#2} #3 } } % --------------------------------------------------------------------------- % redox reactions \tl_new:N \l__chemmacros_redox_begin_tl \tl_new:N \l__chemmacros_redox_end_tl \tl_new:N \l__chemmacros_redox_tikz_tl \tl_new:N \l__chemmacros_redox_shift_tl \tl_new:N \l__chemmacros_redox_anchor_tl \tl_new:N \l__chemmacros_redox_side_tl \fp_new:N \l__chemmacros_redox_shift_fp \dim_new:N \l__chemmacros_redox_sep_dim \dim_new:N \l__chemmacros_redox_sep_default_dim \dim_set:Nn \l__chemmacros_redox_sep_default_dim {.2em} \dim_new:N \l__chemmacros_redox_dist_dim \dim_set:Nn \l__chemmacros_redox_dist_dim {.6em} % place and name nodes: % \OX{,} \NewDocumentCommand \OX { >{\SplitArgument{1}{,}}m } { \chemmacros_redox_partner:nn #1 } % TODO: add option to make `#2' be parsed as chem formula \cs_new_protected:Npn \chemmacros_redox_partner:nn #1#2 { \legacy_if:nTF {measuring@} { \group_begin: \tikz[baseline=(#1.base)] { \node [inner~sep=0pt] (#1) {#2} ; } \group_end: } { \tl_if_blank:nT {#1} { \msg_error:nnnn {chemmacros} {redox} {\OX} {node~name~missing} } \tl_if_blank:nT {#2} { \msg_error:nnnn {chemmacros} {redox} {\OX} {atom~missing} } \tikz[baseline=(#1.base),remember~picture] { \node [inner~sep=0pt] (#1) {#2} ; } } } \cs_new_protected:Npn \__chemmacros_redox_coordinates:nn #1#2 { \tl_set:Nn \l__chemmacros_redox_begin_tl {#1} \tl_set:Nn \l__chemmacros_redox_end_tl {#2} } \cs_new_protected:Npn \chemmacros_redox:nnnnn #1#2#3#4#5 { \tl_clear:N \l__chemmacros_redox_begin_tl \tl_clear:N \l__chemmacros_redox_end_tl \tl_clear:N \l__chemmacros_redox_tikz_tl \__chemmacros_redox_coordinates:nn {#1} {#2} \tl_if_blank:nF {#3} { \tl_set:Nn \l__chemmacros_redox_tikz_tl {#3} } \tl_if_blank:nTF {#4} { \fp_set:Nn \l__chemmacros_redox_shift_fp {1} \tl_set:Nn \l__chemmacros_redox_anchor_tl {above} \tl_set:Nn \l__chemmacros_redox_side_tl {north} \dim_set_eq:NN \l__chemmacros_redox_sep_dim \l__chemmacros_redox_sep_default_dim } { \fp_compare:nNnTF {#4} < {0} { \tl_set:Nn \l__chemmacros_redox_anchor_tl {below} \tl_set:Nn \l__chemmacros_redox_side_tl {south} \exp_args:NNo \dim_set:Nn \l__chemmacros_redox_sep_dim { - \l__chemmacros_redox_sep_default_dim } } { \tl_set:Nn \l__chemmacros_redox_anchor_tl {above} \tl_set:Nn \l__chemmacros_redox_side_tl {north} \dim_set_eq:NN \l__chemmacros_redox_sep_dim \l__chemmacros_redox_sep_default_dim } \fp_set:Nn \l__chemmacros_redox_shift_fp {#4} } \tl_set:Nx \l__chemmacros_redox_shift_tl { \fp_to_decimal:N \l__chemmacros_redox_shift_fp } \tikz[remember~picture,overlay] { \chemmacros_tikz_draw:x { \exp_not:V \l__chemmacros_redox_tikz_tl } ($ (\l__chemmacros_redox_begin_tl . \l__chemmacros_redox_side_tl)+(0,\l__chemmacros_redox_sep_dim) $) -- ++(0,\l__chemmacros_redox_shift_tl * \l__chemmacros_redox_dist_dim) -| node [pos=.25,\l__chemmacros_redox_anchor_tl] { {#5} } ($ (\l__chemmacros_redox_end_tl . \l__chemmacros_redox_side_tl)+(0,\l__chemmacros_redox_sep_dim) $) ; } } \NewDocumentCommand \redox { >{\SplitArgument{1}{,}}r()ooG{} } { \IfNoValueT {#1} { \msg_error:nnnn {chemmacros} {redox} {\redox} {You~need~to~specify~coordinates} } \IfNoValueTF {#2} { \chemmacros_redox:nnnnn #1 { } { } {#4} } { \IfNoValueTF {#3} { \chemmacros_redox:nnnnn #1 {#2} { } {#4} } { \chemmacros_redox:nnnnn #1 {#2} {#3} {#4} } } } % redox-keys \chemmacros_define_keys:nn {redox} { dist .dim_set:N = \l__chemmacros_redox_dist_dim , dist .initial:n = {.6em} , sep .dim_set:N = \l__chemmacros_redox_sep_default_dim , sep .initial:n = {.2em} } % -------------------------------------------------------------------------- \file_input_stop: 2015/09/30 - new option `explicit-zero-sign' 2020/02/02 - lazy boolean evaluation - prefer \legacy_if:nTF over \chemmacros_if_tex:nnTF 2020/03/02 - add option redox/format