File I
Implementation
1 l3draw implementation
1 ⟨∗package⟩
2 ⟨@@=draw⟩
3 \ProvidesExplPackage{l3draw}{2022-04-20}{}
4 {L3 Experimental core drawing support}
1.1 Internal auxiliaries
\s__draw_mark
\s__draw_stop
Internal scan marks.
5 \scan_new:N \s__draw_mark
6 \scan_new:N \s__draw_stop
(End definition for\s__draw_mark and\s__draw_stop.)
\q__draw_recursion_tail
\q__draw_recursion_stop
Internal recursion quarks.
7 \quark_new:N \q__draw_recursion_tail
8 \quark_new:N \q__draw_recursion_stop
(End definition for\q__draw_recursion_tail and\q__draw_recursion_stop.)
\__draw_if_recursion_tail_stop_do:Nn Functions to query recursion quarks.
9 \__kernel_quark_new_test:N \__draw_if_recursion_tail_stop_do:Nn (End definition for\__draw_if_recursion_tail_stop_do:Nn.)
Everything else is in the sub-files!
10 ⟨/package⟩
2 l3draw-boxes implementation
11 ⟨∗package⟩
12 ⟨@@=draw⟩
Inserting boxes requires us to “interrupt” the drawing state, so is closely linked to scoping. At the same time, there are a few additional features required to make text work in a flexible way.
\l__draw_tmp_box
13 \box_new:N \l__draw_tmp_box (End definition for\l__draw_tmp_box.)
\draw_box_use:N
\__draw_box_use:Nnnnn
Before inserting a box, we need to make sure that the bounding box is being updated correctly. As drawings track transformations as a whole, rather than as separate opera- tions, we do the insertion using an almost-raw matrix. The process is split into two so that coffins are also supported.
14 \cs_new_protected:Npn \draw_box_use:N #1
15 {
16 \__draw_box_use:Nnnnn #1
17 { 0pt } { -\box_dp:N #1 } { \box_wd:N #1 } { \box_ht:N #1 }
18 }
19 \cs_new_protected:Npn \__draw_box_use:Nnnnn #1#2#3#4#5
20 {
21 \bool_if:NT \l_draw_bb_update_bool
22 {
23 \__draw_point_process:nn
24 { \__draw_path_update_limits:nn }
25 { \draw_point_transform:n { #2 , #3 } }
26 \__draw_point_process:nn
27 { \__draw_path_update_limits:nn }
28 { \draw_point_transform:n { #4 , #3 } }
29 \__draw_point_process:nn
30 { \__draw_path_update_limits:nn }
31 { \draw_point_transform:n { #4 , #5 } }
32 \__draw_point_process:nn
33 { \__draw_path_update_limits:nn }
34 { \draw_point_transform:n { #2 , #5 } }
35 }
36 \group_begin:
37 \hbox_set:Nn \l__draw_tmp_box
38 {
39 \use:x
40 {
41 \__draw_backend_box_use:Nnnnn #1
42 { \fp_use:N \l__draw_matrix_a_fp }
43 { \fp_use:N \l__draw_matrix_b_fp }
44 { \fp_use:N \l__draw_matrix_c_fp }
45 { \fp_use:N \l__draw_matrix_d_fp }
46 }
47 }
48 \hbox_set:Nn \l__draw_tmp_box
49 {
50 \__kernel_kern:n { \l__draw_xshift_dim }
51 \box_move_up:nn { \l__draw_yshift_dim }
52 { \box_use_drop:N \l__draw_tmp_box }
53 }
54 \box_set_ht:Nn \l__draw_tmp_box { 0pt }
55 \box_set_dp:Nn \l__draw_tmp_box { 0pt }
56 \box_set_wd:Nn \l__draw_tmp_box { 0pt }
57 \box_use_drop:N \l__draw_tmp_box
58 \group_end:
59 }
(End definition for\draw_box_use:N and\__draw_box_use:Nnnnn. This function is documented on page
??.)
\draw_coffin_use:Nnn Slightly more than a shortcut: we have to allow for the fact that coffins have no apparent width before the reference point.
60 \cs_new_protected:Npn \draw_coffin_use:Nnn #1#2#3
61 {
62 \group_begin:
63 \hbox_set:Nn \l__draw_tmp_box
64 { \coffin_typeset:Nnnnn #1 {#2} {#3} { 0pt } { 0pt } }
65 \__draw_box_use:Nnnnn \l__draw_tmp_box
66 { \box_wd:N \l__draw_tmp_box - \coffin_wd:N #1 }
67 { -\box_dp:N \l__draw_tmp_box }
68 { \box_wd:N \l__draw_tmp_box }
69 { \box_ht:N \l__draw_tmp_box }
70 \group_end:
71 }
(End definition for\draw_coffin_use:Nnn. This function is documented on page??.)
72 ⟨/package⟩
3 l3draw-layers implementation
73 ⟨∗package⟩
74 ⟨@@=draw⟩
3.1 User interface
\draw_layer_new:n
75 \cs_new_protected:Npn \draw_layer_new:n #1
76 {
77 \str_if_eq:nnTF {#1} { main }
78 { \msg_error:nnn { draw } { main-reserved } }
79 {
80 \box_new:c { g__draw_layer_ #1 _box }
81 \box_new:c { l__draw_layer_ #1 _box }
82 }
83 }
(End definition for\draw_layer_new:n. This function is documented on page??.)
\l__draw_layer_tl The name of the current layer: we start off withmain.
84 \tl_new:N \l__draw_layer_tl
85 \tl_set:Nn \l__draw_layer_tl { main } (End definition for\l__draw_layer_tl.)
\l__draw_layer_close_bool Used to track if a layer needs to be closed.
86 \bool_new:N \l__draw_layer_close_bool (End definition for\l__draw_layer_close_bool.)
\l_draw_layers_clist
\g__draw_layers_clist
The list of layers to use starts off with just themain one.
87 \clist_new:N \l_draw_layers_clist
88 \clist_set:Nn \l_draw_layers_clist { main }
89 \clist_new:N \g__draw_layers_clist
(End definition for \l_draw_layers_clist and \g__draw_layers_clist. This variable is documented on page??.)
\draw_layer_begin:n
\draw_layer_end:
Layers may be called multiple times and have to work when nested. That drives a bit of grouping to get everything in order. Layers have to be zero width, so they get set as we go along.
90 \cs_new_protected:Npn \draw_layer_begin:n #1
91 {
92 \group_begin:
93 \box_if_exist:cTF { g__draw_layer_ #1 _box }
94 {
95 \str_if_eq:VnTF \l__draw_layer_tl {#1}
96 { \bool_set_false:N \l__draw_layer_close_bool }
97 {
98 \bool_set_true:N \l__draw_layer_close_bool
99 \tl_set:Nn \l__draw_layer_tl {#1}
100 \box_gset_wd:cn { g__draw_layer_ #1 _box } { 0pt }
101 \hbox_gset:cw { g__draw_layer_ #1 _box }
102 \box_use_drop:c { g__draw_layer_ #1 _box }
103 \group_begin:
104 }
105 \draw_linewidth:n { \l_draw_default_linewidth_dim }
106 }
107 {
108 \str_if_eq:nnTF {#1} { main }
109 { \msg_error:nnn { draw } { unknown-layer } {#1} }
110 { \msg_error:nnn { draw } { main-layer } }
111 }
112 }
113 \cs_new_protected:Npn \draw_layer_end:
114 {
115 \bool_if:NT \l__draw_layer_close_bool
116 {
117 \group_end:
118 \hbox_gset_end:
119 }
120 \group_end:
121 }
(End definition for\draw_layer_begin:n and\draw_layer_end:. These functions are documented on page??.)
3.2 Internal cross-links
\__draw_layers_insert: Themainlayer is special, otherwise just dump the layer box inside a scope.
122 \cs_new_protected:Npn \__draw_layers_insert:
123 {
124 \clist_map_inline:Nn \l_draw_layers_clist
125 {
126 \str_if_eq:nnTF {##1} { main }
127 {
128 \box_set_wd:Nn \l__draw_layer_main_box { 0pt }
129 \box_use_drop:N \l__draw_layer_main_box
130 }
131 {
132 \__draw_backend_scope_begin:
133 \box_gset_wd:cn { g__draw_layer_ ##1 _box } { 0pt }
134 \box_use_drop:c { g__draw_layer_ ##1 _box }
135 \__draw_backend_scope_end:
136 }
137 }
138 }
(End definition for\__draw_layers_insert:.)
\__draw_layers_save:
\__draw_layers_restore:
Simple save/restore functions.
139 \cs_new_protected:Npn \__draw_layers_save:
140 {
141 \clist_map_inline:Nn \l_draw_layers_clist
142 {
143 \str_if_eq:nnF {##1} { main }
144 {
145 \box_set_eq:cc { l__draw_layer_ ##1 _box }
146 { g__draw_layer_ ##1 _box }
147 }
148 }
149 }
150 \cs_new_protected:Npn \__draw_layers_restore:
151 {
152 \clist_map_inline:Nn \l_draw_layers_clist
153 {
154 \str_if_eq:nnF {##1} { main }
155 {
156 \box_gset_eq:cc { g__draw_layer_ ##1 _box }
157 { l__draw_layer_ ##1 _box }
158 }
159 }
160 }
(End definition for\__draw_layers_save: and\__draw_layers_restore:.)
161 \msg_new:nnnn { draw } { main-layer }
162 { Material~cannot~be~added~to~’main’~layer. }
163 { The~main~layer~may~only~be~accessed~at~the~top~level. }
164 \msg_new:nnn { draw } { main-reserved }
165 { The~’main’~layer~is~reserved. }
166 \msg_new:nnnn { draw } { unknown-layer }
167 { Layer~’#1’~has~not~been~created. }
168 { You~have~tried~to~use~layer~’#1’,~but~it~was~never~set~up. }
169 % \end{macrocode}
170 %
171 % \begin{macrocode}
172 ⟨/package⟩
4 l3draw-paths implementation
173 ⟨∗package⟩
174 ⟨@@=draw⟩
This sub-module covers more-or-less the same ideas aspgfcorepathconstruct.code.tex, though using the expandable FPU means that the implementation often varies. At present, equivalents of the following are currently absent:
• \pgfpatharcto,\pgfpatharctoprecomputed: These are extremely specialised and are very complex in implementation. If the functionality is required, it is likely that it will be set up from scratch here.
• \pgfpathparabola: Seems to be unused other than defining a TikZ interface, which itself is then not used further.
• \pgfpathsine,\pgfpathcosine: Need to see exactly how these need to work, in particular whether a wider input range is needed and what approximation to make.
• \pgfpathcurvebetweentime,\pgfpathcurvebetweentimecontinue: These don’t seem to be used at all.
\l__draw_path_tmp_tl
\l__draw_path_tmpa_fp
\l__draw_path_tmpb_fp
Scratch space.
175 \tl_new:N \l__draw_path_tmp_tl
176 \fp_new:N \l__draw_path_tmpa_fp
177 \fp_new:N \l__draw_path_tmpb_fp
(End definition for\l__draw_path_tmp_tl,\l__draw_path_tmpa_fp, and\l__draw_path_tmpb_fp.)
4.1 Tracking paths
\g__draw_path_lastx_dim
\g__draw_path_lasty_dim
The last point visited on a path.
178 \dim_new:N \g__draw_path_lastx_dim
179 \dim_new:N \g__draw_path_lasty_dim
(End definition for\g__draw_path_lastx_dim and\g__draw_path_lasty_dim.)
\g__draw_path_xmax_dim
\g__draw_path_xmin_dim
\g__draw_path_ymax_dim
\g__draw_path_ymin_dim
The limiting size of a path.
180 \dim_new:N \g__draw_path_xmax_dim
181 \dim_new:N \g__draw_path_xmin_dim
182 \dim_new:N \g__draw_path_ymax_dim
183 \dim_new:N \g__draw_path_ymin_dim
(End definition for\g__draw_path_xmax_dim and others.)
\__draw_path_update_limits:nn
\__draw_path_reset_limits:
Track the limits of a path and (perhaps) of the picture as a whole. (At present the latter is always true: that will change as more complex functionality is added.)
184 \cs_new_protected:Npn \__draw_path_update_limits:nn #1#2
185 {
186 \dim_gset:Nn \g__draw_path_xmax_dim
187 { \dim_max:nn \g__draw_path_xmax_dim {#1} }
188 \dim_gset:Nn \g__draw_path_xmin_dim
189 { \dim_min:nn \g__draw_path_xmin_dim {#1} }
190 \dim_gset:Nn \g__draw_path_ymax_dim
191 { \dim_max:nn \g__draw_path_ymax_dim {#2} }
192 \dim_gset:Nn \g__draw_path_ymin_dim
193 { \dim_min:nn \g__draw_path_ymin_dim {#2} }
194 \bool_if:NT \l_draw_bb_update_bool
195 {
196 \dim_gset:Nn \g__draw_xmax_dim
197 { \dim_max:nn \g__draw_xmax_dim {#1} }
198 \dim_gset:Nn \g__draw_xmin_dim
199 { \dim_min:nn \g__draw_xmin_dim {#1} }
200 \dim_gset:Nn \g__draw_ymax_dim
201 { \dim_max:nn \g__draw_ymax_dim {#2} }
202 \dim_gset:Nn \g__draw_ymin_dim
203 { \dim_min:nn \g__draw_ymin_dim {#2} }
204 }
205 }
206 \cs_new_protected:Npn \__draw_path_reset_limits:
207 {
208 \dim_gset:Nn \g__draw_path_xmax_dim { -\c_max_dim }
209 \dim_gset:Nn \g__draw_path_xmin_dim { \c_max_dim }
210 \dim_gset:Nn \g__draw_path_ymax_dim { -\c_max_dim }
211 \dim_gset:Nn \g__draw_path_ymin_dim { \c_max_dim }
212 }
(End definition for\__draw_path_update_limits:nn and\__draw_path_reset_limits:.)
\__draw_path_update_last:nn A simple auxiliary to avoid repetition.
213 \cs_new_protected:Npn \__draw_path_update_last:nn #1#2
214 {
215 \dim_gset:Nn \g__draw_path_lastx_dim {#1}
216 \dim_gset:Nn \g__draw_path_lasty_dim {#2}
217 }
(End definition for\__draw_path_update_last:nn.)
4.2 Corner arcs
At the level of pathconstruction, rounded corners are handled by inserting a marker into the path: that is then picked up once the full path is constructed. Thus we need to set up the appropriate data structures here, such that this can be applied every time it is relevant.
\l__draw_corner_xarc_dim
\l__draw_corner_yarc_dim
The two arcs in use.
218 \dim_new:N \l__draw_corner_xarc_dim
219 \dim_new:N \l__draw_corner_yarc_dim
(End definition for\l__draw_corner_xarc_dim and\l__draw_corner_yarc_dim.)
\l__draw_corner_arc_bool A flag to speed up the repeated checks.
220 \bool_new:N \l__draw_corner_arc_bool (End definition for\l__draw_corner_arc_bool.)
\draw_path_corner_arc:nn Calculate the arcs, check they are non-zero.
221 \cs_new_protected:Npn \draw_path_corner_arc:nn #1#2
222 {
223 \dim_set:Nn \l__draw_corner_xarc_dim {#1}
224 \dim_set:Nn \l__draw_corner_yarc_dim {#2}
225 \bool_lazy_and:nnTF
226 { \dim_compare_p:nNn \l__draw_corner_xarc_dim = { 0pt } }
227 { \dim_compare_p:nNn \l__draw_corner_yarc_dim = { 0pt } }
228 { \bool_set_false:N \l__draw_corner_arc_bool }
229 { \bool_set_true:N \l__draw_corner_arc_bool }
230 }
(End definition for\draw_path_corner_arc:nn. This function is documented on page??.)
\__draw_path_mark_corner: Mark up corners for arc post-processing.
231 \cs_new_protected:Npn \__draw_path_mark_corner:
232 {
233 \bool_if:NT \l__draw_corner_arc_bool
234 {
235 \__draw_softpath_roundpoint:VV
236 \l__draw_corner_xarc_dim
237 \l__draw_corner_yarc_dim
238 }
239 }
(End definition for\__draw_path_mark_corner:.)
4.3 Basic path constructions
\draw_path_moveto:n
\draw_path_lineto:n
\__draw_path_moveto:nn
\__draw_path_lineto:nn
\draw_path_curveto:nnn
\__draw_path_curveto:nnnnnn
At present, stick to purely linear transformation support and skip the soft path business:
that will likely need to be revisited later.
240 \cs_new_protected:Npn \draw_path_moveto:n #1
241 {
242 \__draw_point_process:nn
243 { \__draw_path_moveto:nn }
244 { \draw_point_transform:n {#1} }
245 }
246 \cs_new_protected:Npn \__draw_path_moveto:nn #1#2
247 {
248 \__draw_path_update_limits:nn {#1} {#2}
249 \__draw_softpath_moveto:nn {#1} {#2}
250 \__draw_path_update_last:nn {#1} {#2}
251 }
252 \cs_new_protected:Npn \draw_path_lineto:n #1
253 {
254 \__draw_point_process:nn
255 { \__draw_path_lineto:nn }
256 { \draw_point_transform:n {#1} }
257 }
258 \cs_new_protected:Npn \__draw_path_lineto:nn #1#2
259 {
260 \__draw_path_mark_corner:
261 \__draw_path_update_limits:nn {#1} {#2}
262 \__draw_softpath_lineto:nn {#1} {#2}
263 \__draw_path_update_last:nn {#1} {#2}
264 }
265 \cs_new_protected:Npn \draw_path_curveto:nnn #1#2#3
266 {
267 \__draw_point_process:nnnn
268 {
269 \__draw_path_mark_corner:
270 \__draw_path_curveto:nnnnnn
271 }
272 { \draw_point_transform:n {#1} }
273 { \draw_point_transform:n {#2} }
274 { \draw_point_transform:n {#3} }
275 }
276 \cs_new_protected:Npn \__draw_path_curveto:nnnnnn #1#2#3#4#5#6
277 {
278 \__draw_path_update_limits:nn {#1} {#2}
279 \__draw_path_update_limits:nn {#3} {#4}
280 \__draw_path_update_limits:nn {#5} {#6}
281 \__draw_softpath_curveto:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
282 \__draw_path_update_last:nn {#5} {#6}
283 }
(End definition for\draw_path_moveto:n and others. These functions are documented on page??.)
\draw_path_close: A simple wrapper.
284 \cs_new_protected:Npn \draw_path_close:
285 {
286 \__draw_path_mark_corner:
287 \__draw_softpath_closepath:
288 }
(End definition for\draw_path_close:. This function is documented on page??.)
4.4 Canvas path constructions
\draw_path_canvas_moveto:n
\draw_path_canvas_lineto:n
\draw_path_canvas_curveto:nnn
Operations with no application of the transformation matrix.
289 \cs_new_protected:Npn \draw_path_canvas_moveto:n #1
290 { \__draw_point_process:nn { \__draw_path_moveto:nn } {#1} }
291 \cs_new_protected:Npn \draw_path_canvas_lineto:n #1
292 { \__draw_point_process:nn { \__draw_path_lineto:nn } {#1} }
293 \cs_new_protected:Npn \draw_path_canvas_curveto:nnn #1#2#3
294 {
295 \__draw_point_process:nnnn
296 {
297 \__draw_path_mark_corner:
298 \__draw_path_curveto:nnnnnn
299 }
300 {#1} {#2} {#3}
301 }
(End definition for \draw_path_canvas_moveto:n, \draw_path_canvas_lineto:n, and \draw_path_- canvas_curveto:nnn. These functions are documented on page??.)
4.5 Computed curves
More complex operations need some calculations. To assist with those, various constants are pre-defined.
\draw_path_curveto:nn
\__draw_path_curveto:nnnn
\c__draw_path_curveto_a_fp
\c__draw_path_curveto_b_fp
A quadratic curve with one control point (xc, yc). The two required control points are then
x1= 1 3xs+2
3xc y1= 1 3ys+2
3yc and
x2= 1 3xe+2
3xc x2=1 3ye+2
3yc
using the start (last) point (xs, ys) and the end point (xs, ys).
302 \cs_new_protected:Npn \draw_path_curveto:nn #1#2
303 {
304 \__draw_point_process:nnn
305 { \__draw_path_curveto:nnnn }
306 { \draw_point_transform:n {#1} }
307 { \draw_point_transform:n {#2} }
308 }
309 \cs_new_protected:Npn \__draw_path_curveto:nnnn #1#2#3#4
310 {
311 \fp_set:Nn \l__draw_path_tmpa_fp { \c__draw_path_curveto_b_fp * #1 }
312 \fp_set:Nn \l__draw_path_tmpb_fp { \c__draw_path_curveto_b_fp * #2 }
313 \use:x
314 {
315 \__draw_path_mark_corner:
316 \__draw_path_curveto:nnnnnn
317 {
318 \fp_to_dim:n
319 {
320 \c__draw_path_curveto_a_fp * \g__draw_path_lastx_dim
321 + \l__draw_path_tmpa_fp
322 }
323 }
324 {
325 \fp_to_dim:n
326 {
327 \c__draw_path_curveto_a_fp * \g__draw_path_lasty_dim
328 + \l__draw_path_tmpb_fp
329 }
330 }
331 {
332 \fp_to_dim:n
333 { \c__draw_path_curveto_a_fp * #3 + \l__draw_path_tmpa_fp }
334 }
335 {
336 \fp_to_dim:n
337 { \c__draw_path_curveto_a_fp * #4 + \l__draw_path_tmpb_fp }
338 }
339 {#3}
340 {#4}
341 }
342 }
343 \fp_const:Nn \c__draw_path_curveto_a_fp { 1 / 3 }
344 \fp_const:Nn \c__draw_path_curveto_b_fp { 2 / 3 }
(End definition for\draw_path_curveto:nn and others. This function is documented on page??.)
\draw_path_arc:nnn
\draw_path_arc:nnnn
\__draw_path_arc:nnnn
\__draw_path_arc:nnNnn
\__draw_path_arc_auxi:nnnnNnn
\__draw_path_arc_auxi:fnnnNnn
\__draw_path_arc_auxi:fnfnNnn
\__draw_path_arc_auxii:nnnNnnnn
\__draw_path_arc_auxiii:nn
\__draw_path_arc_auxiv:nnnn
\__draw_path_arc_auxv:nn
\__draw_path_arc_auxvi:nn
\__draw_path_arc_add:nnnn
\l__draw_path_arc_delta_fp
\l__draw_path_arc_start_fp
\c__draw_path_arc_90_fp
Drawing an arc means dividing the total curve required into sections: using Bézier curves we can cover at most 90◦at once. To allow for later manipulations, we aim to have roughly equal last segments to the line, with the split set at a final part of 115◦.
345 \cs_new_protected:Npn \draw_path_arc:nnn #1#2#3
346 { \draw_path_arc:nnnn {#1} {#2} {#3} {#3} }
347 \cs_new_protected:Npn \draw_path_arc:nnnn #1#2#3#4
348 {
349 \use:x
10
350 {
351 \__draw_path_arc:nnnn
352 { \fp_eval:n {#1} }
353 { \fp_eval:n {#2} }
354 { \fp_to_dim:n {#3} }
355 { \fp_to_dim:n {#4} }
356 }
357 }
358 \cs_new_protected:Npn \__draw_path_arc:nnnn #1#2#3#4
359 {
360 \fp_compare:nNnTF {#1} > {#2}
361 { \__draw_path_arc:nnNnn {#1} {#2} - {#3} {#4} }
362 { \__draw_path_arc:nnNnn {#1} {#2} + {#3} {#4} }
363 }
364 \cs_new_protected:Npn \__draw_path_arc:nnNnn #1#2#3#4#5
365 {
366 \fp_set:Nn \l__draw_path_arc_start_fp {#1}
367 \fp_set:Nn \l__draw_path_arc_delta_fp { abs( #1 - #2 ) }
368 \fp_while_do:nNnn { \l__draw_path_arc_delta_fp } > { 90 }
369 {
370 \fp_compare:nNnTF \l__draw_path_arc_delta_fp > { 115 }
371 {
372 \__draw_path_arc_auxi:ffnnNnn
373 { \fp_to_decimal:N \l__draw_path_arc_start_fp }
374 { \fp_eval:n { \l__draw_path_arc_start_fp #3 90 } }
375 { 90 } {#2}
376 #3 {#4} {#5}
377 }
378 {
379 \__draw_path_arc_auxi:ffnnNnn
380 { \fp_to_decimal:N \l__draw_path_arc_start_fp }
381 { \fp_eval:n { \l__draw_path_arc_start_fp #3 60 } }
382 { 60 } {#2}
383 #3 {#4} {#5}
384 }
385 }
386 \__draw_path_mark_corner:
387 \__draw_path_arc_auxi:fnfnNnn
388 { \fp_to_decimal:N \l__draw_path_arc_start_fp }
389 {#2}
390 { \fp_eval:n { abs( \l__draw_path_arc_start_fp - #2 ) } }
391 {#2}
392 #3 {#4} {#5}
393 }
The auxiliary is responsible for calculating the required points. The “magic” number required to determine the length of the control vectors is well-established for a right- angle: 43(√
2−1) = 0.552 284 75. For other cases, we follow the calculation used by pgf but with the second common case of 60◦ pre-calculated for speed.
394 \cs_new_protected:Npn \__draw_path_arc_auxi:nnnnNnn #1#2#3#4#5#6#7
395 {
396 \use:x
397 {
398 \__draw_path_arc_auxii:nnnNnnnn
399 {#1} {#2} {#4} #5 {#6} {#7}
400 {
401 \fp_to_dim:n
402 {
403 \cs_if_exist_use:cF
404 { c__draw_path_arc_ #3 _fp }
405 { 4/3 * tand( 0.25 * #3 ) }
406 * #6
407 }
408 }
409 {
410 \fp_to_dim:n
411 {
412 \cs_if_exist_use:cF
413 { c__draw_path_arc_ #3 _fp }
414 { 4/3 * tand( 0.25 * #3 ) }
415 * #7
416 }
417 }
418 }
419 }
420 \cs_generate_variant:Nn \__draw_path_arc_auxi:nnnnNnn { fnf , ff }
We can now calculate the required points. As everything here is non-expandable, that is best done by using x-type expansion to build up the tokens. The three points are calculated out-of-order, since finding the second control point needs the position of the end point. Once the points are found, fire-off the fundamental path operation and update the record of where we are up to. The final point has to be
421 \cs_new_protected:Npn \__draw_path_arc_auxii:nnnNnnnn #1#2#3#4#5#6#7#8
422 {
423 \tl_clear:N \l__draw_path_tmp_tl
424 \__draw_point_process:nn
425 { \__draw_path_arc_auxiii:nn }
426 {
427 \__draw_point_transform_noshift:n
428 { \draw_point_polar:nnn {#7} {#8} { #1 #4 90 } }
429 }
430 \__draw_point_process:nnn
431 { \__draw_path_arc_auxiv:nnnn }
432 {
433 \draw_point_transform:n
434 { \draw_point_polar:nnn {#5} {#6} {#1} }
435 }
436 {
437 \draw_point_transform:n
438 { \draw_point_polar:nnn {#5} {#6} {#2} }
439 }
440 \__draw_point_process:nn
441 { \__draw_path_arc_auxv:nn }
442 {
443 \__draw_point_transform_noshift:n
444 { \draw_point_polar:nnn {#7} {#8} { #2 #4 -90 } }
445 }
446 \exp_after:wN \__draw_path_curveto:nnnnnn \l__draw_path_tmp_tl
447 \fp_set:Nn \l__draw_path_arc_delta_fp { abs ( #2 - #3 ) }
448 \fp_set:Nn \l__draw_path_arc_start_fp {#2}
449 }
The first control point.
450 \cs_new_protected:Npn \__draw_path_arc_auxiii:nn #1#2
451 {
452 \__draw_path_arc_aux_add:nn
453 { \g__draw_path_lastx_dim + #1 }
454 { \g__draw_path_lasty_dim + #2 }
455 }
The end point: simple arithmetic.
456 \cs_new_protected:Npn \__draw_path_arc_auxiv:nnnn #1#2#3#4
457 {
458 \__draw_path_arc_aux_add:nn
459 { \g__draw_path_lastx_dim - #1 + #3 }
460 { \g__draw_path_lasty_dim - #2 + #4 }
461 }
The second control point: extract the last point, do some rearrangement and record.
462 \cs_new_protected:Npn \__draw_path_arc_auxv:nn #1#2
463 {
464 \exp_after:wN \__draw_path_arc_auxvi:nn
465 \l__draw_path_tmp_tl {#1} {#2}
466 }
467 \cs_new_protected:Npn \__draw_path_arc_auxvi:nn #1#2#3#4#5#6
468 {
469 \tl_set:Nn \l__draw_path_tmp_tl { {#1} {#2} }
470 \__draw_path_arc_aux_add:nn
471 { #5 + #3 }
472 { #6 + #4 }
473 \tl_put_right:Nn \l__draw_path_tmp_tl { {#3} {#4} }
474 }
475 \cs_new_protected:Npn \__draw_path_arc_aux_add:nn #1#2
476 {
477 \tl_put_right:Nx \l__draw_path_tmp_tl
478 { { \fp_to_dim:n {#1} } { \fp_to_dim:n {#2} } }
479 }
480 \fp_new:N \l__draw_path_arc_delta_fp
481 \fp_new:N \l__draw_path_arc_start_fp
482 \fp_const:cn { c__draw_path_arc_90_fp } { 4/3 * (sqrt(2) - 1) }
483 \fp_const:cn { c__draw_path_arc_60_fp } { 4/3 * tand(15) }
(End definition for\draw_path_arc:nnn and others. These functions are documented on page??.)
\draw_path_arc_axes:nnnn A simple wrapper.
484 \cs_new_protected:Npn \draw_path_arc_axes:nnnn #1#2#3#4
485 {
486 \draw_transform_triangle:nnn { 0cm , 0cm } {#3} {#4}
487 \draw_path_arc:nnn {#1} {#2} { 1pt }
488 }
(End definition for\draw_path_arc_axes:nnnn. This function is documented on page??.)
\draw_path_ellipse:nnn
\__draw_path_ellipse:nnnnnn
\__draw_path_ellipse_arci:nnnnnn
\__draw_path_ellipse_arcii:nnnnnn
\__draw_path_ellipse_arciii:nnnnnn
\__draw_path_ellipse_arciv:nnnnnn
\c__draw_path_ellipse_fp
Drawing an ellipse is an optimised version of drawing an arc, in particular reusing the same constant. We need to deal with the ellipse in four parts and also deal with moving to the right place, closing it and ending up back at the center. That is handled on a per-arc basis, each in a separate auxiliary for readability.
489 \cs_new_protected:Npn \draw_path_ellipse:nnn #1#2#3
490 {
491 \__draw_point_process:nnnn
492 { \__draw_path_ellipse:nnnnnn }
493 { \draw_point_transform:n {#1} }
494 { \__draw_point_transform_noshift:n {#2} }
495 { \__draw_point_transform_noshift:n {#3} }
496 }
497 \cs_new_protected:Npn \__draw_path_ellipse:nnnnnn #1#2#3#4#5#6
498 {
499 \use:x
500 {
501 \__draw_path_moveto:nn
502 { \fp_to_dim:n { #1 + #3 } } { \fp_to_dim:n { #2 + #4 } }
503 \__draw_path_ellipse_arci:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
504 \__draw_path_ellipse_arcii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
505 \__draw_path_ellipse_arciii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
506 \__draw_path_ellipse_arciv:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
507 }
508 \__draw_softpath_closepath:
509 \__draw_path_moveto:nn {#1} {#2}
510 }
511 \cs_new:Npn \__draw_path_ellipse_arci:nnnnnn #1#2#3#4#5#6
512 {
513 \__draw_path_curveto:nnnnnn
514 { \fp_to_dim:n { #1 + #3 + #5 * \c__draw_path_ellipse_fp } }
515 { \fp_to_dim:n { #2 + #4 + #6 * \c__draw_path_ellipse_fp } }
516 { \fp_to_dim:n { #1 + #3 * \c__draw_path_ellipse_fp + #5 } }
517 { \fp_to_dim:n { #2 + #4 * \c__draw_path_ellipse_fp + #6 } }
518 { \fp_to_dim:n { #1 + #5 } }
519 { \fp_to_dim:n { #2 + #6 } }
520 }
521 \cs_new:Npn \__draw_path_ellipse_arcii:nnnnnn #1#2#3#4#5#6
522 {
523 \__draw_path_curveto:nnnnnn
524 { \fp_to_dim:n { #1 - #3 * \c__draw_path_ellipse_fp + #5 } }
525 { \fp_to_dim:n { #2 - #4 * \c__draw_path_ellipse_fp + #6 } }
526 { \fp_to_dim:n { #1 - #3 + #5 * \c__draw_path_ellipse_fp } }
527 { \fp_to_dim:n { #2 - #4 + #6 * \c__draw_path_ellipse_fp } }
528 { \fp_to_dim:n { #1 - #3 } }
529 { \fp_to_dim:n { #2 - #4 } }
530 }
531 \cs_new:Npn \__draw_path_ellipse_arciii:nnnnnn #1#2#3#4#5#6
532 {
533 \__draw_path_curveto:nnnnnn
534 { \fp_to_dim:n { #1 - #3 - #5 * \c__draw_path_ellipse_fp } }
535 { \fp_to_dim:n { #2 - #4 - #6 * \c__draw_path_ellipse_fp } }
536 { \fp_to_dim:n { #1 - #3 * \c__draw_path_ellipse_fp - #5 } }
537 { \fp_to_dim:n { #2 - #4 * \c__draw_path_ellipse_fp - #6 } }
538 { \fp_to_dim:n { #1 - #5 } }
539 { \fp_to_dim:n { #2 - #6 } }
540 }
541 \cs_new:Npn \__draw_path_ellipse_arciv:nnnnnn #1#2#3#4#5#6
542 {
543 \__draw_path_curveto:nnnnnn
544 { \fp_to_dim:n { #1 + #3 * \c__draw_path_ellipse_fp - #5 } }
545 { \fp_to_dim:n { #2 + #4 * \c__draw_path_ellipse_fp - #6 } }
546 { \fp_to_dim:n { #1 + #3 - #5 * \c__draw_path_ellipse_fp } }
547 { \fp_to_dim:n { #2 + #4 - #6 * \c__draw_path_ellipse_fp } }
548 { \fp_to_dim:n { #1 + #3 } }
549 { \fp_to_dim:n { #2 + #4 } }
550 }
551 \fp_const:Nn \c__draw_path_ellipse_fp { \fp_use:c { c__draw_path_arc_90_fp } } (End definition for\draw_path_ellipse:nnn and others. This function is documented on page??.)
\draw_path_circle:nn A shortcut.
552 \cs_new_protected:Npn \draw_path_circle:nn #1#2
553 { \draw_path_ellipse:nnn {#1} { #2 , 0pt } { 0pt , #2 } } (End definition for\draw_path_circle:nn. This function is documented on page??.)
4.6 Rectangles
\draw_path_rectangle:nn
\__draw_path_rectangle:nnnn
\__draw_path_rectangle_rounded:nnnn
Building a rectangle can be a single operation, or for rounded versions will involve step- by-step construction.
554 \cs_new_protected:Npn \draw_path_rectangle:nn #1#2
555 {
556 \__draw_point_process:nnn
557 {
558 \bool_lazy_or:nnTF
559 { \l__draw_corner_arc_bool }
560 { \l__draw_matrix_active_bool }
561 { \__draw_path_rectangle_rounded:nnnn }
562 { \__draw_path_rectangle:nnnn }
563 }
564 { \draw_point_transform:n {#1} }
565 {#2}
566 }
567 \cs_new_protected:Npn \__draw_path_rectangle:nnnn #1#2#3#4
568 {
569 \__draw_path_update_limits:nn {#1} {#2}
570 \__draw_path_update_limits:nn { #1 + #3 } { #2 + #4 }
571 \__draw_softpath_rectangle:nnnn {#1} {#2} {#3} {#4}
572 \__draw_path_update_last:nn {#1} {#2}
573 }
574 \cs_new_protected:Npn \__draw_path_rectangle_rounded:nnnn #1#2#3#4
575 {
576 \draw_path_moveto:n { #1 + #3 , #2 + #4 }
577 \draw_path_lineto:n { #1 , #2 + #4 }
578 \draw_path_lineto:n { #1 , #2 }
579 \draw_path_lineto:n { #1 + #3 , #2 }
580 \draw_path_close:
581 \draw_path_moveto:n { #1 , #2 }
582 }
(End definition for \draw_path_rectangle:nn, \__draw_path_rectangle:nnnn, and \__draw_path_- rectangle_rounded:nnnn. This function is documented on page??.)
\draw_path_rectangle_corners:nn
\__draw_path_rectangle_corners:nnnn
Another shortcut wrapper.
583 \cs_new_protected:Npn \draw_path_rectangle_corners:nn #1#2
584 {
585 \__draw_point_process:nnn
586 { \__draw_path_rectangle_corners:nnnnn {#1} }
587 {#1} {#2}
588 }
589 \cs_new_protected:Npn \__draw_path_rectangle_corners:nnnnn #1#2#3#4#5
590 { \draw_path_rectangle:nn {#1} { #4 - #2 , #5 - #3 } }
(End definition for \draw_path_rectangle_corners:nn and \__draw_path_rectangle_corners:nnnn.
This function is documented on page??.)
4.7 Grids
\draw_path_grid:nnnn
\__draw_path_grid_auxi:nnnnnn
\__draw_path_grid_auxi:ffnnnn
\__draw_path_grid_auxii:nnnnnn
\__draw_path_grid_auxiii:nnnnnn
\__draw_path_grid_auxiiii:ffnnnn
\__draw_path_grid_auxiv:nnnnnnnn
\__draw_path_grid_auxiv:ffnnnnnn
The main complexity here is lining up the grid correctly. To keep it simple, we tidy up the argument ordering first.
591 \cs_new_protected:Npn \draw_path_grid:nnnn #1#2#3#4
592 {
593 \__draw_point_process:nnn
594 {
595 \__draw_path_grid_auxi:ffnnnn
596 { \dim_eval:n { \dim_abs:n {#1} } }
597 { \dim_eval:n { \dim_abs:n {#2} } }
598 }
599 {#3} {#4}
600 }
601 \cs_new_protected:Npn \__draw_path_grid_auxi:nnnnnn #1#2#3#4#5#6
602 {
603 \dim_compare:nNnTF {#3} > {#5}
604 { \__draw_path_grid_auxii:nnnnnn {#1} {#2} {#5} {#4} {#3} {#6} }
605 { \__draw_path_grid_auxii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6} }
606 }
607 \cs_generate_variant:Nn \__draw_path_grid_auxi:nnnnnn { ff }
608 \cs_new_protected:Npn \__draw_path_grid_auxii:nnnnnn #1#2#3#4#5#6
609 {
610 \dim_compare:nNnTF {#4} > {#6}
611 { \__draw_path_grid_auxiii:nnnnnn {#1} {#2} {#3} {#6} {#5} {#4} }
612 { \__draw_path_grid_auxiii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6} }
613 }
614 \cs_new_protected:Npn \__draw_path_grid_auxiii:nnnnnn #1#2#3#4#5#6
615 {
616 \__draw_path_grid_auxiv:ffnnnnnn
617 { \fp_to_dim:n { #1 * trunc(#3/(#1)) } }
618 { \fp_to_dim:n { #2 * trunc(#4/(#2)) } }
619 {#1} {#2} {#3} {#4} {#5} {#6}
620 }
621 \cs_new_protected:Npn \__draw_path_grid_auxiv:nnnnnnnn #1#2#3#4#5#6#7#8
622 {
623 \dim_step_inline:nnnn
624 {#1}
625 {#3}
626 {#7}
627 {
628 \draw_path_moveto:n { ##1 , #6 }
629 \draw_path_lineto:n { ##1 , #8 }
630 }
631 \dim_step_inline:nnnn
632 {#2}
633 {#4}
634 {#8}
635 {
636 \draw_path_moveto:n { #5 , ##1 }
637 \draw_path_lineto:n { #7 , ##1 }
638 }
639 }
640 \cs_generate_variant:Nn \__draw_path_grid_auxiv:nnnnnnnn { ff }
(End definition for\draw_path_grid:nnnn and others. This function is documented on page??.)
4.8 Using paths
\l__draw_path_use_clip_bool
\l__draw_path_use_fill_bool
\l__draw_path_use_stroke_bool
Actions to pass to the driver.
641 \bool_new:N \l__draw_path_use_clip_bool
642 \bool_new:N \l__draw_path_use_fill_bool
643 \bool_new:N \l__draw_path_use_stroke_bool
(End definition for \l__draw_path_use_clip_bool, \l__draw_path_use_fill_bool, and \l__draw_- path_use_stroke_bool.)
\l__draw_path_use_bb_bool
\l__draw_path_use_clear_bool
Actions handled at the macro layer.
644 \bool_new:N \l__draw_path_use_bb_bool
645 \bool_new:N \l__draw_path_use_clear_bool
(End definition for\l__draw_path_use_bb_bool and\l__draw_path_use_clear_bool.)
\draw_path_use:n
\draw_path_use_clear:n
\__draw_path_use:n
\__draw_path_use_action_draw:
\__draw_path_use_action_fillstroke:
\__draw_path_use_stroke_bb:
\__draw_path_use_stroke_bb_aux:NnN
There are a range of actions which can apply to a path: they are handled in a single function which can carry out several of them. The first step is to deal with the special case of clearing the path.
646 \cs_new_protected:Npn \draw_path_use:n #1
647 {
648 \tl_if_blank:nF {#1}
649 { \__draw_path_use:n {#1} }
650 }
651 \cs_new_protected:Npn \draw_path_use_clear:n #1
652 {
653 \bool_lazy_or:nnTF
654 { \tl_if_blank_p:n {#1} }
655 { \str_if_eq_p:nn {#1} { clear } }
656 {
657 \__draw_softpath_clear:
658 \__draw_path_reset_limits:
659 }
660 { \__draw_path_use:n { #1 , clear } }
661 }
Map over the actions and set up the data: mainly just booleans, but with the possibility to cover more complex cases. The business end of the function is a series of checks on the various flags, then taking the appropriate action(s).
662 \cs_new_protected:Npn \__draw_path_use:n #1
663 {
664 \bool_set_false:N \l__draw_path_use_clip_bool
665 \bool_set_false:N \l__draw_path_use_fill_bool
666 \bool_set_false:N \l__draw_path_use_stroke_bool
667 \clist_map_inline:nn {#1}
668 {
669 \cs_if_exist:cTF { l__draw_path_use_ ##1 _ bool }
670 { \bool_set_true:c { l__draw_path_use_ ##1 _ bool } }
671 {
672 \cs_if_exist_use:cF { __draw_path_use_action_ ##1 : }
673 { \msg_error:nnn { draw } { invalid-path-action } {##1} }
674 }
675 }
676 \__draw_softpath_round_corners:
677 \bool_lazy_and:nnT
678 { \l_draw_bb_update_bool }
679 { \l__draw_path_use_stroke_bool }
680 { \__draw_path_use_stroke_bb: }
681 \__draw_softpath_use:
682 \bool_if:NT \l__draw_path_use_clip_bool
683 {
684 \__draw_backend_clip:
685 \bool_set_false:N \l_draw_bb_update_bool
686 \bool_lazy_or:nnF
687 { \l__draw_path_use_fill_bool }
688 { \l__draw_path_use_stroke_bool }
689 { \__draw_backend_discardpath: }
690 }
691 \bool_lazy_or:nnT
692 { \l__draw_path_use_fill_bool }
693 { \l__draw_path_use_stroke_bool }
694 {
695 \use:c
696 {
697 __draw_backend_
698 \bool_if:NT \l__draw_path_use_fill_bool { fill }
699 \bool_if:NT \l__draw_path_use_stroke_bool { stroke }
700 :
701 }
702 }
703 \bool_if:NT \l__draw_path_use_clear_bool
704 { \__draw_softpath_clear: }
705 }
706 \cs_new_protected:Npn \__draw_path_use_action_draw:
707 {
708 \bool_set_true:N \l__draw_path_use_stroke_bool
709 }
710 \cs_new_protected:Npn \__draw_path_use_action_fillstroke:
711 {
712 \bool_set_true:N \l__draw_path_use_fill_bool
713 \bool_set_true:N \l__draw_path_use_stroke_bool
714 }
Where the path is relevant to size and is stroked, we need to allow for the part which overlaps the edge of the bounding box.
715 \cs_new_protected:Npn \__draw_path_use_stroke_bb:
716 {
717 \__draw_path_use_stroke_bb_aux:NnN x { max } +
718 \__draw_path_use_stroke_bb_aux:NnN y { max } +
719 \__draw_path_use_stroke_bb_aux:NnN x { min } -
720 \__draw_path_use_stroke_bb_aux:NnN y { min } -
721 }
722 \cs_new_protected:Npn \__draw_path_use_stroke_bb_aux:NnN #1#2#3
723 {
724 \dim_compare:nNnF { \dim_use:c { g__draw_ #1#2 _dim } } = { #3 -\c_max_dim }
725 {
726 \dim_gset:cn { g__draw_ #1#2 _dim }
727 {
728 \use:c { dim_ #2 :nn }
729 { \dim_use:c { g__draw_ #1#2 _dim } }
730 {
731 \dim_use:c { g__draw_path_ #1#2 _dim }
732 #3 0.5 \g__draw_linewidth_dim
733 }
734 }
735 }
736 }
(End definition for\draw_path_use:n and others. These functions are documented on page??.)
4.9 Scoping paths
\l__draw_path_lastx_dim
\l__draw_path_lasty_dim
\l__draw_path_xmax_dim
\l__draw_path_xmin_dim
\l__draw_path_ymax_dim
\l__draw_path_ymin_dim
\l__draw_softpath_corners_bool
Local storage for global data. There is already a\l__draw_softpath_main_tlfor path manipulation, so we can reuse that (it is always grouped when the path is being recon- structed).
737 \dim_new:N \l__draw_path_lastx_dim
738 \dim_new:N \l__draw_path_lasty_dim
739 \dim_new:N \l__draw_path_xmax_dim
740 \dim_new:N \l__draw_path_xmin_dim
741 \dim_new:N \l__draw_path_ymax_dim
742 \dim_new:N \l__draw_path_ymin_dim
743 \dim_new:N \l__draw_softpath_lastx_dim
744 \dim_new:N \l__draw_softpath_lasty_dim
745 \bool_new:N \l__draw_softpath_corners_bool (End definition for\l__draw_path_lastx_dim and others.)
\draw_path_scope_begin:
\draw_path_scope_end:
Scoping a path is a bit more involved, largely as there are a number of variables to keep hold of.
746 \cs_new_protected:Npn \draw_path_scope_begin:
747 {
748 \group_begin:
749 \dim_set_eq:NN \l__draw_path_lastx_dim \g__draw_path_lastx_dim
750 \dim_set_eq:NN \l__draw_path_lasty_dim \g__draw_path_lasty_dim
751 \dim_set_eq:NN \l__draw_path_xmax_dim \g__draw_path_xmax_dim
752 \dim_set_eq:NN \l__draw_path_xmin_dim \g__draw_path_xmin_dim
753 \dim_set_eq:NN \l__draw_path_ymax_dim \g__draw_path_ymax_dim
754 \dim_set_eq:NN \l__draw_path_ymin_dim \g__draw_path_ymin_dim
755 \dim_set_eq:NN \l__draw_softpath_lastx_dim \g__draw_softpath_lastx_dim
756 \dim_set_eq:NN \l__draw_softpath_lasty_dim \g__draw_softpath_lasty_dim
757 \__draw_path_reset_limits:
758 \tl_build_get:NN \g__draw_softpath_main_tl \l__draw_softpath_main_tl
759 \bool_set_eq:NN
760 \l__draw_softpath_corners_bool
761 \g__draw_softpath_corners_bool
762 \__draw_softpath_clear:
763 }
764 \cs_new_protected:Npn \draw_path_scope_end:
765 {
766 \__draw_softpath_clear:
767 \bool_gset_eq:NN
768 \g__draw_softpath_corners_bool
769 \l__draw_softpath_corners_bool
770 \__draw_softpath_add:o \l__draw_softpath_main_tl
771 \dim_gset_eq:NN \g__draw_softpath_lastx_dim \l__draw_softpath_lastx_dim
772 \dim_gset_eq:NN \g__draw_softpath_lasty_dim \l__draw_softpath_lasty_dim
773 \dim_gset_eq:NN \g__draw_path_xmax_dim \l__draw_path_xmax_dim
774 \dim_gset_eq:NN \g__draw_path_xmin_dim \l__draw_path_xmin_dim
775 \dim_gset_eq:NN \g__draw_path_ymax_dim \l__draw_path_ymax_dim
776 \dim_gset_eq:NN \g__draw_path_ymin_dim \l__draw_path_ymin_dim
777 \dim_gset_eq:NN \g__draw_path_lastx_dim \l__draw_path_lastx_dim
778 \dim_gset_eq:NN \g__draw_path_lasty_dim \l__draw_path_lasty_dim
779 \group_end:
780 }
(End definition for\draw_path_scope_begin: and\draw_path_scope_end:. These functions are docu- mented on page??.)
781 \msg_new:nnnn { draw } { invalid-path-action }
782 { Invalid~action~’#1’~for~path. }
783 { Paths~can~be~used~with~actions~’draw’,~’clip’,~’fill’~or~’stroke’. }
784 % \end{macrocode}
785 %
786 % \begin{macrocode}
787 ⟨/package⟩
5 l3draw-points implementation
788 ⟨∗package⟩
789 ⟨@@=draw⟩
This sub-module covers more-or-less the same ideas as pgfcorepoints.code.tex, though the approach taken to returning values is different: point expressions here are processed by expansion and return a co-ordinate pair in the form{⟨x⟩}{⟨y⟩}. Equivalents of followingpgf functions are deliberately omitted:
• \pgfpointorigin: Can be given explicitly as0pt,0pt.
• \pgfpointadd,\pgfpointdiff,\pgfpointscale: Can be given explicitly.
• \pgfextractx, \pgfextracty: Available by applying \use_i:nn/\use_ii:nn or similar to the x-type expansion of a point expression.
• \pgfgetlastxy: Unused in the entire pgfcore, may be emulated byx-type expan- sion of a point expression, then using the result.
In addition, equivalents of the followingmaybe added in future but are currently absent:
• \pgfpointcylindrical,\pgfpointspherical: The usefulness of these commands is not currently clear.
• \pgfpointborderrectangle,\pgfpointborderellipse: To be revisited once the semantics and use cases are clear.
• \pgfqpoint,\pgfqpointscale,\pgfqpointpolar,\pgfqpointxy,\pgfqpointxyz:
The expandable approach taken in the code here, along with the absolute require- ment for ε-TEX, means it is likely many use cases for these commands may be covered in other ways. This may be revisited as higher-level structures are con- structed.
5.1 Support functions
\__draw_point_process:nn
\__draw_point_process_auxi:nn
\__draw_point_process_auxii:nw
\__draw_point_process:nnn
\__draw_point_process_auxiii:nnn
\__draw_point_process_auxiv:nw
\__draw_point_process:nnnn
\__draw_point_process_auxv:nnnn
\__draw_point_process_auxvi:nw
\__draw_point_process:nnnnn
\__draw_point_process_auxvii:nnnnn
\__draw_point_process_auxviii:nw
Execute whatever code is passed to extract thexandyco-ordinates. The first argument here should itself absorb two arguments. There is also a version to deal with two co- ordinates: common enough to justify a separate function.
790 \cs_new:Npn \__draw_point_process:nn #1#2
791 {
792 \exp_args:Nf \__draw_point_process_auxi:nn
793 { \draw_point:n {#2} }
794 {#1}
795 }
796 \cs_new:Npn \__draw_point_process_auxi:nn #1#2
797 { \__draw_point_process_auxii:nw {#2} #1 \s__draw_stop }
798 \cs_new:Npn \__draw_point_process_auxii:nw #1 #2 , #3 \s__draw_stop
799 { #1 {#2} {#3} }
800 \cs_new:Npn \__draw_point_process:nnn #1#2#3
801 {
802 \exp_args:Nff \__draw_point_process_auxiii:nnn
803 { \draw_point:n {#2} }
804 { \draw_point:n {#3} }
805 {#1}
806 }
807 \cs_new:Npn \__draw_point_process_auxiii:nnn #1#2#3
808 { \__draw_point_process_auxiv:nw {#3} #1 \s__draw_mark #2 \s__draw_stop }
809 \cs_new:Npn \__draw_point_process_auxiv:nw #1 #2 , #3 \s__draw_mark #4 , #5 \s__draw_stop
810 { #1 {#2} {#3} {#4} {#5} }
811 \cs_new:Npn \__draw_point_process:nnnn #1#2#3#4
812 {
813 \exp_args:Nfff \__draw_point_process_auxv:nnnn
814 { \draw_point:n {#2} }
815 { \draw_point:n {#3} }
816 { \draw_point:n {#4} }
817 {#1}
818 }
819 \cs_new:Npn \__draw_point_process_auxv:nnnn #1#2#3#4
820 { \__draw_point_process_auxvi:nw {#4} #1 \s__draw_mark #2 \s__draw_mark #3 \s__draw_stop }
821 \cs_new:Npn \__draw_point_process_auxvi:nw
822 #1 #2 , #3 \s__draw_mark #4 , #5 \s__draw_mark #6 , #7 \s__draw_stop
823 { #1 {#2} {#3} {#4} {#5} {#6} {#7} }
824 \cs_new:Npn \__draw_point_process:nnnnn #1#2#3#4#5
825 {
826 \exp_args:Nffff \__draw_point_process_auxvii:nnnnn
827 { \draw_point:n {#2} }
828 { \draw_point:n {#3} }
829 { \draw_point:n {#4} }
830 { \draw_point:n {#5} }
831 {#1}
832 }
833 \cs_new:Npn \__draw_point_process_auxvii:nnnnn #1#2#3#4#5
834 {
835 \__draw_point_process_auxviii:nw
836 {#5} #1 \s__draw_mark #2 \s__draw_mark #3 \s__draw_mark #4 \s__draw_stop
837 }
838 \cs_new:Npn \__draw_point_process_auxviii:nw
839 #1 #2 , #3 \s__draw_mark #4 , #5 \s__draw_mark #6 , #7 \s__draw_mark #8 , #9 \s__draw_stop
840 { #1 {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} } (End definition for\__draw_point_process:nn and others.)
5.2 Basic points
\draw_point:n
\__draw_point_to_dim:n
\__draw_point_to_dim:f
\__draw_point_to_dim:w
Co-ordinates are always returned as two dimensions.
841 \cs_new:Npn \draw_point:n #1
842 { \__draw_point_to_dim:f { \fp_eval:n {#1} } }
843 \cs_new:Npn \__draw_point_to_dim:n #1
844 { \__draw_point_to_dim:w #1 }
845 \cs_generate_variant:Nn \__draw_point_to_dim:n { f }
846 \cs_new:Npn \__draw_point_to_dim:w ( #1 , ~ #2 ) { #1pt , #2pt }
5.3 Polar co-ordinates
\draw_point_polar:nn
\draw_point_polar:nnn
\__draw_draw_polar:nnn
\__draw_draw_polar:fnn
Polar co-ordinates may have either one or two lengths, so there is a need to do a sim- ple split before the calculation. As the angle gets used twice, save on any expression evaluation there and force expansion.
847 \cs_new:Npn \draw_point_polar:nn #1#2
848 { \draw_point_polar:nnn {#1} {#1} {#2} }
849 \cs_new:Npn \draw_point_polar:nnn #1#2#3
850 { \__draw_draw_polar:fnn { \fp_eval:n {#3} } {#1} {#2} }
851 \cs_new:Npn \__draw_draw_polar:nnn #1#2#3
852 { \draw_point:n { cosd(#1) * (#2) , sind(#1) * (#3) } }
853 \cs_generate_variant:Nn \__draw_draw_polar:nnn { f }
5.4 Point expression arithmetic
These functions all take point expressions as arguments.
\draw_point_unit_vector:n
\__draw_point_unit_vector:nn
\__draw_point_unit_vector:nnn
The outcome is the normalised vector from (0,0) in the direction of the point,i.e.
Px= x
px2+y2 Py= y px2+y2
except where the length is zero, in which case a vertical vector is returned.
854 \cs_new:Npn \draw_point_unit_vector:n #1
855 { \__draw_point_process:nn { \__draw_point_unit_vector:nn } {#1} }
856 \cs_new:Npn \__draw_point_unit_vector:nn #1#2
857 {
858 \exp_args:Nf \__draw_point_unit_vector:nnn
859 { \fp_eval:n { (sqrt(#1 * #1 + #2 * #2)) } }
860 {#1} {#2}
861 }
862 \cs_new:Npn \__draw_point_unit_vector:nnn #1#2#3
863 {
864 \fp_compare:nNnTF {#1} = \c_zero_fp
865 { 0pt, 1pt }
866 {
867 \draw_point:n
868 { ( #2 , #3 ) / #1 }
869 }
870 }
5.5 Intersection calculations
\draw_point_intersect_lines:nnnn
\__draw_point_intersect_lines:nnnnnn
\__draw_point_intersect_lines:nnnnnnnn
\__draw_point_intersect_lines_aux:nnnnnn
\__draw_point_intersect_lines_aux:ffffff
The intersection pointP between a line joining points (x1, y1) and (x2, y2) with a second line joining points (x3, y3) and (x4, y4) can be calculated using the formulae
Px=(x1y2−y1x2)(x3−x4)−(x3y4−y3x4)(x1−x2) (x1−x2)(y3−y4)−(y1−y2)(x3−x4) and
Py= (x1y2−y1x2)(y3−y5)−(x3y4−y3x4)(y1−y2) (x1−x2)(y3−y4)−(y1−y2)(x3−x4)
The work therefore comes down to expanding the incoming data, then pre-calculating as many parts as possible before the final work to find the intersection. (Expansion and argument re-ordering is much less work than additional floating point calculations.)
871 \cs_new:Npn \draw_point_intersect_lines:nnnn #1#2#3#4
872 {
873 \__draw_point_process:nnnnn
874 { \__draw_point_intersect_lines:nnnnnnnn }
875 {#1} {#2} {#3} {#4}
876 }
At this stage we have all of the information we need, fully expanded:
#1 x1
#2 y1
#3 x2
#4 y2
#5 x3
#6 y3
#7 x4
#8 y4
so now just have to do all of the calculation.
877 \cs_new:Npn \__draw_point_intersect_lines:nnnnnnnn #1#2#3#4#5#6#7#8
878 {
879 \__draw_point_intersect_lines_aux:ffffff
880 { \fp_eval:n { #1 * #4 - #2 * #3 } }
881 { \fp_eval:n { #5 * #8 - #6 * #7 } }
882 { \fp_eval:n { #1 - #3 } }
883 { \fp_eval:n { #5 - #7 } }
884 { \fp_eval:n { #2 - #4 } }
885 { \fp_eval:n { #6 - #8 } }
886 }
887 \cs_new:Npn \__draw_point_intersect_lines_aux:nnnnnn #1#2#3#4#5#6
888 {
889 \draw_point:n
890 {
891 ( #2 * #3 - #1 * #4 , #2 * #5 - #1 * #6 )
892 / ( #4 * #5 - #6 * #3 )
893 }
894 }
895 \cs_generate_variant:Nn \__draw_point_intersect_lines_aux:nnnnnn { ffffff }
\draw_point_intersect_circles:nnnnn
\__draw_point_intersect_circles_auxi:nnnnnnn
\__draw_point_intersect_circles_auxii:nnnnnnn
\__draw_point_intersect_circles_auxii:ffnnnnn
\__draw_point_intersect_circles_auxiii:nnnnnnn
\__draw_point_intersect_circles_auxiii:ffnnnnn
\__draw_point_intersect_circles_auxiv:nnnnnnnn
\__draw_point_intersect_circles_auxiv:fnnnnnnn
\__draw_point_intersect_circles_auxv:nnnnnnnnn
\__draw_point_intersect_circles_auxv:ffnnnnnnn
\__draw_point_intersect_circles_auxvi:nnnnnnnn
\__draw_point_intersect_circles_auxvi:fnnnnnnn
\__draw_point_intersect_circles_auxvii:nnnnnnn
\__draw_point_intersect_circles_auxvii:fffnnnn
Another long expansion chain to get the values in the right places. We have two circles, the first with center (a, b) and radius r, the second with center (c, d) and radiuss. We use the intermediate values
e=c−a f =d−b
p=p e2+f2 k= p2+r2−s2
2p in either
Px=a+ek p +f
p
pr2−k2
Py =b+f k p −e
p
pr2−k2 or
Px=a+ek p −f
p
pr2−k2
Py =b+f k p +e
p
pr2−k2