summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/metafun/metafun-debugging.tex
blob: 7f0c09f107f3f8be98872469cedc62244f9e260c (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
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
% language=us runpath=texruns:manuals/metafun

\startcomponent mfun-debugging

\environment metafun-environment

\startchapter[reference=sec:debugging,title={Debugging}]

\index{debugging}

\startintro

Those familiar with \CONTEXT\ will know that it has quite some visual debugging
features build in. So, what can you expect of the \METAPOST\ macros that come
with \CONTEXT ? In this chapter we will introduce a few commands that show some
insight in what \METAPOST\ is doing.

\stopintro

\startsection[title=Showing paths]

Since the outcome of \METAPOST\ code is in many respects more predictable than
that of \TEX\ code, we don't need that advanced visual debugging features.
Nevertheless we provide a few, that are all based on visualizing paths.

\startbuffer
path p ; p := fullcircle scaled 4cm ;
drawpath p ; drawpoints p ; drawpointlabels p ;
\stopbuffer

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

This visualization is achieved by using dedicated drawing commands:

\typebuffer

Since control points play an important role in defining the shape, visualizing
them may shed some insight in what \METAPOST\ is doing.

\startbuffer
path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
drawpath p ; drawcontrollines p ;
drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
\stopbuffer

\typebuffer

The pre and post control points show up as small dots and are connected to their
parent point with thin lines.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

You can deduce the direction of a path from the way the points are numbered, but
using an arrow to indicate the direction is more clear.

\startbuffer
path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
drawarrowpath p ; drawcontrollines p ;
drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
\stopbuffer

\typebuffer

The \type {drawarrowpath} is responsible for the arrow. Especially when you are
in the process of defining macros that have to calculate intersections or take
subpaths, knowing the direction may be of help.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

The next table summarizes the special drawing commands:

\starttabulate[|lT|l|]
\HL
\NC drawpath           \NC the path                         \NC \NR
\NC drawarrowpath      \NC the direction of the path        \NC \NR
\NC drawcontrollines   \NC the lines to the control points  \NC \NR
\NC drawpoints         \NC the points that make up the path \NC \NR
\NC drawcontrolpoints  \NC the control points of the points \NC \NR
\NC drawpointlabels    \NC the numbers of the points        \NC \NR
\HL
\stoptabulate

You can set the characteristics of these like you set \type {drawoptions}. The
default settings are as follows:

\starttyping
drawpathoptions   (withpen pencircle scaled 5   withcolor .8white) ;
drawpointoptions  (withpen pencircle scaled 4   withcolor   black) ;
drawcontroloptions(withpen pencircle scaled 2.5 withcolor   black) ;
drawlineoptions   (withpen pencircle scaled 1   withcolor .5white) ;
drawlabeloptions  () ;
\stoptyping

Two more options are \type {draworiginoptions} and \type {drawboundoptions} which
are used when visualizing the bounding box and origin.

\startbuffer
swappointlabels  := true ;
path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
drawarrowpath p ; drawcontrollines p ;
drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
drawboundingbox p ; draworigin ;
\stopbuffer

\typebuffer

In this example we have set \type {swappointlabels} to change the place of the
labels. You can set the variable \type {originlength} to tune the appearance of
the origin.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

You can pass options directly, like you do with \type {draw} and \type {fill}.
Those options override the defaults.

\startbuffer
path p ; p := fullcircle xscaled 6cm yscaled 3cm rotated 15 ;
drawarrowpath     p ;
drawcontrollines  p withcolor .625red ;
drawpoints        p withcolor .625yellow ;
drawcontrolpoints p withcolor .625yellow ;
drawpointlabels   p withcolor .625yellow ;
drawboundingbox   p ;
draworigin          withcolor .625red ;
\stopbuffer

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

Here we used the options:

\typebuffer

Sometimes it makes sense to draw a simple coordinate system, and for that purpose
we have three more macros. They draw axis and tickmarks.

\startbuffer
drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ;
\stopbuffer

\typebuffer

The system drawn is based on the bounding box specification of the path passed to
the macro. You can also draw one axis, using \type {drawxticks} or \type
{drawyticks}. Here we show the previous command.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

By default, the ticks are placed at .5cm distance, but you can change this by
setting \type {tickstep} to a different value.

\startbuffer
tickstep := 1cm ; ticklength := 2mm ;
drawticks fullsquare xscaled 4cm yscaled 3cm ;
tickstep := tickstep/2 ; ticklength := ticklength/2 ;
drawticks fullsquare xscaled 4cm yscaled 3cm ;
\stopbuffer

\typebuffer

The \type {ticklength} variable specifies the length of a tick. Here we
manipulated both the variables to get a more advanced system.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

If visualizing a path would mean that we would have to key in al those
draw||commands, you could hardly call it a comfortable tool. Therefore, we can
say:

\startbuffer
drawwholepath fullsquare scaled 3cm rotated 30 randomized 5mm ;
\stopbuffer

\typebuffer

The \type {drawwholepath} command shows everything except the axis.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

If even this is too much labour, you may say:

\starttyping
visualizepaths ;
\stoptyping

This redefines the \type {draw} and \type {fill} command in such a way that they
also show all the information.

\startbuffer
visualizepaths ;
draw fullsquare scaled 3cm rotated 30 randomized 2mm ;
\stopbuffer

\typebuffer

You may compare this feature to the \type {\showmakeup} command available in
\CONTEXT, that redefines the \TEX\ primitives that deal with boxes, glues,
penalties, and alike.

\startlinecorrection[blank]
\processMPbuffer
\stoplinecorrection

Of course you may want to take a look at the \METAPOST\ manual for its built in
(more verbose) tracing options. One command that may prove to be useful is \type
{show}, that you can apply to any variable. This command reports the current
value (if known) to the terminal and log file.

\startlinecorrection[blank]
{\showmakeup\processMPbuffer}
\stoplinecorrection

The previous picture shows what is typeset when we also say \type {\showmakeup}.
This command visualizes \TEX's boxes, skips, kerns and penalties. As you can see,
there are some boxes involved, which is due to the conversion of \METAPOST\
output to \PDF.

\starttyping
\startlinecorrection[blank]
... the graphic ...
\stoplinecorrection
\stoptyping

The small bar is a kern and the small rectangles are penalties. More details on
this debugger can be found in the \CONTEXT\ manuals and the documentation of the
modules involved.

\stopsection

\startsection[title=Comments]

Sometimes, when trouble strikes, you might want to peek in the \PDF\ file to see
what gets written there. Each graphic is marked with a number but when you
have many it might make sense to add a comment to help you locate the code.

\startbuffer
\startMPcode
    comment("test graphic") ;
    message("processing a test graphic") ;
    draw fullsquare scaled 1cm ;
\stopMPcode
\stopbuffer

\typebuffer

This renders as:

\startlinecorrection[blank]
    \getbuffer
\stoplinecorrection

On the console we get these messages:

\starttyping
metapost > message : processing a test graphic
metapost > warning : processing a test graphic
\stoptyping

And in the \PDF\ file we will find:

\starttyping
% mps graphic 1: begin
% mps graphic 1: test graphic
q 0 g 0 G 10 M 1 j 1 J
0.500000000 w
-14.173233032 -14.173233032 m
14.173233032 -14.173233032 l
14.173233032 14.173233032 l
-14.173233032 14.173233032 l
-14.173233032 -14.173233032 l
h S
0 g 0 G Q
% mps graphic 1: end
\stoptyping

Here are some examples of constructed messages:

\starttyping
message "2:                okay           (done)" ;
message "1: " & dq      & "okay" & dq & " (done)" ;
message "3: " & quotation "okay"      & " (done)" ;
message "3: " & quote     "okay"      & " (done)" ;
message "4: " & quotation 123         & " (done)" ;
message "5: " & quotation true        & " (done)" ;
message "6: " & quote     true        & " (done)" ;
message "7: " & tostring  true        & " (done)" ;
message "8: " & tostring  (1,2)       & " (done)" ;
message "9: " & topair    (1,2)       & " (done)" ;
\stoptyping

and this is what you get:

\starttyping
metapost > message : 2: okay (done)
metapost > message : 1: "okay" (done)
metapost > message : 3: "okay" (done)
metapost > message : 3: 'okay' (done)
metapost > message : 4: "123" (done)
metapost > message : 5: "true" (done)
metapost > message : 6: 'true' (done)
metapost > message : 7: true (done)
metapost > message : 8: 1 2 (done)
metapost > message : 9: (1,2) (done)
\stoptyping

\stopsection

\startsection[title=Pens]

A circular pen is applied to a path in a different way than for instance a
square pen. Circular pens are mapped onto \POSTSCRIPT\ pens while for other
pens an outline is calculated that gets filled. Take this code:

\startbuffer[a]
\startMPcode
    draw fullcircle xscaled 6cm yscaled 3cm
        withpen pensquare scaled 5mm rotated 30
        withcolor .625yellow ;
\stopMPcode
\stopbuffer

\startbuffer[b]
\startMPcode
    draw envelope pensquare scaled 5mm rotated 30 of
        (fullcircle xscaled 6cm yscaled 3cm)
        withpen pencircle scaled 1mm
        withcolor .375white ;
\stopMPcode
\stopbuffer

\startbuffer[c]
\enabletrackers[metapost.forcestroke]
\startMPcode
    draw fullcircle xscaled 6cm yscaled 3cm
        withpen pensquare scaled 5mm rotated 30
        withcolor .625red ;
\stopMPcode
\disabletrackers[metapost.forcestroke]
\stopbuffer

\typebuffer[a]

and this:

\typebuffer[b]

and:

\typebuffer[c]

When we overlay these three we get. The envelope only returns the outer curve.

\startlinecorrection[blank]
\startoverlay
    {\getbuffer[a]}
    {\getbuffer[b]}
    {\getbuffer[c]}
\stopoverlay
\stoplinecorrection

We show a few nore examples and let it to the user to come up with applications
for this feature. We start by defining a scaled pen that we apply to a simple
path that has three point.

\startbuffer
pen whateverpen ; whateverpen := makepen(fullcircle xyscaled(1/20,1/40));

path p ; p := (origin .. (1,1/2) .. (1,1/4)) scaled 10 ;
path q ; q := envelope whateverpen of p ;

draw              p withpen pencircle xyscaled(1/2,1/4)
                    withcolor "darkgreen"   withtransparency (1,.5) ;
drawpoints        p withpen pencircle scaled 1
                    withcolor "darkgray"    withtransparency (1,.5) ;
drawcontrolpoints p withpen pencircle scaled 1/2
                    withcolor "darkmagenta" withtransparency (1,.5) ;

currentpicture := currentpicture shifted (-20,0) ;

draw              p withpen pencircle xyscaled(1/2,1/4)
                    withcolor "darkgreen"   withtransparency (1,.5) ;
draw              q withpen pencircle scaled 1/5
                    withcolor "darkblue"    withtransparency (1,.5) ;
drawpoints        q withpen pencircle scaled 1
                    withcolor "darkgray"    withtransparency (1,.5) ;
drawcontrolpoints q withpen pencircle scaled 1/2
                    withcolor "darkyellow"  withtransparency (1,.5) ;
draw              q withpen pencircle scaled 1/50
                    withcolor "white" ;

currentpicture := currentpicture ysized 2cm ;
\stopbuffer

\typebuffer

On the left we see the path drawn with a circular pen. The points and control
points are also shown. The path is repeated on the right but there it gets the
envelope overlayed. We show the points and control points of the envelope: they
nearly overlap but that depends of course on the used pen.

\startlinecorrection[blank]
    \processMPbuffer
\stoplinecorrection

In order to illustrate that the envelope is an outline we blow up a piece of
this image:

\startlinecorrection[blank]
    \scale[height=3cm]{\clip[nx=10,ny=20,x=9,y=7]{\processMPbuffer}}
\stoplinecorrection

Without further explanation we let you ponder the results of the following code.

\startbuffer
drawoptions(withcolor "darkred") ;
draw (envelope pensquare of (up--left)) ;
draw (up--left) shifted (4,0) withpen pensquare ;
draw fullcircle shifted (8,0) ;
draw (envelope pensquare of fullcircle) shifted (12,0) ;
draw (fullcircle) shifted (16,0) withpen pensquare ;
draw (fullcircle) shifted (20,0) withpen pencircle ;
currentpicture := currentpicture xsized TextWidth ;
\stopbuffer

\typebuffer

The efficiency of the output of each draw differs a lot because circles are made
from eight points and because a transformed pen results in two paths. Normally
that is not something you have to loose sleep over.

\startlinecorrection[blank]
    \processMPbuffer
\stoplinecorrection

\stopsection

\startsection[title=Performance]

On the average performance of \METAPOST\ is quite okay. The original program uses
scaled numbers, which are floats packed into an integer. The library also
supports doubles, decimal and binary number models. In \CONTEXT\ we only support
scaled, double and decimal. Because the library has to support multiple models
there is more overhead and therefore it is also slower. There's also more dynamic
memory allocation going on. In the transition from \MKII\ to \MKIV\ some of the
critical code (like the code involved in passing \TEX\ states to \METAPOST) had
to be optimized, although when the \LUA\ interface was added, betters ways became
possible. We have to accept the penalty in performance and often can gain back a
lot because we have the \LUA\ interface.

One of the main bottlenecks in storing quantities. \footnote {Recently, Taco
Hoekwater has done some excellent explanations about the way \METAPOST\ scans the
input and create variables and you can find his presentations at meetings on the
\CONTEXT\ garden.} When we see something \type {a[1]} and \type {a[3]} the \type
{a} is a root variable and the \type {1} and {3} are entries in a linked list
from that root. It's not an array in the sense that there is some upper bound and
that there's also a slot \type {2}. There is order but the list is sparse. When
access is needed, for instance to do some calculations, a linear lookup (from the
head of the list) takes place. This is quite okay performance wise because
normally these list are small. The same is true for a path, which is also a
linked list. If you need point 25, it is looked up by starting at the first knot
of the path. The longer the path, the more time it takes to reach arbitrary
points. In the \LUA\ chapter we give an example of how to get around that
limitation.

Concerning the arrays, here is s trick to get around a performance bottleneck:

\starttyping
numeric foo[];

def set_foo(expr c, s) =
    foo[c] := s ;
enddef ;

def get_foo(expr c) =
    foo[c]
enddef ;
\stoptyping

If you use this as follows:

\starttyping
numeric n ; n = 123 ;

for i=1 upto 20000 :
    set_foo(i,n) ;
endfor ;

for i=1 upto 20000 :
    n := get_foo(i) ;
endfor ;
\stoptyping

the runtime can (for instance) be 3.3 seconds, but when you use the following
variant, it goes down to 0.13 seconds.

\starttyping
numeric foo[][][][]; % 12345 : 1  12  123  44 instead of 12344

def set_foo(expr c, s) =
    foo[c div 10000][c div 1000][c div 100][c] := s ;
enddef ;
def get_foo(expr c) =
    foo[c div 10000][c div 1000][c div 100][c]
enddef ;
\stoptyping

This time the lookup is not split into phases each being relatively fast. So, in
order to reach slot 1234 the engine doesn't have to check and jump over what
comes before that. You basically create a tree here: 0 (hit), 1000 (hit in one),
200 (hit in two), 34 (hit in 34). We could go to a single digit but that doesn't
save much. Before we had ways to store data at the \LUA\ end we used this a few
times in macros that dealt with data (like Alan Braslau's node and graphics
modules). This is typically something one can figure out by looking at the (non
trivial) source code.

Here is another example. In \LUA\ we can easily create a large file, like this:

\starttyping
\startluacode
  local t = { }
  for i=1,10000 do
    t[i] = string.rep(
      "here we have number " ..
      tostring(i) ..
      " out of the 10000 numbers that we will test"
    ,100)
  end
  t = table.concat(t,"\n")
  io.savedata("foo1.tmp",t)
  io.savedata("foo2.tmp",t)
  io.savedata("foo3.tmp",t)
\stopluacode
\stoptyping

We make two copies because we do two experiments and we want to treat them equal with
respect to caching.

\starttyping
\startMPcode
  string f ; f := "foo1.tmp" ;
  string s[] ;
  numeric n ; n := 0 ;
  for i=1 upto 10000 :
    s[i] := readfrom f ;
    exitif s[i] = EOF ;
    n := n + 1 ;
  endfor ;
\stopMPcode
\stoptyping

Say that this runs in 2.2 seconds, how come that the next one runs in 1.7 seconds
instead?

\starttyping
\startMPcode
  string f ; f := "foo2.tmp" ;
  string s[] ;
  string ss ;
  numeric n ; n := 0 ;
  for i=1 upto 10000 :
    ss := readfrom f ;
    exitif ss = EOF ;
    s[i] := ss ;
    n := n + 1 ;
  endfor ;
\stopMPcode
\stoptyping

The main reason is that the first case we have two lookups in the linked list
that determines variable \type {s} and the longer the list, the more time it will
take. In the second case we use an intermediate variable. Although that means
extra memory (de)allocation it still pays of. In practice you don't need to worry
too much about it but of course we can again follow the tree approach:

\startMPcode
  string f ; f := "foo3.tmp" ;
  string s[][][] ;
  string ss ;
  numeric n ; n := 0 ;
  for i=1 upto 10000 :
    ss := readfrom f ;
    exitif ss = EOF ;
    s[i div 1000][i div 100][i] := ss ;
    n := n + 1 ;
  endfor ;
\stopMPcode

This time we go down to 1.5 second. Timings could be a bit different in \MKIV\ and
\LMTX\ because in \LUAMETATEX\ all \METAPOST\ file \IO\ goes through \LUA\ but the
relative performance gains are the same. With \LUATEX\ and \MKIV\ I measures
2.9, 2.5 and 2.1 and with \LUAMETATEX\ and \LMTX\ I got 2.3, 1.7 and 1.5.

\stopsection

\stopchapter

\stopcomponent