% 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.4.1 2026-07-08

% For opacity groups, the relevant scope is grabbed and stored in an XObject, setting the opacity inside

% 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
\box_new:N \l_hawkdraw_pattern_box
\dim_new:N \l__hawkdraw_pattern_width_dim
\dim_new:N \l__hawkdraw_pattern_height_dim
\fp_new:N \l_hawkdraw_pattern_tile_size_fp

\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 {
    \pdfxform_new:nnn {
        hawkdraw_pattern_
        #1 . \str_mdfive_hash:n {#2}
    } { } {
        \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:
    }
    \int_const:cn {
        c__hawkdraw_pattern_obj_ #1 . \str_mdfive_hash:n {#2} _int
    } {
        \hawkdraw_support_pdflastxform:
    }
}

\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}
    }
    \pdfxform_if_exist:nF {
        hawkdraw_pattern_
        #1 . \str_mdfive_hash:n {#2}
    } {
        \hawkdraw_pattern_set:nn {#1} {#2}
    }

    \pdf_object_new:n {
        hawkdraw_pattern_object_
        #1 . \str_mdfive_hash:n {#2}
    }
    \pdf_object_write:nne {
        hawkdraw_pattern_object_
        #1 . \str_mdfive_hash:n {#2}
    } {
        stream
    } {
        {
            /Type ~ /Pattern ~
            /PatternType ~ 1 ~
            /PaintType ~ 1 ~
            /TilingType ~ 1 ~
            /BBox ~ [
                0 ~ 0 ~ 
                \dim_to_decimal_in_bp:n {
                    \pdfxform_wd:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    }
                } ~
                \dim_to_decimal_in_bp:n {
                    \pdfxform_ht:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    } +
                    \pdfxform_dp:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    }
                }
            ] ~
            /XStep ~ 
                \dim_to_decimal_in_bp:n {
                    \pdfxform_wd:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    }
                } ~
            /YStep ~
                \dim_to_decimal_in_bp:n {
                    \pdfxform_ht:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    } +
                    \pdfxform_dp:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    }
                } ~
            /Matrix ~ [ 1 ~ 0 ~ 0 ~ 1 ~ 0 ~ 0 ] ~
            /Resources ~ << ~
                /XObject ~ << ~
                    /hawkdrawPatternObj
                    \int_use:c {
                        c__hawkdraw_pattern_obj_ #1 . \str_mdfive_hash:n {#2} _int
                    } ~
                    \pdfxform_ref:n {
                        hawkdraw_pattern_
                        #1 . \str_mdfive_hash:n {#2}
                    } ~
                >> ~
            >>
        } {
            q ~
            /hawkdrawPatternObj
            \int_use:c {
                c__hawkdraw_pattern_obj_ #1 . \str_mdfive_hash:n {#2} _int
            } ~ Do ~
            Q
        }
    }
    \int_const:cn {
        c__hawkdraw_pattern_ #1 . \str_mdfive_hash:n {#2} _int
    } {
        \hawkdraw_support_pdflastobj:
    }
    
    \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_object_ #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 }
}

%EOF