% 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:
 }