summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/randomwalk/randomwalk.dtx
blob: 58cc4928587794a6f143d317ad9ac12bae23560d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
% \iffalse
%% File: randomwalk.dtx Copyright (C) 2011-2018 Bruno Le Floch
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3c
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   http://www.latex-project.org/lppl.txt
%%
%% This work has the LPPL maintenance status 'maintained'
%% and the current maintainer is Bruno Le Floch.
%%
%% This work consists of the files randomwalk.dtx and randomwalk.ins and
%% derived files randomwalk.sty and randomwalk.pdf.
%% -----------------------------------------------------------------------
%
%<*driver|package>
%</driver|package>
%<*driver>
\RequirePackage{expl3}
\documentclass[full]{l3doc}
\usepackage{randomwalk}
\AtBeginDocument{\chgrand[seed = 411500]\rand}
\usepackage{amsmath}
\begin{document}
  \DocInput{randomwalk.dtx}
\end{document}
%</driver>
% \fi
%
%^^A The date here also is in \ProvidesExplPackage, and in the copyright.
% \def\fileversion{v0.6}
% \def\filedate{2018/12/28}
%
% \title{The \pkg{randomwalk} package: \\
%   customizable random walks\thanks{This file describes version
%     \fileversion, last revised \filedate.}}
% \author{Bruno Le Floch\thanks{E-mail blflatex+randomwalk@gmail.com}}
% \date{Released on \filedate}
%
% \maketitle
% \tableofcontents
%
% \begin{documentation}
%
% \begin{abstract}
%
% The \pkg{randomwalk} package draws random walks. The
% following parameters can be customized:
% \begin{itemize}
%   \item The number of steps, of course.
%   \item The length of the steps, either a fixed length, or a length
%     taken uniformly at random from a given list.
%   \item The angle of each step, either taken uniformly at random
%     from a given list, or uniformly distributed between $0$~and
%     $360$ degrees.
% \end{itemize}
%
% \end{abstract}
%
%
% \section{How to use \pkg{randomwalk}}
%
% \newcommand{\examplei}
%   {\RandomWalk {number = 200, length = {4pt, 10pt}}}
% \newcommand{\exampleii}
%   {\RandomWalk {number = 100, angles = {0,60,120,180,240,300}, degree}}
% \newcommand{\exampleiii}
%   {\RandomWalk {number = 50, length = 1ex, angles = {0,24,48,-24,-48}, degree, angles-relative}}
% \begin{function}{\RandomWalk}
% The \pkg{randomwalk} package has a single user command:
% \cs{RandomWalk}, which takes a list of key-value pairs as its
% argument. A few examples are given in Figures~\ref{examplei}, \ref{exampleii}, and~\ref{exampleiii}:
% \begin{quote}\ttfamily
% \detokenize\expandafter{\examplei}\\
% \detokenize\expandafter{\exampleii}\\
% \detokenize\expandafter{\exampleiii}
% \end{quote}
% Here is a list of all the keys, and their meaning:
% \begin{itemize}
%   \item \texttt{number}: the number of steps (default \(10\))
%   \item \texttt{length}: the length of each step: either one dimension
%     (\emph{e.g.}, |1ex|), or a comma-separated list of dimensions
%     (\emph{e.g.}, |{2pt, 5pt}|), by default |10pt|. The length of each
%     step is a (uniformly distributed) random element in this set of
%     possible dimensions.
%   \item \texttt{angles}: the polar angle for each step: a
%     comma-separated list of angles, and each step takes a random angle
%     in the list.  If this is not specified, then the angle is
%     uniformly distributed along the circle.
%   \item \texttt{degree} or \texttt{degrees}: specify that the angles
%     are given in degrees (by default, they are in radians).
%   \item \texttt{angles-relative}: instead of being absolute, the
%     angles are relative to the direction of the previous step.
%   \item \texttt{revert-random} (boolean, false by default): revert the
%     seed of the random number generator to its original value after
%     the random walk.
% \end{itemize}
% \end{function}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\examplei}
%     \caption{\label{examplei}A \(200\) steps long
%       walk, where each step has one of two lengths:
%       \texttt{\detokenize\expandafter{\examplei}}}
%   \end{center}
% \end{figure}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\exampleii}
%     \caption{\label{exampleii}A walk with constrained angles:
%       \texttt{\detokenize\expandafter{\exampleii}}}
%   \end{center}
% \end{figure}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\exampleiii}
%     \caption{\label{exampleiii}A last example, with small relative
%       angles: \texttt{\detokenize\expandafter{\exampleiii}}}
%   \end{center}
% \end{figure}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{randomwalk} implementation}
%
% \subsection{Packages}
%
% The \pkg{expl3} bundle is loaded first.
%
%<*package>
%    \begin{macrocode}
%<@@=randomwalk>
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage{expl3}[2017/11/14]
\ProvidesExplPackage
  {randomwalk} {2018/12/28} {0.6} {Customizable random walks}
\RequirePackage{xparse}[2017/11/14]
%    \end{macrocode}
%
% Load \pkg{pgfcore} for figures.
%    \begin{macrocode}
\RequirePackage{pgfcore}
%    \end{macrocode}
%
% Load \pkg{lcg} for random numbers.
% It needs to know the smallest and biggest random numbers that
% should be produced, which we take to be $0$ and $\cs{c_@@_lcg_last_int}
% = 2^{31}-2$.  It will then store them in \cs{c@lcg@rand}: the |\c@| is
% there because of how \LaTeXe{} defines counters. To make it clear that
% |\c| has a very special meaning here, I do not follow \LaTeX3 naming
% conventions.  Also of note is that I use \cs{cr@nd} in \cs{@@_walk:}.
%
% It seems that the \pkg{lcg} package has to be loaded after the
% document class, hence we do it \cs{AtBeginDocument}.  Also worth noting
% is the call to \cs{rand}, which avoids some very odd bug.
%    \begin{macrocode}
\int_const:Nn \c_@@_lcg_last_int { \c_max_int - 1 }
\AtBeginDocument
  {
    \RequirePackage
      [
        first= 0 ,
        last = \c_@@_lcg_last_int ,
        counter = lcg@rand
      ]
      { lcg }
    \rand
  }
%    \end{macrocode}
%
% \subsection{Variables}
%
% \begin{variable}{\l_@@_internal_tl, \l_@@_internal_int}
%   Used for scratch assignments.
%    \begin{macrocode}
\tl_new:N \l_@@_internal_tl
\int_new:N \l_@@_internal_int
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_step_number_int}
%   The number of steps requested by the caller.
%    \begin{macrocode}
\int_new:N \l_@@_step_number_int
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_relative_angles_bool, \l_@@_degrees_bool}
%   Booleans for whether angles are relative (keyval option),
%   and whether they are in degrees.
%    \begin{macrocode}
\bool_new:N \l_@@_relative_angles_bool
\bool_new:N \l_@@_degrees_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_revert_random_bool}
%   Booleans for whether to revert the random seed to its original value
%   or keep the last value reached at the end of a random path.
%    \begin{macrocode}
\bool_new:N \l_@@_revert_random_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_next_angle:, \@@_next_length:}
%   Set the \cs{l_@@_angle_fp} and \cs{l_@@_length_fp} of the next step,
%   most often randomly.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_next_angle: { }
\cs_new_protected:Npn \@@_next_length: { }
%    \end{macrocode}
% \end{macro}
%
% \begin{variable}{\l_@@_angle_fp, \l_@@_length_fp}
%   Angle and length of the next step.
%    \begin{macrocode}
\fp_new:N \l_@@_angle_fp
\fp_new:N \l_@@_length_fp
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_x_dim, \l_@@_y_dim}
%   Current coordinates: each \cs{pgfpathlineto} statement
%   goes from the previous value of these to the next.  See
%   \cs{@@_walk_step:}.
%    \begin{macrocode}
\dim_new:N \l_@@_x_dim
\dim_new:N \l_@@_y_dim
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_angles_seq, \l_@@_lengths_seq}
%   Sequences containing all allowed angles and lengths, as
%   floating point numbers.
%    \begin{macrocode}
\seq_new:N \l_@@_angles_seq
\seq_new:N \l_@@_lengths_seq
%    \end{macrocode}
% \end{variable}
%
% \subsection{User command and key-value list}
%
% \begin{macro}{\RandomWalk, \randomwalk:n}
%   The user command \cs{RandomWalk} is based on the code-level
%   command \cs{randomwalk:n}, which simply does the setup and calls
%   the internal macro \cs{@@_walk:}.
%    \begin{macrocode}
\DeclareDocumentCommand \RandomWalk { m }
  { \randomwalk:n {#1} }
\cs_new_protected:Npn \randomwalk:n #1
  {
    \@@_setup_defaults:
    \keys_set:nn { randomwalk } {#1}
    \@@_walk:
  }
%    \end{macrocode}
% \end{macro}
%
% We introduce the keys for the package.
%    \begin{macrocode}
\keys_define:nn { randomwalk }
  {
    number .value_required:n = true ,
    length .value_required:n = true ,
    angles .value_required:n = true ,
    number .int_set:N = \l_@@_step_number_int ,
    length .code:n = { \@@_setup_length:n {#1} } ,
    angles .code:n = { \@@_setup_angles:n {#1} } ,
    degree .bool_set:N = \l_@@_degrees_bool ,
    degrees .bool_set:N = \l_@@_degrees_bool ,
    angles-relative .bool_set:N = \l_@@_relative_angles_bool ,
    revert-random .bool_set:N = \l_@@_revert_random_bool ,
  }
%    \end{macrocode}
%
% \subsection{Setup}
%
% \begin{macro}{\@@_setup_defaults:}
%   The package treats the length of steps, and the angle,
%   completely independently.  The function \cs{@@_next_length:}
%   contains the action that decides the length of the next step, while
%   the function \cs{@@_next_angle:} pertains to the angle.
%
%   \cs{@@_setup_defaults:} sets the default values before processing the
%   user's key-value input.  This also sets initial values of variables
%   that currently cannot be altered through keys, because it might be
%   good to provide keys for their initial values too later on.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_setup_defaults:
  {
    \int_set:Nn \l_@@_step_number_int {10}
    \cs_gset_protected:Npn \@@_next_angle:
      { \@@_fp_set_rand:Nnn \l_@@_angle_fp { 0 } { 360 } }
    \cs_gset_protected:Npn \@@_next_length:
      { \fp_set:Nn \l_@@_length_fp {10} }
    \bool_set_false:N \l_@@_revert_random_bool
    \bool_set_false:N \l_@@_relative_angles_bool
    \fp_zero:N \l_@@_angle_fp
    \fp_zero:N \l_@@_length_fp
    \dim_zero:N \l_@@_x_dim
    \dim_zero:N \l_@@_y_dim
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_setup_length:n}
%   Convert each item in the comma list into a floating point, then
%   define \cs{@@_next_length:} to set \cs{l_@@_length_fp} to a random
%   floating point in the list.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_setup_length:n #1
  {
    \seq_set_split:Nnn \l_@@_lengths_seq { , } {#1}
    \seq_set_map:NNn \l_@@_lengths_seq
      \l_@@_lengths_seq { \dim_to_fp:n {##1} }
    \cs_gset_protected:Npn \@@_next_length:
      {
        \@@_get_rand_seq_item:NN
          \l_@@_lengths_seq \l_@@_internal_tl
        \fp_set:Nn \l_@@_length_fp { \l_@@_internal_tl }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_setup_angles:n}
%   Two complications compared to \cs{@@_setup_length:n}.  First, the
%   angle can be given in radians rather than degrees: then add |rad|
%   after the randomly chosen value (in principle it would be better to
%   convert angles once and for all at the beginning, but that
%   interacts in a complicated way with the fact that keys can be given
%   in any order).  Second, angles can be relative, in which case we
%   use \cs{fp_add:Nn} to take the last angle into account.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_setup_angles:n #1
  {
    \seq_set_split:Nnn \l_@@_angles_seq { , } {#1}
    \seq_set_map:NNn \l_@@_angles_seq
      \l_@@_angles_seq { \fp_to_tl:n {##1} }
    \cs_gset_protected:Npn \@@_next_angle:
      {
        \@@_get_rand_seq_item:NN
          \l_@@_angles_seq \l_@@_internal_tl
        \bool_if:NF \l_@@_degrees_bool
          { \tl_put_right:Nn \l_@@_internal_tl { rad } }
        \bool_if:NTF \l_@@_relative_angles_bool
          { \fp_add:Nn } { \fp_set:Nn }
          \l_@@_angle_fp { \l_@@_internal_tl }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Drawing}
%
% \begin{macro}{\@@_walk:}
%   We are ready to define \cs{@@_walk:}, which draws a \pkg{pgf}
%   picture of a random walk with the parameters set up by the
%   \texttt{keys}.  We reset coordinates to zero originally.
%   Then draw the relevant \pkg{pgf} picture by repeatedly calling
%   \cs{@@_walk_step:}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_walk:
  {
    \@@_walk_start:
    \prg_replicate:nn { \l_@@_step_number_int }
      { \@@_walk_step: }
    \bool_if:NF \l_@@_revert_random_bool
      { \int_gset_eq:NN \cr@nd \cr@nd }
    \@@_walk_stop:
  }
%    \end{macrocode}
%   \cs{cr@nd} is internal to the lcg package.
% \end{macro}
%
% \begin{macro}{\@@_walk_start:, \@@_walk_line:, \@@_walk_stop:}
%   These functions encapsulate all of the \pkg{pgf}-related code.  The
%   \texttt{start} function begins the pgfpicture environment and
%   starts a path at position (x,y).  The \texttt{line} function adds
%   to the path a line from the previous position to the new (x,y).
%   The \texttt{stop} function draws the path constructed by
%   \cs{@@_walk_step:} and ends the pgfpicture environment.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_walk_start:
  {
    \begin{pgfpicture}
      \pgfpathmoveto
        { \pgfpoint { \l_@@_x_dim } { \l_@@_y_dim } }
  }
\cs_new_protected:Npn \@@_walk_line:
  {
    \pgfpathlineto
      { \pgfpoint { \l_@@_x_dim } { \l_@@_y_dim } }
  }
\cs_new_protected:Npn \@@_walk_stop:
  {
      \pgfusepath { stroke }
    \end{pgfpicture}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_walk_step:}
%   \cs{@@_walk_step:} calls \cs{@@_next_length:} and
%   \cs{@@_next_angle:} to determine the length and angle of the new
%   step.  This is then converted to cartesian coordinates and added to
%   the previous end-point.  Finally, we call \pkg{pgf}'s \cs{pgfpathlineto} to
%   produce a line to the new point.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_walk_step:
  {
    \@@_next_length:
    \@@_next_angle:
    \dim_add:Nn \l_@@_x_dim
      {
        \fp_to_dim:n
          { \l_@@_length_fp * cosd ( \l_@@_angle_fp ) }
      }
    \dim_add:Nn \l_@@_y_dim
      {
        \fp_to_dim:n
          { \l_@@_length_fp * sind ( \l_@@_angle_fp ) }
      }
    \@@_walk_line:
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{On random numbers and items}
%
% For random numbers, the interface of \pkg{lcg} is not quite enough, so
% we provide our own \LaTeX3-y functions.  Also, this will allow us to
% change quite easily our source of random numbers.
%
% \begin{macro}{\@@_fp_set_rand:Nnn}
%   We also need floating point random numbers, assigned
%   to the variable |#1|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_fp_set_rand:Nnn #1#2#3
  {
    \rand
    \fp_set:Nn #1
      { #2 + (#3 - (#2)) * \c@lcg@rand / \c_@@_lcg_last_int }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_rand_seq_item:NN}
%   We can now pick an element at random from a sequence.  If the
%   sequence has a single element, no need for randomness.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_rand_seq_item:NN #1#2
  {
    \int_set:Nn \l_@@_internal_int { \seq_count:N #1 }
    \int_compare:nTF { \l_@@_internal_int = 1 }
      { \tl_set:Nx #2 { \seq_item:Nn #1 { 1 } } }
      {
        \rand
        \tl_set:Nx #2
          {
            \seq_item:Nn #1
              {
                1 +
                \int_mod:nn { \c@lcg@rand } { \l_@@_internal_int }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \endinput