% pgf-interference.sty
% LaTeX package for simulating interference patterns
% Author: K. Wehr
% Version 0.1
% 9th January 2022
%
% 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.
%
\ProvidesExplPackage {pgf-interference} {2022-01-09} {0.1} {simulating interference patterns}

\RequirePackage {tikz}

\bool_new:N \g_pgfinterference_draft_bool

\int_new:N \l_pgfinterference_slits_int % Anzahl der Spalte

\fp_new:N \l_pgfinterference_wavelength_fp % Wellenlänge
\fp_new:N \l_pgfinterference_slit_distance_fp % Spaltabstand (in m)
\fp_new:N \l_pgfinterference_slit_width_fp % Spaltbreite (in m)
\fp_new:N \l_pgfinterference_h_fp % Hilfsgröße (in 1/m)
\fp_new:N \l_pgfinterference_hb_fp % Hilfsgröße (dimensionslos)
\fp_new:N \l_pgfinterference_hs_fp % Hilfsgröße (dimensionslos)
\fp_new:N \l_pgfinterference_x_fp % Position auf dem Schirm (in m)
\fp_new:N \l_pgfinterference_screen_distance_fp % Schirmabstand (in m)
\fp_new:N \l_pgfinterference_screen_width_fp % Schirmbreite (in m)
\fp_new:N \l_pgfinterference_screen_height_fp % Schirmhöhe (in m)
\fp_new:N \l_pgfinterference_screen_scale_fp % Skalierung
\fp_new:N \l_pgfinterference_i_fp % relative Intensität innerhalb des Musters
\fp_new:N \l_pgfinterference_i_scale_fp % Skalierung der Intensität

\str_new:N \l_pgfinterference_ruler_type_str

\tl_const:Nn \c_pgfinterference_ruler_unity_tl { \fontsize{9}{9} \upshape \sffamily cm }
\tl_const:Nn \c_pgfinterference_ruler_tick_format_tl { \fontsize{9}{9} \upshape \sffamily }

\dim_new:N \l_pgfinterference_screen_width_dim % Schirmbreite in der Abbildung
\dim_new:N \l_pgfinterference_screen_height_dim % Schirmhöhe in der Abbildung
\dim_new:N \l_pgfinterference_ruler_height_dim % Höhe des Lineals in der Abbildung
\dim_new:N \l_pgfinterference_ruler_ticklength_i_dim % Länge eines langen Skalenstrichs auf dem Lineal
\dim_new:N \l_pgfinterference_ruler_ticklength_ii_dim % Länge eines mittleren Skalenstrichs auf dem Lineal
\dim_new:N \l_pgfinterference_ruler_ticklength_iii_dim % Länge eines kurzen Skalenstrichs auf dem Lineal
\dim_new:N \l_pgfinterference_ruler_linewidth_dim % Strichstärke der Linealskala in der Abbildung
\dim_const:Nn \c_pgfinterference_delta_dim {0.2pt} % Größe eines Bildpunkts

\msg_new:nnn {pgf-interference} {non-positive number}
  {
    The~value~of~#1~must~be~positive~ \msg_line_context: .
  }

\msg_new:nnn {pgf-interference} {wavelength too big}
  {
    The~wavelength~is~greater~than~1~metre~ \msg_line_context: .~
    Radio~waves~are~not~supported!
  }

\msg_new:nnn {pgf-interference} {slit width too big}
  {
    The~slit~width~is~greater~than~the~slit~distance~ \msg_line_context: .~
    This~is~not~possible!
  }

\int_set:Nn \l_pgfinterference_slits_int {2}
\fp_set:Nn \l_pgfinterference_wavelength_fp {632.8e-9}
\fp_set:Nn \l_pgfinterference_slit_width_fp {1e-5}
\fp_set:Nn \l_pgfinterference_slit_distance_fp {1e-4}
\fp_set:Nn \l_pgfinterference_screen_distance_fp {1}
\fp_set:Nn \l_pgfinterference_screen_width_fp {0.1}
\fp_set:Nn \l_pgfinterference_screen_height_fp {0.03}
\fp_set:Nn \l_pgfinterference_screen_scale_fp {1}
\fp_set:Nn \l_pgfinterference_i_scale_fp {1}
\str_set:Nn \l_pgfinterference_ruler_type_str {none}

\colorlet {pgfinterferencescreencolor} {black}

% Paketoption
\DeclareOption {draft}
  {
    \bool_gset_true:N \g_pgfinterference_draft_bool
  }
\ProcessOptions

% Optionen für das Licht
\keys_define:nn {pgf-interference}
  {
    wavelength .value_required:n = true ,
    wavelength .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_wavelength_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {wavelength}
          }
      } ,
    intensity .value_required:n = true ,
    intensity .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_i_scale_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {intensity}
          }
      }
  }

% Optionen für das Beugungsobjekt
\keys_define:nn {pgf-interference}
  {
    slits .value_required:n = true ,
    slits .code:n =
      {
        \int_compare:nNnTF {#1} > {0}
          {
            \int_set:Nn \l_pgfinterference_slits_int {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {slits}
          }
      } ,
    slit-distance .value_required:n = true ,
    slit-distance .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_slit_distance_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {slit-distance}
          }
      } ,
    slit-width .value_required:n = true ,
    slit-width .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_slit_width_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {slit-width}
          }
      }
  }

% Optionen für den Schirm
\keys_define:nn {pgf-interference}
  {
    screen-distance .value_required:n = true ,
    screen-distance .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_screen_distance_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {screen-distance}
          }
      } ,
    screen-width .value_required:n = true ,
    screen-width .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_screen_width_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {screen-width}
          }
      } ,
    screen-height .value_required:n = true ,
    screen-height .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_screen_height_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {screen-height}
          }
      } ,
    screen-color .value_required:n = true ,
    screen-color .code:n = { \colorlet {pgfinterferencescreencolor} {#1} } ,
    scale .value_required:n = true ,
    scale .code:n =
      {
        \fp_compare:nNnTF {#1} > {0}
          {
            \fp_set:Nn \l_pgfinterference_screen_scale_fp {#1}
          }
          {
            \msg_warning:nnn {pgf-interference} {non-positive number} {scale}
          }
      }
  }

% Optionen für das Lineal
\keys_define:nn {pgf-interference}
  {
    ruler .choices:nn = { above , below , screen , none }
      {
        \str_set:Nn \l_pgfinterference_ruler_type_str {#1}
      } ,
    ruler .default:n = {below}
  }

\cs_new:Nn \pgfinterference_prepare_ruler:
  {
    \dim_set:Nn \l_pgfinterference_ruler_height_dim
      {
        \fp_eval:n { \l_pgfinterference_screen_scale_fp * 1.2 }  cm
      }
    \dim_set:Nn \l_pgfinterference_ruler_ticklength_i_dim
      {
        \fp_eval:n { \l_pgfinterference_screen_scale_fp * 5 } mm
      }
    \dim_set:Nn \l_pgfinterference_ruler_ticklength_ii_dim
      {
        \fp_eval:n { \l_pgfinterference_screen_scale_fp * 4 } mm
      }
    \dim_set:Nn \l_pgfinterference_ruler_ticklength_iii_dim
      {
        \fp_eval:n { \l_pgfinterference_screen_scale_fp * 3 } mm
      }
    \dim_set:Nn \l_pgfinterference_ruler_linewidth_dim
      {
        \fp_eval:n { \l_pgfinterference_screen_scale_fp * 0.15 } mm
      }
  }

\cs_new:Nn \pgfinterference_draw_ruler:
  {
    \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {above}
      {
        \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
          ( 0.5 \l_pgfinterference_screen_width_dim , 0.5 \l_pgfinterference_screen_height_dim )
          -- ++ ( - \l_pgfinterference_screen_width_dim , 0 ) -- ++ ( 0 , \l_pgfinterference_ruler_height_dim )
          node [ below~right , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]  { \c_pgfinterference_ruler_unity_tl }
          -- ++ ( \l_pgfinterference_screen_width_dim , 0 ) -- cycle ;
      }
      {
        \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {below}
          {
            \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
              ( 0.5 \l_pgfinterference_screen_width_dim , -0.5 \l_pgfinterference_screen_height_dim )
              -- ++ ( - \l_pgfinterference_screen_width_dim , 0 ) -- ++ ( 0 , - \l_pgfinterference_ruler_height_dim )
              node [ above~right , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]  { \c_pgfinterference_ruler_unity_tl }
              -- ++ ( \l_pgfinterference_screen_width_dim , 0 ) -- cycle ;
          }
          {
            \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
              ( 0.5 \l_pgfinterference_screen_width_dim , 0.5 \l_pgfinterference_screen_height_dim )
              -- ++ ( - \l_pgfinterference_screen_width_dim , 0 ) -- ++ ( 0 , - \l_pgfinterference_ruler_height_dim )
              node [ above~right , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]  { \c_pgfinterference_ruler_unity_tl }
              -- ++ ( \l_pgfinterference_screen_width_dim , 0 ) -- cycle ;
          }
      }
    \int_set:Nn \l_tmpa_int { \fp_eval:n { floor ( 1000 * \l_pgfinterference_screen_width_fp ) - 1 } }
    \int_step_inline:nnn {1} { \l_tmpa_int }
      {
        \dim_set:Nn \l_tmpa_dim { \fp_eval:n { \l_pgfinterference_screen_scale_fp * ##1 } mm }
        \dim_sub:Nn \l_tmpa_dim { 0.5 \l_pgfinterference_screen_width_dim }
        \int_compare:nNnTF { \int_mod:nn {##1} {10} } = {0}
          {
            \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {above}
              {
                \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                  ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                  -- ++ ( 0 , \l_pgfinterference_ruler_ticklength_i_dim )
                  node [ above , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]
                    { \c_pgfinterference_ruler_tick_format_tl \int_eval:n {##1/10} } ;
              }
              {
                \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {below}
                  {
                    \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                      ( \l_tmpa_dim , -0.5 \l_pgfinterference_screen_height_dim )
                      -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_i_dim )
                      node [ below , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]
                        { \c_pgfinterference_ruler_tick_format_tl \int_eval:n {##1/10} } ;
                  }
                  {
                    \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                      ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                      -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_i_dim )
                      node [ below , scale = \fp_use:N \l_pgfinterference_screen_scale_fp ]
                        { \c_pgfinterference_ruler_tick_format_tl \int_eval:n {##1/10} } ;
                  }
              }
          }
          {
            \int_compare:nNnTF { \int_mod:nn {##1} {5} } = {0}
              {
                \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {above}
                  {
                    \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                      ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                      -- ++ ( 0 , \l_pgfinterference_ruler_ticklength_ii_dim ) ;
                  }
                  {
                    \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {below}
                      {
                        \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                          ( \l_tmpa_dim , -0.5 \l_pgfinterference_screen_height_dim )
                          -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_ii_dim ) ;
                      }
                      {
                        \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                          ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                          -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_ii_dim ) ;
                      }
                  }
              }
              {
                \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {above}
                  {
                    \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                      ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                      -- ++ ( 0 , \l_pgfinterference_ruler_ticklength_iii_dim ) ;
                  }
                  {
                    \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {below}
                      {
                        \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                          ( \l_tmpa_dim , -0.5 \l_pgfinterference_screen_height_dim )
                          -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_iii_dim ) ;
                      }
                      {
                        \draw [ line~width = \l_pgfinterference_ruler_linewidth_dim ]
                          ( \l_tmpa_dim , 0.5 \l_pgfinterference_screen_height_dim )
                          -- ++ ( 0 , - \l_pgfinterference_ruler_ticklength_iii_dim ) ;
                      }
                  }
              }
          }
      }
  }

\cs_new:Nn \pgfinterference_draw_pattern:
  {
    \dim_step_inline:nnnn { -0.5 \l_pgfinterference_screen_width_dim + 0.5 \c_pgfinterference_delta_dim }
      { \c_pgfinterference_delta_dim } { 0.5 \l_pgfinterference_screen_width_dim }
      {
        \fp_set:Nn \l_pgfinterference_x_fp { \dim_ratio:nn {##1} {100cm} / \l_pgfinterference_screen_scale_fp }
        \fp_set:Nn \l_pgfinterference_h_fp
          {
            pi / \l_pgfinterference_wavelength_fp
            / sqrt ( ( \l_pgfinterference_screen_distance_fp / \l_pgfinterference_x_fp ) ^ 2 + 1 )
          }
        \fp_set:Nn \l_pgfinterference_hb_fp { \l_pgfinterference_slit_width_fp * \l_pgfinterference_h_fp }
        \fp_set:Nn \l_pgfinterference_hs_fp { \l_pgfinterference_slit_distance_fp * \l_pgfinterference_h_fp }
        \fp_set:Nn \l_pgfinterference_i_fp
          {
            ( sin ( \l_pgfinterference_hb_fp ) / \l_pgfinterference_hb_fp
              * sin ( \l_pgfinterference_slits_int * \l_pgfinterference_hs_fp ) / sin ( \l_pgfinterference_hs_fp )
              / \l_pgfinterference_slits_int ) ^ 2
          }
        \int_set:Nn \l_tmpa_int
          {
            \fp_eval:n { round ( 100 * \l_pgfinterference_i_fp * \l_pgfinterference_i_scale_fp ) }
          }
        \int_compare:nNnTF { \l_tmpa_int } < {100}
          {
            \colorlet {pgfinterferencecolor}
              {
                pgfinterferencelasercolor
                ! \int_use:N \l_tmpa_int
                ! pgfinterferencescreencolor
              }
          }
          {
            \colorlet {pgfinterferencecolor} {pgfinterferencelasercolor}
          }
        \dim_set:Nn \l_tmpa_dim { ##1 - 0.5 \c_pgfinterference_delta_dim }
        \fill [ pgfinterferencecolor ] ( \l_tmpa_dim , -0.5 \l_pgfinterference_screen_height_dim )
          rectangle ++ ( \c_pgfinterference_delta_dim , \l_pgfinterference_screen_height_dim ) ;
      }
  }

\NewDocumentCommand \pgfinterferenceoptions {m}
  {
    \keys_set:nn {pgf-interference} {#1}
  }

\NewDocumentCommand \pgfinterferencepattern {m}
  { 
    \group_begin:
    \keys_set:nn {pgf-interference} {#1}
    \selectcolormodel {rgb}
    \fp_compare:nNnTF { \l_pgfinterference_wavelength_fp * 1e9 } < {1000}
      {
        \definecolor {pgfinterferencelasercolor} {wave}
          {
            \fp_eval:n { \l_pgfinterference_wavelength_fp * 1e9 }
          }
      }
      {
        \colorlet {pgfinterferencelasercolor} {black}
      }
    \dim_set:Nn \l_pgfinterference_screen_width_dim
      {
        \fp_eval:n { 100 * \l_pgfinterference_screen_scale_fp * \l_pgfinterference_screen_width_fp } cm
      }
    \str_if_eq:VnF \l_pgfinterference_ruler_type_str {none}
      {
        \pgfinterference_prepare_ruler:
      }
    \str_if_eq:VnTF \l_pgfinterference_ruler_type_str {screen}
      {
        \colorlet {pgfinterferencescreencolor} {white}
        \dim_set_eq:NN \l_pgfinterference_screen_height_dim \l_pgfinterference_ruler_height_dim
      }
      {
        \dim_set:Nn \l_pgfinterference_screen_height_dim
          {
            \fp_eval:n { 100 * \l_pgfinterference_screen_scale_fp * \l_pgfinterference_screen_height_fp }  cm
          }
      }
    \begin {tikzpicture}
      \extractcolorspec {pgfinterferencescreencolor} \pgfinterference_tmpa:
      \extractcolorspec {white} \pgfinterference_tmpb:
      \bool_lazy_and:nnTF { ! \str_if_eq_p:Vn \l_pgfinterference_ruler_type_str {screen} }
        { \cs_if_eq_p:NN \pgfinterference_tmpa: \pgfinterference_tmpb: }
        {
          \draw [ fill = pgfinterferencescreencolor ]
            ( -0.5 \l_pgfinterference_screen_width_dim , -0.5 \l_pgfinterference_screen_height_dim )
            rectangle ( 0.5 \l_pgfinterference_screen_width_dim , 0.5 \l_pgfinterference_screen_height_dim ) ;
        }
        {
          \fill [ pgfinterferencescreencolor ]
            ( -0.5 \l_pgfinterference_screen_width_dim , -0.5 \l_pgfinterference_screen_height_dim )
            rectangle ( 0.5 \l_pgfinterference_screen_width_dim , 0.5 \l_pgfinterference_screen_height_dim ) ;
        }
      \fp_compare:nNnTF { \l_pgfinterference_wavelength_fp } > {1}
        {
          \msg_warning:nn {pgf-interference} {wavelength too big}
        }
        {
          \fp_compare:nNnTF { \l_pgfinterference_slit_width_fp } > { \l_pgfinterference_slit_distance_fp }
            {
              \msg_warning:nn {pgf-interference} {slit width too big}
            }
            {
              \bool_if:NF \g_pgfinterference_draft_bool {  \pgfinterference_draw_pattern: }
            }
        }
      \str_if_eq:VnF \l_pgfinterference_ruler_type_str {none}
        {
          \pgfinterference_draw_ruler:
        }
    \end {tikzpicture}
    \group_end:
  }
