% 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-20} {0.0.6} {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. } \cs_if_exist:NF \ProcessKeyOptions { \msg_critical:nn { hawkdraw } { old-kernel } } % === \ProcessKeyOptions [ ] \RequirePackage { l3draw } % === \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: } \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-12-31 } { \cs_set_eq:NN \draw_layer_new:n \prg_do_nothing: } { } % === \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 } % === \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 \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_clip_bool \str_new:N \l_hawkdraw_path_stroke_color_str \str_new:N \l_hawkdraw_path_fill_color_str \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 \str_new:N \l_hawkdraw_path_cap_str \str_new:N \l_hawkdraw_path_join_str \fp_new:N \l_hawkdraw_path_miterlimit_fp \clist_new:N \l_hawkdraw_path_corner_arc_clist \str_new:N \l_hawkdraw_path_fill_rule_str \clist_new:N \l_hawkdraw_path_dash_pattern_clist \dim_new:N \l_hawkdraw_path_dash_phase_dim \fp_new:N \l_hawkdraw_path_shift_fp \fp_new:N \l_hawkdraw_path_rotate_fp \clist_new:N \l_hawkdraw_path_scale_clist \clist_new:N \l_hawkdraw_path_slant_clist \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_a_fp \fp_new:N \l__hawkdraw_point_b_fp \fp_new:N \l__hawkdraw_point_c_fp \str_new:N \l_hawkdraw_scope_layer_str \cs_generate_variant:Nn \color_select:n { V } \cs_generate_variant:Nn \color_fill:n { V } \cs_generate_variant:Nn \color_stroke:n { V } \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_transform_shift:n { V } \cs_generate_variant:Nn \draw_transform_rotate:n { V } \cs_generate_variant:Nn \draw_transform_scale:n { e } \cs_generate_variant:Nn \draw_transform_xscale:n { e } \cs_generate_variant:Nn \draw_transform_yscale:n { e } \cs_generate_variant:Nn \draw_transform_xslant:n { e } \cs_generate_variant:Nn \draw_transform_yslant: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 .str_set:N = \l_hawkdraw_path_stroke_color_str , stroke ~ color .initial:n = { . } , fill ~ color .str_set:N = \l_hawkdraw_path_fill_color_str , 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 = { \str_set:Nn \l_hawkdraw_path_cap_str { butt } } , line ~ cap / round .code:n = { \str_set:Nn \l_hawkdraw_path_cap_str { round } } , line ~ cap / rectangle .code:n = { \str_set:Nn \l_hawkdraw_path_cap_str { rectangle } } , line ~ cap .initial:n = { butt } , line ~ join .choice: , line ~ join / miter .code:n = { \str_set:Nn \l_hawkdraw_path_join_str { miter } } , line ~ join / round .code:n = { \str_set:Nn \l_hawkdraw_path_join_str { round } } , line ~ join / bevel .code:n = { \str_set:Nn \l_hawkdraw_path_join_str { 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 = { \str_set:Nn \l_hawkdraw_path_fill_rule_str { evenodd } } , fill ~ rule / non-zero .code:n = { \str_set:Nn \l_hawkdraw_path_fill_rule_str { nonzero } } , fill ~ rule .initial:n = { non-zero } , shift .fp_set:N = \l_hawkdraw_path_shift_fp , shift .initial:n = { ( 0pt , 0pt ) } , rotate .fp_set:N = \l_hawkdraw_path_rotate_fp , rotate .initial:n = { 0 } , scale .clist_set:N = \l_hawkdraw_path_scale_clist , scale .initial:n = { 1 , 1 } , slant .clist_set:N = \l_hawkdraw_path_slant_clist , slant .initial:n = { 0 , 0 } , } \cs_new:Npn \__hawkdraw_fp_tuple_use_i:w ( #1 , #2 ) {#1} \cs_new:Npn \hawkdraw_fp_tuple_use_i:n #1 { \fp_to_dim:n { \exp_last_unbraced:Ne \__hawkdraw_fp_tuple_use_i:w { \fp_eval:n {#1} } } } \cs_generate_variant:Nn \hawkdraw_fp_tuple_use_i:n { V } \cs_new:Npn \__hawkdraw_fp_tuple_use_ii:w ( #1 , #2 ) {#2} \cs_new:Npn \hawkdraw_fp_tuple_use_ii:n #1 { \fp_to_dim:n { \exp_last_unbraced:Ne \__hawkdraw_fp_tuple_use_ii:w { \fp_eval:n {#1} } } } \cs_generate_variant:Nn \hawkdraw_fp_tuple_use_ii:n { V } \cs_new_protected:Npn \hawkdraw_set_path_options: { \color_stroke:V \l_hawkdraw_path_stroke_color_str \color_fill:V \l_hawkdraw_path_fill_color_str \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_str : } \use:c { draw_set_join_ \l_hawkdraw_path_join_str : } \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_str _rule: } \draw_set_dash_pattern:VV \l_hawkdraw_path_dash_pattern_clist \l_hawkdraw_path_dash_phase_dim \draw_transform_shift:V \l_hawkdraw_path_shift_fp \draw_transform_rotate:V \l_hawkdraw_path_rotate_fp \int_compare:nNnTF { \clist_count:N \l_hawkdraw_path_scale_clist } > { 1 } { \draw_transform_xscale:e { \clist_item:Nn \l_hawkdraw_path_scale_clist { 1 } } \draw_transform_yscale:e { \clist_item:Nn \l_hawkdraw_path_scale_clist { 2 } } } { \draw_transform_scale:e { \clist_item:Nn \l_hawkdraw_path_scale_clist { 1 } } } \int_compare:nNnTF { \clist_count:N \l_hawkdraw_path_slant_clist } > { 1 } { \draw_transform_xslant:e { \clist_item:Nn \l_hawkdraw_path_slant_clist { 1 } } \draw_transform_yslant:e { \clist_item:Nn \l_hawkdraw_path_slant_clist { 2 } } } { \draw_transform_xslant:e { \clist_item:Nn \l_hawkdraw_path_slant_clist { 1 } } \draw_transform_yslant:e { \clist_item:Nn \l_hawkdraw_path_slant_clist { 1 } } } } % === \scan_new:N \s__hawkdraw_stop \fp_new:N \l_hawkdraw_point_at_fp \tl_new:N \l_hawkdraw_point_name_tl \keys_define:nn { hawkdraw / path / point } { at .fp_set:N = \l_hawkdraw_point_at_fp , name .tl_set:N = \l_hawkdraw_point_name_tl , } \cs_new_protected:cpn { __hawkdraw_process_path_ p :w } point #1 [ #2 ] #3 \s__hawkdraw_stop { \fp_set_eq:NN \l_hawkdraw_point_at_fp \g_hawkdraw_path_last_point_fp \keys_set:nn { hawkdraw / path / point } {#2} \prop_gput:cVV { g__hawkdraw_points_ \int_use:N \g_hawkdraw_id_int _prop } \l_hawkdraw_point_name_tl \l_hawkdraw_point_at_fp \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } \cs_new_protected:Npn \hawkdraw_parse_point:Nn #1#2 { \prop_if_in:cnTF { g__hawkdraw_points_ \int_use:N \g_hawkdraw_id_int _prop } {#2} { \exp_args:NNe \fp_set:Nn #1 { \prop_item:cn { g__hawkdraw_points_ \int_use:N \g_hawkdraw_id_int _prop } {#2} } } { \tl_if_in:nnTF {#2} { > } { \fp_set:Nn #1 { \__hawkdraw_parse_point_polar:w #2 \s__hawkdraw_stop } } { \tl_if_in:nVTF {#2} \c_colon_str { \fp_set:Nn #1 { \__hawkdraw_parse_point_polar_compat:w #2 \s__hawkdraw_stop } } { \fp_set:Nn #1 {#2} } } } } \cs_new:Npn \__hawkdraw_parse_point_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_parse_point_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:Npn \hawkdraw_calculate_slope:nn #1#2 { \fp_eval:n { atand( \hawkdraw_fp_tuple_use_ii:n {#2} - \hawkdraw_fp_tuple_use_ii:n {#1} , \hawkdraw_fp_tuple_use_i:n {#2} - \hawkdraw_fp_tuple_use_i:n {#1} ) - 90 } } \cs_generate_variant:Nn \hawkdraw_calculate_slope:nn { nV , Vn , VV } % === \cs_new_protected:Npn \hawkdraw_path:w #1 ; { \draw_scope_begin: \tl_use:N \l_hawkdraw_path_preactions_tl \tl_trim_spaces_apply:nN {#1} \__hawkdraw_process_path:e \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 { \bool_set_true:N \l__hawkdraw_path_clip_bool \tl_trim_spaces_apply:nN {#1} \__hawkdraw_process_path:e \draw_path_use_clear:n { clip } \bool_set_false:N \l__hawkdraw_path_clip_bool \bool_gset_false:N \g__hawkdraw_path_use_clip_bool } } \cs_new_protected:Npn \__hawkdraw_process_path:n #1 { \cs_if_exist_use:cT { __hawkdraw_process_path_ \tl_head:n {#1} :w } { #1 \s__hawkdraw_stop } } \cs_generate_variant:Nn \__hawkdraw_process_path:n { e } \cs_new_protected:Npn \__hawkdraw_continue_path:n #1 { \tl_if_blank:nF {#1} { \__hawkdraw_process_path:n {#1} } } \cs_new_protected:cpn { __hawkdraw_process_path_ [ :w } [ #1 ] #2 \s__hawkdraw_stop { \bool_if:NF \l__hawkdraw_path_clip_bool { \keys_set:nn { hawkdraw / path } {#1} \hawkdraw_set_path_options: } \tl_trim_spaces_apply:nN {#2} \__hawkdraw_continue_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_ ( :w } ( #1 ) #2 \s__hawkdraw_stop { \hawkdraw_parse_point: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_continue_path:n } % === \cs_new:Npn \__hawkdraw_process_line_path:n #1 { \cs_if_exist_use:cT { __hawkdraw_process_path_line_ \tl_head:n {#1} :w } { #1 \s__hawkdraw_stop } } \cs_new_protected:cpn { __hawkdraw_process_path_ - :w } - #1 \s__hawkdraw_stop { \tl_trim_spaces_apply:nN {#1} \__hawkdraw_process_line_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_line_ - :w } - #1 ( #2 ) #3 \s__hawkdraw_stop { % what do we do with #1? \hawkdraw_parse_point: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 \g_hawkdraw_path_last_point_fp } } \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { \hawkdraw_calculate_slope:VV \g_hawkdraw_path_last_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 {#3} \__hawkdraw_continue_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_line_ | :w } | #1 ( #2 ) #3 \s__hawkdraw_stop { % what do we do with #1? \hawkdraw_parse_point:Nn \l__hawkdraw_point_a_fp {#2} \hawkdraw_parse_point:Nn \l__hawkdraw_point_b_fp { ( \hawkdraw_fp_tuple_use_i:V \l__hawkdraw_point_a_fp , \hawkdraw_fp_tuple_use_ii:V \g_hawkdraw_path_last_point_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 \g_hawkdraw_path_last_point_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_a_fp \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_ | :w } |- #1 ( #2 ) #3 \s__hawkdraw_stop { % what do we do with #1? \hawkdraw_parse_point:Nn \l__hawkdraw_point_a_fp {#2} \hawkdraw_parse_point:Nn \l__hawkdraw_point_b_fp { ( \hawkdraw_fp_tuple_use_i:V \g_hawkdraw_path_last_point_fp , \hawkdraw_fp_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 \g_hawkdraw_path_last_point_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_a_fp \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } % === \cs_new:Npn \__hawkdraw_process_curve_path:n #1 { \cs_if_exist_use:cT { __hawkdraw_process_path_curve_ \tl_head:n {#1} :w } { #1 \s__hawkdraw_stop } } \cs_new_protected:cpn { __hawkdraw_process_path_ . :w } .. #1 ( #2 ) #3 \s__hawkdraw_stop { % what do we do with #1? \hawkdraw_parse_point:Nn \l__hawkdraw_point_a_fp {#2} \tl_trim_spaces_apply:nN {#3} \__hawkdraw_process_curve_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_curve_ . :w } .. #1 ( #2 ) #3 \s__hawkdraw_stop { % what do we do with #1? \hawkdraw_parse_point: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 \g_hawkdraw_path_last_point_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_b_fp \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_curve_ & :w } & #1 ( #2 ) #3 .. #4 ( #5 ) #6 \s__hawkdraw_stop { % what do we do with #1, #3, #4? \hawkdraw_parse_point:Nn \l__hawkdraw_point_b_fp {#2} \hawkdraw_parse_point: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 \g_hawkdraw_path_last_point_fp } } \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { \hawkdraw_calculate_slope:VV \l__hawkdraw_point_b_fp \l__hawkdraw_point_c_fp } \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l__hawkdraw_point_c_fp \tl_trim_spaces_apply:nN {#6} \__hawkdraw_continue_path:n } \cs_new_protected:cpn { __hawkdraw_process_path_ ! :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_continue_path: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_process_path_ c :w } circle #1 [ #2 ] #3 \s__hawkdraw_stop { \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_continue_path: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 ~ a .code:n = { \fp_set:Nn \l_hawkdraw_path_ellipse_vector_a_fp { ( #1 , 0pt ) } } , radius ~ b .code:n = { \fp_set:Nn \l_hawkdraw_path_ellipse_vector_b_fp { ( 0pt , #1 ) } } , } \cs_new_protected:cpn { __hawkdraw_process_path_ e :w } ellipse #1 [ #2 ] #3 \s__hawkdraw_stop { \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_continue_path:n } \bool_new:N \l_hawkdraw_path_arc_axes_bool \dim_new:N \l_hawkdraw_path_arc_radius_a_dim \dim_new:N \l_hawkdraw_path_arc_radius_b_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 ~ a .code:n = { \bool_set_false:N \l_hawkdraw_path_arc_axes_bool \dim_set:Nn \l_hawkdraw_path_arc_radius_a_dim {#1} } , radius ~ a .initial:n = { 1cm } , radius ~ b .code:n = { \bool_set_false:N \l_hawkdraw_path_arc_axes_bool \dim_set:Nn \l_hawkdraw_path_arc_radius_b_dim {#1} } , radius ~ b .initial:n = { 1cm } , radius .code:n = { \bool_set_false:N \l_hawkdraw_path_arc_axes_bool \dim_set:Nn \l_hawkdraw_path_arc_radius_a_dim {#1} \dim_set:Nn \l_hawkdraw_path_arc_radius_b_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_process_path_ a :w } arc #1 [ #2 ] #3 \s__hawkdraw_stop { \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_a_dim \l_hawkdraw_path_arc_radius_b_dim \l_hawkdraw_path_arc_angle_start_fp \l_hawkdraw_path_arc_angle_end_fp } \bool_if:NTF \l_hawkdraw_path_arc_axes_bool { \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } { \fp_gset:Nn \g_hawkdraw_path_first_slope_fp { atand( \hawkdraw_fp_tuple_use_ii:V \l_hawkdraw_path_arc_vector_b_fp * cosd( \l_hawkdraw_path_arc_angle_start_fp ) - \hawkdraw_fp_tuple_use_ii:V \l_hawkdraw_path_arc_vector_a_fp * sind( \l_hawkdraw_path_arc_angle_start_fp ) , \hawkdraw_fp_tuple_use_i:V \l_hawkdraw_path_arc_vector_b_fp * cosd( \l_hawkdraw_path_arc_angle_start_fp ) - \hawkdraw_fp_tuple_use_i:V \l_hawkdraw_path_arc_vector_a_fp * sind( \l_hawkdraw_path_arc_angle_start_fp ) ) + 90 } } \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { atand( \hawkdraw_fp_tuple_use_ii:V \l_hawkdraw_path_arc_vector_b_fp * cosd( \l_hawkdraw_path_arc_angle_end_fp ) - \hawkdraw_fp_tuple_use_ii:V \l_hawkdraw_path_arc_vector_a_fp * sind( \l_hawkdraw_path_arc_angle_end_fp ) , \hawkdraw_fp_tuple_use_i:V \l_hawkdraw_path_arc_vector_b_fp * cosd( \l_hawkdraw_path_arc_angle_end_fp ) - \hawkdraw_fp_tuple_use_i:V \l_hawkdraw_path_arc_vector_a_fp * sind( \l_hawkdraw_path_arc_angle_end_fp ) ) - 90 } } { \fp_if_nan:nT { \g_hawkdraw_path_first_slope_fp } { \fp_gset:Nn \g_hawkdraw_path_first_slope_fp { atand( \l_hawkdraw_path_arc_radius_b_dim * ( - cosd( \l_hawkdraw_path_arc_angle_start_fp ) ) , \l_hawkdraw_path_arc_radius_a_dim * ( sind( \l_hawkdraw_path_arc_angle_start_fp ) ) ) - 90 } } \fp_gset:Nn \g_hawkdraw_path_last_slope_fp { atand( \l_hawkdraw_path_arc_radius_b_dim * ( - cosd( \l_hawkdraw_path_arc_angle_end_fp ) ) , \l_hawkdraw_path_arc_radius_a_dim * ( sind( \l_hawkdraw_path_arc_angle_end_fp ) ) ) + 90 } } \fp_set_eq:NN \l__hawkdraw_point_a_fp \g_hawkdraw_path_last_point_fp \bool_if:NTF \l_hawkdraw_path_arc_axes_bool { \group_begin: \draw_transform_triangle:nnn { \l__hawkdraw_point_a_fp } { \l_hawkdraw_path_arc_vector_a_fp } { \l_hawkdraw_path_arc_vector_b_fp } \fp_gset:Nn \g_hawkdraw_path_last_point_fp { \draw_point_transform:n { cosd( \l_hawkdraw_path_arc_angle_end_fp ) - cosd( \l_hawkdraw_path_arc_angle_start_fp ) , sind( \l_hawkdraw_path_arc_angle_end_fp ) - sind( \l_hawkdraw_path_arc_angle_start_fp ) } } \group_end: } { \fp_gset:Nn \g_hawkdraw_path_last_point_fp { ( \hawkdraw_fp_tuple_use_i:V \l__hawkdraw_point_a_fp + \l_hawkdraw_path_arc_radius_a_dim * ( cosd( \l_hawkdraw_path_arc_angle_end_fp ) - cosd( \l_hawkdraw_path_arc_angle_start_fp ) ) , \hawkdraw_fp_tuple_use_ii:V \l__hawkdraw_point_a_fp + \l_hawkdraw_path_arc_radius_b_dim * ( sind( \l_hawkdraw_path_arc_angle_end_fp ) - sind( \l_hawkdraw_path_arc_angle_start_fp ) ) ) } } \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path: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 .fp_set:N = \l_hawkdraw_path_rectangle_corner_fp , corner .initial:n = { ( 1cm , 1cm ) } , } \cs_new_protected:cpn { __hawkdraw_process_path_ r :w } rectangle #1 [ #2 ] #3 \s__hawkdraw_stop { \keys_set:nn { hawkdraw / path / rectangle } {#2} \fp_set_eq:NN \l__hawkdraw_point_a_fp \g_hawkdraw_path_last_point_fp \bool_if:NTF \l_hawkdraw_path_rectangle_relative_bool { \draw_path_rectangle:VV \l__hawkdraw_point_a_fp \l_hawkdraw_path_rectangle_corner_fp \fp_gset:Nn \g_hawkdraw_path_last_point_fp { \l__hawkdraw_point_a_fp + \l_hawkdraw_path_rectangle_corner_fp } } { \draw_path_rectangle_corners:VV \l__hawkdraw_point_a_fp \l_hawkdraw_path_rectangle_corner_fp \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l_hawkdraw_path_rectangle_corner_fp } \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } \clist_new:N \l_hawkdraw_path_grid_step_clist \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 } , corner .fp_set:N = \l_hawkdraw_path_grid_corner_fp , corner .initial:n = { ( 1cm , 1cm ) } , } \cs_new_protected:cpn { __hawkdraw_process_path_ g :w } grid #1 [ #2 ] #3 \s__hawkdraw_stop { \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 } } \g_hawkdraw_path_last_point_fp \l_hawkdraw_path_grid_corner_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 } } \g_hawkdraw_path_last_point_fp \l_hawkdraw_path_grid_corner_fp } \fp_gset_eq:NN \g_hawkdraw_path_last_point_fp \l_hawkdraw_path_grid_corner_fp \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } % === \int_new:N \l_hawkdraw_map_count_int \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 \cs_generate_variant:Nn \fp_step_function:nnnN { VVV } \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_process_path_ m :w } map #1 [ #2 ] #3#4 \s__hawkdraw_stop { \keys_set:nn { hawkdraw / path / map } {#2} \cs_set_protected:Npn \__hawkdraw_map_function:n ##1 { \tl_trim_spaces_apply:nN {#3} \__hawkdraw_continue_path:n } \int_compare:nNnTF { \clist_count:N \l_hawkdraw_map_list_clist } > { 0 } { \clist_map_function:NN \l_hawkdraw_map_list_clist \__hawkdraw_map_function:n } { \fp_step_function:VVVN \l_hawkdraw_map_start_fp \l_hawkdraw_map_step_fp \l_hawkdraw_map_end_fp \__hawkdraw_map_function:n } \tl_trim_spaces_apply:nN {#4} \__hawkdraw_continue_path:n } \cs_new:Npn \__hawkdraw_map_function:n #1 { } % === \keys_define:nn { hawkdraw / instance } { baseline .fp_set:N = \l_draw_instance_baseline_fp , layers .code:n = { \clist_map_inline:nn {#1} { \clist_if_in:NnF \l_draw_layers_clist {##1} { \draw_layer_new:n {##1} } } \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 .str_set:N = \l_hawkdraw_scope_layer_str , } % === \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 \prop_if_exist:cF { g__hawkdraw_points_ \int_use:N \g_hawkdraw_id_int _prop } { \prop_new_linked:c { g__hawkdraw_points_ \int_use:N \g_hawkdraw_id_int _prop } } \cs_set_eq:NN \path \hawkdraw_path:w \cs_set:Npn \stroke { \path [ stroke ] } \cs_set:Npn \fill { \path [ fill ] } \cs_set:Npn \clip { \path [ clip ] } \cs_set:Npn \node { \path ( 0pt , 0pt ) node } \cs_set:Npn \point { \path ( 0pt , 0pt ) point } \cs_set:Npn \map { \path ( 0pt , 0pt ) map } \NewExpandableDocumentCommand \firstpoint { } { \fp_use:N \g_hawkdraw_path_first_point_fp } \NewExpandableDocumentCommand \lastpoint { } { \fp_use:N \g_hawkdraw_path_last_point_fp } \NewExpandableDocumentCommand \firstslope { } { \fp_use:N \g_hawkdraw_path_first_slope_fp } \NewExpandableDocumentCommand \lastslope { } { \fp_use:N \g_hawkdraw_path_last_slope_fp } \fp_set:Nn \l__hawkdraw_point_a_fp { ( 0pt , 0pt ) } \fp_set:Nn \l__hawkdraw_point_b_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 \str_if_empty:NF \l_hawkdraw_scope_layer_str { \draw_layer_begin:V \l_hawkdraw_scope_layer_str } } { \str_if_empty:NF \l_hawkdraw_scope_layer_str { \draw_layer_end: } \draw_scope_end: } \clist_clear:N \l__hawkdraw_keys_unprocessed_clist \fp_set:Nn \l_draw_instance_baseline_fp { nan } \keys_set_known:nnN { hawkdraw / instance } {#1} \l__hawkdraw_keys_unprocessed_clist \keys_set:nV { hawkdraw / path } \l__hawkdraw_keys_unprocessed_clist \draw_begin: \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