1
2
3\environment luametafunstyle
4
5\startcomponent luametafuncontour
6
7\startchapter[title={Contour}]
8
9This feature started out as experiment triggered by a request on the mailing
10list. In the end it was a nice exploration of what is possible with a bit of
11\LUA. In a sense it is more subsystem than a simple \METAPOST\ macro because
12quite some \LUA\ code is involved and more might be used in the future. Its part
13of the fun.
14
15A contour is a line through equivalent values $z$ that result from applying a
16function to two variables $x$ and $y$. There is quite a bit of analysis needed
17to get these lines. In \METAFUN\ we currently support three methods for generating
18a colorful background and three for putting lines on top:
19
20One solution is to use the the isolines and isobands methods are described on the
21marching squares page of wikipedia:
22
23\starttyping
24https:en.wikipedia.orgwikiMarchingsquares
25\stoptyping
26
27This method is relative efficient as we dont do much optimization, simply
28because it takes time and the gain is not that much relevant. Because we support
29filling of multiple curves in one go, we get efficient paths anyway without side
30effects that normally can occur from many small paths alongside. In these days of
31multi megabyte movies and sound clips a request of making a \PDF\ file small is kind
32of strange anyway. In practice the penalty is not that large.
33
34As background we can use a bitmap. This method is also quite efficient because we
35use indexed colors which results in a very good compression. We use a simple
36mapping on a range of values.
37
38A third method is derived from the one that is distributed as \CCODE\ source
39file at:
40
41\starttyping
42https:physiology.arizona.edupeoplesecombcontours
43https:github.comsecombGreensV4
44\stoptyping
45
46We can create a background image, which uses a sequence of closed curves
47\footnote {I have to figure out how to improve it a bit so that multiple path
48dont get connected.}. It can also provide two variants of lines around the
49contours (we tag them shape and shade). Its all a matter of taste. In the
50meantime I managed to optimize the code a bit and I suppose that when I buy a new
51computer (the code was developed on an 8 year old machine) performance is
52probably acceptable.
53
54In order of useability you can think of isoband (band) with isolines (cell),
55bitmap (bitmap) with isolines (cell) and finally shapes (shape) with edges
56(edge). But lets start with a couple of examples.
57
58\startbuffer[1]
59\startMPcode{doublefun}
60 draw lmt_contour [
61 xmin = 0, xmax = 4pi, xstep = .05,
62 ymin = -6, ymax = 6, ystep = .05,
63
64 levels = 7,
65 height = 5cm,
66 preamble = "local sin, cos = math.sin, math.cos",
67 function = "cos(x) + sin(y)",
68 background = "bitmap",
69 foreground = "edge",
70 linewidth = 12,
71 cache = true,
72 ] ;
73\stopMPcode
74\stopbuffer
75
76\startplacefigure[reference=contour:1]
77 \getbuffer[1]
78\stopplacefigure
79
80\typebuffer[1][option=TEX]
81
82In \in {figure} [contour:1] we see the result. There is a in this case black and
83white image generated and on top of that we see lines. The step determines the
84resolution of the image. In practice using a bitmap is quite okay and also rather
85efficient: we use an indexed colorspace and, as already was mentioned, because
86the number of colors is limited such an image compresses well. A different
87rendering is seen in \in {figure} [contour:2] where we use the shape method for
88the background. That method creates outlines but is much slower, and when you use
89a high resolution (small step) it can take quite a while to identify the shapes.
90This is why we set the cache flag.
91
92\startbuffer[2]
93\startMPcode{doublefun}
94 draw lmt_contour [
95 xmin = 0, xmax = 4pi, xstep = .10,
96 ymin = -6, ymax = 6, ystep = .10,
97
98 levels = 7,
99 preamble = "local sin, cos = math.sin, math.cos",
100 function = "cos(x) - sin(y)",
101 background = "shape",
102 foreground = "shape",
103 linewidth = 12,
104 cache = true,
105 ] ;
106\stopMPcode
107\stopbuffer
108
109\typebuffer[2][option=TEX]
110
111\startplacefigure[reference=contour:2]
112 \getbuffer[2]
113\stopplacefigure
114
115We mentioned colorspace but havent seen any color yet, so lets set some in \in
116{figure} [contour:3]. Two variants are shown: a background \type {shape} with
117foreground \type {shape} and a background \type {bitmap} with a foreground \type
118{edge}. The bitmap renders quite fast, definitely when we compare with the shape,
119while the quality is as good at this size.
120
121\startbuffer[3a]
122\startMPcode{doublefun}
123 draw lmt_contour [
124 xmin = -10, xmax = 10, xstep = .1,
125 ymin = -10, ymax = 10, ystep = .1,
126
127 levels = 10,
128 height = 7cm,
129 color = "shade({1/2,1/2,0},{0,0,1/2})",
130 function = "x^2 + y^2",
131 background = "shape",
132 foreground = "shape",
133 linewidth = 12,
134 cache = true,
135 ] xsized .45TextWidth ;
136\stopMPcode
137\stopbuffer
138
139\startbuffer[3b]
140\startMPcode{doublefun}
141 draw lmt_contour [
142 xmin = -10, xmax = 10, xstep = .1,
143 ymin = -10, ymax = 10, ystep = .1,
144
145 levels = 10,
146 height = 7cm,
147 color = "shade({1/2,0,0},{0,0,1/2})",
148 function = "x^2 + y^2",
149 background = "bitmap",
150 foreground = "edge",
151 linewidth = 12,
152 cache = true,
153 ] xsized .45TextWidth ;
154\stopMPcode
155\stopbuffer
156
157\typebuffer[3a][option=TEX]
158
159\startplacefigure[reference=contour:3]
160 \startcombination
161 {\getbuffer[3a]} {\bf shape}
162 {\getbuffer[3b]} {\bf bitmap}
163 \stopcombination
164\stopplacefigure
165
166We use the \type {doublefun} instance because we need to be sure that we dont
167run into issues with scaled numbers, the default model in \METAPOST. The
168function that gets passed is {\em not} using \METAPOST\ but \LUA, so basically
169you can do very complex things. Here we directly pass code, but you can for
170instance also do this:
171
172\starttyping[option=TEX]
173\startluacode
174 function document.MyContourA(x,y)
175 return x^2 + y^2
176 end
177\stopluacode
178\stoptyping
179
180and then \type {function = "document.MyContourA(x,y)"}. As long as the function
181returns a valid number were okay. When you pass code directly you can use the
182\type {preamble} key to set local shortcuts. In the previous examples we took
183\type {sin} and \type {cos} from the math library but you can also roll out your
184own functions andor use the more elaborate \type {xmath} library. The color
185parameter is also a function, one that returns one or three arguments. In the
186next example we use \type {lin} to calculate a fraction of the current level and
187total number of levels.
188
189\startbuffer[4a]
190\startMPcode{doublefun}
191 draw lmt_contour [
192 xmin = -3, xmax = 3, xstep = .01,
193 ymin = -1, ymax = 1, ystep = .01,
194
195 levels = 10,
196 default = .5,
197 height = 5cm,
198 function = "x^2 + y^2 + x + y/2",
199 color = "lin(l), 0, 1/2",
200 background = "bitmap"
201 foreground = "none",
202 cache = true,
203 ] xsized TextWidth ;
204\stopMPcode
205\stopbuffer
206
207\typebuffer[4a][option=TEX]
208
209\startplacefigure[reference=contour:4a]
210 \getbuffer[4a]
211\stopplacefigure
212
213Instead of a bitmap we can use an isoband, which boils down to a set of tiny
214shapes that make up a bigger one. This is shown in \in {figure} [contour:4b].
215
216\startbuffer[4b]
217\startMPcode{doublefun}
218 draw lmt_contour [
219 xmin = -3, xmax = 3, xstep = .01,
220 ymin = -1, ymax = 1, ystep = .01,
221
222 levels = 10,
223 default = .5,
224 height = 5cm,
225 function = "x^2 + y^2 + x + y/2",
226 color = "lin(l), 1/2, 0",
227 background = "band",
228 foreground = "none",
229 cache = true,
230 ] xsized TextWidth ;
231\stopMPcode
232\stopbuffer
233
234\typebuffer[4b][option=TEX]
235
236\startplacefigure[reference=contour:4b]
237 \getbuffer[4b]
238\stopplacefigure
239
240You can draw several functions and see where they overlap:
241
242\startbuffer[5]
243\startMPcode{doublefun}
244 draw lmt_contour [
245 xmin = pi, xmax = 4pi, xstep = .1,
246 ymin = -3, ymax = 3, ystep = .1,
247
248 range = { -.1, .1 },
249 preamble = "local sin, cos = math.sin, math.cos",
250 functions = {
251 "sin(x) + sin(y)", "sin(x) + cos(y)",
252 "cos(x) + sin(y)", "cos(x) + cos(y)"
253 },
254 background = "bitmap",
255 linecolor = "black",
256 linewidth = 110,
257 color = "shade({1,1,0},{0,0,1})"
258 cache = true,
259 ] xsized TextWidth ;
260\stopMPcode
261\stopbuffer
262
263\typebuffer[5][option=TEX]
264
265\startplacefigure[reference=contour:5]
266 \getbuffer[5]
267\stopplacefigure
268
269The range determines the $z$ value(s) that we take into account. You can also
270pass a list of colors to be used. In \in {figure} [contour:6] this is
271demonstrated. There we also show a variant foreground \type {cell}, which uses a
272bit different method for calculating the edges. \footnote {This a bit of a
273playground: more variants might show up in due time.}
274
275\startbuffer[6]
276\startMPcode{doublefun}
277 draw lmt_contour [
278 xmin = -2pi, xmax = 2pi, xstep = .01,
279 ymin = -3, ymax = 3, ystep = .01,
280
281 range = { -.1, .1 },
282 preamble = "local sin, cos = math.sin, math.cos",
283 functions = { "sin(x) + sin(y)", "sin(x) + cos(y)" },
284 background = "bitmap",
285 foreground = "cell",
286 linecolor = "white",
287 linewidth = 110,
288 colors = { (12,12,12), red, green, blue }
289 level = 3,
290 linewidth = 6,
291 cache = true,
292 ] xsized TextWidth ;
293\stopMPcode
294\stopbuffer
295
296\typebuffer[6][option=TEX]
297
298Here the number of levels depends on the number of functions as each can overlap
299with another; for instance the outcome of two functions can overlap or not which
300means 3 cases, and with a value not being seen that gives 4 different cases.
301
302\startplacefigure[reference=contour:6]
303 \getbuffer[6]
304\stopplacefigure
305
306\startbuffer[7]
307\startMPcode{doublefun}
308 draw lmt_contour [
309 xmin = -2pi, xmax = 2pi, xstep = .01,
310 ymin = -3, ymax = 3, ystep = .01,
311
312 range = { -.1, .1 },
313 preamble = "local sin, cos = math.sin, math.cos",
314 functions = {
315 "sin(x) + sin(y)",
316 "sin(x) + cos(y)",
317 "cos(x) + sin(y)",
318 "cos(x) + cos(y)"
319 },
320 background = "bitmap",
321 foreground = "none",
322 level = 3,
323 color = "shade({2/3,0,0},{2/3,1,2/3})"
324 cache = true,
325 ] xsized TextWidth ;
326\stopMPcode
327\stopbuffer
328
329\typebuffer[7][option=TEX]
330
331Of course one can wonder how useful showing many functions but it can give nice
332pictures, as shown in \in {figure} [contour:7].
333
334\startplacefigure[reference=contour:7]
335 \getbuffer[7]
336\stopplacefigure
337
338\startbuffer[8]
339\startMPcode{doublefun}
340 draw lmt_contour [
341 xmin = -2pi, xmax = 2pi, xstep = .01,
342 ymin = -3, ymax = 3, ystep = .01,
343
344 range = { -.3, .3 },
345 preamble = "local sin, cos = math.sin, math.cos",
346 functions = {
347 "sin(x) + sin(y)",
348 "sin(x) + cos(y)",
349 "cos(x) + sin(y)",
350 "cos(x) + cos(y)"
351 },
352 background = "bitmap",
353 foreground = "none",
354 level = 3,
355 color = "shade({1,0,0},{0,1,0})"
356 cache = true,
357 ] xsized TextWidth ;
358\stopMPcode
359\stopbuffer
360
361\typebuffer[8][option=TEX]
362
363We can enlargen the window, which is demonstrated in \in {figure} [contour:8]. I
364suppose that such images only make sense in educational settings.
365
366\startplacefigure[reference=contour:8]
367 \getbuffer[8]
368\stopplacefigure
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
529In \in {figure} [contour:10] we see different combinations of backgrounds (in color)
530and foregrounds (edges) in action.
531
532\startbuffer[10a]
533\startMPcode{doublefun}
534 draw lmt_contour [
535 xmin = 0, xmax = 4pi, xstep = 0,
536 ymin = -6, ymax = 6, ystep = 0,
537
538 levels = 5, legend = false, linewidth = 12,
539
540 preamble = "local sin, cos = math.sin, math.cos",
541 function = "cos(x) - sin(y)",
542 color = "shade({1/2,0,0},{0,0,1/2})",
543
544 background = "bitmap", foreground = "edge",
545 ] xsized .3TextWidth ;
546\stopMPcode
547\stopbuffer
548
549\startbuffer[10b]
550\startMPcode{doublefun}
551 draw lmt_contour [
552 xmin = 0, xmax = 4pi, xstep = 0,
553 ymin = -6, ymax = 6, ystep = 0,
554
555 levels = 5, legend = false, linewidth = 12,
556
557 preamble = "local sin, cos = math.sin, math.cos",
558 function = "cos(x) - sin(y)",
559 color = "shade({1/2,0,0},{0,0,1/2})",
560
561 background = "bitmap", foreground = "cell",
562 ] xsized .3TextWidth ;
563\stopMPcode
564\stopbuffer
565
566\startbuffer[10c]
567\startMPcode{doublefun}
568 draw lmt_contour [
569 xmin = 0, xmax = 4pi, xstep = 0,
570 ymin = -6, ymax = 6, ystep = 0,
571
572 levels = 5, legend = false, linewidth = 12,
573
574 preamble = "local sin, cos = math.sin, math.cos",
575 function = "cos(x) - sin(y)",
576 color = "shade({1/2,0,0},{0,0,1/2})",
577
578 background = "bitmap", foreground = "none",
579 ] xsized .3TextWidth ;
580\stopMPcode
581\stopbuffer
582
583\startbuffer[10d]
584\startMPcode{doublefun}
585 draw lmt_contour [
586 xmin = 0, xmax = 4pi, xstep = 0,
587 ymin = -6, ymax = 6, ystep = 0,
588
589 levels = 5, legend = false, linewidth = 12,
590
591 preamble = "local sin, cos = math.sin, math.cos",
592 function = "cos(x) - sin(y)",
593 color = "shade({1/2,0,0},{0,0,1/2})",
594
595 background = "shape", foreground = "shape", cache = true,
596 ] xsized .3TextWidth ;
597\stopMPcode
598\stopbuffer
599
600\startbuffer[10e]
601\startMPcode{doublefun}
602 draw lmt_contour [
603 xmin = 0, xmax = 4pi, xstep = 0,
604 ymin = -6, ymax = 6, ystep = 0,
605
606 levels = 5, legend = false, linewidth = 12,
607
608 preamble = "local sin, cos = math.sin, math.cos",
609 function = "cos(x) - sin(y)",
610 color = "shade({1/2,0,0},{0,0,1/2})",
611
612 background = "shape", foreground = "edge", cache = true,
613 ] xsized .3TextWidth ;
614\stopMPcode
615\stopbuffer
616
617\startbuffer[10f]
618\startMPcode{doublefun}
619 draw lmt_contour [
620 xmin = 0, xmax = 4pi, xstep = 0,
621 ymin = -6, ymax = 6, ystep = 0,
622
623 levels = 5, legend = false, linewidth = 12,
624
625 preamble = "local sin, cos = math.sin, math.cos",
626 function = "cos(x) - sin(y)",
627 color = "shade({1/2,0,0},{0,0,1/2})",
628
629 background = "shape", foreground = "none", cache = true,
630 ] xsized .3TextWidth ;
631\stopMPcode
632\stopbuffer
633
634\startbuffer[10g]
635\startMPcode{doublefun}
636 draw lmt_contour [
637 xmin = 0, xmax = 4pi, xstep = 0,
638 ymin = -6, ymax = 6, ystep = 0,
639
640 levels = 5, legend = false, linewidth = 12,
641
642 preamble = "local sin, cos = math.sin, math.cos",
643 function = "cos(x) - sin(y)",
644 color = "shade({1/2,0,0},{0,0,1/2})",
645
646 background = "band", foreground = "edge",
647 ] xsized .3TextWidth ;
648\stopMPcode
649\stopbuffer
650
651\startbuffer[10h]
652\startMPcode{doublefun}
653 draw lmt_contour [
654 xmin = 0, xmax = 4pi, xstep = 0,
655 ymin = -6, ymax = 6, ystep = 0,
656
657 levels = 5, legend = false, linewidth = 12,
658
659 preamble = "local sin, cos = math.sin, math.cos",
660 function = "cos(x) - sin(y)",
661 color = "shade({1/2,0,0},{0,0,1/2})",
662
663 background = "band", foreground = "cell",
664 ] xsized .3TextWidth ;
665\stopMPcode
666\stopbuffer
667
668\startbuffer[10i]
669\startMPcode{doublefun}
670 draw lmt_contour [
671 xmin = 0, xmax = 4pi, xstep = 0,
672 ymin = -6, ymax = 6, ystep = 0,
673
674 levels = 5, legend = false, linewidth = 12,
675
676 preamble = "local sin, cos = math.sin, math.cos",
677 function = "cos(x) - sin(y)",
678 color = "shade({1/2,0,0},{0,0,1/2})",
679
680 background = "band", foreground = "none",
681 ] xsized .3TextWidth ;
682\stopMPcode
683\stopbuffer
684
685\typebuffer[10b][option=TEX]
686
687
688
689There are quite some settings. Some deal with the background, some with the
690foreground and quite some deal with the legend.
691
692\starttabulate[TTTp]
693\FL
694\BC name \BC type \BC default \BC comment \NC \NR
695\ML
696\NC xmin \NC numeric \NC 0 \NC needs to be set \NC \NR
697\NC xmax \NC numeric \NC 0 \NC needs to be set \NC \NR
698\NC ymin \NC numeric \NC 0 \NC needs to be set \NC \NR
699\NC ymax \NC numeric \NC 0 \NC needs to be set \NC \NR
700\NC xstep \NC numeric \NC 0 \NC auto 1200 when zero \NC \NR
701\NC ystep \NC numeric \NC 0 \NC auto 1200 when zero \NC \NR
702\NC checkresult \NC boolean \NC false \NC checks for overflow and NaN \NC \NR
703\NC defaultnan \NC numeric \NC 0 \NC the value to be used when NaN \NC \NR
704\NC defaultinf \NC numeric \NC 0 \NC the value to be used when overflow \NC \NR
705\ML
706\NC levels \NC numeric \NC 10 \NC number of different levels to show \NC \NR
707\NC level \NC numeric \NC \NC only show this level (foreground) \NC \NR
708\ML
709\NC preamble \NC string \NC \NC shortcuts \NC \NR
710\NC function \NC string \NC x y \NC the result z value \NC \NR
711\NC functions \NC list \NC \NC multiple functions (overlapping levels) \NC \NR
712\NC color \NC string \NC lin(l) \NC the result color value for level l (1 or 3 values) \NC \NR
713\NC colors \NC numeric \NC \NC used when set \NC \NR
714\ML
715\NC background \NC string \NC bitmap \NC band, bitmap, shape \NC \NR
716\NC foreground \NC string \NC auto \NC cell, edge, shape auto \NC \NR
717\ML
718\NC linewidth \NC numeric \NC .25 \NC \NC \NR
719
720\NC linecolor \NC string \NC gray \NC \NC \NR
721\ML
722\NC width \NC numeric \NC 0 \NC automatic when zero \NC \NR
723\NC height \NC numeric \NC 0 \NC automatic when zero \NC \NR
724\ML
725\NC trace \NC boolean \NC false \NC \NC \NR
726\ML
727\NC legend \NC string \NC all \NC x y z function range all \NC \NR
728\NC legendheight \NC numeric \NC LineHeight \NC \NC \NR
729\NC legendwidth \NC numeric \NC LineHeight \NC \NC \NR
730\NC legendgap \NC numeric \NC 0 \NC \NC \NR
731\NC legenddistance \NC numeric \NC EmWidth \NC \NC \NR
732\NC textdistance \NC numeric \NC 2EmWidth3 \NC \NC \NR
733\NC functiondistance \NC numeric \NC ExHeight \NC \NC \NR
734\NC functionstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
735\NC xformat \NC string \NC @0.2N \NC number format template \NC \NR
736\NC yformat \NC string \NC @0.2N \NC number format template \NC \NR
737\NC zformat \NC string \NC @0.2N \NC number format template \NC \NR
738\NC xstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
739\NC ystyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
740\NC zstyle \NC string \NC \NC \CONTEXT\ style name \NC \NR
741\ML
742\NC axisdistance \NC numeric \NC ExHeight \NC \NC \NR
743\NC axislinewidth \NC numeric \NC .25 \NC \NC \NR
744\NC axisoffset \NC numeric \NC ExHeight4 \NC \NC \NR
745\NC axiscolor \NC string \NC black \NC \NC \NR
746\NC ticklength \NC numeric \NC ExHeight \NC \NC \NR
747\ML
748\NC xtick \NC numeric \NC 5 \NC \NC \NR
749\NC ytick \NC numeric \NC 5 \NC \NC \NR
750\NC xlabel \NC numeric \NC 5 \NC \NC \NR
751\NC ylabel \NC numeric \NC 5 \NC \NC \NR
752\LL
753\stoptabulate
754
755\startplacefigure[reference=contour:10]
756 \startcombination[3*3]
757 {\getbuffer[10a]} {\bf bitmap edge}
758 {\getbuffer[10b]} {\bf bitmap cell}
759 {\getbuffer[10c]} {\bf bitmap none}
760 {\getbuffer[10d]} {\bf shape shape}
761 {\getbuffer[10e]} {\bf shape edge}
762 {\getbuffer[10f]} {\bf shape none}
763 {\getbuffer[10g]} {\bf band edge}
764 {\getbuffer[10h]} {\bf band cell}
765 {\getbuffer[10i]} {\bf band none}
766 \stopcombination
767\stopplacefigure
768
769\stopchapter
770
771\stopcomponent
772 |