% Author.........: C. Pierquet
% licence........: Released under the LaTeX Project Public License v1.3c or later, see http://www.latex-project.org/lppl.txt

\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{pixelarttikz-l3}{2025-12-07}{0.20a}{PixelArts with LaTeX3 and TikZ}
% 0.20a  New key in legacy verison (for anamorphosis)
% 0.2.0  Bugfix with borders
% 0.1.9  l3 version (simple usage)

%------Package(s)
\RequirePackage{l3keys2e}
\RequirePackage{tikz}

%-----Option(s)
\bool_new:N \g_pixelarttikz_legacy_bool
\keys_define:nn { pixelarttikz }
  {
    legacy .bool_set:N = \g_pixelarttikz_legacy_bool,
    legacy .default:n   = true,
    legacy .initial:n   = false
  }

% Traiter les options du package
\ProcessKeysOptions { pixelarttikz }

% Charger le package si nécessaire
\bool_if:NT \g_pixelarttikz_legacy_bool
  {
    \RequirePackage{PixelArtTikz}
  }

%------Styles
\tikzset{pixlartcut/.style={very~thick,purple}}
\tikzset{pixlartborder/.style={ultra~thin}}

% variables
\prop_new:N  \g_pixelart_color_prop
\int_new:N   \l_pixelart_row_int
\int_new:N   \l_pixelart_col_int
\ior_new:N   \g_pixelart_file_ior
\tl_new:N    \l_pixelart_line_tl
\tl_new:N    \g_pixlarttikz_unit_tl
\tl_new:N    \g_pixlarttikz_font_tl
\int_new:N   \g_pixelart_nblig_int
\int_new:N   \g_pixelart_nbcol_int
\tl_new:N    \g_pixlarttikz_decoup_tl
\bool_new:N  \g_pixlarttikz_borders_bool
\bool_new:N  \g_pixlarttikz_correction_bool
\bool_new:N  \g_pixlarttikz_symbols_bool
\bool_new:N  \g_pixlarttikz_codes_bool
\fp_new:N    \g_pixlarttikz_font_scale
\clist_new:N \g_pixelart_clist_prop

\keys_define:nn { pixlarttikz }
{
  unit        .tl_set:N   = \g_pixlarttikz_unit_tl,
  unit        .initial:n  = {1.0},
  borders     .bool_set:N = \g_pixlarttikz_borders_bool,
  borders     .initial:n  = {true},
  correction  .bool_set:N = \g_pixlarttikz_correction_bool,
  correction  .initial:n  = {false},
  symbols     .bool_set:N = \g_pixlarttikz_symbols_bool,
  symbols     .initial:n  = {false},
  codes       .bool_set:N = \g_pixlarttikz_codes_bool,
  codes       .initial:n  = {true},
  font        .tl_set:N   = \g_pixlarttikz_font_tl,
  font        .initial:n  = {\sffamily},
  cut         .tl_set:N   = \g_pixlarttikz_decoup_tl,
  cut         .initial:n  = {},
  text~size   .fp_set:N   = \g_pixlarttikz_font_scale,
  text~size   .initial:n  = 0.55,
}

% element (list) manipulating
\NewDocumentCommand\displistsymbolspixlarttikz{ m }{%
  \pixelart_traite_liste:n { #1 }
}

\cs_new_protected:Npn \pixelart_traite_liste:n #1
  {

    \str_if_in:nnTF { #1 } { § }
      {
        % cut list with §
        \seq_set_split:Nne \l_tmpa_seq { § } { #1 }
        % randint
        \int_set:Nn \l_tmpa_int { \seq_count:N \l_tmpa_seq }
        \int_set:Nn \l_tmpb_int { \int_rand:nn { 1 } { \l_tmpa_int } }
        % get random element
        \seq_item:Nn \l_tmpa_seq { \l_tmpb_int }
      }
      {
        % no §, raw argument
        #1
      }
  }

% exanding variant
\cs_generate_variant:Nn \pixelart_traite_liste:n { V }

% main macro
\NewDocumentCommand{\pixlarttikz}{ O{} D<>{} m m }
{
  % 1 = keys
  % 2 = tikzpicture options
  % 3 = map codes/colors/symbols
  % 4 = file
  % keys
  \group_begin:
  \keys_set:nn { pixlarttikz } { unit = 1 , codes = true , borders = true , correction = false , symbols = false , font = \sffamily , cut = {} }
  \keys_set:nn { pixlarttikz } { #1 }
  % booleans gestion
  \bool_if:NT \g_pixlarttikz_correction_bool { \keys_set:nn { pixlarttikz } { codes = false } }
  \bool_if:NT \g_pixlarttikz_symbols_bool    { \keys_set:nn { pixlarttikz } { codes = false } }
  % init prop
  \prop_clear:N \g_pixelart_color_prop
  % parser letter = color
  \clist_set:Ne \g_pixelart_clist_prop { #3 }
  \clist_map_inline:Nn { \g_pixelart_clist_prop }
  {
    \__pixelart_parse_assignment:n { ##1 }
  }
  % draw pixlart
  \__pixelart_draw:nn { #4 } { #2 }
  \group_end:
}

% internal parser letter = color
\cs_new_protected:Nn \__pixelart_parse_assignment:n
{
  \seq_set_split:Nnn \l_tmpa_seq { = } { #1 }
  \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
  \tl_set:Ne \l_tmpb_tl { \seq_item:Nn \l_tmpa_seq { 2 } }
  \tl_trim_spaces:N \l_tmpa_tl
  \tl_trim_spaces:N \l_tmpb_tl
  \prop_gput:NVV \g_pixelart_color_prop \l_tmpa_tl \l_tmpb_tl
}

% generate necessary variants
\cs_generate_variant:Nn \prop_gput:Nnn { NVV }
\cs_generate_variant:Nn \prop_get:NnN { NV }

% cutting function
\cs_new_protected:Nn \__pixelart_draw_decoup:
{
  \tl_if_empty:NF \g_pixlarttikz_decoup_tl
  {
    \tl_if_in:NnTF \g_pixlarttikz_decoup_tl { x }
    {
      % Format: numrowblock x numcolblock
      \seq_set_split:NnV \l_tmpa_seq { x } \g_pixlarttikz_decoup_tl
      \int_set:Nn \l_tmpa_int { \seq_item:Nn \l_tmpa_seq { 1 } }
      \int_set:Nn \l_tmpb_int { \seq_item:Nn \l_tmpa_seq { 2 } }
      % hrules
      \int_step_inline:nnn { 0 } { \g_pixelart_nblig_int }
      {
        \int_compare:nNnT { \int_mod:nn { ##1 } { \l_tmpa_int } } = { 0 }
        {
          \draw[pixlartcut] ({-0.5-0.33}, {-##1+0.5}) -- ({\int_use:N \g_pixelart_nbcol_int-0.5+0.33}, {-##1+0.5});
        }
      }
      % vrules
      \int_step_inline:nnn { 0 } { \g_pixelart_nbcol_int }
      {
        \int_compare:nNnT { \int_mod:nn { ##1 } { \l_tmpb_int } } = { 0 }
        {
          \draw[pixlartcut] ({##1-0.5}, {0.5+0.33}) -- ({##1-0.5}, {-\int_use:N \g_pixelart_nblig_int+0.5-0.33});
        }
      }
    }
    {
      \tl_if_in:NnT \g_pixlarttikz_decoup_tl { + }
      {
        % Format: numrowblock + numcolblock
        \seq_set_split:NnV \l_tmpa_seq { + } \g_pixlarttikz_decoup_tl
        \int_set:Nn \l_tmpa_int { \seq_item:Nn \l_tmpa_seq { 1 } }
        \int_set:Nn \l_tmpb_int { \seq_item:Nn \l_tmpa_seq { 2 } }
        % block size
        \fp_set:Nn \l_tmpa_fp { \g_pixelart_nblig_int / \l_tmpa_int }
        \fp_set:Nn \l_tmpb_fp { \g_pixelart_nbcol_int / \l_tmpb_int }
        % hrules
        \int_step_inline:nn { \l_tmpa_int + 1 }
        {
          \draw[pixlartcut] ({-0.5-0.33}, {-(##1-1)*\fp_use:N \l_tmpa_fp+0.5}) -- ({\int_use:N \g_pixelart_nbcol_int-0.5+0.33}, {-(##1-1)*\fp_use:N \l_tmpa_fp+0.5});
        }
        % vrules
        \int_step_inline:nn { \l_tmpb_int + 1 }
        {
          \draw[pixlartcut] ({(##1-1)*\fp_use:N \l_tmpb_fp-0.5}, {0.5+0.33}) -- ({(##1-1)*\fp_use:N \l_tmpb_fp-0.5}, {-\int_use:N \g_pixelart_nblig_int+0.5-0.33});
        }
      }
    }
  }
}

\cs_generate_variant:Nn \seq_set_split:Nnn { NnV }

% internal drawer pixlart from csv
\cs_new_protected:Nn \__pixelart_draw:nn
{
  \begin{tikzpicture}[x=\fp_eval:n \g_pixlarttikz_unit_tl cm,y=\fp_eval:n \g_pixlarttikz_unit_tl cm,#2]
    \int_set:Nn \l_pixelart_row_int { 0 }
    % open csv file
    \ior_open:Nn \g_pixelart_file_ior { #1 }
    % read line by line
    \ior_map_inline:Nn \g_pixelart_file_ior
    {
      \int_set:Nn \l_pixelart_col_int { 0 }
      % parse line with sep ,
      \clist_map_inline:nn { ##1 }
      {
        \tl_set:Nn \l_tmpa_tl { ####1 }
        \tl_trim_spaces:N \l_tmpa_tl
        % get color from prop
        \prop_get:NVNTF \g_pixelart_color_prop \l_tmpa_tl \l_tmpb_tl
        {
          \bool_if:NT \g_pixlarttikz_correction_bool
          {
            % draw cell with or without border
            \bool_if:NTF \g_pixlarttikz_borders_bool
            {
              \draw[pixlartborder,fill=\l_tmpb_tl] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1,-1) ;
            }
            {
              \filldraw[pixlartborder,\l_tmpb_tl] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1);
            }
          }
          \bool_if:NT \g_pixlarttikz_codes_bool
          {
            \draw[pixlartborder] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1) node[inner~sep=0pt,midway,font=\tl_use:N \g_pixlarttikz_font_tl,text~depth=0pt] {\resizebox*{!}{\fp_eval:n {\g_pixlarttikz_font_scale * \g_pixlarttikz_unit_tl} cm}{\vphantom{AZERTYUIOPQSDFGHJKLMWXCVBN0123456789azertyuiopqsdfghjklmwxcvbn}\tl_use:N \l_tmpa_tl}} ;
          }
          \bool_if:NT \g_pixlarttikz_symbols_bool
          {
            \draw[pixlartborder] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1) node[inner~sep=0pt,midway,font=\tl_use:N \g_pixlarttikz_font_tl,text~depth=0pt] {\resizebox*{!}{\fp_eval:n {\g_pixlarttikz_font_scale * \g_pixlarttikz_unit_tl} cm}{\vphantom{AZERTYUIOPQSDFGHJKLMWXCVBN0123456789azertyuiopqsdfghjklmwxcvbn}\pixelart_traite_liste:V \l_tmpb_tl}} ;
          }
        }
        {
          % default color if not found
          \bool_if:NT \g_pixlarttikz_borders_bool
          {
            \draw[pixlartborder,black,fill=white] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1);
          }
        }
        \int_incr:N \l_pixelart_col_int
      }
      % columns count
      \int_gset:Nn \g_pixelart_nbcol_int { \int_max:nn { \g_pixelart_nbcol_int } { \l_pixelart_col_int } }
      \int_incr:N \l_pixelart_row_int
    }
    \int_gset:Nn \g_pixelart_nblig_int { \l_pixelart_row_int }
    % close file
    \ior_close:N \g_pixelart_file_ior
    % cutting
    \__pixelart_draw_decoup:
  \end{tikzpicture}
}

\cs_new_protected:Nn \__pixelart_getdim:n
{
  \int_set:Nn \l_pixelart_row_int { 0 }
  % open csv file
  \ior_open:Nn \g_pixelart_file_ior { #1 }
  % read line by line
  \ior_map_inline:Nn \g_pixelart_file_ior
    {
      \int_set:Nn \l_pixelart_col_int { 0 }
      \clist_map_inline:nn { ##1 }
      {
        \tl_set:Nn \l_tmpa_tl { ####1 }
        \tl_trim_spaces:N \l_tmpa_tl
        \int_incr:N \l_pixelart_col_int
      }
      % Stocker le max de colonnes
      \int_gset:Nn \g_pixelart_nbcol_int { \int_max:nn { \g_pixelart_nbcol_int } { \l_pixelart_col_int } }
      \int_incr:N \l_pixelart_row_int
    }
  \int_gset:Nn \g_pixelart_nblig_int { \l_pixelart_row_int }
  % close file
  \ior_close:N \g_pixelart_file_ior
}

\tl_new:N  \g_pixlarttikz_blocknumber_tl
\int_new:N \l_pixelart_block_nbrows_int
\int_new:N \l_pixelart_block_nbcols_int
\int_new:N \l_pixelart_block_rmin_int
\int_new:N \l_pixelart_block_rmax_int
\int_new:N \l_pixelart_block_cmin_int
\int_new:N \l_pixelart_block_cmax_int
\int_new:N \l_pixelart_block_rownb_int
\int_new:N \l_pixelart_block_colnb_int

% internal drawer pixlart from csv
\cs_new_protected:Nn \__pixelart_draw_block:nnn
{
  % 1 = file
  % 2 = options tikz
  % 3 = block number
  \begin{tikzpicture}[x=\fp_eval:n \g_pixlarttikz_unit_tl cm,y=\fp_eval:n \g_pixlarttikz_unit_tl cm]
    % calcs
    \tl_set:Nx \g_pixlarttikz_blocknumber_tl { #3 }
    \seq_set_split:NnV \l_tmpa_seq { / } \g_pixlarttikz_blocknumber_tl
    \int_set:Nn \l_pixelart_block_rownb_int { \seq_item:Nn \l_tmpa_seq { 1 } }
    \int_set:Nn \l_pixelart_block_colnb_int { \seq_item:Nn \l_tmpa_seq { 2 } }
    \tl_if_in:NnTF \g_pixlarttikz_decoup_tl { x }
    {
      % Format: numrowblock x numcolblock
      \seq_set_split:NnV \l_tmpa_seq { x } \g_pixlarttikz_decoup_tl
      \int_set:Nn \l_pixelart_block_nbrows_int { \seq_item:Nn \l_tmpa_seq { 1 } }
      \int_set:Nn \l_pixelart_block_nbcols_int { \seq_item:Nn \l_tmpa_seq { 2 } }
    }
    {
      \tl_if_in:NnT \g_pixlarttikz_decoup_tl { + }
      {
        % Format: numrowblock + numcolblock
        \seq_set_split:NnV \l_tmpa_seq { + } \g_pixlarttikz_decoup_tl
        \int_set:Nn \l_tmpa_int { \seq_item:Nn \l_tmpa_seq { 1 } }
        \int_set:Nn \l_tmpb_int { \seq_item:Nn \l_tmpa_seq { 2 } }
        \int_set:Nn \l_pixelart_block_nbrows_int { \int_div_truncate:nn { \g_pixelart_nblig_int } { \l_tmpa_int } }
        \int_set:Nn \l_pixelart_block_nbcols_int { \int_div_truncate:nn { \g_pixelart_nbcol_int } { \l_tmpb_int } }
      }
    }
    
    \typeout{nbrows:~\int_use:N\l_pixelart_block_nbrows_int}
    \typeout{rownb:~\int_use:N\l_pixelart_block_rownb_int}
      
    \int_set:Nn \l_pixelart_block_rmin_int { \l_pixelart_block_nbrows_int * ( \l_pixelart_block_rownb_int - 1 ) + 1 }
    \int_set:Nn \l_pixelart_block_rmax_int { \l_pixelart_block_nbrows_int * \l_pixelart_block_rownb_int }
    \int_set:Nn \l_pixelart_block_cmin_int { \l_pixelart_block_nbcols_int * ( \l_pixelart_block_colnb_int - 1 ) + 1 }
    \int_set:Nn \l_pixelart_block_cmax_int { \l_pixelart_block_nbcols_int * \l_pixelart_block_colnb_int }
    % back to normal
    \int_set:Nn \l_pixelart_row_int { 0 }
    % open csv file
    \ior_open:Nn \g_pixelart_file_ior { #1 }
    % read line by line
    \ior_map_inline:Nn \g_pixelart_file_ior
    {
      %\int_incr:N \l_pixelart_row_int
      \int_set:Nn \l_pixelart_col_int { 0 }
      % parse line with sep ,
      \clist_map_inline:nn { ##1 }
      {
        %\int_incr:N \l_pixelart_col_int
        \tl_set:Nn \l_tmpa_tl { ####1 }
        \tl_trim_spaces:N \l_tmpa_tl
        % get color from prop
        \prop_get:NVNTF \g_pixelart_color_prop \l_tmpa_tl \l_tmpb_tl
        {
          \bool_if:NT \g_pixlarttikz_correction_bool
          {
            % draw cell with or without border
            \bool_if:NTF \g_pixlarttikz_borders_bool
            {
              \bool_if:nT
              {
                \int_compare_p:n { \l_pixelart_row_int + 1 >= \l_pixelart_block_rmin_int }
                &&
                \int_compare_p:n { \l_pixelart_row_int + 1 <= \l_pixelart_block_rmax_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 >= \l_pixelart_block_cmin_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 <= \l_pixelart_block_cmax_int }
              }
              {
                \draw[pixlartborder,fill=\l_tmpb_tl] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1,-1) ;
              }
            }
            {
              \bool_if:nT
              {
                \int_compare_p:n { \l_pixelart_row_int + 1 >= \l_pixelart_block_rmin_int }
                &&
                \int_compare_p:n { \l_pixelart_row_int + 1 <= \l_pixelart_block_rmax_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 >= \l_pixelart_block_cmin_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 <= \l_pixelart_block_cmax_int }
              }
              {
                \filldraw[pixlartborder,\l_tmpb_tl] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1);
              }
            }
          }
          \bool_if:NT \g_pixlarttikz_codes_bool
          {
            \bool_if:nT
              {
                \int_compare_p:n { \l_pixelart_row_int + 1 >= \l_pixelart_block_rmin_int }
                &&
                \int_compare_p:n { \l_pixelart_row_int + 1 <= \l_pixelart_block_rmax_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 >= \l_pixelart_block_cmin_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 <= \l_pixelart_block_cmax_int }
              }
              {
                \draw[pixlartborder] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1) node[inner~sep=0pt,midway,font=\tl_use:N \g_pixlarttikz_font_tl,text~depth=0pt] {\resizebox*{!}{\fp_eval:n {\g_pixlarttikz_font_scale * \g_pixlarttikz_unit_tl} cm}{\vphantom{AZERTYUIOPQSDFGHJKLMWXCVBN0123456789azertyuiopqsdfghjklmwxcvbn}\tl_use:N \l_tmpa_tl}} ;
              }
          }
          \bool_if:NT \g_pixlarttikz_symbols_bool
          {
            \draw[pixlartborder] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1) node[inner~sep=0pt,midway,font=\tl_use:N \g_pixlarttikz_font_tl,text~depth=0pt] {\resizebox*{!}{\fp_eval:n {\g_pixlarttikz_font_scale * \g_pixlarttikz_unit_tl} cm}{\vphantom{AZERTYUIOPQSDFGHJKLMWXCVBN0123456789azertyuiopqsdfghjklmwxcvbn}\tl_use:N \l_tmpb_tl}} ;
          }
        }
        {
          % default color if not found
          \bool_if:NT \g_pixlarttikz_borders_bool
          {
            \bool_if:nT
              {
                \int_compare_p:n { \l_pixelart_row_int + 1 >= \l_pixelart_block_rmin_int }
                &&
                \int_compare_p:n { \l_pixelart_row_int + 1 <= \l_pixelart_block_rmax_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 >= \l_pixelart_block_cmin_int }
                &&
                \int_compare_p:n { \l_pixelart_col_int + 1 <= \l_pixelart_block_cmax_int }
              }
              {
                \draw[pixlartborder,black,fill=white] ({\int_use:N \l_pixelart_col_int-0.5}, {-\int_use:N \l_pixelart_row_int+0.5}) rectangle ++(1, -1);
              }
          }
        }
        \int_incr:N \l_pixelart_col_int
      }
    \int_incr:N \l_pixelart_row_int
    }
    % close file
    \ior_close:N \g_pixelart_file_ior
  \end{tikzpicture}
}

\NewDocumentCommand{\pixlarttikzblock}{ O{} D<>{} m m m }
{
  % 1 = keys
  % 2 = tikzpicture options
  % 3 = map codes/colors/symbols
  % 4 = block
  % 5 = file
  % keys
  \group_begin:
  \keys_set:nn { pixlarttikz } { unit = 1 , codes = true , borders = true , correction = false , symbols = false , font = \sffamily , cut = {} }
  \keys_set:nn { pixlarttikz } { #1 }
  % booleans
  \bool_if:NT \g_pixlarttikz_correction_bool { \keys_set:nn { pixlarttikz } { codes = false } }
  \bool_if:NT \g_pixlarttikz_symbols_bool    { \keys_set:nn { pixlarttikz } { codes = false } }
  % init prop
  \prop_clear:N \g_pixelart_color_prop
  % parser letter = color
  \clist_set:Ne \g_pixelart_clist_prop { #3 }
  \clist_map_inline:Nn { \g_pixelart_clist_prop }
  {
    \__pixelart_parse_assignment:n { ##1 }
  }
  % get dim
  \__pixelart_getdim:n { #5 }
  % draw pixlart block
  \__pixelart_draw_block:nnn { #5 } { #2 } { #4 }
  \group_end:
}

\endinput