% File: hawkdraw-patterns.code.tex
% Copyright 2026 Jasper Habicht (mail(at)jasperhabicht.de).
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License version 1.3c,
% available at http://www.latex-project.org/lppl/.
%
% This file is part of the `hawkdraw' package (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% This work has the LPPL maintenance status `maintained'.
%
% BOF

% v0.5.0 2026-07-20

% To improve rendering speed, we make use of PDF /Pattern objects. To this end, we first create an PDF
% XObject from the pattern (unique to pattern type and options) which can be reused. Upon use, a new PDF
% Object is created with the required /Pattern attributes and with the XObject in the stream (via Do).
% The pattern is then linked to the page attributes, And a literal is added into the code for the
% postaction to the path. This makes it possible to use a fill and a pattern together, which otherwise
% is not possible as PDF handles a /Pattern as fill.

\msg_new:nnn { hawkdraw } { pattern-unknown } {
    Pattern ~ `#1` ~ unknown.
}

\prop_new:N \g_hawkdraw_pattern_default_keys_prop
\clist_new:N \l_hawkdraw_pattern_keys_clist
\fp_new:N \l_hawkdraw_pattern_tile_size_fp

\int_new:N \g__hawkdraw_pattern_id_int
\box_new:N \l__hawkdraw_pattern_box
\dim_new:N \l__hawkdraw_pattern_width_dim
\dim_new:N \l__hawkdraw_pattern_height_dim

\keys_define:nn { hawkdraw / path } {
    pattern                 .code:n         = {
        \tl_put_right:Nn \l_hawkdraw_path_postactions_tl {
            \draw_scope_begin:
                \tl_trim_spaces_apply:nN { \g_hawkdraw_softpath_original_tl } \__hawkdraw_path_process:o
                \tl_use:N \g_hawkdraw_softpath_original_tl
                \hawkdraw_pattern_use:nV {#1} \l_hawkdraw_pattern_keys_clist
                \draw_path_use_clear:n { fill }
                \bool_gset_false:N \g__hawkdraw_path_use_clip_bool
            \draw_scope_end:
        }
    } ,
    pattern ~ style ~ set   .clist_set:N    = \l_hawkdraw_pattern_keys_clist ,
    pattern ~ style ~ add   .code:n         = {
        \clist_put_right:Nn \l_hawkdraw_pattern_keys_clist {#1}
    } ,
    pattern ~ tile ~ size   .fp_set:N      = \l_hawkdraw_pattern_tile_size_fp ,
    pattern ~ tile ~ size   .initial:n     = { ( 10pt , 10pt ) } ,
}

\cs_new_protected:Npn \hawkdraw_pattern_set:nn #1#2 {
    \int_gincr:N \g__hawkdraw_pattern_id_int

    \hbox_set:Nn \l__hawkdraw_pattern_box {
        \draw_suspend_begin:
            \draw_begin:
                \draw_set_baseline:n { 0pt }
                \clist_clear:N \l_hawkdraw_path_use_clist
                \keys_set:ne { hawkdraw / path } {
                    \prop_item:Nn \g_hawkdraw_pattern_default_keys_prop {#1}
                }
                \keys_set:nn { hawkdraw / path } {#2}
                \hawkdraw_path_set_options:
                \draw_path_rectangle:nn { 0pt , 0pt } { \l_hawkdraw_pattern_tile_size_fp }
                \draw_path_use_clear:n { }
                \bool_set_false:N \l_draw_bb_update_bool
                \use:c { hawkdraw_pattern_ #1 : }
            \draw_end:
        \draw_suspend_end:
    }

    % We need to prevent to include the page resources for /Pattern here
    \hawkdraw_support_pdfxform:nnN { } { }
        \l__hawkdraw_pattern_box
    \int_const:cn {
        c__hawkdraw_pattern_obj_ #1 . \str_mdfive_hash:n {#2} _int
    } {
        \hawkdraw_support_pdfxform_last:
    }

    \pdf_object_new:n {
        hawkdraw_pattern_
        #1 . \str_mdfive_hash:n {#2}
    }
    \pdf_object_write:nne {
        hawkdraw_pattern_
        #1 . \str_mdfive_hash:n {#2}
    } { stream } {
        {
            /Type ~ /Pattern ~
            /PatternType ~ 1 ~
            /PaintType ~ 1 ~
            /TilingType ~ 1 ~
            /BBox ~ [ ~
                0 ~ 0 ~
                \dim_to_decimal_in_bp:n {
                    \fp_to_dim:n {
                        \hawkdraw_tuple_use_i:V \l_hawkdraw_pattern_tile_size_fp
                    }
                } ~
                \dim_to_decimal_in_bp:n {
                    \fp_to_dim:n {
                        \hawkdraw_tuple_use_ii:V \l_hawkdraw_pattern_tile_size_fp
                    }
                } ~
            ] ~
            /XStep ~
                \dim_to_decimal_in_bp:n {
                    \fp_to_dim:n {
                        \hawkdraw_tuple_use_i:V \l_hawkdraw_pattern_tile_size_fp
                    }
                } ~
            /YStep ~
                \dim_to_decimal_in_bp:n {
                    \fp_to_dim:n {
                        \hawkdraw_tuple_use_ii:V \l_hawkdraw_pattern_tile_size_fp
                    }
                } ~
            /Matrix ~ [ ~
                1 ~ 0 ~ 0 ~ 1 ~ 0 ~ 0 ~
            ] ~
            /Resources ~ << ~
                /XObject ~ << ~
                    /hawkdrawPatternObj \int_use:N \g__hawkdraw_pattern_id_int \c_space_tl
                    \hawkdraw_support_pdfxform_ref:c {
                        c__hawkdraw_pattern_obj_ #1 . \str_mdfive_hash:n {#2} _int
                    } ~
                >> ~
            >>
        } {
            q ~
            /hawkdrawPatternObj \int_use:N \g__hawkdraw_pattern_id_int \c_space_tl Do ~
            Q
        }
    }
    \int_const:cn {
        c__hawkdraw_pattern_ #1 . \str_mdfive_hash:n {#2} _int
    } {
        \g__hawkdraw_pattern_id_int
    }
}

\cs_new_protected:Npn \hawkdraw_pattern_use:nn #1#2 {
    \prop_if_in:NnF \g_hawkdraw_pattern_default_keys_prop {#1} {
        \msg_warning:nnn { hawkdraw } { pattern-unknown } {#1}
    }
    \int_if_exist:cF {
        c__hawkdraw_pattern_ #1 . \str_mdfive_hash:n {#2} _int
    } {
        \hawkdraw_pattern_set:nn {#1} {#2}
    }

    \pdfmanagement_add:nee {
        Page / Resources / Pattern
    } {
        hawkdraw Pattern
        \int_use:c {
            c__hawkdraw_pattern_ #1 . \str_mdfive_hash:n {#2} _int
        }
    } {
        \pdf_object_ref:n {
            hawkdraw_pattern_
            #1 . \str_mdfive_hash:n {#2}
        }
    }

    \hawkdraw_support_pdfliteral:n {
        /Pattern ~ cs ~ /hawkdraw Pattern
        \int_use:c {
            c__hawkdraw_pattern_ #1 . \str_mdfive_hash:n {#2} _int
        }
        \c_space_tl scn ~
    }
}
\cs_generate_variant:Nn \hawkdraw_pattern_use:nn { nV }

\cs_new_protected:Npn \hawkdraw_pattern_create:nnn #1#2#3 {
    \prop_gput:Nnn \g_hawkdraw_pattern_default_keys_prop {#1} {#2}
    \cs_new_protected:cpn { hawkdraw_pattern_ #1 : } {
        #3
    }
}

% ===

\hawkdraw_pattern_create:nnn { lines } { } {
    \draw_path_moveto:n { 0pt , 5pt }
    \draw_path_lineto:n { 10pt , 5pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { waves } { } {
    \draw_path_moveto:n { 0pt , 2.5pt }
    \draw_path_curveto:nnn { 2.5pt , 2.5pt } { 2.5pt , 7.5pt } { 5pt , 7.5pt }
    \draw_path_curveto:nnn { 7.5pt , 7.5pt } { 7.5pt , 2.5pt } { 10pt , 2.5pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { hatch } { } {
    \draw_path_moveto:n { 0pt , 5pt }
    \draw_path_lineto:n { 10pt , 5pt }
    \draw_path_moveto:n { 5pt , 0pt }
    \draw_path_lineto:n { 5pt , 10pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { dots } { } {
    \draw_path_circle:nn { 5pt , 5pt } { 2.5pt }
    \draw_path_use_clear:n { fill }
}

\hawkdraw_pattern_create:nnn { circles } { } {
    \draw_path_circle:nn { 5pt , 5pt } { 2.5pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { plus } { } {
    \draw_path_moveto:n { 5pt , 2.5pt }
    \draw_path_lineto:n { 5pt , 7.5pt }
    \draw_path_moveto:n { 2.5pt , 5pt }
    \draw_path_lineto:n { 7.5pt , 5pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { minus } { } {
    \draw_path_moveto:n { 2.5pt , 5pt }
    \draw_path_lineto:n { 7.5pt , 5pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { check } { } {
    \draw_path_rectangle:nn { 0pt , 0pt } { 5pt , 5pt }
    \draw_path_rectangle:nn { 5pt , 5pt } { 10pt , 10pt }
    \draw_path_use_clear:n { fill }
}

\hawkdraw_pattern_create:nnn { masonry } { } {
    \draw_path_moveto:n { 0pt , 2.5pt }
    \draw_path_lineto:n { 10pt , 2.5pt }
    \draw_path_moveto:n { 0pt , 7.5pt }
    \draw_path_lineto:n { 10pt , 7.5pt }
    \draw_path_moveto:n { 2.5pt , 10pt }
    \draw_path_lineto:n { 2.5pt , 7.5pt }
    \draw_path_moveto:n { 7.5pt , 7.5pt }
    \draw_path_lineto:n { 7.5pt , 2.5pt }
    \draw_path_moveto:n { 2.5pt , 2.5pt }
    \draw_path_lineto:n { 2.5pt , 0pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { herringbone } { } {
    \draw_path_moveto:n { 0pt , 0pt }
    \draw_path_lineto:n { 5pt , 5pt }
    \draw_path_lineto:n { 10pt , 0pt }
    \draw_path_moveto:n { 2.5pt , 2.5pt }
    \draw_path_lineto:n { 5pt , 0pt }
    \draw_path_moveto:n { 7.5pt , 2.5pt }
    \draw_path_lineto:n { 10pt , 5pt }
    \draw_path_moveto:n { 0pt , 5pt }
    \draw_path_lineto:n { 5pt , 10pt }
    \draw_path_lineto:n { 10pt , 5pt }
    \draw_path_moveto:n { 2.5pt , 7.5pt }
    \draw_path_lineto:n { 5pt , 5pt }
    \draw_path_moveto:n { 7.5pt , 7.5pt }
    \draw_path_lineto:n { 10pt , 10pt }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { hexagonal } {
    pattern ~ tile ~ size = {
        \fp_to_dim:n { cosd( 30 ) * 20/3pt } ,
        10pt
    }
} {
    \draw_path_moveto:n { 0pt , 0pt }
    \draw_path_lineto:n { 0pt , 5/3pt }
    \draw_path_lineto:n {
        ( 0pt , 5/3pt ) +
        ( \draw_point_polar:nn { 10/3pt } { 30 } )
    }
    \draw_path_lineto:n {
        ( 0pt , 15/3pt ) +
        ( \draw_point_polar:nn { 10/3pt } { 30 } )
    }
    \draw_path_lineto:n { 0pt , 25/3pt }
    \draw_path_lineto:n { 0pt , 10pt }
    \draw_path_moveto:n {
        ( 0pt , 15/3pt ) +
        ( \draw_point_polar:nn { 10/3pt } { 30 } )
    }
    \draw_path_lineto:n {
        ( 0pt , 15/3pt ) +
        ( \draw_point_polar:nn { 20/3pt } { 30 } )
    }
    \draw_path_lineto:n {
        ( 0pt , 25/3pt ) +
        ( \draw_point_polar:nn { 20/3pt } { 30 } )
    }
    \draw_path_moveto:n {
        ( 0pt , 5/3pt ) +
        ( \draw_point_polar:nn { 10/3pt } { 30 } )
    }
    \draw_path_lineto:n {
        ( 0pt , 15/3pt ) +
        ( \draw_point_polar:nn { 20/3pt } { -30 } )
    }
    \draw_path_lineto:n {
        ( 0pt , 10/3pt ) +
        ( \draw_point_polar:nn { 20/3pt } { -30 } )
    }
    \draw_path_use_clear:n { stroke }
}

\hawkdraw_pattern_create:nnn { dinner } {
    pattern ~ tile ~ size = {
        22.25pt ,
        22.25pt
    }
} {
    \draw_transform_scale:n { 10 }
    \color_select:nn { rgb } { 0.925 , 0.85 , 0.075 }
    \draw_path_moveto:n { 0.513 , 1.145 }
    \draw_path_curveto:nnn { 0.267 , 1.102 } { -0.125 , 0.657 } { 0.289 , 0.261 }
    \draw_path_curveto:nnn { 0.704 , -0.135 } { 2.863 , 0.130 } { 1.818 , 1.419 }
    \draw_path_curveto:nnn { 0.938 , 0.946 } { 1.240 , 1.379 } { 0.513 , 1.145 }
    \draw_path_close:
    \draw_path_ellipse:nnn { 0.90 , 1.50 } { 0.50 , 0 } { 0 , 0.625 }
    \draw_path_use_clear:n { fill }
    \color_select:nn { rgb } { 0.875 , 0.5 , 0.125 }
    \draw_path_moveto:n { 0.406 , 1.472 }
    \draw_path_curveto:nnn { 0.643 , 1.530 } { 0.541 , 1.303 } { 0.910 , 1.370 }
    \draw_path_curveto:nnn { 0.083 , 0.850 } { 0.269 , 1.369 } { 0.406 , 1.472 }
    \draw_path_close:
    \draw_path_use_clear:n { fill }
    \color_select:nn { rgb } { 1 , 1 , 0.85 }
    \draw_transform_rotate:n { -20 }
    \draw_path_ellipse:nnn { -0.06 , 1.74 } { 0.0786 , 0 } { 0 , 0.1143 }
    \draw_path_ellipse:nnn { 0.23 , 1.7675 } { 0.0893 , 0 } { 0 , 0.125 }
    \draw_path_use_clear:n { fill }
    \color_select:nn { rgb } { 0 , 0 , 0 }
    \draw_path_ellipse:nnn { -0.03 , 1.73 } { 0.0286 , 0 } { 0 , 0.0643 }
    \draw_path_ellipse:nnn { 0.26 , 1.7575 } { 0.0357 , 0 } { 0 , 0.0714 }
    \draw_path_use_clear:n { fill }
}

%EOF