summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/randomwalk/randomwalk.dtx
blob: 81efc30acf7915c65f3a537b042e638b2ae7ba49 (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
% \iffalse
%% File: randomwalk.dtx Copyright (C) 2011-2012 Bruno Le Floch
%%
%% It may be distributed and/or modified under the conditions of the
%% LaTeX Project Public License (LPPL), either version 1.3c of this
%% license or (at your option) any later version.  The latest version
%% of this license is in the file
%%
%%    http://www.latex-project.org/lppl.txt
%%
%% -----------------------------------------------------------------------
%
%<*driver|package>
%</driver|package>
%<*driver>
\RequirePackage[check-declarations]{expl3}
\documentclass[full]{l3doc}
\usepackage{randomwalk}
\usepackage{amsmath}
\begin{document}
  \DocInput{randomwalk.dtx}
\end{document}
%</driver>
% \fi
%
%
% \title{The \textsf{randomwalk} package: \\
%   customizable random walks using TikZ\thanks{This file has version
%     number 0.2b, last revised 2012-08-24.}}
% \author{Bruno Le Floch}
% \date{August 24, 2012}
%
% \maketitle
% \tableofcontents
%
% \begin{documentation}
%
% \begin{abstract}
%
% The \pkg{randomwalk} package draws random walks using TikZ. 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 at random from a given set.
%   \item The angle of each step, either taken at random from a given
%     set, or uniformly distributed.
% \end{itemize}
%
% \end{abstract}
%
%
% \section{How to use it}
%
% The \pkg{randomwalk} package has exactly one user command:
% \cs{RandomWalk}, which takes a list of key-value pairs as its
% argument. A few examples:
% \begin{verbatim}
% \RandomWalk {number = 100, length = {4pt, 10pt}}
% \RandomWalk {number = 100, angles = {0,60,120,180,240,300}, degree}
% \RandomWalk {number = 100, length = 2em,
%   angles = {0,10,20,-10,-20}, degree, angles-relative}
% \end{verbatim}
% The simplest is to give 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.}, |1em|), or a comma-separated list of dimensions
%     (\emph{e.g.}, |{2pt, 5pt}|), by default |10pt|. The length of each
%     step is a 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
%     among 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.
%   \item \texttt{angles-relative}: instead of being absolute, the
%     angles are relative to the direction of the previous step.
% \end{itemize}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\RandomWalk {number = 400, length = {4pt, 10pt}}}
%     \caption{The result of \texttt{RandomWalk\{number\ =\ 400,\
%         length\ =\ \{4pt,\ 10pt\}\}}: a \(400\) steps long walk, where
%       each step has one of two lengths.}
%   \end{center}
% \end{figure}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\RandomWalk{number = 100, angles =
%         {0,60,120,180,240,300}, degrees}}
%     \caption{The result of \texttt{\string\RandomWalk\{number\ =\
%         100,\ angles\ =\ \{0,60,120,180,240,300\}, degrees\}}: angles
%       are constrained.}
%   \end{center}
% \end{figure}
%
% \begin{figure}
%   \begin{center}
%     \framebox{\RandomWalk {number = 40, length = 1em, angles =
%         {0,15,30,-15,-30}, degree, angles-relative}}
%     \caption{A last example: \texttt{\string\RandomWalk\ \{number\ =\
%         100,\ length\ =\ 2em,\ angles\ =\ \{0,10,20,-10,-20\},\
%         degree,\ angles-relative\}}}
%   \end{center}
% \end{figure}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{randomwalk} implementation}
%
% \subsection{Packages}
%
% The whole \pkg{expl3} bundle is loaded first.
%
%<*package>
%    \begin{macrocode}
%<@@=randomwalk>
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage {expl3} [2012/08/14]
\ProvidesExplPackage
  {randomwalk.sty} {2012/08/24} {0.2b} {Customizable random walks using TikZ}
\RequirePackage {xparse} [2012/08/14]
%    \end{macrocode}
%
% I use some \LaTeXe{} packages: \pkg{TikZ}, for figures, and \pkg{lcg}
% for random numbers.
%    \begin{macrocode}
\RequirePackage {tikz}
%    \end{macrocode}
%
% \pkg{lcg} needs to know the smallest and biggest random numbers that
% it should produce, 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.
%
% It seems that the \pkg{lcg} package has to be loaded after the
% document class, hence we do it \cs{AtBeginDocument}.
%    \begin{macrocode}
\int_const:Nn \c_@@_lcg_last_int { \c_max_int - \c_one }
\AtBeginDocument
  {
    \RequirePackage
      [
        first= \c_zero ,
        last = \c_@@_lcg_last_int ,
        counter = lcg@rand
      ]
      { lcg }
    \rand % This \rand avoids some very odd bug.
  }
%    \end{macrocode}
%
% \subsection{Variables}
%
% \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}
%   Booleans for whether angles are relative (keyval option).
%    \begin{macrocode}
\bool_new:N \l_@@_relative_angles_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}{\@@_rand_angle:, \@@_rand_length:}
%   Set the \cs{l_@@_angle_fp} and \cs{l_@@_length_fp} of the next step,
%   most often randomly.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_rand_angle: { }
\cs_new_protected_nopar:Npn \@@_rand_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_@@_old_x_fp, \l_@@_old_y_fp}
% \begin{variable}{\l_@@_new_x_fp, \l_@@_new_y_fp}
%   Coordinates of the two ends of each step: each \cs{draw} statement
%   goes from the |_old| point to the |_new| point.  See
%   \cs{@@_step_draw:}.
%    \begin{macrocode}
\fp_new:N \l_@@_old_x_fp
\fp_new:N \l_@@_old_y_fp
\fp_new:N \l_@@_new_x_fp
\fp_new:N \l_@@_new_y_fp
%    \end{macrocode}
% \end{variable}
% \end{variable}
%
% \begin{variable}{\l_@@_angles_seq, \l_@@_lengths_seq}
%   Sequences containing all allowed angles and lengths.
%    \begin{macrocode}
\seq_new:N \l_@@_angles_seq
\seq_new:N \l_@@_lengths_seq
%    \end{macrocode}
% \end{variable}
%
% \subsection{How the key-value list is treated}
%
% \begin{macro}{\RandomWalk}
%   The only user command is \cs{RandomWalk}: it simply does the setup,
%   and calls the internal macro \cs{@@_walk:}.
%    \begin{macrocode}
\DeclareDocumentCommand \RandomWalk { m }
  {
    \@@_set_defaults:
    \keys_set:nn { randomwalk } { #1 }
    \@@_walk:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_set_defaults:}
%   Currently, the package treats the length of steps, and the angle,
%   completely independently.  The function \cs{@@_rand_length:}
%   contains the action that decides the length of the next step, while
%   the function \cs{@@_rand_angle:} pertains to the angle.
%
%   \cs{@@_set_defaults:} sets the default values before processing the
%   user's key-value input.
%    \begin{macrocode}
\cs_new:Npn \@@_set_defaults:
  {
    \int_set:Nn \l_@@_step_number_int {10}
    \cs_gset_protected_nopar:Npn \@@_rand_angle:
      { \@@_fp_set_rand:Nnn \l_@@_angle_fp { - pi } { pi } }
    \cs_gset_protected_nopar:Npn \@@_rand_length:
      { \fp_set:Nn \l_@@_length_fp {10} }
    \bool_set_false:N \l_@@_revert_random_bool
    \bool_set_false:N \l_@@_relative_angles_bool
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\keys_define:nn}
%   We introduce the keys for the package.
%    \begin{macrocode}
\keys_define:nn { randomwalk }
  {
    number .value_required: ,
    length .value_required: ,
    angles .value_required: ,
    number .int_set:N = \l_@@_step_number_int ,
    length .code:n =
      {
        \seq_set_split:Nnn \l_@@_lengths_seq { , } {#1}
        \seq_set_map:NNn \l_@@_lengths_seq
          \l_@@_lengths_seq { \dim_to_fp:n {##1} }
        \int_compare:nNnTF { \seq_length:N \l_@@_lengths_seq } = {1}
          {
            \cs_gset_protected_nopar:Npn \@@_rand_length:
              { \fp_set:Nn \l_@@_length_fp {#1} }
          }
          {
            \cs_gset_protected_nopar:Npn \@@_rand_length:
              {
                \@@_fp_set_rand_seq_item:NN
                  \l_@@_length_fp \l_@@_lengths_seq
              }
          }
      } ,
    angles .code:n  =
      {
        \seq_set_split:Nnn \l_@@_angles_seq { , } {#1}
        \cs_gset_protected_nopar:Npn \@@_rand_angle:
          {
            \bool_if:NTF \l_@@_relative_angles_bool
              { \@@_fp_add_rand_seq_item:NN }
              { \@@_fp_set_rand_seq_item:NN }
              \l_@@_angle_fp \l_@@_angles_seq
          }
      } ,
    degree .code:n  =
      { \@@_radians_from_degrees:N \l_@@_angles_seq } ,
    degrees .code:n =
      { \@@_radians_from_degrees:N \l_@@_angles_seq } ,
    angles-relative .code:n =
      { \bool_set_true:N \l_@@_relative_angles_bool } ,
    revert-random .bool_set:N = \l_@@_revert_random_bool ,
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_radians_from_degrees:N}
%   Helper macro to convert all items in |#1| to degrees.
%    \begin{macrocode}
\cs_new:Npn \@@_radians_from_degrees:N #1
  { \seq_set_map:NNn #1 #1 { \fp_eval:n { ##1 deg } } }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Drawing}
%
% \begin{macro}{\@@_walk:}
%   We are ready to define \cs{@@_walk:}, which draws a \pkg{TikZ}
%   picture of a random walk with the parameters set up by the
%   \texttt{keys}.  We reset all the coordinates to zero originally.
%   Then we draw the relevant \pkg{TikZ} picture by repeatedly calling
%   \cs{@@_step_draw:}.
%    \begin{macrocode}
\cs_new:Npn \@@_walk:
  {
    \begin{tikzpicture}
      \fp_zero:N \l_@@_old_x_fp
      \fp_zero:N \l_@@_old_y_fp
      \fp_zero:N \l_@@_new_x_fp
      \fp_zero:N \l_@@_new_y_fp
      \prg_replicate:nn { \l_@@_step_number_int } { \@@_step_draw: }
      \bool_if:NF \l_@@_revert_random_bool
        { \int_gset_eq:NN \cr@nd \cr@nd }
    \end{tikzpicture}
  }
%    \end{macrocode}
%   \cs{cr@nd} is internal to the lcg package.
% \end{macro}
%
% \begin{macro}{\@@_step_draw:}
%   \cs{@@_step_draw:} calls \cs{@@_rand_length:} and
%   \cs{@@_rand_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{TikZ}'s \cs{draw} to
%   produce a line from the |_old| to the |_new| point.
%    \begin{macrocode}
\cs_new:Npn \@@_step_draw:
  {
    \@@_rand_length:
    \@@_rand_angle:
    \fp_set_eq:NN \l_@@_old_x_fp \l_@@_new_x_fp
    \fp_set_eq:NN \l_@@_old_y_fp \l_@@_new_y_fp
    \fp_add:Nn \l_@@_new_x_fp { \l_@@_length_fp * cos \l_@@_angle_fp }
    \fp_add:Nn \l_@@_new_y_fp { \l_@@_length_fp * sin \l_@@_angle_fp }
    \draw ( \fp_to_dim:N \l_@@_old_x_fp, \fp_to_dim:N \l_@@_old_y_fp )
       -- ( \fp_to_dim:N \l_@@_new_x_fp, \fp_to_dim:N \l_@@_new_y_fp );
  }
%    \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}[aux]{\@@_int_set_rand:Nnn}
%   Sets the integer register |#1| equal to a random integer between
%   |#2| and |#3| inclusive.
%    \begin{macrocode}
\cs_new:Npn \@@_int_set_rand:Nnn #1#2#3
  {
    \rand
    \int_set:Nn #1 { #2 + \int_mod:nn {\c@lcg@rand} { #3 + 1 - (#2) } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux]{\@@_fp_set_rand:Nnn, \@@_fp_add_rand:Nnn}
% \begin{macro}[aux]{\@@_fp_set_rand_aux:NNnn}
%   We also need floating point random numbers, both assigned and added
%   to the variable |#1| (well, |#2| of the auxiliary).
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_fp_set_rand:Nnn
  { \@@_fp_set_rand_aux:NNnn \fp_set:Nn }
\cs_new_nopar:Npn \@@_fp_add_rand:Nnn
  { \@@_fp_set_rand_aux:NNnn \fp_add:Nn }
\cs_new:Npn \@@_fp_set_rand_aux:NNnn #1#2#3#4
  {
    \rand
    #1 #2 { #3 + (#4 - (#3)) * \c@lcg@rand / \c_@@_lcg_last_int }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux]{\@@_fp_set_rand_seq_item:NN, \@@_fp_add_rand_seq_item:NN}
% \begin{macro}[aux]{\@@_fp_set_rand_item_aux:NNNNN}
%   We can now pick an element at random from a sequence, and either
%   assign it or add it to the fp variable |#4|.  The same auxiliary
%   could be used for picking random items from other types of lists.
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_fp_set_rand_seq_item:NN
  { \@@_fp_set_rand_item_aux:NNNNN \fp_set:Nn \seq_item:Nn \seq_length:N }
\cs_new_protected_nopar:Npn \@@_fp_add_rand_seq_item:NN
  { \@@_fp_set_rand_item_aux:NNNNN \fp_add:Nn \seq_item:Nn \seq_length:N }
\cs_new_protected:Npn \@@_fp_set_rand_item_aux:NNNNN #1#2#3#4#5
  {
    \rand
    #1 #4 { #2 #5 { 1 + \int_mod:nn { \c@lcg@rand } { #3 #5 } } }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \endinput