% File: hawkdraw.sty
% 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'.
%
\ProvidesExplPackage {hawkdraw} {2026-05-30} {0.0.8}
    {An experimental package for drawing vector graphics with the l3draw package and a TikZ-like syntax.}

\msg_new:nnn { hawkdraw } { old-kernel } {
    LaTeX ~ kernel ~ too ~ old. \iow_newline:
    The ~ hawkdraw ~ package ~ does ~ not ~ support ~ this ~ LaTeX ~ version. \iow_newline:
    Please ~ update ~ to ~ a ~ newer ~ version.
}

\msg_new:nnn { hawkdraw } { compat-mode } {
    Old ~ version ~ of ~ l3draw ~ detected. \iow_newline:
    Compatibility ~ mode ~ activated.
}

\msg_new:nnn { hawkdraw } { documentmetadata-required } {
    \token_to_str:N \DocumentMetadata \c_space_tl ~ must ~ be ~ set ~
    to ~ enable ~ full ~ functionality.
}

\cs_if_exist:NF \ProcessKeyOptions {
    \msg_critical:nn { hawkdraw } { old-kernel }
}

% ===

\ProcessKeyOptions [ ]

\RequirePackage { l3draw }

\IfPDFManagementActiveTF { } {
    \msg_warning:nn { hawkdraw } { documentmetadata-required }
}

% ===

\IfExplAtLeastTF { 2025-04-14 } { } {
    \RequirePackage { l3opacity }
}

% backward compatibility
\cs_if_exist:NF \draw_set_linewidth:n {
    \cs_set_eq:NN \draw_set_linewidth:n \draw_linewidth:n
}
\cs_if_exist:NF \draw_set_baseline:n {
    \cs_set_eq:NN \draw_set_baseline:n \draw_baseline:n
}
\cs_if_exist:NF \draw_set_cap_round: {
    \cs_set_eq:NN \draw_set_cap_round: \draw_cap_round:
}
\cs_if_exist:NF \draw_set_join_round: {
    \cs_set_eq:NN \draw_set_join_round: \draw_join_round:
}
\cs_if_exist:NF \draw_set_miterlimit:n {
    \cs_set_eq:NN \draw_set_miterlimit:n \draw_miterlimit:n
}
\cs_if_exist:NF \draw_set_dash_pattern:nn {
    \cs_set_eq:NN \draw_set_dash_pattern:nn \draw_dash_pattern:nn
}
\cs_if_exist:NF \draw_point_interpolate_curve:nnnnn {
    \cs_set_eq:NN \draw_point_interpolate_curve:nnnnn \draw_point_interpolate_curve:nnnnnn
}
% \draw_set_evenodd_rule: \draw_set_nonzero_rule:
% \draw_set_cap_butt: \draw_set_cap_rectangle: \draw_set_cap_round:
% \draw_set_join_bevel: \draw_set_join_miter: \draw_set_join_round:
\cs_if_exist:NF \draw_point_interpolate_curve:nnnn {
    \cs_set:Npn \draw_point_interpolate_curve:nnnn #1#2#3#4 {
        \draw_point_interpolate_curve:nnnnn {#1} {#2} {
            ( #2 + 2 * #3 ) / 3
        } {
            ( #4 + 2 * #3 ) / 3
        } {#4}
    }
}
\cs_if_exist:NF \draw_point_interpolate_arc_axes:nnnnnn {
    \cs_set_eq:NN \draw_point_interpolate_arc_axes:nnnnnn \draw_point_interpolate_arcaxes:nnnnnn
}
\IfExplAtLeastTF { 2026-04-28 } { } {
    \msg_warning:nn { hawkdraw } { compat-mode }
    \cs_set_eq:NN \__hawkdraw_compat_draw_path_arc:nnnn \draw_path_arc:nnnn
    \cs_set_protected:Npn \draw_path_arc:nnnn #1#2#3#4 {
        \__hawkdraw_compat_draw_path_arc:nnnn {#3} {#4} {#1} {#2}
    }
    \cs_set_protected:Npn \draw_path_arc:nnn #1#2#3 {
        \__hawkdraw_compat_draw_path_arc:nnnn {#2} {#3} {#1} {#1}
    }
    \cs_set_protected:Npn \draw_path_arc_axes:nnnn #1#2#3#4 {
        \group_begin:
            \draw_transform_triangle:nnn { 0cm , 0cm } {#1} {#2}
            \__hawkdraw_compat_draw_path_arc:nnnn {#3} {#4} { 1pt } { 1pt }
        \group_end:
    }
}
\IfExplAtLeastTF { 2026-05-26 } { } {
    \cs_set:Npn \__draw_point_interpolate_line_aux:nnnnnn #1#2#3#4#5#6 {
        \draw_point:n { #1 * #3 + #2 * #5 , #1 * #4 + #2 * #6 }
    }
    \cs_set:Npn \__draw_point_interpolate_curve_auxvii:nnnnnnnn #1#2#3#4#5#6#7#8 {
        \__draw_point_interpolate_curve_auxviii:eeeenn
            { \fp_eval:n { #1 * #3 + #2 * #5 } }
            { \fp_eval:n { #1 * #4 + #2 * #6 } }
            { \fp_eval:n { #1 * #5 + #2 * #7 } }
            { \fp_eval:n { #1 * #6 + #2 * #8 } }
            {#1} {#2}
    }
    \cs_set:Npn \__draw_point_interpolate_curve_auxviii:nnnnnn #1#2#3#4#5#6 {
        \draw_point:n {
            { #5 * #1 + #6 * #3 , #5 * #2 + #6 * #4 }
        }
    }
}
% ===

\msg_new:nnn { hawkdraw } { style-undefined } {
    The ~ style ~ `#1` ~ is ~ undefined.
}
\msg_new:nnn { hawkdraw } { module-missing } {
    The ~ module ~ `#1` ~ is ~ missing.
}

\cs_new_protected:Npn \hawkdraw_modules_load:n #1 {
    \clist_map_inline:nn {#1} {
        \file_if_exist_input:nF { hawkdraw- ##1 .code.tex } {
            \msg_warning:nnn { hawkdraw } { module-missing } {##1}
        }
    }
}

\hawkdraw_modules_load:n { nodes , arrows }

\IfPDFManagementActiveTF {
    \hawkdraw_modules_load:n { patterns }
} {
    \msg_warning:nn { hawkdraw } { documentmetadata-required }
    \keys_define:nn { hawkdraw / path } {
        fill ~ pattern          .code:n     = { } ,
        pattern ~ style ~ set   .code:n     = { } ,
        pattern ~ style ~ add   .code:n     = { } ,
        pattern ~ x             .code:n     = { } ,
        pattern ~ y             .code:n     = { } ,
    }
}

% ===

\cs_generate_variant:Nn \prg_new_conditional:Npnn { Npne }

\cs_new:Npn \__hawkdraw_exp_last_unbraced:NNe #1#2#3 {
    \use:e {
        \exp_not:N #1 \exp_not:N #2 #3
    }
}

% ===

\clist_const:Nn \c__hawkdraw_node_vpoles_clist {
    l , hc , r
}
\clist_const:Nn \c__hawkdraw_node_hhpoles_clist {
    t , vc , b , H
}
\clist_const:Nn \c__hawkdraw_node_vhpoles_clist {
    t , vc , b , H , T , B
}

\clist_new:N \l__hawkdraw_keys_unprocessed_clist

\int_new:N \g_hawkdraw_id_int

\bool_new:N \l_hawkdraw_instance_overlay_bool
\fp_new:N \l_draw_instance_baseline_fp

\tl_new:N \g_hawkdraw_path_current_tl
\tl_new:N \l_hawkdraw_path_preactions_tl
\tl_new:N \l_hawkdraw_path_postactions_tl

\clist_new:N \l_hawkdraw_path_use_clist
\bool_new:N \g__hawkdraw_path_use_clip_bool
\bool_new:N \l__hawkdraw_path_ignore_keys_bool
\tl_new:N \l_hawkdraw_path_stroke_color_tl
\tl_new:N \l_hawkdraw_path_fill_color_tl
\fp_new:N \l_hawkdraw_path_stroke_opacity_fp
\fp_new:N \l_hawkdraw_path_fill_opacity_fp
\dim_new:N \l_hawkdraw_path_linewidth_dim

\tl_new:N \l_hawkdraw_path_cap_tl
\tl_new:N \l_hawkdraw_path_join_tl
\fp_new:N \l_hawkdraw_path_miterlimit_fp
\clist_new:N \l_hawkdraw_path_corner_arc_clist
\tl_new:N \l_hawkdraw_path_fill_rule_tl

\clist_new:N \l_hawkdraw_path_dash_pattern_clist
\dim_new:N \l_hawkdraw_path_dash_phase_dim

\tl_new:N \l__hawkdraw_path_type_tl
\fp_new:N \g_hawkdraw_path_first_point_fp
\fp_new:N \g_hawkdraw_path_last_point_fp
\fp_new:N \g_hawkdraw_path_first_slope_fp
\fp_new:N \g_hawkdraw_path_last_slope_fp
\fp_new:N \l__hawkdraw_point_o_fp
\fp_new:N \l__hawkdraw_point_a_fp
\fp_new:N \l__hawkdraw_point_b_fp
\fp_new:N \l__hawkdraw_point_c_fp

\tl_new:N \l_hawkdraw_scope_layer_tl

\cs_generate_variant:Nn \color_select:n { e }
\cs_generate_variant:Nn \color_fill:n { e }
\cs_generate_variant:Nn \color_stroke:n { e }
\cs_generate_variant:Nn \opacity_select:n { V }
\cs_generate_variant:Nn \opacity_fill:n { V }
\cs_generate_variant:Nn \opacity_stroke:n { V }
\cs_generate_variant:Nn \draw_set_linewidth:n { V }
\cs_generate_variant:Nn \draw_set_miterlimit:n { V }
\cs_generate_variant:Nn \draw_set_dash_pattern:nn { VV }
\cs_generate_variant:Nn \draw_path_corner_arc:nn { ee }
\cs_generate_variant:Nn \draw_path_use_clear:n { e }
\cs_generate_variant:Nn \draw_layer_begin:n { V }

\exp_args_generate:n { NNNe }

\cs_new_protected:Npn \__hawkdraw_style_set:nn #1#2 {
    \clist_if_exist:cF { l__hawkdraw_style_ #1 _value_clist } {
        \clist_new:c { l__hawkdraw_style_ #1 _value_clist }
    }
    \clist_put_right:cn { l__hawkdraw_style_ #1 _value_clist } {#2}
    \keys_define:ne { hawkdraw / path } {
        #1                  .meta:n         = {
            \clist_use:c { l__hawkdraw_style_ #1 _value_clist }
        }
    }
}

\cs_new_protected:Npn \__hawkdraw_style_put_right:nn #1#2 {
    \clist_if_exist:cTF { l__hawkdraw_style_ #1 _value_clist } {
        \clist_put_right:cn { l__hawkdraw_style_ #1 _value_clist } {#2}
        \keys_define:ne { hawkdraw / path } {
            #1              .meta:n         = {
                \clist_use:c { l__hawkdraw_style_ #1 _value_clist }
            }
        }
    } {
        \msg_error:nnn { hawkdraw } { style-undefined } {#1}
    }
}

\keys_define:nn { hawkdraw / path } {
    style ~ set             .code:n         = {
        \keyval_parse:NNn \use:n \__hawkdraw_style_set:nn {#1}
    } ,
    style ~ add             .code:n         = {
        \keyval_parse:NNn \use:n \__hawkdraw_style_put_right:nn {#1}
    } ,
}

\keys_define:nn { hawkdraw / path } {
    stroke                  .code:n         = {
        \clist_put_right:Nn \l_hawkdraw_path_use_clist { stroke }
    } ,
    fill                    .code:n         = {
        \clist_put_right:Nn \l_hawkdraw_path_use_clist { fill }
    } ,
    clip                    .bool_gset:N    = \g__hawkdraw_path_use_clip_bool ,
    clip                    .default:n      = { true } ,
    clip                    .initial:n      = { false } ,
    % ===
    stroke ~ color          .tl_set:N       = \l_hawkdraw_path_stroke_color_tl ,
    stroke ~ color          .initial:n      = { . } ,
    fill ~ color            .tl_set:N       = \l_hawkdraw_path_fill_color_tl ,
    fill ~ color            .initial:n      = { . } ,
    stroke ~ opacity        .fp_set:N       = \l_hawkdraw_path_stroke_opacity_fp ,
    stroke ~ opacity        .initial:n      = { 1 } ,
    fill ~ opacity          .fp_set:N       = \l_hawkdraw_path_fill_opacity_fp ,
    fill ~ opacity          .initial:n      = { 1 } ,
    % ===
    line ~ width            .dim_set:N      = \l_hawkdraw_path_linewidth_dim ,
    line ~ width            .initial:V      = \l_draw_default_linewidth_dim ,
    line ~ cap              .choice: ,
    line ~ cap / butt       .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_cap_tl { butt }
    } ,
    line ~ cap / round      .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_cap_tl { round }
    } ,
    line ~ cap / rectangle  .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_cap_tl { rectangle }
    } ,
    line ~ cap              .initial:n      = { butt } ,
    line ~ join             .choice: ,
    line ~ join / miter     .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_join_tl { miter }
    } ,
    line ~ join / round     .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_join_tl { round }
    } ,
    line ~ join / bevel     .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_join_tl { bevel }
    } ,
    line ~ join             .initial:n      = { miter } ,
    miter ~ limit           .fp_set:N       = \l_hawkdraw_path_miterlimit_fp ,
    miter ~ limit           .initial:n      = { 10 } ,
    dash ~ pattern          .clist_set:N    = \l_hawkdraw_path_dash_pattern_clist ,
    dash ~ pattern          .initial:n      = { } ,
    dash ~ phase            .dim_set:N      = \l_hawkdraw_path_dash_phase_dim ,
    dash ~ phase            .initial:n      = { 0pt } ,
    corner ~ arc            .clist_set:N    = \l_hawkdraw_path_corner_arc_clist ,
    corner ~ arc            .initial:n      = { 0pt } ,
    fill ~ rule             .choice: ,
    fill ~ rule / even-odd  .code:n        = {
        \tl_set:Nn \l_hawkdraw_path_fill_rule_tl { evenodd }
    } ,
    fill ~ rule / non-zero  .code:n         = {
        \tl_set:Nn \l_hawkdraw_path_fill_rule_tl { nonzero }
    } ,
    fill ~ rule             .initial:n      = { non-zero } ,
    % ===
    shift                   .code:n         = {
        \draw_transform_shift:n {#1}
    } ,
    rotate                  .code:n         = {
        \draw_transform_rotate:n {#1}
    } ,
    scale                   .code:n         = {
        \int_compare:nNnTF { \clist_count:n {#1} } > { 1 } {
            \draw_transform_xscale:n {
                \clist_item:nn {#1} { 1 }
            }
            \draw_transform_yscale:n {
                \clist_item:nn {#1} { 2 }
            }
        } {
            \draw_transform_scale:n {#1}
        }
    } ,
    slant                   .code:n         = {
        \int_compare:nNnTF { \clist_count:n {#1} } > { 1 } {
            \draw_transform_xslant:n {
                \clist_item:nn {#1} { 1 }
            }
            \draw_transform_yslant:n {
                \clist_item:nn {#1} { 2 }
            }
        } {
            \draw_transform_slant:n {#1}
        }
    } ,
}

\cs_new:Npn \__hawkdraw_tuple_use_i:w ( #1 , #2 ) {#1}
\cs_new:Npn \hawkdraw_tuple_use_i:n #1 {
    \fp_to_dim:n {
        \exp_last_unbraced:Ne
            \__hawkdraw_tuple_use_i:w { \fp_eval:n {#1} }
    }
}
\cs_generate_variant:Nn \hawkdraw_tuple_use_i:n { V }

\cs_new:Npn \__hawkdraw_tuple_use_ii:w ( #1 , #2 ) {#2}
\cs_new:Npn \hawkdraw_tuple_use_ii:n #1 {
    \fp_to_dim:n {
        \exp_last_unbraced:Ne
            \__hawkdraw_tuple_use_ii:w { \fp_eval:n {#1} }
    }
}
\cs_generate_variant:Nn \hawkdraw_tuple_use_ii:n { V }

\cs_new:Npn \__hawkdraw_cs_if_exist_use_secure:nnTF #1#2#3#4 {
    \exp_last_unbraced:Ne
    \token_if_cs:NTF { \tl_head:n {#1} } {#4} {
        \cs_if_exist_use:cTF {#2} {#3} {#4}
    }
}

\cs_new:Npn \__hawkdraw_cs_if_exist_use_secure:nnT #1#2#3 {
    \exp_last_unbraced:Ne
    \token_if_cs:NF { \tl_head:n {#1} } {
        \cs_if_exist_use:cT {#2} {#3}
    }
}

\cs_new_protected:Npn \hawkdraw_path_set_options: {
    \color_stroke:e { \l_hawkdraw_path_stroke_color_tl }
    \color_fill:e { \l_hawkdraw_path_fill_color_tl }
    \opacity_stroke:V \l_hawkdraw_path_stroke_opacity_fp
    \opacity_fill:V \l_hawkdraw_path_fill_opacity_fp
    \draw_set_linewidth:V \l_hawkdraw_path_linewidth_dim
    \draw_set_miterlimit:V \l_hawkdraw_path_miterlimit_fp
    \use:c { draw_set_cap_ \l_hawkdraw_path_cap_tl : }
    \use:c { draw_set_join_ \l_hawkdraw_path_join_tl : }
    \int_compare:nNnTF { \clist_count:N \l_hawkdraw_path_corner_arc_clist } > { 1 } {
        \draw_path_corner_arc:ee {
            \clist_item:Nn \l_hawkdraw_path_corner_arc_clist { 1 }
        } {
            \clist_item:Nn \l_hawkdraw_path_corner_arc_clist { 2 }
        }
    } {
        \draw_path_corner_arc:ee {
            \clist_item:Nn \l_hawkdraw_path_corner_arc_clist { 1 }
        } {
            \clist_item:Nn \l_hawkdraw_path_corner_arc_clist { 1 }
        }
    }
    \use:c { draw_set_ \l_hawkdraw_path_fill_rule_tl _rule: }
    \draw_set_dash_pattern:VV \l_hawkdraw_path_dash_pattern_clist
        \l_hawkdraw_path_dash_phase_dim
}

% ===

\scan_new:N \s__hawkdraw_stop

\fp_new:N \l_hawkdraw_point_at_fp
\tl_new:N \l_hawkdraw_point_at_name_tl
\tl_new:N \l_hawkdraw_point_name_tl
\fp_new:N \l_hawkdraw_point_at_part_fp
\fp_new:N \l_hawkdraw_point_slope_fp

\keys_define:nn { hawkdraw / path / point } {
    at                      .code:n         = {
        \hawkdraw_point_parse_set:Nn \l_hawkdraw_point_at_fp {#1}
        \tl_set:Nn \l_hawkdraw_point_at_name_tl {#1}
    } ,
    name                    .tl_set:N       = \l_hawkdraw_point_name_tl ,
    at ~ part               .fp_set:N       = \l_hawkdraw_point_at_part_fp ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ p :w } point #1 [ #2 ] #3 \s__hawkdraw_stop {
    \fp_set_eq:NN \l_hawkdraw_point_at_fp \g_hawkdraw_path_last_point_fp
    \fp_set_eq:NN \l_hawkdraw_point_slope_fp \g_hawkdraw_path_last_slope_fp
    \fp_set:Nn \l_hawkdraw_point_at_part_fp { nan }
    \tl_clear:N \l_hawkdraw_point_at_name_tl
    \keys_set:nn { hawkdraw / path / point } {#2}
    \fp_if_nan:nF { \l_hawkdraw_point_at_part_fp } {
        \fp_set:Nn \l_hawkdraw_point_at_fp {
            \cs_if_exist_use:cTF {
                __hawkdraw_point_part_ \l__hawkdraw_path_type_tl :V
            } {
                \l_hawkdraw_point_at_part_fp
            } {
                0pt , 0pt
            }
        }
        \fp_set:Nn \l_hawkdraw_point_slope_fp {
            \cs_if_exist_use:cTF {
                __hawkdraw_point_part_slope_ \l__hawkdraw_path_type_tl :V
            } {
                \l_hawkdraw_point_at_part_fp
            } {
                0
            }
        }
    }
    \tl_if_blank:VF \l_hawkdraw_point_name_tl {
        \prop_if_exist:cF {
            g__hawkdraw_point_ \l_hawkdraw_point_name_tl _prop
        } {
            \prop_new_linked:c {
                g__hawkdraw_point_ \l_hawkdraw_point_name_tl _prop
            }
        }
        \prop_gput:cnV { g__hawkdraw_point_ \l_hawkdraw_point_name_tl _prop }
            { point } \l_hawkdraw_point_at_fp
        \prop_gput:cnV { g__hawkdraw_point_ \l_hawkdraw_point_name_tl _prop }
            { slope } \l_hawkdraw_point_slope_fp
    }
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

% ===

\cs_new_protected:Npn \hawkdraw_point_parse_set:Nn #1#2 {
    \fp_set:Nn #1 { \hawkdraw_point_parse:n {#2} }
}

\cs_new:Npn \hawkdraw_point_parse:n #1 {
    \prop_if_exist:cTF {
        g__hawkdraw_point_
        \__hawkdraw_point_parse_trim:n {#1}
        _prop
    } {
        \fp_eval:n {
            \prop_item:cn {
                g__hawkdraw_point_
                \__hawkdraw_point_parse_trim:n {#1}
                _prop
            } { point }
        }
    } {
        \__hawkdraw_tl_has_greater_than_sign:nTF {#1} {
            \fp_eval:n {
                \__hawkdraw_point_parse_polar:w #1 \s__hawkdraw_stop
            }
        } {
            \__hawkdraw_tl_has_colon:nTF {#1} {
                \fp_eval:n {
                    \__hawkdraw_point_parse_polar_compat:w #1 \s__hawkdraw_stop
                }
            } {
                \fp_eval:n {#1}
            }
        }
    }
}

\prg_new_conditional:Npne \__hawkdraw_tl_has_colon:n #1 { TF } {
    \exp_not:N \__hawkdraw_tl_has_colon:w
    \exp_not:N \q_mark #1 \c_colon_str \exp_not:N \q_nil \c_colon_str \exp_not:N \q_stop
}
\__hawkdraw_exp_last_unbraced:NNe
\cs_new:Npn \__hawkdraw_tl_has_colon:w {
    #1 \c_colon_str #2 \c_colon_str #3 \exp_not:N \q_stop
} {
    \quark_if_nil:nTF {#2} { \prg_return_false: } { \prg_return_true: }
}

\prg_new_conditional:Npnn \__hawkdraw_tl_has_greater_than_sign:n #1 { TF } {
    \__hawkdraw_tl_has_greater_than_sign:w \q_mark #1 > \q_nil > \q_stop
}
\cs_new:Npn \__hawkdraw_tl_has_greater_than_sign:w #1 > #2 > #3 \q_stop {
    \quark_if_nil:nTF {#2} { \prg_return_false: } { \prg_return_true: }
}

\cs_new:Npn \__hawkdraw_point_parse_trim:n #1 {
    \tl_trim_spaces:e {
        \tl_if_head_eq_meaning:eNTF { \tl_trim_spaces:n {#1} } ( {
            \exp_last_unbraced:Ne
            \__hawkdraw_point_parse_trim:w { \tl_trim_spaces:n {#1} } \s_hawkdraw_stop
        } {
            #1
        }
    }
}

\cs_new:Npn \__hawkdraw_point_parse_trim:w ( #1 ) \s_hawkdraw_stop {
    #1
}

\cs_new:Npn \__hawkdraw_point_parse_polar:w #1 > #2 \s__hawkdraw_stop {
    \int_compare:nNnTF { \clist_count:n {#2} } > { 1 } {
        \draw_point_polar:nnn {
            \clist_item:nn {#1} { 1 }
        } {
            \clist_item:nn {#1} { 2 }
        } {#2}
    } {
        \draw_point_polar:nn {#1} {#2}
    }
}

\exp_last_unbraced:NNNNo
\cs_new:Npn \__hawkdraw_point_parse_polar_compat:w #1 \c_colon_str #2 \s__hawkdraw_stop {
    \int_compare:nNnTF { \clist_count:n {#2} } > { 1 } {
        \draw_point_polar:nnn {
            \clist_item:nn {#2} { 1 }
        } {
            \clist_item:nn {#2} { 2 }
        } {#1}
    } {
        \draw_point_polar:nn {#2} {#1}
    }
}

% ===

\cs_new_protected:Npn \hawkdraw_path:w #1 ; {
    \draw_scope_begin:
        \tl_gset:Nn \g_hawkdraw_path_current_tl {#1}
        \tl_use:N \l_hawkdraw_path_preactions_tl
        \tl_trim_spaces_apply:nN {#1} \__hawkdraw_path_process:n
        \clist_remove_duplicates:N \l_hawkdraw_path_use_clist
        \draw_path_use_clear:e { \clist_use:N \l_hawkdraw_path_use_clist }
        \tl_use:N \l_hawkdraw_path_postactions_tl
    \draw_scope_end:
    \bool_if:NT \g__hawkdraw_path_use_clip_bool {
        \__hawkdraw_path_process_clip:V \g_hawkdraw_path_current_tl
    }
}

\cs_new_protected:Npn \__hawkdraw_path_process:n #1 {
    \__hawkdraw_cs_if_exist_use_secure:nnT {#1} {
        __hawkdraw_path_process_ \tl_head:n {#1} :w
    } {
        #1 \s__hawkdraw_stop
    }
}
\cs_generate_variant:Nn \__hawkdraw_path_process:n { o }

\cs_new_protected:Npn \__hawkdraw_path_process_clip:n #1 {
    \bool_set_true:N \l__hawkdraw_path_ignore_keys_bool
    \tl_trim_spaces_apply:nN {#1} \__hawkdraw_path_process:n
    \draw_path_use_clear:n { clip }
    \bool_set_false:N \l__hawkdraw_path_ignore_keys_bool
    \bool_gset_false:N \g__hawkdraw_path_use_clip_bool
}
\cs_generate_variant:Nn \__hawkdraw_path_process_clip:n { V }

\cs_new_protected:Npn \__hawkdraw_path_continue:n #1 {
    \tl_if_blank:nF {#1} {
        \__hawkdraw_path_process:n {#1}
    }
}

\cs_new_protected:cpn { __hawkdraw_path_process_ [ :w } [ #1 ] #2 \s__hawkdraw_stop {
    \bool_if:NF \l__hawkdraw_path_ignore_keys_bool {
        \keys_set:nn { hawkdraw / path } {#1}
        \hawkdraw_path_set_options:
    }
    \tl_trim_spaces_apply:nN {#2} \__hawkdraw_path_continue:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_ ( :w } ( #1 ) #2 \s__hawkdraw_stop {
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_a_fp {#1}
    \draw_path_moveto:n {
        \l__hawkdraw_point_a_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_first_point_fp \l__hawkdraw_point_a_fp
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#2} \__hawkdraw_path_continue:n
}

% ===

\cs_generate_variant:Nn \draw_point_interpolate_line:nnn { nVV }
\cs_generate_variant:Nn \draw_point_interpolate_curve:nnnn { nVVV }
\cs_generate_variant:Nn \draw_point_interpolate_curve:nnnnn { nVVVV }
\cs_generate_variant:Nn \draw_point_interpolate_arc_axes:nnnnnn { nnVVVV , nVVV }
\cs_generate_variant:Nn \draw_point_polar:nnn { VV }

\cs_new:Npn \__hawkdraw_calculate_slope:nn #1#2 {
    \fp_eval:n {
        atand(
            \hawkdraw_tuple_use_ii:n {#2} -
            \hawkdraw_tuple_use_ii:n {#1}
        ,
            \hawkdraw_tuple_use_i:n {#2} -
            \hawkdraw_tuple_use_i:n {#1}
        )
    }
}
\cs_generate_variant:Nn \__hawkdraw_calculate_slope:nn { nV , V , VV }

\cs_new:Npn \__hawkdraw_point_part_line:n #1 {
    \draw_point_interpolate_line:nVV {#1}
        \l__hawkdraw_point_o_fp
        \l__hawkdraw_point_a_fp
}
\cs_generate_variant:Nn \__hawkdraw_point_part_line:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_line:n #1 {
    \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_o_fp
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_line:n { V }

\cs_new:Npn \__hawkdraw_point_part_line_orthogonal:n #1 {
    \fp_compare:nNnTF {#1} > { 0.5 } {
        \draw_point_interpolate_line:nnn { 2 * ( #1 - 0.5 ) }
            { \l__hawkdraw_point_b_fp }
            { \l__hawkdraw_point_a_fp }
    } {
        \draw_point_interpolate_line:nnn { 2 * #1 }
            { \l__hawkdraw_point_o_fp }
            { \l__hawkdraw_point_b_fp }
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_line_orthogonal:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_line_orthogonal:n #1 {
    \fp_compare:nNnTF {#1} > { 0.5 } {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_b_fp
    } {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_b_fp \l__hawkdraw_point_o_fp
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_line_orthogonal:n { V }

\cs_new:Npn \__hawkdraw_point_part_curve_quadratic:n #1 {
    \draw_point_interpolate_curve:nVVV {#1}
        \l__hawkdraw_point_o_fp
        \l__hawkdraw_point_a_fp
        \l__hawkdraw_point_b_fp
}
\cs_generate_variant:Nn \__hawkdraw_point_part_curve_quadratic:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_curve_quadratic:n #1 {
    \fp_eval:n {
        atand(
            (
                (
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp -
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_o_fp
                ) * ( 1 - #1 ) +
                (
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_b_fp -
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp
                ) * #1
            ) * 2
        ,
            (
                (
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp -
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_o_fp
                ) * ( 1 - #1 ) +
                (
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_b_fp -
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp
                ) * #1
            ) * 2
        )
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_curve_quadratic:n { V }

\cs_new:Npn \__hawkdraw_point_part_curve_cubic:n #1 {
    \draw_point_interpolate_curve:nVVVV {#1}
        \l__hawkdraw_point_o_fp
        \l__hawkdraw_point_a_fp
        \l__hawkdraw_point_b_fp
        \l__hawkdraw_point_c_fp
}
\cs_generate_variant:Nn \__hawkdraw_point_part_curve_cubic:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_curve_cubic:n #1 {
    \fp_eval:n {
        atand(
            (
                (
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp -
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_o_fp
                ) * ( 1 - #1 ) * ( 1 - #1 ) +
                (
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_b_fp -
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp
                ) * ( 1 - #1 ) * #1 * 2 +
                (
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_c_fp -
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_b_fp
                ) * #1 * #1
            ) * 3
        ,
            (
                (
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp -
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_o_fp
                ) * ( 1 - #1 ) * ( 1 - #1 ) +
                (
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_b_fp -
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp
                ) * ( 1 - #1 ) * #1 * 2 +
                (
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_c_fp -
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_b_fp
                ) * #1 * #1
            ) * 3
        )
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_curve_cubic:n { V }

\cs_new:Npn \__hawkdraw_point_part_circle:n #1 {
    \draw_point_polar:nn { \l_hawkdraw_path_circle_radius_dim } { #1 * 360 }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_circle:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_circle:n #1 {
    \fp_eval:n { #1 * 360 - 90 }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_circle:n { V }

\cs_new:Npn \__hawkdraw_point_part_ellipse:n #1 {
    \draw_point_interpolate_arc_axes:nVVVnn {#1}
        \l__hawkdraw_point_o_fp
        \l_hawkdraw_path_ellipse_vector_a_fp
        \l_hawkdraw_path_ellipse_vector_b_fp
        { 0 }
        { 360 }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_ellipse:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_ellipse:n #1 {
    \fp_eval:n {
        atand(
            \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_ellipse_vector_b_fp *
            cosd( #1 * 360 ) -
            \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_ellipse_vector_a_fp *
            sind( #1 * 360 )
        ,
            \hawkdraw_tuple_use_i:V \l_hawkdraw_path_ellipse_vector_b_fp *
            cosd( #1 * 360 ) -
            \hawkdraw_tuple_use_i:V \l_hawkdraw_path_ellipse_vector_a_fp *
            sind( #1 * 360 )
        )
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_ellipse:n { V }

\cs_new:Npn \__hawkdraw_point_part_arc:n #1 {
    \bool_if:NTF \l_hawkdraw_path_arc_axes_bool {
        \draw_point_interpolate_arc_axes:nnVVVV {#1}
            {
                (
                    \hawkdraw_tuple_use_i:V \l_hawkdraw_path_arc_vector_a_fp *
                    cosd( \l_hawkdraw_path_arc_angle_start_fp ) +
                    \hawkdraw_tuple_use_i:V \l_hawkdraw_path_arc_vector_b_fp *
                    sind( \l_hawkdraw_path_arc_angle_start_fp )
                ,
                    \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_arc_vector_a_fp *
                    cosd( \l_hawkdraw_path_arc_angle_start_fp ) +
                    \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_arc_vector_b_fp *
                    sind( \l_hawkdraw_path_arc_angle_start_fp )
                ) * -1
            }
            \l_hawkdraw_path_arc_vector_a_fp
            \l_hawkdraw_path_arc_vector_b_fp
            \l_hawkdraw_path_arc_angle_start_fp
            \l_hawkdraw_path_arc_angle_end_fp
    } {
        \fp_eval:n {
            (
                \draw_point_polar:VVn \l_hawkdraw_path_arc_radius_x_dim \l_hawkdraw_path_arc_radius_y_dim {
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                }
            ) -
            (
                \draw_point_polar:VVn \l_hawkdraw_path_arc_radius_x_dim \l_hawkdraw_path_arc_radius_y_dim {
                    \l_hawkdraw_path_arc_angle_start_fp
                }
            )
        }
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_arc:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_arc:n #1 {
    \bool_if:NTF \l_hawkdraw_path_arc_axes_bool {
        \fp_eval:n {
            atand(
                \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_arc_vector_b_fp *
                cosd(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                ) -
                \hawkdraw_tuple_use_ii:V \l_hawkdraw_path_arc_vector_a_fp *
                sind(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                )
            ,
                \hawkdraw_tuple_use_i:V \l_hawkdraw_path_arc_vector_b_fp *
                cosd(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                ) -
                \hawkdraw_tuple_use_i:V \l_hawkdraw_path_arc_vector_a_fp *
                sind(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                )
            )
        }
    } {
        \fp_eval:n {
            atand(
                 \l_hawkdraw_path_arc_radius_x_dim *
                sind(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                )
            ,
                \l_hawkdraw_path_arc_radius_y_dim *
                cosd(
                    \l_hawkdraw_path_arc_angle_start_fp +
                    #1 * ( \l_hawkdraw_path_arc_angle_end_fp - \l_hawkdraw_path_arc_angle_start_fp )
                )  
            ) - 90
        }
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_arc:n { V }

\cs_new:Npn \__hawkdraw_point_part_rectangle:n #1 {
    \fp_compare:nNnTF {#1} > { 0.25 } {
        \fp_compare:nNnTF {#1} > { 0.5 } {
            \fp_compare:nNnTF {#1} > { 0.75 } {
                \draw_point_interpolate_line:nnn { 4 * ( #1 - 0.75 ) }
                    {
                        \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp
                    ,
                        \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_o_fp
                    }
                    { \l__hawkdraw_point_o_fp }
            } {
                \draw_point_interpolate_line:nnn { 4 * ( #1 - 0.5 ) }
                    { \l__hawkdraw_point_a_fp }
                    {
                        \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp
                    ,
                        \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_o_fp
                    }
            }
        } {
            \draw_point_interpolate_line:nnn { 4 * ( #1 - 0.25 ) }
                {
                    \hawkdraw_tuple_use_i:V \l__hawkdraw_point_o_fp
                ,
                    \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp
                }
                { \l__hawkdraw_point_a_fp }   
        }
    } {
        \draw_point_interpolate_line:nnn { 4 * #1 }
            { \l__hawkdraw_point_o_fp }
            {
                \hawkdraw_tuple_use_i:V \l__hawkdraw_point_o_fp
            ,
                \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp
            }
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_rectangle:n { V }

\cs_new:Npn \__hawkdraw_point_part_slope_rectangle:n #1 {
    \fp_compare:nNnTF {#1} > { 0.25 } {
        \fp_compare:nNnTF {#1} > { 0.5 } {
            \fp_compare:nNnTF {#1} > { 0.75 } {
                \fp_eval:n { 180 }
            } {
                \fp_eval:n { 270 }
            }
        } {
            \fp_eval:n { 0 }
        }
    } {
        \fp_eval:n { 90 }
    }
}
\cs_generate_variant:Nn \__hawkdraw_point_part_slope_rectangle:n { V }

% ===

\cs_new:Npn \__hawkdraw_path_process_line:n #1 {
    \__hawkdraw_cs_if_exist_use_secure:nnT {#1} {
        __hawkdraw_path_process_line_ \tl_head:n {#1} :w
    } {
        #1 \s__hawkdraw_stop
    }
}

\cs_new_protected:cpn { __hawkdraw_path_process_ - :w } - #1 \s__hawkdraw_stop {
    \tl_trim_spaces_apply:nN {#1} \__hawkdraw_path_process_line:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_line_ - :w } - #1 ( #2 ) #3 \s__hawkdraw_stop {
    % what do we do with #1?
    \str_set:Nn \l__hawkdraw_path_type_tl { line }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_a_fp {#2}
    \draw_path_lineto:n {
        \l__hawkdraw_point_a_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_o_fp
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_o_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_line_ | :w } | #1 ( #2 ) #3 \s__hawkdraw_stop {
    % what do we do with #1?
    \str_set:Nn \l__hawkdraw_path_type_tl { line_orthogonal }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_a_fp {#2}
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_b_fp {
        (
            \hawkdraw_tuple_use_i:V \l__hawkdraw_point_a_fp
        ,
            \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_o_fp
        )
    }
    \draw_path_lineto:n {
        \l__hawkdraw_point_b_fp
    }
    \draw_path_lineto:n {
        \l__hawkdraw_point_a_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_b_fp \l__hawkdraw_point_o_fp
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_b_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_ | :w } |- #1 ( #2 ) #3 \s__hawkdraw_stop {
    % what do we do with #1?
    \str_set:Nn \l__hawkdraw_path_type_tl { line_orthogonal }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_a_fp {#2}
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_b_fp {
        (
            \hawkdraw_tuple_use_i:V \l__hawkdraw_point_o_fp
        ,
            \hawkdraw_tuple_use_ii:V \l__hawkdraw_point_a_fp
        )
    }
    \draw_path_lineto:n {
        \l__hawkdraw_point_b_fp
    }
    \draw_path_lineto:n {
        \l__hawkdraw_point_a_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_b_fp \l__hawkdraw_point_o_fp
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_b_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

% ===

\cs_new:Npn \__hawkdraw_path_process_curve:n #1 {
    \__hawkdraw_cs_if_exist_use_secure:nnT {#1} {
        __hawkdraw_path_process_curve_ \tl_head:n {#1} :w
    } {
        #1 \s__hawkdraw_stop
    }
}

\cs_new_protected:cpn { __hawkdraw_path_process_ . :w } .. #1 ( #2 ) #3 \s__hawkdraw_stop {
    % what do we do with #1?
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_a_fp {#2}
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_process_curve:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_curve_ . :w } .. #1 ( #2 ) #3 \s__hawkdraw_stop {
    % what do we do with #1?
    \str_set:Nn \l__hawkdraw_path_type_tl { curve_quadratic }
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_b_fp {#2}
    \draw_path_curveto:nn {
        \l__hawkdraw_point_a_fp
    } {
        \l__hawkdraw_point_b_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_o_fp
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_b_fp \l__hawkdraw_point_a_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_b_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_curve_ & :w } & #1 ( #2 ) #3 .. #4 ( #5 ) #6 \s__hawkdraw_stop {
    % what do we do with #1, #3, #4?
    \str_set:Nn \l__hawkdraw_path_type_tl { curve_cubic }
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_b_fp {#2}
    \hawkdraw_point_parse_set:Nn \l__hawkdraw_point_c_fp {#5}
    \draw_path_curveto:nnn {
        \l__hawkdraw_point_a_fp
    } {
        \l__hawkdraw_point_b_fp
    } {
        \l__hawkdraw_point_c_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_a_fp \l__hawkdraw_point_o_fp
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_calculate_slope:VV \l__hawkdraw_point_c_fp \l__hawkdraw_point_b_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_c_fp
    \tl_trim_spaces_apply:nN {#6} \__hawkdraw_path_continue:n
}

\cs_new_protected:cpn { __hawkdraw_path_process_ ! :w } ! #1 \s__hawkdraw_stop {
    \draw_path_close:
    \fp_gset:Nn \g_hawkdraw_path_first_slope_fp { nan }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { nan }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \g_hawkdraw_path_first_point_fp
    \tl_trim_spaces_apply:nN {#1} \__hawkdraw_path_continue:n
}

% ===

\dim_new:N \l_hawkdraw_path_circle_radius_dim

\cs_generate_variant:Nn \draw_path_circle:nn { VV }

\keys_define:nn { hawkdraw / path / circle } {
    radius                  .dim_set:N      = \l_hawkdraw_path_circle_radius_dim ,
    radius                  .initial:n      = { 1cm } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ c :w } circle #1 [ #2 ] #3 \s__hawkdraw_stop {
    \str_set:Nn \l__hawkdraw_path_type_tl { circle }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \keys_set:nn { hawkdraw / path / circle } {#2}
    \draw_path_circle:VV \g_hawkdraw_path_last_point_fp
        \l_hawkdraw_path_circle_radius_dim
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\fp_new:N \l_hawkdraw_path_ellipse_vector_a_fp
\fp_new:N \l_hawkdraw_path_ellipse_vector_b_fp

\cs_generate_variant:Nn \draw_path_ellipse:nnn { VVV }

\keys_define:nn { hawkdraw / path / ellipse } {
    vector ~ a              .fp_set:N       = \l_hawkdraw_path_ellipse_vector_a_fp ,
    vector ~ a              .initial:n      = { ( 1cm , 0pt ) } ,
    vector ~ b              .fp_set:N       = \l_hawkdraw_path_ellipse_vector_b_fp ,
    vector ~ b              .initial:n      = { ( 0pt , 1cm ) } ,
    radius ~ x              .code:n         = {
        \fp_set:Nn \l_hawkdraw_path_ellipse_vector_a_fp { ( #1 , 0pt ) }
    } ,
    radius ~ y              .code:n         = {
        \fp_set:Nn \l_hawkdraw_path_ellipse_vector_b_fp { ( 0pt , #1 ) }
    } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ e :w } ellipse #1 [ #2 ] #3 \s__hawkdraw_stop {
    \str_set:Nn \l__hawkdraw_path_type_tl { ellipse }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \keys_set:nn { hawkdraw / path / ellipse } {#2}
    \draw_path_ellipse:VVV \g_hawkdraw_path_last_point_fp
        \l_hawkdraw_path_ellipse_vector_a_fp
        \l_hawkdraw_path_ellipse_vector_b_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\bool_new:N \l_hawkdraw_path_arc_axes_bool
\dim_new:N \l_hawkdraw_path_arc_radius_x_dim
\dim_new:N \l_hawkdraw_path_arc_radius_y_dim
\fp_new:N \l_hawkdraw_path_arc_vector_a_fp
\fp_new:N \l_hawkdraw_path_arc_vector_b_fp
\fp_new:N \l_hawkdraw_path_arc_angle_start_fp
\fp_new:N \l_hawkdraw_path_arc_angle_end_fp

\cs_generate_variant:Nn \draw_path_arc:nnnn { VVVV }
\cs_generate_variant:Nn \draw_path_arc_axes:nnnn { VVVV }

\keys_define:nn { hawkdraw / path / arc } {
    radius ~ x              .code:n         = {
        \bool_set_false:N \l_hawkdraw_path_arc_axes_bool
        \dim_set:Nn \l_hawkdraw_path_arc_radius_x_dim {#1}
    } ,
    radius ~ x              .initial:n      = { 1cm } ,
    radius ~ y              .code:n         = {
        \bool_set_false:N \l_hawkdraw_path_arc_axes_bool
        \dim_set:Nn \l_hawkdraw_path_arc_radius_y_dim {#1}
    } ,
    radius ~ y              .initial:n      = { 1cm } ,
    radius                  .code:n         = {
        \bool_set_false:N \l_hawkdraw_path_arc_axes_bool
        \dim_set:Nn \l_hawkdraw_path_arc_radius_x_dim {#1}
        \dim_set:Nn \l_hawkdraw_path_arc_radius_y_dim {#1}
    } ,
    vector ~ a              .code:n         = {
        \bool_set_true:N \l_hawkdraw_path_arc_axes_bool
        \fp_set:Nn \l_hawkdraw_path_arc_vector_a_fp {#1}
    } ,
    vector ~ b              .code:n         = {
        \bool_set_true:N \l_hawkdraw_path_arc_axes_bool
        \fp_set:Nn \l_hawkdraw_path_arc_vector_b_fp {#1}
    } ,
    angle ~ start           .fp_set:N       = \l_hawkdraw_path_arc_angle_start_fp ,
    angle ~ start           .initial:n      = { 0 } ,
    angle ~ end             .fp_set:N       = \l_hawkdraw_path_arc_angle_end_fp ,
    angle ~ end             .initial:n      = { 90 } ,
    angle ~ delta           .code:n         = {
        \fp_set:Nn \l_hawkdraw_path_arc_angle_end_fp {
            \l_hawkdraw_path_arc_angle_start_fp + ( #1 )
        }
    } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ a :w } arc #1 [ #2 ] #3 \s__hawkdraw_stop {
    \str_set:Nn \l__hawkdraw_path_type_tl { arc }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \keys_set:nn { hawkdraw / path / arc } {#2}
    \bool_if:NTF \l_hawkdraw_path_arc_axes_bool {
        \draw_path_arc_axes:VVVV \l_hawkdraw_path_arc_vector_a_fp
            \l_hawkdraw_path_arc_vector_b_fp
            \l_hawkdraw_path_arc_angle_start_fp
            \l_hawkdraw_path_arc_angle_end_fp
    } {
         \draw_path_arc:VVVV \l_hawkdraw_path_arc_radius_x_dim
            \l_hawkdraw_path_arc_radius_y_dim
            \l_hawkdraw_path_arc_angle_start_fp
            \l_hawkdraw_path_arc_angle_end_fp
    }
    \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } {
        \fp_gset:Nn \g_hawkdraw_path_first_slope_fp {
            \__hawkdraw_point_part_slope_arc:n { 0 }
        }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp {
        \__hawkdraw_point_part_slope_arc:n { 1 }
    }
    \fp_gset:Nn \g_hawkdraw_path_last_point_fp {
        \__hawkdraw_point_part_arc:n { 1 }
    }
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\bool_new:N \l_hawkdraw_path_rectangle_relative_bool
\fp_new:N \l_hawkdraw_path_rectangle_corner_fp

\cs_generate_variant:Nn \draw_path_rectangle:nn { ee , VV }
\cs_generate_variant:Nn \draw_path_rectangle_corners:nn { VV }

\keys_define:nn { hawkdraw / path / rectangle } {
    relative                .bool_set:N     = \l_hawkdraw_path_rectangle_relative_bool ,
    relative                .default:n      = { true } ,
    relative                .initial:n      = { false } ,
    corner                  .code:n         = {
        \hawkdraw_point_parse_set:Nn \l_hawkdraw_path_rectangle_corner_fp {#1}
    } ,
    corner                  .initial:n      = { ( 1cm , 1cm ) } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ r :w } rectangle #1 [ #2 ] #3 \s__hawkdraw_stop {
    \str_set:Nn \l__hawkdraw_path_type_tl { rectangle }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \keys_set:nn { hawkdraw / path / rectangle } {#2}
    \bool_if:NTF \l_hawkdraw_path_rectangle_relative_bool {
        \fp_set:Nn \l__hawkdraw_point_a_fp {
            \l__hawkdraw_point_o_fp + \l_hawkdraw_path_rectangle_corner_fp
        }
        \draw_path_rectangle:VV \l__hawkdraw_point_o_fp
            \l_hawkdraw_path_rectangle_corner_fp
    } {
        \fp_set_eq:NN \l__hawkdraw_point_a_fp \l_hawkdraw_path_rectangle_corner_fp
        \draw_path_rectangle_corners:VV \l__hawkdraw_point_o_fp
            \l_hawkdraw_path_rectangle_corner_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

\clist_new:N \l_hawkdraw_path_grid_step_clist
\bool_new:N \l_hawkdraw_path_grid_relative_bool
\fp_new:N \l_hawkdraw_path_grid_corner_fp

\cs_generate_variant:Nn \draw_path_grid:nnnn { eeVV }

\keys_define:nn { hawkdraw / path / grid } {
    step                    .clist_set:N    = \l_hawkdraw_path_grid_step_clist ,
    step                    .initial:n      = { 1cm } ,
    relative                .bool_set:N     = \l_hawkdraw_path_grid_relative_bool ,
    relative                .default:n      = { true } ,
    relative                .initial:n      = { false } ,
    corner                  .code:n         = {
        \hawkdraw_point_parse_set:Nn \l_hawkdraw_path_grid_corner_fp {#1}
    } ,
    corner                  .initial:n      = { ( 1cm , 1cm ) } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ g :w } grid #1 [ #2 ] #3 \s__hawkdraw_stop {
    \str_set:Nn \l__hawkdraw_path_type_tl { grid }
    \fp_set_eq:NN \l__hawkdraw_point_o_fp \g_hawkdraw_path_last_point_fp
    \bool_if:NTF \l_hawkdraw_path_grid_relative_bool {
        \fp_set:Nn \l__hawkdraw_point_a_fp {
            \l__hawkdraw_point_o_fp + \l_hawkdraw_path_grid_corner_fp
        }
    } {
        \fp_set_eq:NN \l__hawkdraw_point_a_fp \l_hawkdraw_path_grid_corner_fp    
    }
    \keys_set:nn { hawkdraw / path / grid } {#2}
    \int_compare:nNnTF { \clist_count:N \l_hawkdraw_path_grid_step_clist } > { 1 } {
        \draw_path_grid:eeVV {
            \clist_item:Nn \l_hawkdraw_path_grid_step_clist { 1 }
        } {
            \clist_item:Nn \l_hawkdraw_path_grid_step_clist { 2 }
        }
            \l__hawkdraw_point_o_fp
            \l__hawkdraw_point_a_fp
    } {
        \draw_path_grid:eeVV {
            \clist_item:Nn \l_hawkdraw_path_grid_step_clist { 1 }
        } {
            \clist_item:Nn \l_hawkdraw_path_grid_step_clist { 1 }
        }
            \l__hawkdraw_point_o_fp
            \l__hawkdraw_point_a_fp
    }
    \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_a_fp
    \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
}

% ===

\clist_new:N \l_hawkdraw_map_list_clist
\fp_new:N \l_hawkdraw_map_start_fp
\fp_new:N \l_hawkdraw_map_step_fp
\fp_new:N \l_hawkdraw_map_end_fp
\int_new:N \l_hawkdraw_map_index_int
\int_new:N \l_hawkdraw_map_level_int

\keys_define:nn { hawkdraw /path / map } {
    list                    .clist_set:N    = \l_hawkdraw_map_list_clist ,
    start                   .fp_set:N       = \l_hawkdraw_map_start_fp ,
    start                   .initial:n      = { 1 } ,
    step                    .fp_set:N       = \l_hawkdraw_map_step_fp ,
    step                    .initial:n      = { 1 } ,
    end                     .fp_set:N       = \l_hawkdraw_map_end_fp ,
    end                     .initial:n      = { 1 } ,
}

\cs_new_protected:cpn { __hawkdraw_path_process_ m :w } map #1 [ #2 ] #3#4 \s__hawkdraw_stop {
    \hawkdraw_map:nn {#2} {
        \tl_trim_spaces_apply:nN {#3} \__hawkdraw_path_continue:n
    }
    \tl_trim_spaces_apply:nN {#4} \__hawkdraw_path_continue:n
}

\cs_generate_variant:Nn \cs_generate_variant:Nn { cn }

\cs_new_protected:Npn \hawkdraw_map:nn #1#2 {
    \keys_set:nn { hawkdraw / path / map } {#1}
    \int_zero:N \l_hawkdraw_map_index_int
    \int_incr:N \l_hawkdraw_map_level_int
    \cs_if_exist:cF {
        __hawkdraw_map_function_ \int_use:N \l_hawkdraw_map_level_int :nn
    } {
        \cs_new_protected:cpn {
            __hawkdraw_map_function_ \int_use:N \l_hawkdraw_map_level_int :nn
        } ##1##2 { }
        \cs_generate_variant:cn {
            __hawkdraw_map_function_ \int_use:N \l_hawkdraw_map_level_int :nn
        } { nV }
    }
    \cs_set_protected:cpn {
        __hawkdraw_map_function_ \int_use:N \l_hawkdraw_map_level_int :nn
    } ##1##2 {#2}
    \clist_if_empty:NTF \l_hawkdraw_map_list_clist {
        \fp_step_function:nnnN
            { \l_hawkdraw_map_start_fp }
            { \l_hawkdraw_map_step_fp }
            { \l_hawkdraw_map_end_fp }
            \__hawkdraw_map_function:n
    } {
        \clist_map_function:NN
            \l_hawkdraw_map_list_clist
            \__hawkdraw_map_function:n
    }
    \int_decr:N \l_hawkdraw_map_level_int
}

\cs_new_protected:Npn \__hawkdraw_map_function:n #1 {
    \int_incr:N \l_hawkdraw_map_index_int
    \use:c { __hawkdraw_map_function_ \int_use:N \l_hawkdraw_map_level_int :nV }
        {#1} \l_hawkdraw_map_index_int
}

% ===

\keys_define:nn { hawkdraw / instance } {
    baseline                .fp_set:N       = \l_draw_instance_baseline_fp ,
    layers                  .code:n         = {
        \clist_set:Nn \l_draw_layers_clist {#1}
    } ,
    overlay                 .bool_set:N     = \l_hawkdraw_instance_overlay_bool ,
    overlay                 .default:n      = { true } ,
    overlay                 .initial:n      = { false } ,
}

\keys_define:nn { hawkdraw / scope } {
    layer                   .tl_set:N      = \l_hawkdraw_scope_layer_tl ,
}

% ===

\NewDocumentCommand \hawkdrawarrayset { m m } {
    \clist_new:c { g__hawkdraw_user_array_ #1 _clist }
    \clist_gset:cn { g__hawkdraw_user_array_ #1 _clist } {#2}
}

\NewExpandableDocumentCommand \hawkdrawarrayitem { m m } {
    \clist_item:cn { g__hawkdraw_user_array_ #1 _clist } {#2}
}

\NewDocumentCommand \hawkdrawmap { O{} m } {
    \hawkdraw_map:nn {#1} {#2}
}

\NewDocumentCommand \hawkdrawsetcolor { m m m } {
    \str_if_eq:nnTF {#2} { named } {
        \color_set:nn {#1} {#3}
    } {
        \color_set:nnn {#1} {#2} {#3}
    }
}

\NewDocumentCommand \hawkdrawset { m } {
    \keys_set:nn { hawkdraw / path } {#1}
}

\NewDocumentEnvironment { hawkdraw } { O{} } {
    \mode_leave_vertical:
    \nullfont
    % ensure that relevant characters are not active
    \int_gincr:N \g_hawkdraw_id_int
    \cs_set_eq:NN \path \hawkdraw_path:w
    \cs_set_eq:NN \getpoint \hawkdraw_point_parse:n
    \cs_set_protected:Npn \stroke { \path [ stroke ] }
    \cs_set_protected:Npn \fill { \path [ fill ] }
    \cs_set_protected:Npn \clip { \path [ clip ] }
    \cs_set_protected:Npn \node { \path ( 0pt , 0pt ) node }
    \cs_set_protected:Npn \point { \path ( 0pt , 0pt ) point }
    \cs_set_protected:Npn \map { \path ( 0pt , 0pt ) map }
    \NewExpandableDocumentCommand \getfirstpoint { } {
        \fp_use:N \g_hawkdraw_path_first_point_fp
    }
    \NewExpandableDocumentCommand \getlastpoint { } {
        \fp_use:N \g_hawkdraw_path_last_point_fp
    }
    \NewExpandableDocumentCommand \getfirstslope { } {
        \fp_use:N \g_hawkdraw_path_first_slope_fp
    }
    \NewExpandableDocumentCommand \getlastslope { } {
        \fp_use:N \g_hawkdraw_path_last_slope_fp
    }
    \fp_set:Nn \l__hawkdraw_point_o_fp { ( 0pt , 0pt ) }
    \fp_set:Nn \l__hawkdraw_point_a_fp { ( 0pt , 0pt ) }
    \fp_set:Nn \l__hawkdraw_point_b_fp { ( 0pt , 0pt ) }
    \fp_set:Nn \l__hawkdraw_point_c_fp { ( 0pt , 0pt ) }
    \fp_gset:Nn \g_hawkdraw_path_first_point_fp { ( 0pt , 0pt ) }
    \fp_gset:Nn \g_hawkdraw_path_last_point_fp { ( 0pt , 0pt ) }
    \fp_gset:Nn \g_hawkdraw_path_first_slope_fp { nan }
    \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { nan }
    \NewDocumentEnvironment { scope } { O{} } {
        \draw_scope_begin:
            \clist_clear:N \l__hawkdraw_keys_unprocessed_clist
            \keys_set_known:nnN { hawkdraw / scope } {##1}
                \l__hawkdraw_keys_unprocessed_clist
            \keys_set:nV { hawkdraw / path } \l__hawkdraw_keys_unprocessed_clist
            \tl_if_empty:NF \l_hawkdraw_scope_layer_tl {
                \draw_layer_begin:V \l_hawkdraw_scope_layer_tl
            }
    } {
            \tl_if_empty:NF \l_hawkdraw_scope_layer_tl {
                \draw_layer_end:
            }
        \draw_scope_end:
    }
    \fp_set:Nn \l_draw_instance_baseline_fp { nan }
    \draw_begin:
        \clist_clear:N \l__hawkdraw_keys_unprocessed_clist
        \keys_set_known:nnN { hawkdraw / instance } {#1}
            \l__hawkdraw_keys_unprocessed_clist
        \keys_set:nV { hawkdraw / path } \l__hawkdraw_keys_unprocessed_clist
        \fp_if_nan:nF { \l_draw_instance_baseline_fp } {
            \draw_set_baseline:n {
                \fp_to_dim:n { \l_draw_instance_baseline_fp }
            }
        }
        \bool_if:NT \l_hawkdraw_instance_overlay_bool {
            \bool_set_false:N \l_draw_bb_update_bool
        }
} {
    \draw_end:
}

% EOF
