1
2
3\startcomponent metafunexamples
4
5\environment metafunenvironment
6
7\startchapter[title={A few applications}]
8
9\startintro
10
11For those who need to be inspired, we will demonstrate how \METAPOST\ can be used
12to enhance your document with simple graphics. In these examples we will try to
13be not too clever, simply because we lack the experience to be that clever. The
14real tricks can be found in the files that come with \METAPOST.
15
16\stopintro
17
18\startsection[reference={sec:coils,sec:springs},title={Simple drawings}]
19
20In the words of John Hobby, the creator of \METAPOST, \quotation {\METAPOST\ is
21particularly wellsuited for generating figures for technical documents where
22some aspects of a picture may be controlled by mathematical or geometrical
23constraints that are best expressed symbolically. In other words, \METAPOST\ is
24not meant to take the place of a freehand drawing tool or even an interactive
25graphics editor}.
26
27An example of such a picture is the following one, which is dedicated to David
28Arnold, who asked me once how to draw a spring. So, imagine that we want to draw
29a schematic view of a system of four springs.
30
31\startbuffer[a]
32def spring (expr a, b, w, h, n) =
33 ( ( (0,0) (0,h)
34 for i=1 upto n1: (if odd(i) : fi w2,ih) endfor
35 (0,nh) (0,n2h) )
36 yscaled ((xpart (ba) ypart (ba))(n2h))
37 rotatedaround(origin,90angle(ba))
38 shifted a )
39enddef ;
40\stopbuffer
41
42\startbuffer[b]
43vardef spring (expr a, b, w, h, n) =
44 pair vec ; path pat ; numeric len ; numeric ang ;
45 vec := (ba) ;
46 pat := for i=1 upto n1: (if odd(i):fi w2,i)endfor (0,n) ;
47 pat := (0,0)(0,h) pat shifted (0,h)(0,nh)(0,n2h) ;
48 len := (xpart vec ypart vec)(n2h) ;
49 ang := 90angle(vec) ;
50 ( pat yscaled len rotatedaround(origin,ang) shifted a )
51enddef ;
52\stopbuffer
53
54\startbuffer[c]
55path p ; p :=
56 (0,0)spring((.5cm,0),(2.5cm,0),.5cm,0,10)(3cm,0) ;
57
58draw p withpen pencircle scaled 2pt ;
59draw p withpen pencircle scaled 1pt withcolor .8white;
60\stopbuffer
61
62\startbuffer[d]
63z1 = (2cm,0) ; z2 = (0,2cm) ;
64z3 = (2cm,0) ; z4 = (0,2cm) ;
65
66pickup pencircle scaled 1.5pt ;
67
68drawoptions (withcolor .625red) ;
69
70draw spring (z1, z2, .75cm, 2, 10) ; draw z1 1.5 z1 ;
71draw spring (z2, z3, .75cm, 2, 9) ; draw z2 1.1 z2 ;
72draw spring (z3, z4, .75cm, 2, 8) ; draw z3 1.5 z3 ;
73draw spring (z4, z1, .75cm, 2, 7) ; draw z4 1.1 z4 ;
74\stopbuffer
75
76\startbuffer[e]
77drawarrow
78 (0,0)spring((.5cm,0),(2.5cm,0),.5cm,0,10)(3cm,0)
79 withpen pencircle scaled 2pt withcolor .625red ;
80\stopbuffer
81
82\startbuffer[f]
83numeric u ; u := 1mm ; pickup pencircle scaled (u2) ;
84drawoptions (withcolor .625red) ;
85draw (0,0)spring((5u,0),(25u,0),5u,0,10)(30u,0) ;
86drawoptions (dashed evenly withcolor .5white) ;
87draw (0,0)spring((5u,0),(35u,0),(2535)*5u,0,10)(40u,0) ;
88\stopbuffer
89
90\startlinecorrection[blank]
91\processMPbuffer[a,d]
92\stoplinecorrection
93
94A rather natural way to define such a system is:
95
96\typebuffer[d]
97
98Here, the macro \type {spring} takes 5arguments: two points, the width of the
99winding, the length of the connecting pieces, and the number of elements (half
100windings). The definition of \type {spring} is less complicated than readable.
101
102\typebuffer[a]
103
104First we build a path starting in the origin, going left or right depending on
105the counter being an odd number.
106
107\starttyping
108pat := (0,0) ;
109for i=1 upto n1:
110 if odd(i) :
111 pat := pat (w2,i) ;
112 else :
113 pat := pat (w2,i) ;
114 fi ;
115endfor ;
116pat := pat (0,n) ;
117\stoptyping
118
119Once you are accustomed to the way \METAPOST\ interprets (specialists may say
120expand) the source code, you will start using \type {if} and \type {for}
121statements in assignments. The previous code can be converted in a one liner,
122using the pattern:
123
124\starttyping
125pat := for i=1 upto n1: (x,y) endfor (0,n) ;
126\stoptyping
127
128The loop splits out a series of \type {(x,y)} but the last point is added
129outside the loop. Otherwise \type {pat} would have ended with a dangling \type
130{}. Of course we need to replace \type {(x,y)} by something meaningful, so we
131get:
132
133\starttyping
134pat := for i=1 upto n1: (if odd(i):fi w2,i)endfor (0,n) ;
135\stoptyping
136
137We scale this path to the length needed. The expression $ba$ calculates a
138vector, starting at $a$ and ending at $b$. In \METAPOST, the expression \type
139{ab} is identical to $\sqrt{a2b2}$. Thus, the expression \typ {xpart (ba)
140 ypart (ba)} calculates the length of the vector $ba$. Because the unscaled
141spring has length $n2h$, scaling by the expression \typ {((xpart (ba) ypart
142(ba)) (n2h))} gives the spring the same length as the vector $ba$.
143
144Because we have drawn our spring in the vertical position, we first rotate it 90
145degrees clockwise to a horizontal position, and then rotate it through an angle
146equal to the angle in which the vector $ba$ is pointing. After that, we shift it
147to the first point. The main complications are that we also want to draw
148connecting lines at the beginning and end, as well as support springs that
149connect arbitrary points. Since no check is done on the parameters, you should be
150careful in using this macro.
151
152When we want to improve the readability, we have to use intermediate variables.
153Since the macro is expected to return a path, we must make sure that the content
154matches this expectation.
155
156\typebuffer[b]
157
158If you use \type {vardef}, then the last statement is the return value. Here,
159when \typ {p := spring (z1, z2, .75cm, 2, 10)} is being parsed, the macro is
160expanded, the variables are kept invisible for the assignment, and the path at
161the end is considered to be the return value. In a \type {def} the whole body of
162the macro is \quote {pasted} in the text, while in a \type {vardef} only the last
163line is visible. We will demonstrate this with a simple example.
164
165\starttyping
166def one = (n,n) ; n := n1 ; enddef ;
167def two = n := n 1 ; (n,n) enddef ;
168\stoptyping
169
170Now, when we say:
171
172\starttyping
173pair a, b ; numeric n ; n= 10 ; a := one ; b := two ;
174\stoptyping
175
176we definitely get an error message. This is because, when macro \type {two} is
177expanded, \METAPOST\ sees something:
178
179\starttyping
180b := n := n 1 ;
181\stoptyping
182
183By changing the second definition in
184
185\starttyping
186vardef two = n := n 1 ; (n,n) enddef ;
187\stoptyping
188
189the increment is expanded out of sight for \type {b :=} and the pair \type
190{(n,n)} is returned.
191
192We can draw a slightly better looking spring by drawing twice with a different
193pen. The following commands use the spring macro implemented by the \type
194{vardef}.
195
196\typebuffer[c]
197
198This time we get:
199
200\startlinecorrection[blank]
201\processMPbuffer[a,c]
202\stoplinecorrection
203
204Since the \type {spring} macro returns a path, you can do whatever is possible
205with a path, like drawing an arrow:
206
207\startlinecorrection[blank]
208\processMPbuffer[a,e]
209\stoplinecorrection
210
211Or even (watch how we use the neutral unit \type {u} to specify the dimensions):
212
213\startlinecorrection[blank]
214\processMPbuffer[a,f]
215\stoplinecorrection
216
217This was keyed in as:
218
219\typebuffer[e]
220
221and:
222
223\typebuffer[f]
224
225\stopsection
226
227\startsection[reference=sec:free labels,title={Free labels}]
228
229\index {labels}
230
231The \METAPOST\ label macro enables you to position text at certain points. This
232macro is kind of special, because it also enables you to influence the
233positioning. For that purpose it uses a special kind of syntax which we will not
234discuss here in detail.
235
236\startbuffer[a]
237pickup pencircle scaled 1mm ;
238path p ; p := fullcircle scaled 3cm ;
239draw p withcolor .625yellow ;
240dotlabel.rt ("right" , point 0 of p) ;
241dotlabel.urt ("upper right" , point 1 of p) ;
242dotlabel.top ("top" , point 2 of p) ;
243dotlabel.ulft ("upper left" , point 3 of p) ;
244dotlabel.lft ("left" , point 4 of p) ;
245dotlabel.llft ("lower left" , point 5 of p) ;
246dotlabel.bot ("bottom" , point 6 of p) ;
247dotlabel.lrt ("lower right" , point 7 of p) ;
248\stopbuffer
249
250\typebuffer[a]
251
252The \type {label} command just typesets a text, while \type {dotlabel} also draws
253a dot at the position of the label. The \type {thelabel} (not shown here) command
254returns a picture.
255
256\startlinecorrection[blank]
257\processMPbuffer[a]
258\stoplinecorrection
259
260There is a numeric constant \type {labeloffset} that can be set to influence the
261distance between the point given and the content of the label. When we set the
262offset to zero, we get the following output.
263
264\startbuffer[x]
265interim labeloffset := 0pt ;
266\stopbuffer
267
268\startlinecorrection[blank]
269\processMPbuffer[x,a]
270\stoplinecorrection
271
272This kind of positioning works well as long as we know where we want the label to
273be placed. However, when we place labels automatically, for instance in a macro,
274we have to apply a few clever tricks. There are for sure many ways to accomplish
275this goal, but here we will follow the mathless method.
276
277\startbuffer[a]
278pickup pencircle scaled 1mm ;
279path p ; p := fullcircle scaled 3cm ;
280draw p withcolor .625yellow ;
281vardef do (expr str) =
282 save currentpicture ; picture currentpicture ;
283 currentpicture := thelabel(str,origin) ;
284 draw boundingbox currentpicture withpen pencircle scaled .5pt ;
285 currentpicture
286enddef ;
287\stopbuffer
288
289\startbuffer[b]
290dotlabel.rt (do("right") , point 0 of p) ;
291dotlabel.urt (do("upper right") , point 1 of p) ;
292dotlabel.top (do("top") , point 2 of p) ;
293dotlabel.ulft (do("upper left") , point 3 of p) ;
294dotlabel.lft (do("left") , point 4 of p) ;
295dotlabel.llft (do("lower left") , point 5 of p) ;
296dotlabel.bot (do("bottom") , point 6 of p) ;
297dotlabel.lrt (do("lower right") , point 7 of p) ;
298\stopbuffer
299
300\startlinecorrection[blank]
301\processMPbuffer[x,a,b]
302\stoplinecorrection
303
304The previous graphic visualizes the bounding box of the labels. This bounding box
305is rather tight and therefore the placement of labels will always be suboptimal.
306Compare the alignment of the left and rightmost labels. The \type {btex}\type
307{etex} method is better, since then we can add struts, like:
308
309\starttyping
310btex \strut right etex
311\stoptyping
312
313to force labels with uniform depths and heights. The next graphic demonstrates
314that this looks better indeed. Also, as \TEX\ does the typesetting we get the
315current text font instead of the label font and the content will be properly
316typeset; for instance kerning will be applied when applicable. Spending some time
317on such details pays back in better graphics.
318
319\startbuffer[b]
320dotlabel.rt (do(btex \strut right etex) , point 0 of p) ;
321dotlabel.urt (do(btex \strut upper right etex) , point 1 of p) ;
322dotlabel.top (do(btex \strut top etex) , point 2 of p) ;
323dotlabel.ulft (do(btex \strut upper left etex) , point 3 of p) ;
324dotlabel.lft (do(btex \strut left etex) , point 4 of p) ;
325dotlabel.llft (do(btex \strut lower left etex) , point 5 of p) ;
326dotlabel.bot (do(btex \strut bottom etex) , point 6 of p) ;
327dotlabel.lrt (do(btex \strut lower right etex) , point 7 of p) ;
328\stopbuffer
329
330\startlinecorrection[blank]
331\processMPbuffer[x,a,b]
332\stoplinecorrection
333
334Now, what happens when we want to place labels in other positions? In the worst
335case, given that we place the labels manually, we end up in vague arguments in
336favour for one or the other placement.
337
338\startbuffer[y]
339p := p rotatedaround(center p, 22.5) ;
340\stopbuffer
341
342\startlinecorrection[blank]
343\processMPbuffer[x,a,y,b]
344\stoplinecorrection
345
346Although any automatic mechanism will be suboptimal, we can give it a try to
347write a macro that deals with arbitrary locations. This macro will accept three
348arguments and return a picture.
349
350\starttyping
351thefreelabel("some string or picture",a position,the origin)
352\stoptyping
353
354Our testcase is just a simple \type {for} loop that places a series of labels.
355The \type {freedotlabel} macro is derived from \type {thefreelabel}.
356
357\startbuffer[c]
358pickup pencircle scaled 1mm ;
359path p ; p := fullcircle scaled 3cm ;
360draw p withcolor .625yellow ;
361for i=0 step .5 until 7.5 :
362 freedotlabel ("text" , point i of p, center p) ;
363endfor ;
364\stopbuffer
365
366\typebuffer[c]
367
368As a first step we will simply place the labels without any correction. We also
369visualize the bounding box.
370
371\startbuffer[b]
372vardef freedotlabel (expr str, loc, ori) =
373 drawdot loc ; draw thefreelabel(str,loc,ori) ;
374enddef ;
375
376vardef freelabel (expr str, loc, ori) =
377 draw thefreelabel(str,loc,ori) ;
378enddef ;
379\stopbuffer
380
381\startbuffer[a]
382vardef thefreelabel (expr str, loc, ori) =
383 save s ; picture s ; s := thelabel(str,loc) ;
384 draw boundingbox s withpen pencircle scaled .5pt ;
385 s
386enddef ;
387\stopbuffer
388
389\typebuffer[a]
390
391To make our lives more easy, we also define a macro that draws the dot as well as
392a macro that draws the label.
393
394\typebuffer[b]
395
396Now we get:
397
398\startlinecorrection[blank]
399\processMPbuffer[x,a,b,c]
400\stoplinecorrection
401
402The original label macros permits us to align the label at positions, 4corners
403and 4points halfway the sides. It happens that circles are also composed of
4048points. Because in most cases the label is to be positioned in the direction of
405the center of a curve and the point at hand, it makes sense to take circles as
406the starting points for positioning the labels.
407
408To help us in positioning, we define a special square path, \type {freesquare}.
409This path is constructed out of 8points that match the positions that are used
410to align labels.
411
412\startbuffer[d]
413path freesquare ;
414
415freesquare := ((1,0)(1,1)(0,1)(1,1)
416 (1,0)(1,1)(0,1)(1,1)cycle) scaled .5 ;
417\stopbuffer
418
419\typebuffer[d]
420
421We now show this free path together with a circle, using the following
422definitions:
423
424\startbuffer[e]
425drawpath fullcircle scaled 3cm ;
426drawpoints fullcircle scaled 3cm ;
427drawpointlabels fullcircle scaled 3cm ;
428currentpicture := currentpicture shifted (5cm,0) ;
429drawpath freesquare scaled 3cm ;
430drawpoints freesquare scaled 3cm ;
431drawpointlabels freesquare scaled 3cm ;
432\stopbuffer
433
434\typebuffer[e]
435
436We use two drawing macros that are part of the suite of visual debugging macros.
437
438\startlinecorrection[blank]
439\processMPbuffer[x,d,e]
440\stoplinecorrection
441
442As you can see, point1 is the corner point that suits best for alignment when a
443label is put at point1 of the circle. We will now rewrite \type {thefreelabel}
444in such a way that the appropriate point of the associated \type {freesquare} is
445found.
446
447\startbuffer[a]
448vardef thefreelabel (expr str, loc, ori) =
449 save s, p, q, l ; picture s ; path p, q ; pair l ;
450 s := thelabel(str,loc) ;
451 p := fullcircle scaled (2*length(locori)) shifted ori ;
452 q := freesquare xyscaled (urcorner s llcorner s) ;
453 l := point (xpart (p intersectiontimes (oriloc))) of q ;
454 draw q shifted loc withpen pencircle scaled .5pt ;
455 draw l shifted loc withcolor .625yellow ;
456 draw loc withcolor .625red ;
457 s
458enddef ;
459\stopbuffer
460
461\typebuffer[a]
462
463The macro xyscaled is part of \METAFUN\ and scales in two directions at once. The
464\METAPOST\ primitive \type {intersectiontimes} returns a pair of time values of
465the point where two paths intersect. The first part of the pair concerns the
466first path.
467
468\startlinecorrection[blank]
469\processMPbuffer[x,a,b,c]
470\stoplinecorrection
471
472We are now a small step from the exact placement. If we change the last line of
473the macro into:
474
475\starttyping
476(s shifted l)
477\stoptyping
478
479we get the displacement we want. Although the final look and feel is also
480determined by the text itself, the average result is quite acceptable.
481
482\startbuffer[a]
483vardef thefreelabel (expr str, loc, ori) =
484 save s, p, q, l ; picture s ; path p, q ; pair l ;
485 s := thelabel(str,loc) ;
486 p := fullcircle scaled (2*length(locori)) shifted ori ;
487 q := freesquare xyscaled (urcorner s llcorner s) ;
488 l := point (xpart (p intersectiontimes (oriloc))) of q ;
489 draw q shifted loc withpen pencircle scaled .5pt ;
490 draw l shifted loc withcolor .625yellow ;
491 draw loc withcolor .625red ;
492 (s shifted l)
493enddef ;
494\stopbuffer
495
496\startlinecorrection[blank]
497\processMPbuffer[x,a,b,c]
498\stoplinecorrection
499
500Because we also want to pass pictures, and add a bit of offset too, the final
501implementation is slightly more complicated. The picture is handled with an
502additional condition, and the offset with the \METAFUN\ macro \type {enlarged}.
503
504\startbuffer[a]
505newinternal freelabeloffset ; freelabeloffset := 3pt ;
506
507vardef thefreelabel (expr str, loc, ori) =
508 save s, p, q, l ; picture s ; path p, q ; pair l ;
509 interim labeloffset := freelabeloffset ;
510 s := if string str : thelabel(str,loc)
511 else : str shifted center str shifted loc fi ;
512 setbounds s to boundingbox s enlarged freelabeloffset ;
513 p := fullcircle scaled (2*length(locori)) shifted ori ;
514 q := freesquare xyscaled (urcorner s llcorner s) ;
515 l := point (xpart (p intersectiontimes (oriloc))) of q ;
516 setbounds s to boundingbox s enlarged freelabeloffset ;
517 (s shifted l)
518enddef ;
519\stopbuffer
520
521\typebuffer[a]
522
523Watch how we temporarily enlarge the bounding box of the typeset label text. We
524will now test this macro on a slightly rotated circle, using labels typeset by
525\TEX. The \type {reverse} is there purely for cosmetic reasons, to suit the label
526texts.
527
528\startbuffer[b]
529pickup pencircle scaled 1mm ;
530path p ; p := reverse fullcircle rotated 25 scaled 3cm ;
531draw p withcolor .625yellow ; pair cp ; cp := center p ;
532freedotlabel (btex \strut We can etex, point 0 of p, cp) ;
533freedotlabel (btex \strut go on etex, point 1 of p, cp) ;
534freedotlabel (btex \strut and on etex, point 2 of p, cp) ;
535freedotlabel (btex \strut in etex, point 3 of p, cp) ;
536freedotlabel (btex \strut defining etex, point 4 of p, cp) ;
537freedotlabel (btex \strut funny etex, point 5 of p, cp) ;
538freedotlabel (btex \strut macros. etex, point 6 of p, cp) ;
539freedotlabel (btex \strut Cant we? etex, point 7 of p, cp) ;
540\stopbuffer
541
542\startlinecorrection[blank]
543\processMPbuffer[a,b]
544\stoplinecorrection
545
546\typebuffer[b]
547
548Unfortunately we can run into problems due to rounding errors. Therefore we use a
549less readable but more safe expression for calculating the intersection points.
550Instead of using point \type {loc} as endpoint we use \type {loc} shifted over a
551very small distance into the direction \type {loc} from \type{ori}. In the
552assignment to\type {l} we replace \type {loc} by:
553
554\starttyping
555 ( (1eps) * arclength(oriloc) * unitvector(locori) )
556\stoptyping
557
558\stopsection
559
560\startsection[title={Marking angles}]
561
562\index{angles}
563
564A convenient \METAPOST\ macro is \type {unitvector}. When we draw a line segment
565from the origin to the point returned by this macro, the segment has a length of
5661base point. This macro has a wide range of applications, but some basic
567knowledge of vector algebra is handy. The following lines of \METAPOST\ code
568demonstrate the basics behind unitvectors.
569
570\startbuffer
571pair uv ; pickup pencircle scaled 1mm ; autoarrows := true ;
572draw fullcircle scaled 2cm withcolor .625red ;
573for i=(10,35), (40,20), (85,15) :
574 draw origini dashed evenly withcolor .625white ;
575 drawarrow originunitvector(i) scaled 1cm withcolor .625yellow ;
576endfor ;
577draw origin withcolor .625red ;
578\stopbuffer
579
580\typebuffer
581
582The circle has a radius of 1cm, and the three line segments are drawn from the
583origin in the direction of the points that are passed as arguments. Because the
584vector has length of1, we scale it to the radius to let it touch the circle. By
585setting \type {autoarrows} we make sure that the arrowheads are scaled
586proportionally to the linewidth of 1mm.
587
588\startlinecorrection[blank]
589\processMPbuffer
590\stoplinecorrection
591
592An application of this macro is drawing the angle between two lines. In the
593\METAPOST\ manual you can find two macros for drawing angles: \type {markangle}
594and \type {markrtangle}. You may want to take a look at their definitions
595before we start developing our own alternatives.
596
597\startbuffer[x]
598pickup pencircle scaled 1mm ; autoarrows := true ;
599drawoptions(withcolor .625white) ;
600\stopbuffer
601
602\startbuffer[a]
603def anglebetween (expr a, b) =
604 (unitvector(a){a rotated 90} .. unitvector(b))
605enddef ;
606\stopbuffer
607
608\startbuffer[b]
609pair a, b ; a := (2cm,1cm) ; b := (3cm,1cm) ;
610drawarrow origina ; drawarrow originb ;
611drawarrow anglebetween(a,b) scaled 1cm withcolor .625red ;
612\stopbuffer
613
614\startlinecorrection[blank]
615\processMPbuffer[x,a,b]
616\stoplinecorrection
617
618The previous graphic demonstrates what we want to accomplish: a circular curve
619indicating the angle between two straight lines. The lines and curve are drawn
620with the code:
621
622\typebuffer[b]
623
624where \type {anglebetween} is defined as:
625
626\typebuffer[a]
627
628Both unitvectors return just a point on the line positioned 1unit (later scaled
629to 1cm) from the origin. We connect these points by a curve that starts in the
630direction at the first point. If we omit the \type {a rotated 90} direction
631specifier, we get:
632
633\startbuffer[a]
634def anglebetween (expr a, b) =
635 (unitvector(a) .. unitvector(b))
636enddef ;
637\stopbuffer
638
639\startlinecorrection[blank]
640\processMPbuffer[x,a,b]
641\stoplinecorrection
642
643These definitions of \type {anglebetween} are far from perfect. If we dont start
644in the origin, we get the curve in the wrong place and when we swap both points,
645we get the wrong curve.
646
647\startbuffer[a]
648def anglebetween (expr endofa, endofb, common, length) =
649 (unitvector (endofacommon){(endofacommon) rotated 90} ..
650 unitvector (endofbcommon)) scaled length shifted common
651enddef ;
652\stopbuffer
653
654\startbuffer[b]
655pair a, b, c ; a := (2cm,1cm) ; b := (3cm,1cm) ; c := (1cm,.5cm) ;
656drawarrow ca ; drawarrow cb ;
657drawarrow anglebetween(a,b,c,1cm) withcolor .625red ;
658\stopbuffer
659
660The solution for the displacement is given in the \METAPOST\ manual and looks
661like this (we package the macro a bit different):
662
663\typebuffer[a]
664
665As you can see, we compensate for the origin of both vectors. This macro is
666called with a few more parameters. We need to pass the length, since we want to
667add the shift to the macro and the shift takes place after the scaling.
668
669\typebuffer[b]
670
671That the results are indeed correct, is demonstrated by the output of following
672example:
673
674\startlinecorrection[blank]
675\processMPbuffer[x,a,b]
676\stoplinecorrection
677
678However, when we swap the points, we get:
679
680\startbuffer[a]
681def anglebetween (expr endofb, endofa, common, length) =
682 (unitvector (endofacommon){(endofacommon) rotated 90} ..
683 unitvector (endofbcommon)) scaled length shifted common
684enddef ;
685\stopbuffer
686
687\startlinecorrection[blank]
688\processMPbuffer[x,a,b]
689\stoplinecorrection
690
691This means that instead of rotating over $90$ degrees, we have to rotate over
692$90$ or $270$ degrees. That way the arrow will also point in the other
693direction. There are undoubtedly more ways to determine the direction, but the
694following method also demonstrates the use of \type {turningnumber}, which
695reports the direction of a path. For this purpose we compose a dummy cyclic path.
696
697\startbuffer[a]
698vardef anglebetween (expr endofa, endofb, common, length) =
699 save tn ; tn := turningnumber(commonendofaendofbcycle) ;
700show tn ;
701 (unitvector(endofacommon){(endofacommon) rotated (tn*90)} ..
702 unitvector(endofbcommon)) scaled length shifted common
703enddef ;
704\stopbuffer
705
706\typebuffer[a]
707
708Because we use an intermediate variable, just to keep things readable, we have to
709use \type {vardef} to hide the assignment for the outside world. We demonstrate
710this macro using the following code:
711
712\startbuffer[b]
713pair a, b, c ; a := (2cm,1cm) ; b := (3cm,1cm) ; c := (1cm,.5cm) ;
714drawarrow ca ; drawarrow cb ;
715drawarrow anglebetween(a,b,c,0.75cm) withcolor .625red ;
716drawarrow anglebetween(b,a,c,1.50cm) withcolor .625red ;
717\stopbuffer
718
719\typebuffer[b]
720
721Watch how both arrows point in the direction of the line that is determined by
722the second point.
723
724\startlinecorrection[blank]
725\processMPbuffer[x,a,b]
726\stoplinecorrection
727
728We now have the framework of an angle drawing macro ready and can start working
729placing the label.
730
731\startbuffer[a]
732vardef anglebetween (expr endofa, endofb, common, length, str) =
733 save curve, where ; path curve ; numeric where ;
734 where := turningnumber (commonendofaendofbcycle) ;
735 curve := (unitvector(endofacommon){(endofacommon) rotated (where*90)}
736 .. unitvector(endofbcommon)) scaled length shifted common ;
737 draw thefreelabel(str,point .5 of curve,common) withcolor black ;
738 curve
739enddef ;
740\stopbuffer
741
742\typebuffer[a]
743
744The macro \type {thefreelabel} is part of \METAFUN\ and is explained in detail in
745\in {section} [sec:free labels]. This macro tries to place the label as good as
746possible without user interference.
747
748\startbuffer[b]
749pair a ; a := (2cm,1cm) ; drawarrow origina ;
750pair b ; b := (3cm, 1cm) ; drawarrow originb ;
751drawarrow
752 anglebetween(a,b,origin,1cm,btex $\alpha$ etex)
753 withcolor .625red ;
754\stopbuffer
755
756\typebuffer[b]
757
758Instead of a picture we may also pass a string, but using \TEX\ by means of \type
759{btex}\type {etex} often leads to better results.
760
761\startlinecorrection[blank]
762\processMPbuffer[x,a,b]
763\stoplinecorrection
764
765Because in most cases we want the length to be consistent between figures and
766because passing two paths is more convenient than passing three points, the final
767definition looks slightly different.
768
769\startbuffer[a]
770numeric anglelength ; anglelength := 20pt ;
771
772vardef anglebetween (expr a, b, str) =
773 save endofa, endofb, common, curve, where ;
774 pair endofa, endofb, common ; path curve ; numeric where ;
775 endofa := point length(a) of a ;
776 endofb := point length(b) of b ;
777 if round point 0 of a = round point 0 of b :
778 common := point 0 of a ;
779 else :
780 common := a intersectionpoint b ;
781 fi ;
782 where := turningnumber (commonendofaendofbcycle) ;
783 curve := (unitvector (endofacommon){(endofacommon) rotated (where*90)} ..
784 unitvector (endofbcommon)) scaled anglelength shifted common ;
785 draw thefreelabel(str,point .5 of curve,common) withcolor black ;
786 curve
787enddef ;
788\stopbuffer
789
790\typebuffer[a]
791
792This macro has a few more \type {if}s than its predecessor. First we test if the
793label is a string, and if so, we calculate the picture ourselves, otherwise we
794leave this to the user.
795
796\startbuffer[b]
797path a, b, c, d, e, f ;
798a := origin( 2cm, 1cm) ; b := origin( 1cm, 2cm) ;
799c := origin(2cm, 2cm) ; d := origin(2cm,1cm) ;
800e := origin(1cm,2cm) ; f := origin( 1cm,2cm) ;
801for i=a, b, c, d, e, f : drawarrow i ; endfor ;
802anglelength := 1.0cm ; drawoptions(withcolor .625red) ;
803drawarrow anglebetween(a,b,btex $\alpha $ etex) ;
804drawarrow anglebetween(c,d,btex $\gamma $ etex) ;
805drawarrow anglebetween(e,f,btex $\epsilon$ etex) ;
806anglelength := 1.5cm ; drawoptions(withcolor .625yellow) ;
807drawdblarrow anglebetween(b,c,btex $\beta $ etex) ;
808drawarrow reverse anglebetween(d,e,btex $\delta $ etex) ;
809drawarrow anglebetween(a,f,btex $\zeta $ etex) ;
810\stopbuffer
811
812\typebuffer[b]
813
814Because \type {anglebetween} returns a path, you can apply transformations to it,
815like reversing. Close reading of the previous code learns that the macro handles
816both directions.
817
818\startlinecorrection[blank]
819\processMPbuffer[x,a,b]
820\stoplinecorrection
821
822Multiples of 90 degrees are often identified by a rectangular symbol. We will now
823extend the previously defined macro in such a way that more types can be drawn.
824
825\startbuffer[a]
826numeric anglelength ; anglelength := 20pt ;
827numeric anglemethod ; anglemethod := 1 ;
828
829vardef anglebetween (expr a, b, str) =
830 save pointa, pointb, common, middle, offset ;
831 pair pointa, pointb, common, middle, offset ;
832 save curve ; path curve ;
833 save where ; numeric where ;
834 if round point 0 of a = round point 0 of b :
835 common := point 0 of a ;
836 else :
837 common := a intersectionpoint b ;
838 fi ;
839 pointa := point anglelength on a ;
840 pointb := point anglelength on b ;
841 where := turningnumber (commonpointapointbcycle) ;
842 middle := ((commonpointa) rotatedaround (pointa,where*90))
843 intersectionpoint
844 ((commonpointb) rotatedaround (pointb, where*90)) ;
845 if anglemethod = 1 :
846 curve := pointa{unitvector(middlepointa)}.. pointb;
847 middle := point .5 along curve ;
848 elseif anglemethod = 2 :
849 middle := common rotatedaround(.5[pointa,pointb],180) ;
850 curve := pointamiddlepointb ;
851 elseif anglemethod = 3 :
852 curve := pointamiddlepointb ;
853 elseif anglemethod = 4 :
854 curve := pointa..controls middle..pointb ;
855 middle := point .5 along curve ;
856 fi ;
857 draw thefreelabel(str, middle, common) withcolor black ;
858 curve
859enddef ;
860\stopbuffer
861
862\typebuffer[a]
863
864\startbuffer[p]
865anglemethod := 1 ;
866\stopbuffer
867
868\startbuffer[q]
869anglemethod := 2 ;
870\stopbuffer
871
872\startbuffer[r]
873anglemethod := 3 ;
874\stopbuffer
875
876\startbuffer
877\startcombination[3*1]
878 {\processMPbuffer[x,a,p,b]} {method 1}
879 {\processMPbuffer[x,a,q,b]} {method 2}
880 {\processMPbuffer[x,a,r,b]} {method 3}
881\stopcombination
882\stopbuffer
883
884\placefigure
885 [here][fig:three methods]
886 {Three ways of marking angles.}
887 {\getbuffer}
888
889\in {Figure} [fig:three methods] shows the first three alternative methods
890implemented here. Instead of using \typ {unitvectors}, we now calculate the
891points using the \typ {arctime} and \typ {arclength} primitives. Instead of
892complicated expressions, we use the \METAFUN\ operators \type {along} and \type
893{on}. The following expressions are equivalent.
894
895\starttyping
896pointa := point anglelength on a ;
897middle := point .5 along curve ;
898
899pointa := point (arctime anglelength of a) of a ;
900middle := arctime (.5(arclength curve)) of curve) of curve ;
901\stoptyping
902
903The third method can be implemented in different, more math intensive ways, but
904the current implementation suits rather well and is understood by the author.
905
906\stopsection
907
908\startsection[reference=sec:color circles,title={Color circles}]
909
910\index{color}
911
912In \in {chapter} [sec:embedding] we showed a few color circles. Drawing such a
913graphic can be done in several ways, and here we will show a few methods. First
914we will demonstrate how you can apply \type {cutafter} and \type {cutbefore},
915next we will show how the \METAPOST\ macro \type {buildpath} can be used, and
916finally we will present a clean solution using \type {subpath}. We will assume
917that the circle is called with the macro:
918
919\starttyping
920colorcircle (4cm, red, green, blue) ;
921\stoptyping
922
923\startbuffer[circle]
924vardef colorcircle (expr size, red, green, blue) =
925 save r, g, b, rr, gg, bb, cc, mm, yy ;
926 save br, bg, gr, gb ;
927 save radius ;
928
929 path r, g, b, rr, bb, gg, cc, mm, yy ;
930 pair br, bg, gr, gb ;
931
932 numeric radius ; radius := 3cm ;
933
934 pickup pencircle scaled (radius20) ;
935
936 r := g := b := fullcircle scaled radius shifted (0,radius4);
937
938 r := r rotatedaround(origin, 15) ;
939 g := g rotatedaround(origin,135) ;
940 b := b rotatedaround(origin,255) ;
941
942 br := b intersectionpoint r ;
943 bg := b intersectionpoint g ;
944 gr := reverse g intersectionpoint r ;
945 gb := reverse g intersectionpoint b ;
946
947 bb := b cutafter br ; bb := bb cutbefore bg ;
948 gg := g cutbefore bg ; gg := gg cutafter gr ;
949 rr := r cutbefore gr r cutafter br ;
950
951 cc := b cutbefore br ; cc := cc cutafter gb ;
952 yy := g cutbefore gr ; yy := yy cutafter gb ;
953 mm := r cutbefore gr r cutafter br ;
954
955 bb := gg rr reverse bb cycle ;
956 gg := bb rotatedaround(origin,120) ;
957 rr := bb rotatedaround(origin,240) ;
958
959 cc := mm cc reverse yy cycle ;
960 yy := cc rotatedaround(origin,120) ;
961 mm := cc rotatedaround(origin,240) ;
962
963 fill fullcircle scaled radius withcolor white ;
964
965 fill rr withcolor red ; fill cc withcolor whitered ;
966 fill gg withcolor green ; fill mm withcolor whitegreen ;
967 fill bb withcolor blue ; fill yy withcolor whiteblue ;
968
969 for i = rr,gg,bb,cc,mm,yy : draw i withcolor .5white ; endfor ;
970
971 currentpicture := currentpicture xsized size ;
972enddef ;
973\stopbuffer
974
975We need to calculate seven paths. The first implementation does all the handywork
976itself and thereby is rather long, complicated and unreadable. It does not really
977use the strength of \METAPOST\ yet.
978
979\typebuffer[circle]
980
981In determining the right intersection points, you need to know where the path
982starts and in what direction it moves. In case of doubt, drawing the path as an
983arrow helps. If you want to see the small paths used, you need to comment the
984lines with the \type {fill}s and uncomment the lines with \type {draw}s. Due to
985the symmetry and the fact that we keep the figure centered around the origin, we
986only need to calculate two paths since we can rotate them.
987
988There are for sure more (efficient) ways to draw such a figure, but this one
989demonstrates a few new tricks, like grouping. We use grouping here because we
990want to use \type {mm} to indicate the magenta path, and \type {mm} normally
991means millimeter. Within a group, you can save variables. These get their old
992values when the group is left.
993
994With \type {for} we process multiple paths after each other. In this case it
995hardly saves tokens, but it looks more clever.
996
997One of the more efficient methods is using the \type {buildcycle} macro. This
998macro takes two or more paths and calculates the combined path. Although this is
999a rather clever macro, you should be prepared to help it a bit when paths have
1000multiple intersection points. Again, we could follow a more secure mathematical
1001method, but the next one took only a few minutes of trial and error. To save some
1002memory, we redefine the \type {colors} graphic.
1003
1004\startbuffer[demo]
1005colorcircle(4cm, red, green, blue) ;
1006\stopbuffer
1007
1008When we call this macro as:
1009
1010\typebuffer[demo]
1011
1012we get:
1013
1014\startlinecorrection[blank]
1015\processMPbuffer[circle,demo]
1016\stoplinecorrection
1017
1018Of course this macro is only used for demonstration purposes and has no real use.
1019
1020\startbuffer[circle]
1021vardef colorcircle (expr size, red, green, blue) =
1022 save r, g, b, rr, gg, bb, cc, mm, yy ; save radius ;
1023 path r, g, b, rr, bb, gg, cc, mm, yy ; numeric radius ;
1024
1025 radius := 5cm ; pickup pencircle scaled (radius25) ;
1026
1027 r := g := b := fullcircle scaled radius shifted (0,radius4) ;
1028
1029 r := r rotatedaround (origin, 15) ;
1030 g := g rotatedaround (origin,135) ;
1031 b := b rotatedaround (origin,255) ;
1032
1033 r := r rotatedaround(center r,90) ;
1034 g := g rotatedaround(center g, 90) ;
1035
1036 gg := buildcycle(buildcycle(reverse r,b),g) ;
1037 cc := buildcycle(buildcycle(b,reverse g),r) ;
1038
1039 rr := gg rotatedaround(origin,120) ;
1040 bb := gg rotatedaround(origin,240) ;
1041
1042 yy := cc rotatedaround(origin,120) ;
1043 mm := cc rotatedaround(origin,240) ;
1044
1045 fill fullcircle scaled radius withcolor white ;
1046
1047 fill rr withcolor red ; fill cc withcolor whitered ;
1048 fill gg withcolor green ; fill mm withcolor whitegreen ;
1049 fill bb withcolor blue ; fill yy withcolor whiteblue ;
1050
1051 for i = rr,gg,bb,cc,mm,yy : draw i withcolor .5white ; endfor ;
1052
1053 currentpicture := currentpicture xsized size ;
1054enddef ;
1055\stopbuffer
1056
1057\typebuffer [circle]
1058
1059Since we dont want to duplicate a graphic, this time we show the dark
1060alternatives.
1061
1062\startbuffer[demo]
1063colorcircle(4cm, .5red, .5green, .5blue) ;
1064\stopbuffer
1065
1066\typebuffer[demo]
1067
1068This kind of unsafe path calculations are very sensitive to breaking. Changing
1069the \type {radius4} into something else demonstrates this but we will not
1070challenge this macro that much. Therefore, the 50\% color circle shows up as:
1071
1072\startlinecorrection[blank]
1073\processMPbuffer[circle,demo]
1074\stoplinecorrection
1075
1076This command is part of \METAFUN\ and can be used to determine nice color
1077combinations by also looking at their complementary colors.
1078
1079\startbuffer[demo]
1080colorcircle (4cm, .7red, .5green, .3blue) ;
1081\stopbuffer
1082
1083\typebuffer[demo]
1084
1085\startlinecorrection[blank]
1086\processMPbuffer[circle,demo]
1087\stoplinecorrection
1088
1089The next circle that we draw shows the three main colors used in this document.
1090This circle is not that beautiful.
1091
1092\startbuffer[demo]
1093colorcircle(4cm,.625red,.625yellow,.625white) ;
1094\stopbuffer
1095
1096\typebuffer[demo]
1097
1098\startlinecorrection[blank]
1099\processMPbuffer[circle,demo]
1100\stoplinecorrection
1101
1102This definition can be cleaned up a bit by using \type {transform}, but the fuzzy
1103\type {buildcycle}s remain.
1104
1105\startbuffer[circle]
1106vardef colorcircle (expr size, red, green, blue) =
1107 save r, g, b, rr, gg, bb, cc, mm, yy ; save radius ;
1108 path r, g, b, rr, bb, gg, cc, mm, yy ; numeric radius ;
1109
1110 radius := 5cm ; pickup pencircle scaled (radius25) ;
1111
1112 transform t ; t := identity rotatedaround(origin,120) ;
1113
1114 r := fullcircle scaled radius
1115 shifted (0,radius4) rotatedaround(origin,15) ;
1116
1117 g := r transformed t ; b := g transformed t ;
1118
1119 r := r rotatedaround(center r,90) ;
1120 g := g rotatedaround(center g, 90) ;
1121
1122 gg := buildcycle(buildcycle(reverse r,b),g) ;
1123 cc := buildcycle(buildcycle(b,reverse g),r) ;
1124
1125 rr := gg transformed t ; bb := rr transformed t ;
1126 yy := cc transformed t ; mm := yy transformed t ;
1127
1128 fill fullcircle scaled radius withcolor white ;
1129
1130 fill rr withcolor red ; fill cc withcolor whitered ;
1131 fill gg withcolor green ; fill mm withcolor whitegreen ;
1132 fill bb withcolor blue ; fill yy withcolor whiteblue ;
1133
1134 for i = rr,gg,bb,cc,mm,yy : draw i withcolor .5white ; endfor ;
1135
1136 currentpicture := currentpicture xsized size ;
1137enddef ;
1138\stopbuffer
1139
1140\typebuffer [circle]
1141
1142\startbuffer[demo]
1143colorcircle(4cm,(.4,.6,.8),(.8,.4,.6),(.6,.8,.4));
1144\stopbuffer
1145
1146\startlinecorrection[blank]
1147\processMPbuffer[circle,demo]
1148\stoplinecorrection
1149
1150This rather nice circle is defined as:
1151
1152\typebuffer[demo]
1153
1154The final implementation, which is part of \METAFUN, is slightly more efficient.
1155
1156\startbuffer[circle]
1157vardef colorcircle (expr size, red, green, blue) =
1158 save r, g, b, c, m, y, w ; save radius ;
1159 path r, g, b, c, m, y, w ; numeric radius ;
1160
1161 radius := 5cm ; pickup pencircle scaled (radius25) ;
1162
1163 transform t ; t := identity rotatedaround(origin,120) ;
1164
1165 r := fullcircle rotated 90 scaled radius
1166 shifted (0,radius4) rotatedaround(origin,135) ;
1167
1168 b := r transformed t ; g := b transformed t ;
1169
1170 c := buildcycle(subpath(1,7) of g, subpath(1,7) of b) ;
1171 y := c transformed t ; m := y transformed t ;
1172
1173 w := buildcycle(subpath(3,5) of r,
1174 subpath(3,5) of g, subpath(3,5) of b) ;
1175
1176 pushcurrentpicture ;
1177
1178 fill r withcolor red ;
1179 fill g withcolor green ;
1180 fill b withcolor blue ;
1181 fill c withcolor whitered ;
1182 fill m withcolor whitegreen ;
1183 fill y withcolor whiteblue ;
1184 fill w withcolor white ;
1185
1186 for i = r,g,b,c,m,y : draw i withcolor .5white ; endfor ;
1187
1188 currentpicture := currentpicture xsized size ;
1189
1190 popcurrentpicture ;
1191enddef ;
1192\stopbuffer
1193
1194\typebuffer [circle]
1195
1196Here, we first fill the primary circles, next we fill the secondary ones. These
1197also cover the center, which is why finally we fill the center with white.
1198
1199\startbuffer[demo]
1200colorcircle(4cm,(.2,.5,.8),(.8,.2,.5),(.5,.8,.2));
1201\stopbuffer
1202
1203\startlinecorrection[blank]
1204\processMPbuffer[circle,demo]
1205\stoplinecorrection
1206
1207The circle uses the following colors:
1208
1209\typebuffer[demo]
1210
1211The next graphic demonstrates how the subpaths look that build the shapes.
1212
1213\startbuffer[circle]
1214vardef colorcircle (expr size, red, green, blue) =
1215 save r, g, b, c, m, y, w ; save radius ;
1216 path r, g, b, c, m, y, w ; numeric radius ;
1217
1218 radius := 5cm ; pickup pencircle scaled (radius25) ;
1219
1220 transform t ; t := identity rotatedaround(origin,120) ;
1221
1222 r := fullcircle rotated 90 scaled radius
1223 shifted (0,radius4) rotatedaround(origin,135) ;
1224
1225 b := r transformed t ; g := b transformed t ;
1226
1227 c := buildcycle(subpath(1,7) of g,subpath(1,7) of b) ;
1228 y := c transformed t ; m := y transformed t ;
1229
1230 w := buildcycle(subpath(3,5) of r,
1231 subpath(3,5) of g, subpath(3,5) of b) ;
1232
1233 pushcurrentpicture ;
1234
1235 def doit =
1236 fill r withcolor red ;
1237 fill g withcolor green ;
1238 fill b withcolor blue ;
1239 fill c withcolor whitered ;
1240 fill m withcolor whitegreen ;
1241 fill y withcolor whiteblue ;
1242 fill w withcolor white ;
1243 for i = r,g,b,c,m,y : draw i withcolor .5white ; endfor ;
1244 enddef ;
1245
1246 autoarrows := true ;
1247
1248 doit ;
1249 for i=r,g,b : drawarrow i withcolor black ; endfor ;
1250 currentpicture := currentpicture shifted (2radius,0) ;
1251
1252 doit ;
1253 for i=r,g,b : drawarrow subpath(1,7) of i withcolor black ; endfor ;
1254 currentpicture := currentpicture shifted (2radius,0) ;
1255
1256 doit ;
1257 for i=r,g,b : drawarrow subpath(3,5) of i withcolor black ; endfor ;
1258 currentpicture := currentpicture shifted (4radius,2radius) ;
1259
1260 drawarrow r withpen pencircle scaled (radius10) withcolor red ;
1261 drawarrow g withpen pencircle scaled (radius20) withcolor green ;
1262 drawarrow b withpen pencircle scaled (radius40) withcolor blue ;
1263 currentpicture := currentpicture shifted (2radius,0) ;
1264
1265 drawarrow c withpen pencircle scaled (radius10) withcolor whitered ;
1266 drawarrow m withpen pencircle scaled (radius20) withcolor whitegreen ;
1267 drawarrow y withpen pencircle scaled (radius40) withcolor whiteblue ;
1268 currentpicture := currentpicture shifted (2radius,0) ;
1269
1270 drawarrow w withcolor black ;
1271
1272 currentpicture := currentpicture xsized 3size ;
1273
1274 popcurrentpicture ;
1275enddef ;
1276\stopbuffer
1277
1278\startlinecorrection[blank]
1279\processMPbuffer[circle,demo]
1280\stoplinecorrection
1281
1282We did not mention what the push and pop commands are responsible for. Scaling
1283the current picture is well defined as long as we deal with one graphic. However,
1284if the current picture already has some content, this content is also scaled. The
1285push and pop commands let us add content to the current picture as well as
1286manipulating the picture as a whole without any side effects. The final result is
1287put on top of the already drawn content. Instead of the sequence:
1288
1289\starttyping
1290pushcurrentpicture ;
1291 ...
1292 currentpicture := currentpicture ... transformations ... ;
1293popcurrentpicture ;
1294\stoptyping
1295
1296you can say:
1297
1298\starttyping
1299pushcurrentpicture ;
1300 ...
1301popcurrentpicture ... transformations ... ;
1302\stoptyping
1303
1304Both are equivalent to:
1305
1306\starttyping
1307draw image ( ... ) ... transformations ... ;
1308\stoptyping
1309
1310For larger sequences of commands, the pushpop alternative gives a bit more more
1311readable code.
1312
1313\stopsection
1314
1315\startsection[title={Fool yourself}]
1316
1317When doing a literature search on the human perception of blackwhite edges, I
1318ran into several articles with graphics that I remember having seen before in
1319books on psychology, physiology andor ergonomics. One of the articles was by
1320Edward H.Adelson of MIT and we will use a few of his example graphics in our
1321exploration to what extend \METAPOST\ can be of help in those disciplines. Since
1322such graphics normally occur in typeset documents, we will define them in the
1323document source.
1324
1325\startbuffer[a]
1326\startbuffer
1327interim linecap := butt ; numeric u ; u := 1cm ;
1328pickup pencircle scaled .5u ;
1329for i=1u step u until 5u :
1330 draw (0,i) (5u,i) ;
1331endfor ;
1332for i=2u step u until 4u :
1333 draw (u,i) (2u,i) withcolor .5white ;
1334 draw ((3u,i) (4u,i)) shifted (0,.5u) withcolor .5white ;
1335endfor ;
1336\stopbuffer
1337\stopbuffer
1338
1339\startbuffer[b]
1340\placefigure
1341 [here][fig:tricked 1]
1342 {Whites illusion.}
1343 {\processMPbuffer}
1344\stopbuffer
1345
1346\getbuffer[a,b]
1347
1348Unless you belong to the happy few whose visual capabilities are not distorted by
1349neural optimizations, in \in {figure} [fig:tricked 1] the gray rectangles at the
1350left look lighter than those on the right. Alas, you can fool yourself, but
1351\METAPOST\ does not cheat. This graphic, referred to as Whites illusion, is
1352defined as follows.
1353
1354\typebuffer[a]
1355
1356Watch how we include the code directly. We have packaged this graphic in a buffer
1357which we include as a floating figure.
1358
1359\typebuffer[b]
1360
1361When passed to \METAPOST, this code is encapsulated in its \type {beginfig} and
1362\type {endfig} macros and thereby grouped. But any change to a variable that is
1363not explicitly saved, migrates to the outer level. In order to prevent all
1364successive graphics to have buttd linecaps, we have to change this line
1365characteristic locally. Because \type {linecap} is defined as an internal
1366variable, we have to use \type {interim} to overload its value. Because \type {u}
1367is a rather commonly used scratch variable, we dont save its value.
1368
1369Watch how we use \type {u} as the loop step. In spite of what your eyes tell you,
1370this graphic only has two explicit color directives, both being 50\% black. In
1371the next example we will use some real colors.
1372
1373\startbuffer[a]
1374\startuseMPgraphic{first}
1375 numeric size, delta ;
1376 size := 2.5cm ; delta := size3 ;
1377 color mainshade, topshade, bottomshade, centershade ;
1378 mainshade := \MPcolor{funcolor} ;
1379 topshade := .9mainshade ; bottomshade := .5mainshade ;
1380 centershade := .5[topshade,bottomshade] ;
1381\stopuseMPgraphic
1382\stopbuffer
1383
1384\startbuffer[b]
1385\startuseMPgraphic{second}
1386 \includeMPgraphic{first}
1387 fill fullsquare scaled size withcolor topshade ;
1388 fill fullsquare scaled delta withcolor centershade ;
1389\stopuseMPgraphic
1390\stopbuffer
1391
1392\startbuffer[c]
1393\startuseMPgraphic{third}
1394 \includeMPgraphic{first}
1395 fill fullsquare scaled size withcolor bottomshade ;
1396 fill fullsquare scaled delta withcolor centershade ;
1397\stopuseMPgraphic
1398\stopbuffer
1399
1400\getbuffer[a,b,c]
1401
1402\startbuffer[d]
1403\startcombination[5*2]
1404 {\definecolor[funcolor][red] \useMPgraphic{second}} {}
1405 {\definecolor[funcolor][green] \useMPgraphic{second}} {}
1406 {\definecolor[funcolor][blue] \useMPgraphic{second}} {}
1407 {\definecolor[funcolor][yellow]\useMPgraphic{second}} {}
1408 {\definecolor[funcolor][white] \useMPgraphic{second}} {}
1409 {\definecolor[funcolor][red] \useMPgraphic{third}} {}
1410 {\definecolor[funcolor][green] \useMPgraphic{third}} {}
1411 {\definecolor[funcolor][blue] \useMPgraphic{third}} {}
1412 {\definecolor[funcolor][yellow]\useMPgraphic{third}} {}
1413 {\definecolor[funcolor][white] \useMPgraphic{third}} {}
1414\stopcombination
1415\stopbuffer
1416
1417\placefigure
1418 [here][fig:tricked 2]
1419 {The simultaneous contrast effect.}
1420 {\getbuffer[d]}
1421
1422In \in {figure} [fig:tricked 2] the small squares in the center of each colored
1423pair of big squares have the same shade, but the way we perceive them are
1424influenced by their surroundings. Both sets of squares are defined using usable
1425graphics. The top squares are defined as:
1426
1427\typebuffer[b]
1428
1429and the bottom squares are coded as:
1430
1431\typebuffer[c]
1432
1433Because both graphics share code, we have defined that code as a separate
1434graphic, that we include. The only point of interest in this definition is the
1435fact that we let \METAPOST\ interpolate between the two colors using \type {.5[
1436]}.
1437
1438\typebuffer[a]
1439
1440The color \type {funcolor} is provided by \CONTEXT, and since we want to use this
1441graphic with different colors, this kind of mapping is quite convenient. The
1442bunch of graphics is packaged in a combination with empty captions. Note how we
1443set the color before we include the graphic.
1444
1445\typebuffer[d]
1446
1447We use a similar arrangement for the following graphic, where we have replaced
1448the definitions of \type {first}, \type {second} and \type {third} by new
1449definitions.
1450
1451\startbuffer[a]
1452\startuseMPgraphic{first}
1453 numeric height, width, radius, gap ; gap := 1mm ;
1454 height = 2.5cm ; width := height2 ; radius := height2.5 ;
1455 color mainshade, leftshade, rightshade, centershade ;
1456 mainshade := \MPcolor{funcolor} ;
1457 leftshade := .9mainshade ; rightshade := .5mainshade ;
1458 centershade := .5[leftshade,rightshade] ;
1459 fill unitsquare xyscaled ( width,height) withcolor leftshade ;
1460 fill unitsquare xyscaled (width,height) withcolor rightshade ;
1461 draw (fullcircle scaled radius) shifted (0,height2)
1462 withpen pencircle scaled (radius2) withcolor centershade ;
1463\stopuseMPgraphic
1464\stopbuffer
1465
1466\startbuffer[b]
1467\startuseMPgraphic{second}
1468 \includeMPgraphic{first}
1469 interim linecap := butt ; pickup pencircle scaled gap ;
1470 draw (0,0) -- (0,height) withcolor white ;
1471\stopuseMPgraphic
1472\stopbuffer
1473
1474\startbuffer[c]
1475\startuseMPgraphic{third}
1476 \includeMPgraphic{first}
1477 picture p, q ; p := q := currentpicture ;
1478 clip p to unitsquare xscaled width yscaled height ;
1479 clip q to unitsquare xscaled width yscaled height ;
1480 currentpicture := p ;
1481 addto currentpicture also q shifted (0,radius2) ;
1482\stopuseMPgraphic
1483\stopbuffer
1484
1485\getbuffer[a,b,c]
1486
1487\startbuffer[d]
1488\startcombination[5*3]
1489 {\definecolor[funcolor][red] \useMPgraphic{first}} {}
1490 {\definecolor[funcolor][green] \useMPgraphic{first}} {}
1491 {\definecolor[funcolor][blue] \useMPgraphic{first}} {}
1492 {\definecolor[funcolor][yellow]\useMPgraphic{first}} {}
1493 {\definecolor[funcolor][white] \useMPgraphic{first}} {}
1494 {\definecolor[funcolor][red] \useMPgraphic{second}} {}
1495 {\definecolor[funcolor][green] \useMPgraphic{second}} {}
1496 {\definecolor[funcolor][blue] \useMPgraphic{second}} {}
1497 {\definecolor[funcolor][yellow]\useMPgraphic{second}} {}
1498 {\definecolor[funcolor][white] \useMPgraphic{second}} {}
1499 {\definecolor[funcolor][red] \useMPgraphic{third}} {}
1500 {\definecolor[funcolor][green] \useMPgraphic{third}} {}
1501 {\definecolor[funcolor][blue] \useMPgraphic{third}} {}
1502 {\definecolor[funcolor][yellow]\useMPgraphic{third}} {}
1503 {\definecolor[funcolor][white] \useMPgraphic{third}} {}
1504\stopcombination
1505\stopbuffer
1506
1507\placefigure
1508 [here][fig:tricked 3]
1509 {Koffkas examples of manipulating contrast by changing
1510 the spatial configuration.}
1511 {\getbuffer[d]}
1512
1513The definition of the first row of \in {figure} [fig:tricked 3] is used in the
1514second and third and therefore is the most complicated. We use quite some scratch
1515variables to reach a high level of abstraction. The \type {xyscaled} operator is
1516a \METAFUN\ macro.
1517
1518\typebuffer[a]
1519
1520The graphics of the second row extends those of the first by drawing a white line
1521through the middle. In this example setting the linecap is not really needed,
1522because rounded top and bottoms in white are invisible and the part that extends
1523beyond the points does not count in calculating the bounding box.
1524
1525\typebuffer[b]
1526
1527The third row graphics again extend the first graphic. First we copy the picture
1528constructed so far. Watch the double assignment. Next we clip the pictures in
1529half, and shift the right half down over the width of the circle.
1530
1531\typebuffer[c]
1532
1533\stopsection
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557\startsection[title={Growing graphics}]
1558
1559Although \METAPOST\ is not really suited as a simulation engine, it is possible
1560to build graphics that are built and displayed incrementally with a sequence of
1561mouse clicks. The following example is the result of an email discussion David
1562Arnold and the author had while \METAFUN\ evolved.
1563
1564Instead of defining the graphics in a separate \METAPOST\ file, we will
1565incorporate them in the document source in which they are used. We can use
1566several methods.
1567
1568\startitemize[n]
1569\startitem
1570 Define macros and figures in a separate file and include the graphics as
1571 external graphics.
1572\stopitem
1573\startitem
1574 Define everything in the document source as usable graphics and include the
1575 graphics using \type {\useMPgraphic}.
1576\stopitem
1577\startitem
1578 Package the graphic components in buffers and paste those together as
1579 graphics that can be processed at run time.
1580\stopitem
1581\stopitemize
1582
1583The first method is the most independent one, which has its advantages if we want
1584to use the graphics in other applications too. The second method works well in
1585graphics where parts of the definitions change between invocations of the
1586graphic. This method follows the template:
1587
1588\starttyping
1589\startuseMPgraphic{whatever}
1590 ...
1591\stopuseMPgraphic
1592
1593\startuseMPgraphic{result}
1594 ...
1595 \includeMPgraphic{whatever}
1596 ...
1597\stopuseMPgraphic
1598
1599\useMPgraphic{result}
1600\stoptyping
1601
1602The disadvantage of this method is that it cannot be combined with \type
1603{btex}\type {etex} since it is nearly impossible to determine when, how, and to
1604what extent the content of a graphic should be expanded before writing it to the
1605temporary \METAPOST\ file.
1606
1607Therefore, we will demonstrate how buffers can be used. This third method closely
1608parallels the first way of defining graphics. A nice side effect is that we can
1609easily typeset these buffers verbatim, which we did to typeset this document.
1610
1611We are going to do a classic compass and straightedge construction, the bisection
1612of a line segment joining two arbitrary points. We will construct five graphics,
1613where each one displays one step of the construction. We will embed each graphic
1614in a startstop command. Later we will see the advantage of this strategy.
1615
1616\startbuffer
1617\startbuffer[a]
1618def starteverything = enddef ;
1619def stopeverything = enddef ;
1620\stopbuffer
1621\stopbuffer
1622
1623\typebuffer \getbuffer
1624
1625\startbuffer
1626\startbuffer[b]
1627numeric u, w ; u := .5cm ; w := 1pt ;
1628
1629pickup pencircle scaled w ;
1630
1631def drawdot expr p =
1632 draw p withpen pencircle scaled 3w ;
1633enddef ;
1634
1635def standout =
1636 drawoptions(withcolor .625red) ;
1637enddef ;
1638\stopbuffer
1639\stopbuffer
1640
1641We are going to draw a few dots, and to force consistency we first define a macro
1642\type {drawdot}. The current step will be highlighted in red using \type
1643{standout}.
1644
1645\typebuffer \getbuffer
1646
1647\startbuffer
1648\startbuffer[c]
1649def drawbasics =
1650 pair pointA, pointB ; path lineAB ;
1651 pointA := origin ; pointB := pointA shifted (5u,0) ;
1652 lineAB := pointA pointB ;
1653 draw lineAB ;
1654 drawdot pointA ; label.lft(btex A etex, pointA) ;
1655 drawdot pointB ; label.rt (btex B etex, pointB) ;
1656enddef ;
1657\stopbuffer
1658\stopbuffer
1659
1660First, we construct the macro that will plot two points $A$ and $B$ and connect
1661them with a line segment.
1662
1663\typebuffer \getbuffer
1664
1665\startbuffer
1666\startbuffer[1]
1667starteverything ;
1668 standout ; drawbasics ;
1669stopeverything ;
1670\stopbuffer
1671\stopbuffer
1672
1673The code in this buffer executes the preceding macros. The \type {...everything}
1674commands are still undefined, but later we can use these hooks for special
1675purposes.
1676
1677\typebuffer \getbuffer
1678
1679This graphic can now be embedded by the \CONTEXT\ command
1680\type {\processMPbuffer}. This command, like the ordinary
1681buffer inclusion commands, accepts a list of buffers.
1682
1683\startbuffer
1684\startlinecorrection[blank]
1685\ruledhbox{\processMPbuffer[a,b,c,1]}
1686\stoplinecorrection
1687\stopbuffer
1688
1689\typebuffer
1690
1691We use \type {\ruledhbox} to show the tight bounding box of the graphic. The line
1692correction takes care of proper spacing around non textual content, like
1693graphics. \footnote {These spacing commands try to get the spacing around the
1694content visually compatible, and take the height and depth of the preceding and
1695following text into account.} This is only needed when the graphic is part of the
1696text flow!
1697
1698\getbuffer
1699
1700Next, we draw two circles of equal radius, one centered at point $A$, the other
1701at point $B$.
1702
1703\startbuffer
1704\startbuffer[d]
1705def drawcircles =
1706 path circleA, circleB ; numeric radius, distance ;
1707 distance := (xpart pointB) (xpart pointA) ;
1708 radius := 23 * distance ;
1709 circleA := fullcircle scaled (2*radius) ;
1710 circleB := circleA shifted pointB ;
1711 draw circleA ;
1712 draw circleB ;
1713enddef ;
1714\stopbuffer
1715\stopbuffer
1716
1717\typebuffer \getbuffer
1718
1719\startbuffer
1720\startbuffer[2]
1721starteverything ;
1722 drawbasics ; standout ; drawcircles ;
1723stopeverything ;
1724\stopbuffer
1725\stopbuffer
1726
1727As you can see, we move down the \type {standout} macro so that only the
1728additions are colored red.
1729
1730\typebuffer \getbuffer
1731
1732We now use \type{\processMPbuffer[a,b,c,d,2]} to include the latest step.
1733
1734\startlinecorrection[blank]
1735\ruledhbox{\processMPbuffer[a,b,c,d,2]}
1736\stoplinecorrection
1737
1738The next step in the construction of the perpendicular bisector requires that we
1739find and label the points of intersection of the two circles centered at points
1740$A$ and $B$. The intersection points are calculated as follows. Watch the \type
1741{reverse} operation, which makes sure that we get the second intersection point.
1742
1743\startbuffer
1744\startbuffer[e]
1745def drawintersection =
1746 pair pointC, pointD ;
1747 pointC := circleA intersectionpoint circleB ;
1748 pointD := (reverse circleA) intersectionpoint (reverse circleB) ;
1749 drawdot pointC ; label.lft(btex C etex, pointC shifted (2w,0)) ;
1750 drawdot pointD ; label.lft(btex D etex, pointD shifted (2w,0)) ;
1751enddef ;
1752\stopbuffer
1753\stopbuffer
1754
1755\typebuffer \getbuffer
1756
1757\startbuffer
1758\startbuffer[3]
1759starteverything ;
1760 drawbasics ; drawcircles ; standout ; drawintersection ;
1761stopeverything ;
1762\stopbuffer
1763\stopbuffer
1764
1765In placing the label, we must make sure that the text runs free of the lines and
1766curves. Again, move the \type {standout} macro just prior to \type
1767{drawintersection} macro, so that this step is highlighted in the drawing color,
1768while prior steps are drawn in the default color (in this case black).
1769
1770\typebuffer \getbuffer
1771
1772\startlinecorrection[blank]
1773\ruledhbox{\processMPbuffer[a,b,c,d,e,3]}
1774\stoplinecorrection
1775
1776The line drawn through points $C$ and $D$ will be the perpendicular bisector of
1777the line segment connecting points $A$ and $B$. In the next step we will draw a
1778line using the plain \METAPOST\ \type {drawdblarrow} macro that draws arrowheads
1779at each end of a path.
1780
1781\startbuffer
1782\startbuffer[f]
1783def drawbisector =
1784 path lineCD ;
1785 lineCD := origin origin shifted (2*distance,0) ;
1786 lineCD := lineCD rotated 90 shifted 0.5[pointA,pointB] ;
1787 lineCD := lineCD shifted (0,distance) ;
1788 drawdblarrow lineCD ;
1789enddef ;
1790\stopbuffer
1791
1792\startbuffer[4]
1793starteverything ;
1794 drawbasics ; drawcircles ; drawintersection ; standout ;
1795 drawbisector ;
1796stopeverything ;
1797\stopbuffer
1798\stopbuffer
1799
1800\typebuffer \getbuffer
1801
1802\startlinecorrection[blank]
1803\ruledhbox{\processMPbuffer[a,b,c,d,e,f,4]}
1804\stoplinecorrection
1805
1806The following code draws the intersection of line $CD$ and line segment $AB$,
1807which can be shown to be the midpoint of segment $AB$.
1808
1809\startbuffer
1810\startbuffer[g]
1811def drawmidpoint =
1812 pair pointM ;
1813 pointM := lineCD intersectionpoint lineAB ;
1814 drawdot pointM ; label.llft(btex M etex, pointM) ;
1815enddef ;
1816\stopbuffer
1817
1818\startbuffer[5]
1819starteverything ;
1820 drawbasics ; drawcircles ; drawintersection ; drawbisector ;
1821 standout ; drawmidpoint ;
1822stopeverything ;
1823\stopbuffer
1824\stopbuffer
1825
1826\typebuffer \getbuffer
1827
1828\startlinecorrection[blank]
1829\ruledhbox{\processMPbuffer[a,b,c,d,e,f,g,5]}
1830\stoplinecorrection
1831
1832As long as we place the graphics as individual insertions in our document,
1833everything is fine. However, if we wish to place them all at once, or as we shall
1834see later, place them on top of one another in a fieldstack, it makes sense to
1835give them all the same bounding box. We can do this by completing the \type
1836{starteverything} and \type {stopeverything} commands.
1837
1838\startbuffer
1839\startbuffer[a]
1840def starteverything =
1841 path bb ;
1842 drawbasics ;
1843 drawcircles ;
1844 drawintersection ;
1845 drawbisector ;
1846 drawmidpoint ;
1847 bb := boundingbox currentpicture ;
1848 currentpicture := nullpicture ;
1849enddef ;
1850
1851def stopeverything =
1852 setbounds currentpicture to bb ;
1853enddef ;
1854\stopbuffer
1855\stopbuffer
1856
1857\typebuffer \getbuffer
1858
1859In \in {figure} [fig:1 till 5] we demonstrate the effect of this redefinition.
1860For this purpose we scale down the graphic to a comfortable 40\%, of course by
1861using an additional buffer. We also visualize the bounding box.
1862
1863\startbuffer
1864\startbuffer[h]
1865def stopeverything =
1866 setbounds currentpicture to bb ;
1867 draw bb withpen pencircle scaled .5pt withcolor .625yellow ;
1868 currentpicture := currentpicture scaled .4 ;
1869enddef ;
1870\stopbuffer
1871\stopbuffer
1872
1873\typebuffer \getbuffer
1874
1875The graphic itself is defined as follows. Watch how we use the default buffer to
1876keep the definitions readable.
1877
1878\startbuffer
1879\startbuffer
1880\startcombination[5*1]
1881 {\processMPbuffer[a,b,c,h,d,e,f,g,1]} {step 1}
1882 {\processMPbuffer[a,b,c,h,d,e,f,g,2]} {step 2}
1883 {\processMPbuffer[a,b,c,h,d,e,f,g,3]} {step 3}
1884 {\processMPbuffer[a,b,c,h,d,e,f,g,4]} {step 4}
1885 {\processMPbuffer[a,b,c,h,d,e,f,g,5]} {step 5}
1886\stopcombination
1887\stopbuffer
1888
1889\placefigure
1890 [here][fig:1 till 5]
1891 {The five graphics, each with the same bounding box.}
1892 {\getbuffer}
1893\stopbuffer
1894
1895\typebuffer \getbuffer
1896
1897As the original purpose of these graphics was not to show them side by side, but
1898to present them as field stack in a document to be viewed at the computer screen.
1899For this purpose we have to define the graphics as symbols.
1900
1901\startbuffer
1902\definesymbol[step 1][{\processMPbuffer[a,b,c,d,e,f,g,1]}]
1903\definesymbol[step 2][{\processMPbuffer[a,b,c,d,e,f,g,2]}]
1904\definesymbol[step 3][{\processMPbuffer[a,b,c,d,e,f,g,3]}]
1905\definesymbol[step 4][{\processMPbuffer[a,b,c,d,e,f,g,4]}]
1906\definesymbol[step 5][{\processMPbuffer[a,b,c,d,e,f,g,5]}]
1907\stopbuffer
1908
1909\typebuffer \getbuffer
1910
1911A field stack is a sequence of overlayed graphics. We will arrange these to cycle
1912manually, with clicks of the mouse, through the sequence of graphs depicting the
1913construction of the midpoint of segment $AB$. So, in fact we are dealing with a
1914manual simulation. The definition of such a stack is as follows:
1915
1916\startbuffer
1917\definefieldstack
1918 [midpoint construction]
1919 [step 1, step 2, step 3, step 4, step 5]
1920 [frame=on,offset=3pt,framecolor=darkyellow,rulethickness=1pt]
1921\stopbuffer
1922
1923\typebuffer \getbuffer
1924
1925The first argument is to be a unique identifier, the second argument takes a list
1926of symbols, while the third argument accepts settings. More on this command can
1927be found in the \CONTEXT\ manuals.
1928
1929The stack is shown as \in {figure} [fig:steps]. Its caption provides a button,
1930which enables the reader to cycle through the stack. We call this a stack because
1931the graphics are positioned on top of each other. Only one of them is visible at
1932any time.
1933
1934\startbuffer
1935\placefigure
1936 [here][fig:steps]
1937 {Bisecting a line segment with compass and straightedge? Just
1938 click \goto {here} [JS(WalkField{midpoint construction})] to
1939 walk through the construction! (This stack is only visible
1940 in a \PDF\ viewer that supports widgets.)}
1941 {\framed{\startoverlay
1942 {\symbol[step 1]}
1943 {\fieldstack[midpoint construction]}
1944 \stopoverlay}}
1945\stopbuffer
1946
1947\typebuffer
1948
1949We cheat a bit and overlay the stack over the first symbol because otherwise
1950nothing shows up in print (nowadays I mostly use sumatrapdf).
1951
1952{\setupinteraction[color=darkred,contrastcolor=darkred]\getbuffer}
1953
1954At the start of this section, we mentioned three methods. When we use the first
1955method of putting all the graphics in an external \METAPOST\ file, the following
1956framework suits. We assume that the file is called \type {step.mp} and that it is
1957kept by the user along with his document source. We start with the definitions of
1958the graphic steps. These are the same as the ones shown previously.
1959
1960\starttyping
1961def drawbasics = ... enddef ;
1962def drawcircles = ... enddef ;
1963def drawintersection = ... enddef ;
1964def drawbisector = ... enddef ;
1965def drawmidpoint = ... enddef ;
1966def standout = ... enddef ;
1967\stoptyping
1968
1969We can safe some code by letting the \type {...everything} take care of the
1970\type {beginfig} and \type {endfig} macros.
1971
1972\starttyping
1973def starteverything (expr n) = beginfig(n) ; ... enddef ;
1974def stopeverything = ... ; endfig ; enddef ;
1975\stoptyping
1976
1977The five graphics now become:
1978
1979\starttyping
1980starteverything (1) ;
1981 standout ; drawbasics ;
1982stopeverything ;
1983
1984starteverything (2) ;
1985 drawbasics ; standout ; drawcircles ;
1986stopeverything ;
1987
1988starteverything (3) ;
1989 drawbasics ; drawcircles ; standout ; drawintersection ;
1990stopeverything ;
1991
1992starteverything (4) ;
1993 drawbasics ; drawcircles ; drawintersection ; standout ;
1994 drawbisector ;
1995stopeverything ;
1996
1997starteverything (5) ;
1998 drawbasics ; drawcircles ; drawintersection ; drawbisector ;
1999 standout ; drawmidpoint ;
2000stopeverything ;
2001\stoptyping
2002
2003The definitions of the symbols now refer to an external
2004figure.
2005
2006\starttyping
2007\definesymbol[step 1][{\externalfigure[step.1]}]
2008\definesymbol[step 2][{\externalfigure[step.2]}]
2009\definesymbol[step 3][{\externalfigure[step.3]}]
2010\definesymbol[step 4][{\externalfigure[step.4]}]
2011\definesymbol[step 5][{\externalfigure[step.5]}]
2012\stoptyping
2013
2014Which method is used, depends on the way the graphics are used. In this example
2015we wanted to change the definition of \type {...everything}, so here the third
2016method was quite useful.
2017
2018\stopsection
2019
2020\startsection[title={Simple Logos}]
2021
2022\startbuffer[ns]
2023numeric width, height, line, delta ;
2024width = 5cm ; height = width2 ; line = height4 ; delta = line ;
2025
2026linejoin := mitered ; pickup pencircle scaled line ;
2027
2028color nsblue ; nsblue := (0,0,1) ;
2029color nsyellow ; nsyellow := (1,1,0) ;
2030
2031z1 = (0, height2) ;
2032z2 = (width2height4, y1) ;
2033z3 = (width2height4, y4) ;
2034z4 = (width, 0) ;
2035
2036z5 = (x4height2, y1) ;
2037z6 = (x4, 2y1) ;
2038z7 = 1.5[z5,z6] ;
2039
2040path p ; p := z1z2z3z4 ; path q ; q := z3z4z5z7 ;
2041
2042numeric d, lx, ly, ux, uy ; d = line2 ;
2043
2044lx = 3d d3 ; ly = d ; ux = rt x5 d3 ; uy = top y6 ;
2045
2046path r ; r := (lx,ly)(ux,ly)(ux,uy)(lx,uy)cycle;
2047
2048lx := lxdelta ; ly := lydelta ; ux := uxdelta ; uy := uydelta ;
2049
2050path s ; s := (lx,ly)(ux,ly)(ux,uy)(lx,uy)cycle;
2051
2052draw p withcolor nsblue ; draw q withcolor nsblue ;
2053
2054addto currentpicture also currentpicture
2055 rotatedaround (.5[z2,z3],180) shifted (height4,height2) ;
2056
2057picture savedpicture ; savedpicture := currentpicture ;
2058
2059clip currentpicture to r ;
2060setbounds currentpicture to r ;
2061
2062savedpicture := currentpicture ; currentpicture := nullpicture ;
2063
2064fill s withcolor nsyellow ;
2065addto currentpicture also savedpicture ;
2066\stopbuffer
2067
2068Many company logos earn their beauty from their simplicity. One of the logos that
2069most Dutch people have imprinted in their mind is that of the Dutch Railway
2070Company (NS). An interesting feature of this logo is that, although it is widely
2071known, drawing it on a piece of paper from mind is a task that many people fail.
2072
2073\startlinecorrection[blank]
2074\processMPbuffer[ns]
2075\stoplinecorrection
2076
2077This logo makes a good candidate for demonstrating a few fine points of drawing
2078graphics, like using linear equations, setting line drawing characteristics,
2079clipping and manipulating bounding boxes.
2080
2081The implementation below is quite certainly not according to the official
2082specifications, but it can nevertheless serve as an example of defining such
2083logos.
2084
2085\startbuffer[a]
2086numeric width ; width = 3cm ;
2087numeric height ; height = width2 ;
2088numeric line ; line = height4 ;
2089\stopbuffer
2090
2091As always, we need to determine the dimensions first. Here, both the height and
2092line width depend on the width of the graphic.
2093
2094Instead of calculating the blue shape such that it will be a filled outline, we
2095will draw the logo shape using line segments. This is why we need the \type
2096{line} parameter.
2097
2098\typebuffer[a]
2099
2100We want sharp corners which can be achieved by setting \type {linejoin} to \type
2101{mitered}.
2102
2103\startbuffer[b]
2104linejoin := mitered ; pickup pencircle scaled line ;
2105\stopbuffer
2106
2107\typebuffer[b]
2108
2109The colors are rather primary blue and yellow. At the time of writing this
2110manual, Dutch trains are still painted yellow, so we will use that shade as
2111background color.
2112
2113\startbuffer[c]
2114color nsblue ; nsblue := (0,0,1) ;
2115color nsyellow ; nsyellow := (1,1,0) ;
2116\stopbuffer
2117
2118\typebuffer[c]
2119
2120We will now describe the main curves. Although these expressions are not that
2121advanced, they demonstrate that we can express relationships instead of using
2122assignments.
2123
2124\startbuffer[d]
2125z1 = (0, height2) ;
2126z2 = (width2height4, y1) ;
2127z3 = (width2height4, y4) ;
2128z4 = (width, 0) ;
2129
2130path p ; p := z1z2z3z4 ;
2131\stopbuffer
2132
2133\typebuffer[d]
2134
2135Although it is accepted to consider \type {z} to be a variable, it is in fact a
2136\type {vardef} macro, that expands into a pair \type {(x,y)}. This means that the
2137previous definitions internally become:
2138
2139\starttyping
2140(x1,y1) = (0, height2) ;
2141(x2,y2) = (width2height4, y1) ;
2142(x3,y3) = (width2height4, y4) ;
2143(x4,y4) = (width, 0) ;
2144\stoptyping
2145
2146These 8 relations can be solved by \METAPOST, since all dependencies are known.
2147
2148\starttyping
2149x1 = 0 ; y1 = height2 ;
2150x2 = width2height4 ; y2 = y1 ;
2151x3 = width2height4 ; y3 = y4 ;
2152x4 = width ; y4 = 0 ;
2153\stoptyping
2154
2155Since we express the variables \type {x} and \type {y} in terms of relations, we
2156cannot reuse them, because that would mean that inconsistent relations occur. So,
2157the following lines will lead to an error message:
2158
2159\starttyping
2160z1 = (10,20) ; z1 = (30,50) ;
2161\stoptyping
2162
2163For similar reasons, we may not assign a value (using \type {:=}) to such a \type
2164{z} variable. Within a \METAPOST\ figure, \type {z} variables are automatically
2165saved, which means that they can be reused for each figure.
2166
2167\startbuffer[x]
2168drawpath p ; drawpoints p ; drawpointlabels p ;
2169\stopbuffer
2170
2171So far, we have defined the following segment of the logo.
2172
2173\startlinecorrection[blank]
2174\processMPbuffer[a,b,c,d,x]
2175\stoplinecorrection
2176
2177\startbuffer[e]
2178z5 = (x4height2, y1) ;
2179z6 = (x4, 2y1) ;
2180z7 = 1.5[z5,z6] ;
2181
2182path q ; q := z3z4z5z7 ;
2183\stopbuffer
2184
2185The next expressions are used to define the second segment. The third expression
2186determines \type {z7} to be positioned on the line \type {z5z6}, where we
2187extend this line by 50\%.
2188
2189\typebuffer[e]
2190
2191\startbuffer[x]
2192drawpath q ; drawpoints q ; drawpointlabels q ;
2193\stopbuffer
2194
2195\startlinecorrection[blank]
2196\processMPbuffer[a,b,c,d,e,x]
2197\stoplinecorrection
2198
2199If we combine these two segments, we get:
2200
2201\startbuffer[x]
2202drawpath p ; drawpoints p ; drawpointlabels p ;
2203swappointlabels := true ;
2204drawpath q ; drawpoints q ; drawpointlabels q ;
2205\stopbuffer
2206
2207\startlinecorrection[blank]
2208\processMPbuffer[a,b,c,d,e,x]
2209\stoplinecorrection
2210
2211However, when we draw them using the right linewidth and color, you will notice
2212that were not yet done:
2213
2214\startbuffer[f]
2215draw p withcolor nsblue ; draw q withcolor nsblue ;
2216\stopbuffer
2217
2218\startlinecorrection[blank]
2219\processMPbuffer[a,b,c,d,e,f]
2220\stoplinecorrection
2221
2222The second curve is similar to the first one, but rotated over 180 degrees.
2223
2224\startbuffer[g]
2225addto currentpicture also currentpicture
2226 rotatedaround (.5[z2,z3],180) shifted (height4,height2) ;
2227\stopbuffer
2228
2229\typebuffer[g]
2230
2231\startlinecorrection[blank]
2232\processMPbuffer[a,b,c,d,e,f,g]
2233\stoplinecorrection
2234
2235In order to get the sharp edges, we need to clip off part of
2236the curves and at first sight, we may consider using a
2237scaled bounding box. However, when we show the natural
2238bounding box, you will notice that a more complicated bit of
2239calculations is needed.
2240
2241\startbuffer[x]
2242draw boundingbox currentpicture
2243 withpen pencircle scaled .5mm withcolor .625white ;
2244\stopbuffer
2245
2246\startlinecorrection[blank]
2247\processMPbuffer[a,b,c,d,e,f,g,x]
2248\stoplinecorrection
2249
2250The right clip path is calculated using the following expressions. Watch how we
2251use \type {rt} and \type {top} to correct for the linewidth.
2252
2253\startbuffer[h]
2254numeric d, lx, ly, ux, uy ; d = line2 ;
2255
2256lx = 3d d3 ; ly = d ; ux = rt x5 d3 ; uy = top y6 ;
2257
2258path r ; r := (lx,ly)(ux,ly)(ux,uy)(lx,uy)cycle;
2259\stopbuffer
2260
2261\typebuffer[h]
2262
2263The clipping path is applied by saying:
2264
2265\startbuffer[i]
2266clip currentpicture to r ;
2267\stopbuffer
2268
2269\typebuffer[i]
2270
2271The result is quite acceptable:
2272
2273\startlinecorrection[blank]
2274\processMPbuffer[a,b,c,d,e,f,g,h,i]
2275\stoplinecorrection
2276
2277But, if you watch closely to how this graphic extends into to left margin of this
2278document, you will see that the bounding box is not yet right.
2279
2280\startlinecorrection[blank]
2281\processMPbuffer[a,b,c,d,e,f,g,h,i,x]
2282\stoplinecorrection
2283
2284\startbuffer[j]
2285setbounds currentpicture to r ;
2286\stopbuffer
2287
2288\typebuffer[j]
2289
2290We use the same path \type {r} to correct the bounding box.
2291
2292\startlinecorrection[blank]
2293\processMPbuffer[a,b,c,d,e,f,g,h,i,j,x]
2294\stoplinecorrection
2295
2296There are a few subtle points involved, like setting the \type {linejoin}
2297variable. If we had not set it to \type {mitered}, we would have got round
2298corners. We dont set the \type {linecap}, because a flat cap would not extend
2299far enough into the touching curve and would have left a small hole. The next
2300example shows what happens if we set these variables to the wrong values:
2301
2302\startbuffer[bb]
2303linejoin := rounded ; linecap := mitered ;
2304\stopbuffer
2305
2306\startlinecorrection[blank]
2307\processMPbuffer[a,b,bb,c,d,e,f,g,h,i,j]
2308\stoplinecorrection
2309
2310In fact we misuse the fact that both curves overlay each other.
2311
2312\startbuffer[f]
2313draw p withcolor nsblue ; draw q withcolor .625white ;
2314\stopbuffer
2315
2316\startlinecorrection[blank]
2317\processMPbuffer[a,b,c,d,e,f,g,h,i,j]
2318\stoplinecorrection
2319
2320The complete logo definition is a bit more extensive because we also want to add
2321a background. Because we need to clip the blue foreground graphic, we must
2322temporarily store it when we fill the background.
2323
2324\typebuffer[ns]
2325
2326For practical use it makes sense to package this definition in a macro to which
2327we pass the dimensions.
2328
2329\stopsection
2330
2331\startsection[title={Music sheets}]
2332
2333The next example demonstrates quite some features. Imagine that we want to make
2334us a couple of sheets so that we can write a musical masterpiece. Lets also
2335forget that \TEX\ can draw lines, which means that somehow we need to use
2336\METAPOST.
2337
2338Drawing a bar is not that complicated as the following code demonstrates.
2339
2340\startbuffer
2341\startusableMPgraphic{bar}
2342 vardef MusicBar (expr width, gap, linewidth, barwidth) =
2343 image
2344 ( interim linecap := butt ;
2345 for i=1 upto 5 :
2346 draw ((0,0)--(width,0)) shifted (0,(i-1)gap)
2347 withpen pencircle scaled linewidth ;
2348 endfor ;
2349 for i=llcorner currentpicture -- ulcorner currentpicture ,
2350 lrcorner currentpicture -- urcorner currentpicture :
2351 draw i withpen pencircle scaled barwidth ;
2352 endfor ; )
2353 enddef ;
2354\stopusableMPgraphic
2355\stopbuffer
2356
2357\typebuffer \getbuffer
2358
2359We can define the sidebars a bit more efficient using two predefined subpaths:
2360
2361\starttyping
2362for i=leftboundary currentpicture, rightboundary currentpicture :
2363\stoptyping
2364
2365We define a macro \type {MusicBar} that takes four arguments. The first two
2366determine the dimensions, the last two concern the line widths. Now watch how we
2367can use this macro:
2368
2369\startbuffer
2370\includeMPgraphic{bar} ;
2371draw MusicBar (200pt, 6pt, 1pt, 2pt) ;
2372draw MusicBar (300pt, 6pt, 1pt, 2pt) shifted (0,30pt) ;
2373\stopbuffer
2374
2375\typebuffer
2376
2377\startlinecorrection[blank]
2378\processMPbuffer
2379\stoplinecorrection
2380
2381As you can see in this example, the bar is a picture that can be transformed
2382(shifted in our case). However, a close look at the macro teaches us that it does
2383a couple of draws too. This is possible because we wrap the whole in an image
2384using the \type {image} macro. This macro temporary saves the current picture,
2385and at the end puts the old \type {currentpicture} under the new one.
2386
2387We wrap the whole in a \type {vardef}. This means that the image is returned as
2388if it was a variable. Actually, the last thing in a \type {vardef} should be a
2389proper return value, in our case a picture. This also means that we may not end
2390the \type {vardef} with a semi colon. So, when the content of the \type {vardef}
2391is expanded, we get something
2392
2393\starttyping
2394draw somepicture ... ;
2395\stoptyping
2396
2397Because we are still drawing something, we can add transform directives and set
2398attributes, like the color.
2399
2400The second \type {for} loop demonstrates two nice features. Instead of repeating
2401the draw operation by copying code, we apply it to a list, in our case a list of
2402paths. This list contains two simple line paths. Because an \type {image} starts
2403with a fresh \type {currentpicture}, we can safely use the bounding box data to
2404determine the height of the line.
2405
2406The next step in producing the sheets of paper is to put several bars on a page,
2407preferable with the width of the current text. This time we will use a reusable
2408graphic, because each bar is the same.
2409
2410\startbuffer
2411\startreusableMPgraphic{bars}
2412 \includeMPgraphic{bar} ;
2413 draw MusicBar (TextWidth, 6pt, 1pt, 2pt) withcolor .625yellow ;
2414\stopreusableMPgraphic
2415\stopbuffer
2416
2417\typebuffer \getbuffer
2418
2419\startlinecorrection[blank]
2420\reuseMPgraphic{bars}
2421\stoplinecorrection
2422
2423Instead of going through the trouble of letting \METAPOST\ calculate the positions
2424of the bars, we will use \TEX. We put 12 bars on a page and let \TEX\ take care
2425of the interbar spacing. Because we only want stretchable space between bars,
2426called glue in \TEX, we need to remove the last added glue.
2427
2428\startnotmode[screen]
2429
2430\startbuffer[music]
2431\startstandardmakeup[doublesided=no,page=]
2432 \dorecurse{15}{\reuseMPgraphic{bars}\vfill}\removelastskip
2433\stopstandardmakeup
2434\stopbuffer
2435
2436\stopnotmode
2437
2438\startmode[screen]
2439
2440\startbuffer[music]
2441\startstandardmakeup[doublesided=no,page=]
2442 \dorecurse{10}{\reuseMPgraphic{bars}\vfill}\removelastskip
2443\stopstandardmakeup
2444\stopbuffer
2445
2446\stopmode
2447
2448\typebuffer[music]
2449
2450\startusableMPgraphic{bar}
2451 vardef MusicBar (expr width, gap, linewidth, barwidth) =
2452 image
2453 ( interim linecap := butt ;
2454 for i=1 upto 5 :
2455 draw ((0,0)--(width,0))
2456 randomized (1pt,1.5pt)
2457 shifted (0,(i-1)gap)
2458 withpen pencircle scaled linewidth ;
2459 endfor ;
2460 for i=llcorner currentpicture -- ulcorner currentpicture ,
2461 lrcorner currentpicture -- urcorner currentpicture :
2462 draw i randomized 2pt shifted (0,-1pt)
2463 withpen pencircle scaled barwidth ;
2464 endfor ; )
2465 enddef ;
2466\stopusableMPgraphic
2467
2468\startreusableMPgraphic{bars}
2469 \includeMPgraphic{bar} ;
2470 draw MusicBar (TextWidth, 6pt, 1pt, 2pt) withcolor .625yellow ;
2471\stopreusableMPgraphic
2472
2473It may add to the atmosphere of handywork if you slightly randomize the lines.
2474We leave it up to the reader to figure out how the code should be changed to
2475accomplish this.
2476
2477\startlinecorrection[blank]
2478\reuseMPgraphic{bars}
2479\stoplinecorrection
2480
2481The complete result is shown on the next page.
2482
2483\startpostponing
2484\getbuffer[music]
2485\stoppostponing
2486
2487\stopsection
2488
2489\startsection[title={The euro symbol}]
2490
2491When Patrick Gundlach posted a nice \METAPOST\ version of the euro symbol to the
2492\CONTEXT\ discussion list, he added the comment \quotation {The official
2493construction is ambiguous: how thick are the horizontal bars? How much do they
2494stick out to the left? Is this thing a circle or what? Are the angles on the left
2495side of the bars the same as the one on the right side? \unknown} The alternative
2496below is probably not as official as his, but permits a finetuning. You are
2497warned: whatever you try, the euro {\em is} and {\em will remain} an ugly symbol.
2498
2499We use a couple of global variables to control the euro shape within reasonable
2500bounds. Then we define two circles. Next we define a vertical line that we use in
2501a couple of cut and paste operations. Watch how the top left point of the outer
2502circle determines the slant of the line that we use to slice the vertical bars.
2503
2504\startbuffer[euro]
2505boolean traceeuro ; traceeuro := false ;
2506
2507vardef eurosymbol = image (
2508
2509if unknown euroradius : euroradius := 2cm ; fi ;
2510if unknown eurowidth : eurowidth := 3euroradius16 ; fi ;
2511if unknown euroroffset : euroroffset := eurowidth ; fi ;
2512if unknown euroloffset : euroloffset := euroradius32 ; fi ;
2513if unknown eurolshift : eurolshift := euroroffset ; fi ;
2514if unknown eurovdelta : eurovdelta := eurowidth4 ; fi ;
2515
2516save
2517 outercircle, innercircle, horbar,
2518 rightline, rightslant, topslant, botslant,
2519 eurocircle, eurotopbar, eurobotbar ;
2520
2521path
2522 outercircle, innercircle, horbar,
2523 rightline, rightslant, topslant, botslant,
2524 eurocircle, eurotopbar, eurobotbar ;
2525
2526outercircle := fullcircle scaled euroradius ;
2527innercircle := fullcircle scaled (euroradiuseurowidth) ;
2528
2529if traceeuro : for i = outercircle, innercircle :
2530 draw i withpen pencircle scaled 1pt withcolor .5white ;
2531endfor ; fi ;
2532
2533rightline :=
2534 (lrcorner outercircle urcorner outercircle)
2535 shifted (euroroffset,0) ;
2536
2537outercircle := outercircle cutbefore rightline ;
2538
2539rightslant :=
2540 point 0 of outercircle
2541 origin shifted (0,ypart lrcorner outercircle) ;
2542
2543eurocircle := buildcycle(outercircle, rightline,
2544 reverse innercircle, reverse rightslant) ;
2545
2546horbar := (euroradius,0) (euroradius,0) ;
2547
2548topslant :=
2549 rightslant shifted (euroradiuseuroroffseteuroloffset,0) ;
2550
2551botslant :=
2552 topslant shifted (0,eurolshift) ;
2553
2554if traceeuro : for i = rightline, rightslant, topslant, botslant :
2555 draw i withpen pencircle scaled 1pt withcolor .5white ;
2556endfor ; fi ;
2557
2558eurotopbar := buildcycle
2559 (topslant, horbar shifted (0, eurovdelta),
2560 rightslant, horbar shifted (0, eurovdeltaeurowidth2)) ;
2561
2562eurobotbar := buildcycle
2563 (botslant, horbar shifted (0,eurovdelta),
2564 rightslant, horbar shifted (0,eurovdeltaeurowidth2)) ;
2565
2566for i = eurocircle, eurotopbar, eurobotbar :
2567 draw i withpen pencircle scaled 0 ;
2568endfor ;
2569for i = eurocircle, eurotopbar, eurobotbar :
2570 fill i withpen pencircle scaled 0 ;
2571endfor ;
2572
2573if traceeuro :
2574 drawpoints eurocircle withcolor red ;
2575 drawpoints eurotopbar withcolor green ;
2576 drawpoints eurobotbar withcolor blue ;
2577fi ;
2578
2579) enddef ;
2580\stopbuffer
2581
2582\typebuffer[euro]
2583
2584We only set a parameter when it is not yet set. This has
2585the advantage that we dont have to set them when we change
2586one. This way of manipulating paths (cutting and building)
2587does not always work well because of rounding errors, but
2588here it does work.
2589
2590\startbuffer[demo]
2591euroradius := 4cm ; traceeuro := true ; draw eurosymbol ;
2592\stopbuffer
2593
2594\typebuffer[demo]
2595
2596For educational purposes, we have added a bit of
2597tracing. When enabled, the euro shows up as:
2598
2599\startlinecorrection[blank]
2600\processMPbuffer[euro,demo]
2601\stoplinecorrection
2602
2603Of course it would be best to define the euro as one shape, but we wont go
2604though that process right now. By packaging the combined paths in an image, we
2605can conveniently color the euro symbol:
2606
2607\startbuffer[demo]
2608draw eurosymbol withcolor .625red ;
2609\stopbuffer
2610
2611\typebuffer[demo]
2612
2613\startlinecorrection[blank]
2614\processMPbuffer[euro,demo]
2615\stoplinecorrection
2616
2617You may wonder why we both draw and fill the euro, using a pen with zero width.
2618Weve done this in order to demonstrate the \type {redraw} and \type {refill}
2619macros.
2620
2621\startbuffer[extra]
2622redraw currentpicture withpen pencircle scaled 4pt withcolor .625yellow ;
2623refill currentpicture withcolor .625white ;
2624setbounds currentpicture to boundingbox currentpicture enlarged 2pt ;
2625\stopbuffer
2626
2627\typebuffer[extra]
2628
2629\startlinecorrection[blank]
2630\processMPbuffer[euro,demo,extra]
2631\stoplinecorrection
2632
2633\stopsection
2634
2635\startsection[title={Killing time}]
2636
2637Not seldom \TEX\ users want to use this program and its metarelatives as
2638general purpose tools, even at the cost of quite some effort or suboptimal
2639results. Imagine that you are under way from our planet to Mars. After a long
2640period of sleep you wake up and start wondering on what track you are. You even
2641start questioning the experts that send you on your way, so you pop open your
2642laptop, launch your editor and start metaposting.
2643
2644First you need to determine the begin and end points of your journey. For now it
2645is enough to know the relative angle of the paths that both planets follow as
2646well as the path themselves. We assume circular paths.
2647
2648\startbuffer
2649path a ; a := fullcircle scaled 3cm ;
2650path b ; b := fullcircle scaled 2cm rotated 120 ;
2651
2652draw a withpen pencircle scaled 1mm withcolor .625red ;
2653draw b withpen pencircle scaled 1mm withcolor .625yellow ;
2654
2655draw point 0 of a withpen pencircle scaled 2mm ;
2656draw point 0 of b withpen pencircle scaled 2mm ;
2657\stopbuffer
2658
2659\typebuffer
2660
2661The rotation 120 can be calculated from the relative starting points and time the
2662journey will take. Alternatively we can use the time along the path, but this
2663would be a bit more fuzzy later on. \footnote {In case you wonder why \METAPOST\
2664talks about the time on a path, you now have a cue.}
2665
2666\startlinecorrection[blank]
2667\processMPbuffer
2668\stoplinecorrection
2669
2670After a bit of playing with drawing paths between the two points, you decide to
2671make a macro. We want to feed the angle between the paths but also the connecting
2672path. So, we have to pass a path, but unfortunately we dont have direct access
2673to the points. By splitting the argument definition we can pass an expression
2674first, and a wildcard argument next.
2675
2676\startbuffer
2677\startuseMPgraphic{gamble}
2678def Gamble (expr rot) (text track) =
2679 path a ; a := fullcircle scaled 3cm ;
2680 path b ; b := fullcircle scaled 2cm rotated rot ;
2681
2682 pair aa ; aa := point 0 of a ;
2683 pair bb ; bb := point 0 of b ;
2684 path ab ; ab := track ;
2685
2686 draw a withpen pencircle scaled 1mm withcolor .625red ;
2687 draw b withpen pencircle scaled 1mm withcolor .625yellow ;
2688
2689 draw aa withpen pencircle scaled 2mm ;
2690 draw bb withpen pencircle scaled 2mm ;
2691
2692 drawarrow ab withpen pencircle scaled 1mm withcolor .625white ;
2693
2694 setbounds currentpicture to boundingbox a enlarged 2mm ;
2695 draw boundingbox currentpicture withpen pencircle scaled .25mm ;
2696enddef ;
2697\stopuseMPgraphic
2698\stopbuffer
2699
2700\typebuffer \getbuffer
2701
2702Because at this distance nobody will bother us with the thickness of the pen and
2703colors, we code them the hard way. We create our own universe by setting a fixed
2704boundingbox.
2705
2706We leave the Earth in the most popular way, straight upwards and after a few
2707cycles, we leave it parallel to the surface. The path drawn reminds much of the
2708trajectories shown in popular magazines.
2709
2710\startbuffer
2711\startMPcode
2712\includeMPgraphic{gamble} ;
2713Gamble(120, aa {(0,1)} .. bb) ;
2714\stopMPcode
2715\stopbuffer
2716
2717\typebuffer
2718
2719\startlinecorrection[blank] \getbuffer \stoplinecorrection
2720
2721According to \METAPOST, when we leave the Earth straight upwards and want a
2722smooth trajectory, we have to pass through outer space.
2723
2724\startbuffer
2725\startMPcode
2726\includeMPgraphic{gamble} ;
2727Gamble(120,aa {(1,0)} .. bb) ;
2728\stopMPcode
2729\stopbuffer
2730
2731\typebuffer
2732
2733\startlinecorrection[blank] \getbuffer \stoplinecorrection
2734
2735Given that we want a smooth path as well as a short journey, we can best follow
2736Mars path. Here we face the risk that when we travel slower than Mars does, we
2737have a problem.
2738
2739\startbuffer
2740\startMPcode
2741\includeMPgraphic{gamble} ;
2742Gamble(120,aa {dir 90} .. {precontrol 0 of b rotated 90} bb) ;
2743\stopMPcode
2744\stopbuffer
2745
2746\typebuffer
2747
2748\startlinecorrection[blank] \getbuffer \stoplinecorrection
2749
2750We can even travel a shorter path when we leave Earth at the surface that faces
2751the point of arrival.
2752
2753\startbuffer
2754\startMPcode
2755\includeMPgraphic{gamble} ;
2756Gamble(120,aa .. {precontrol 0 of b rotated 90} bb) ;
2757\stopMPcode
2758\stopbuffer
2759
2760\typebuffer
2761
2762\startlinecorrection[blank] \getbuffer \stoplinecorrection
2763
2764In the end we decide that although the trajectories look impressive, we will not
2765trust our lives to \METAPOST. A beautiful path is not neccessarily a good path.
2766But even then, this macro provides a nice way to experiment with directions,
2767controls and tensions.
2768
2769\stopsection
2770
2771
2772
2773
2774
2775
2776
2777
2778\startsection[title={Selective randomization}]
2779
2780In this document we have used a lot of randomization. Because \CONTEXT\ often
2781needs multiple runs to sort out cross references, positions, tables of contents,
2782and so on, being real random every run would result in endless runs to get things
2783right, because the size of graphics changes. This is prevented by storing the
2784random seed betweeen runs. You can remove the \type {tuc} file to get a new seed
2785(or run \type {context purgeall}).
2786
2787Here is another example of randomization. This time we only randomize the control
2788points so the main shape sort of remains intact which can be handy when you use
2789such random shapes around text but still want a predictable size.
2790
2791\startbuffer
2792\startMPcode
2793fill fullcircle scaled 2cm
2794 randomizedcontrols 0.1cm
2795 withcolor darkred
2796 withtransparency (1,.5) ;
2797fill ((1cm,0)--(0,1cm)--(-1cm,0)cycle)
2798 randomizedcontrols 0.1cm
2799 withcolor darkblue
2800 withtransparency (1,.5) ;
2801\stopMPcode
2802\stopbuffer
2803
2804\typebuffer
2805
2806\startlinecorrection[blank] \getbuffer \stoplinecorrection
2807
2808\startbuffer
2809\startMPcode
2810draw image (
2811 fill fullcircle scaled 2cm
2812 withcolor darkred
2813 withtransparency (1,.5) ;
2814 fill ((1cm,0)--(0,1cm)--(-1cm,0)cycle)
2815 withcolor darkblue
2816 withtransparency (1,.5) ;
2817) randomizedcontrols 0.1cm ;
2818\stopMPcode
2819\stopbuffer
2820
2821\typebuffer
2822
2823\startlinecorrection[blank] \getbuffer \stoplinecorrection
2824
2825\stopsection
2826
2827\startsection[title=Snapping]
2828
2829There are quite some helpers in \METAFUN\ and I must admit that I forgot
2830about most. Some just ended up in the core because they can be useful, others
2831serve as illustration. Heres one of them: \type {snapped}. First we define
2832a few helpers that we then use to check out a few shapes.
2833
2834\startbuffer
2835\startMPdefinitions
2836def ShowSnapGrid(text shape) =
2837 fill (shape xsized 77mm) withcolor white3 ;
2838 draw image (
2839 for i=10mm step 5mm until 100mm :
2840 draw fullsquare scaled i ;
2841 endfor ;
2842 ) withcolor 2white3 ;
2843 drawpoints (shape xsized 77mm) withcolor black ;
2844enddef ;
2845
2846vardef SnapShape expr shape =
2847 image (
2848 draw shape ;
2849 drawpoints shape ;
2850 )
2851enddef ;
2852
2853vardef ShowSnapShape expr shape =
2854 ShowSnapGrid(shape);
2855
2856 draw SnapShape(shape xsized 77mm snapped -5mm ) withcolor red ;
2857 draw SnapShape(shape xsized 77mm snapped 5mm ) withcolor red ;
2858 draw SnapShape(shape xsized 77mm snapped (5mm,10mm)) withcolor green ;
2859 draw SnapShape(shape xsized 77mm snapped (5mm,15mm)) withcolor blue ;
2860 draw SnapShape(shape xsized 77mm snapped (5mm,20mm)) withcolor yellow ;
2861enddef ;
2862\stopMPdefinitions
2863\stopbuffer
2864
2865\typebuffer \getbuffer
2866
2867In \in {figures} [fig:snapper:1], \in [fig:snapper:2] and \in [fig:snapper:3] we
2868see how the original shape gets snapped on the grid. Of course in more complex
2869images the direction of the snapping can change the result in an unwanted way,
2870like overlapping shapes that obscure others, but normally this snapping is only
2871useful for simple predictable cases (like title pages).
2872
2873\startplacefigure[reference=fig:snapper:1,title={Messing with \type{fullcircle}.}]
2874\startMPcode
2875 ShowSnapShape(fullcircle);
2876\stopMPcode
2877\stopplacefigure
2878
2879\startplacefigure[reference=fig:snapper:2,title={\type{fullsquare}}]
2880\startMPcode
2881 ShowSnapShape(fullsquare);
2882\stopMPcode
2883\stopplacefigure
2884
2885\startplacefigure[reference=fig:snapper:3,title={\type{fulltriangle}}]
2886\startMPcode
2887 ShowSnapShape(fulltriangle);
2888\stopMPcode
2889\stopplacefigure
2890
2891\stopsection
2892
2893\startsection[title=Arrowheads]
2894
2895Arrows are actually drawn quite well in \METAPOST, as the arrowheads nicely adapt
2896to the direction of the point where the arrowhead is attached. There are however
2897some limitations as the following examples demonstrate: arrows dont work well
2898with transparency and you can probably figure out why. Alan Braslau came up with
2899an extension that allows to set the dimple of the head. You can see all this
2900in \in {figure} [fig:arrowheads].
2901
2902\startbuffer[a]
2903numeric unit ; unit := mm ;
2904
2905drawoptions(withcolor .6blue withtransparency (1,.25)) ;
2906
2907pickup pencircle scaled 2unit ; ahlength := 6unit ;
2908
2909picture p ; p := image (
2910 drawarrow reverse fullcircle rotated 5 scaled 50unit ;
2911 drawarrow reverse fullcircle rotated 10 scaled 30unit ;
2912) shifted ( 45unit, 0unit) ;
2913
2914for i=0 step 90 until 360 : draw p rotated i ; endfor ;
2915
2916currentpicture := currentpicture shifted center currentpicture ;
2917
2918p := currentpicture ; p := image (
2919 draw llcorner p center p ;
2920 drawarrow llcorner p 0.875[llcorner p,center p] ;
2921) ;
2922
2923for i=0 step 90 until 360 : draw p rotated i ; endfor ;
2924
2925clip currentpicture to boundingbox (fullcircle scaled 80unit) ;
2926
2927if lua.mp.mode("screen") :
2928 currentpicture := currentpicture ysized .4TextHeight ;
2929else :
2930 currentpicture := currentpicture xsized .4TextWidth ;
2931fi ;
2932\stopbuffer
2933
2934\typebuffer[a]
2935
2936\startbuffer[b]
2937 resetarrows ;
2938\stopbuffer
2939
2940\startbuffer[a1]
2941 ahvariant := 1 ;
2942\stopbuffer
2943\startbuffer[a2]
2944 ahvariant := 2 ;
2945\stopbuffer
2946
2947\startbuffer[a3]
2948 ahvariant := 1 ; ahdimple := 12 ;
2949\stopbuffer
2950\startbuffer[a4]
2951 ahvariant := 1 ; ahdimple := 1 ;
2952\stopbuffer
2953\startbuffer[a5]
2954 ahvariant := 1 ; ahdimple := 52 ;
2955\stopbuffer
2956
2957\startplacefigure[reference=fig:arrowheads,title=The way arrowheads are constructed.]
2958 \doifelsemode {screen} {
2959 \setupcombination[nx=3,ny=2]
2960 } {
2961 \setupcombination[nx=2,ny=3]
2962 }
2963 \startcombination[distance=2em]
2964 {\processMPbuffer[a, b]} {\tttf ahvariant=0}
2965 {\processMPbuffer[a1,a,b]} {\tttf ahvariant=1}
2966 {\processMPbuffer[a2,a,b]} {\tttf ahvariant=2}
2967 {\processMPbuffer[a3,a,b]} {\tttf ahvariant=1, ahdimple=12}
2968 {\processMPbuffer[a4,a,b]} {\tttf ahvariant=1, ahdimple=1}
2969 {\processMPbuffer[a5,a,b]} {\tttf ahvariant=1, ahdimple=52}
2970 \stopcombination
2971\stopplacefigure
2972
2973\stopsection
2974
2975\startsection[title=Teaser]
2976
2977Sometimes, when playing with \METAPOST\ you run into interesting cases. Here is
2978one. The result is shown in \in {figure} [fig:teaser:1].
2979
2980\startbuffer
2981\startusableMPgraphic{BackgroundTeaser}
2982 fill OverlayBox enlarged 1mm withcolor darkyellow ;
2983 path p ; p := OverlayBox enlarged -5mm ;
2984 path q ; q := OverlayBox enlarged -10mm ;
2985 fill q withcolor white ;
2986 drawoptions(withcolor darkred) ;
2987 fill reverse topboundary q -- topboundary p -- cycle ;
2988 fill reverse bottomboundary q -- bottomboundary p -- cycle ;
2989 drawoptions(withcolor darkgreen) ;
2990 fill reverse leftboundary q -- leftboundary p -- cycle ;
2991 fill reverse rightboundary q -- rightboundary p -- cycle ;
2992\stopusableMPgraphic
2993
2994\defineoverlay
2995 [BackgroundTeaser]
2996 [\useMPgraphic{BackgroundTeaser}]
2997
2998\framed
2999 [frame=off,
3000 offset=15mm,
3001 background=BackgroundTeaser,
3002 align=normal]
3003 {\input knuth }
3004\stopbuffer
3005
3006\typebuffer
3007
3008\startplacefigure[reference=fig:teaser:1,title=Can you guess what happens here?]
3009 \getbuffer
3010\stopplacefigure
3011
3012\stopsection
3013
3014\startsection[title={Lists}]
3015
3016For some specific purpose I needed to sort a list of paths and therefore
3017\METAFUN\ comes with a quick sort macro. Its working can be demonstrated by an
3018example.
3019
3020\startbuffer[a]
3021pair p[], pp[] ; numeric n ; n := 25 ;
3022for i=1 upto n : p[i] := origin randomized 4cm ; endfor ;
3023\stopbuffer
3024
3025\startbuffer[b]
3026copylist(p,pp) ;
3027drawarrow listtolines(pp) shifted ( 0,0) withcolor darkblue ;
3028\stopbuffer
3029
3030\startbuffer[c]
3031copylist(p,pp) ; sortlist(pp)() ;
3032drawarrow listtolines(pp) shifted (300,0) withcolor darkyellow ;
3033\stopbuffer
3034
3035\startbuffer[d]
3036copylist(p,pp) ; sortlist(pp)(xpart) ;
3037drawarrow listtolines(pp) shifted (100,0) withcolor darkred ;
3038
3039\stopbuffer
3040\startbuffer[e]
3041copylist(p,pp) ; sortlist(pp)(ypart) ;
3042drawarrow listtolines(pp) shifted (200,0) withcolor darkgreen ;
3043\stopbuffer
3044
3045\startbuffer[f]
3046vardef whow expr p = (xpart p ypart p) enddef ;
3047
3048copylist(p,pp) ; sortlist(pp)(whow) ;
3049drawarrow listtolines(pp) shifted (400,0) withcolor darkcyan ;
3050\stopbuffer
3051
3052\startbuffer[g]
3053vardef whow expr p = (xpart p ypart p) enddef ;
3054
3055copylist(p,pp) ; sortlist(pp)(whow) ;
3056drawarrow listtolines(pp) shifted (500,0) withcolor darkmagenta ;
3057\stopbuffer
3058
3059\typebuffer[a,b,c,d,e,f,g]
3060
3061The result of this code is shown in \in {figure} [fig:sorting].
3062
3063\startplacefigure[reference=fig:sorting,title={Using the sorter.}]
3064 \startcombination[3*2]
3065 {\processMPbuffer[a,b]} {\tttf unsorted}
3066 {\processMPbuffer[a,c]} {\tttf sorted}
3067 {\processMPbuffer[a,d]} {\tttf xpart}
3068 {\processMPbuffer[a,e]} {\tttf ypath}
3069 {\processMPbuffer[a,f]} {\tttf xpart p ypart p}
3070 {\processMPbuffer[a,g]} {\tttf xpart p ypart p}
3071 \stopcombination
3072\stopplacefigure
3073
3074There is a helper that converts a list of paths into a shape that covers all
3075of them. In \in {figure} [fig:shapedlist] three shaped lists are shown.
3076
3077\startbuffer[a]
3078 def ShowShape(expr e) =
3079 draw image (
3080
3081 save p ; path p[] ;
3082
3083 def MakeShape(expr i,w,h,x,y) =
3084 p[i] := e
3085 xysized ((w,h) randomized (2mm,1mm))
3086 shifted ((x,y) randomized (2mm,1mm)) ;
3087 enddef ;
3088
3089 MakeShape(1,40mm,6mm,10mm, 0mm) ;
3090 MakeShape(2,50mm,5mm, 5mm,10mm) ;
3091 MakeShape(3,20mm,8mm,30mm,20mm) ;
3092 MakeShape(4,55mm,5mm,10mm,30mm) ;
3093 MakeShape(5,55mm,5mm, 5mm,50mm) ;
3094
3095 save s ; path s ; s := shapedlist(p) ; drawarrow s ;
3096
3097 linejoin := butt ;
3098
3099 for i=1 upto 5 :
3100 fill p[i] withcolor .75white withtransparency (1,.5) ;
3101 draw thetextext("\tttf " decimal i, center p[i]) ;
3102 endfor ;
3103
3104 ) ysized 4cm ;
3105 enddef ;
3106\stopbuffer
3107
3108\typebuffer[a]
3109
3110\startbuffer[b]
3111ShowShape(unitsquare)
3112\stopbuffer
3113
3114\startbuffer[c]
3115ShowShape(unitcircle)
3116\stopbuffer
3117
3118\startbuffer[d]
3119ShowShape(unittriangle)
3120\stopbuffer
3121
3122\startplacefigure[reference=fig:shapedlist,title={The \type {shapedlist} macro returns the envelope that covers all the paths in the list.}]
3123 \startcombination[3*1]
3124 {\processMPbuffer[a,b]} {\tttf unitsquare}
3125 {\processMPbuffer[a,c]} {\tttf unitcircle}
3126 {\processMPbuffer[a,d]} {\tttf unittriangle}
3127 \stopcombination
3128\stopplacefigure
3129
3130\stopsection
3131
3132\startsection[title=Table cells]
3133
3134Sometimes a standard \CONTEXT\ feature doesnt work out as expected. Take the
3135following table:
3136
3137\startbuffer
3138\bTABLE[frame=on,framecolor=blue,rulethickness=1pt]
3139 \bTR
3140 \bTD test \eTD
3141 \bTD test \eTD
3142 \bTD test \eTD
3143 \bTD[framecolor=magenta] test \eTD
3144 \bTD test \eTD
3145 \bTD test \eTD
3146 \eTR
3147 \bTR
3148 \bTD test \eTD
3149 \bTD[framecolor=red] test \eTD
3150 \bTD test \eTD
3151 \bTD test \eTD
3152 \bTD test \eTD
3153 \bTD[framecolor=green] test \eTD
3154 \eTR
3155\eTABLE
3156\stopbuffer
3157
3158\typebuffer
3159
3160Because cells are drawn topdown and leftright a next cell border
3161overruns the previous one.
3162
3163\startlinecorrection
3164\getbuffer
3165\stoplinecorrection
3166
3167\startbuffer
3168\bTABLE[frame=on,framecolor=blue,rulethickness=1pt]
3169 \bTR
3170 \bTD test \eTD
3171 \bTD test \eTD
3172 \bTD test \eTD
3173 \bTD[framecolor=magenta,frameoffset=.5pt] test \eTD
3174 \bTD test \eTD
3175 \bTD test \eTD
3176 \eTR
3177 \bTR
3178 \bTD test \eTD
3179 \bTD[framecolor=red,frameoffset=.5pt] test \eTD
3180 \bTD test \eTD
3181 \bTD test \eTD
3182 \bTD test \eTD
3183 \bTD[framecolor=green,frameoffset=.5pt] test \eTD
3184 \eTR
3185\eTABLE
3186\stopbuffer
3187
3188You can try this:
3189
3190\typebuffer
3191
3192which gives us something that is not okay either for cells that touch an edge:
3193
3194\startlinecorrection
3195\getbuffer
3196\stoplinecorrection
3197
3198but we can cheat:
3199
3200\startlinecorrection
3201\framed
3202 [offset=overlay,
3203 frameoffset=.5pt,
3204 framecolor=blue,
3205 rulethickness=1pt]
3206 {\getbuffer}
3207\stoplinecorrection
3208
3209This is achieved by framing the whole table:
3210
3211\starttyping
3212\framed
3213 [offset=overlay,
3214 frameoffset=.5pt,
3215 framecolor=blue,
3216 rulethickness=1pt]
3217 {...}
3218\stoptyping
3219
3220\startbuffer
3221\startuseMPgraphic{cell:innerframe}{innercolor}
3222 draw OverlayBox enlarged -1.5OverlayLineWidth
3223 withpen pensquare scaled OverlayLineWidth
3224 withcolor \MPvar{innercolor} ;
3225\stopuseMPgraphic
3226
3227\defineoverlay
3228 [innerframe]
3229 [{\uniqueMPgraphic{cell:innerframe}
3230 {innercolor=\framedparameter{innercolor}}}]
3231
3232\bTABLE[frame=on,framecolor=blue,rulethickness=1pt,innercolor=magenta]
3233 \bTR
3234 \bTD test \eTD
3235 \bTD test \eTD
3236 \bTD test \eTD
3237 \bTD[background=innerframe] test \eTD
3238 \bTD test \eTD
3239 \bTD test \eTD
3240 \eTR
3241 \bTR
3242 \bTD test \eTD
3243 \bTD[background=innerframe,innercolor=red] test \eTD
3244 \bTD test \eTD
3245 \bTD test \eTD
3246 \bTD test \eTD
3247 \bTD[background=innerframe,innercolor=green] test \eTD
3248 \eTR
3249\eTABLE
3250\stopbuffer
3251
3252A \METAPOST\ alternative is also possible and it gives a bit nicer
3253interface too:
3254
3255\typebuffer
3256
3257We get:
3258
3259\startlinecorrection
3260\getbuffer
3261\stoplinecorrection
3262
3263\stopsection
3264
3265\startsection[title=Educational]
3266
3267I made this example long ago, when some family member had to learn tables by
3268heart. For some reason, at school, this is made into a complex issue, with tricks
3269and such, even it if only involves only a few numbers. My own experience (if I
3270remember right) was that some of these numbers are trivial, and that there is
3271quite some symmetry, so in practice only a quarter needs to be remembered. And,
3272assuming that you can easily deduct the trivial cases, one can just calculate the
3273rest if needed.
3274
3275\startbuffer
3276\start \ttbf \startMPcode
3277 def MyDraw(expr i, j, c) =
3278 fill fullsquare shifted (i,j) withcolor c withtransparency (1,.5) ;
3279 enddef ;
3280
3281 for i = 1 upto 10 :
3282 for j = 1 upto 10 : MyDraw( i, j, "middlered" ) ; endfor ; endfor ;
3283
3284 for j = 1 upto 10 : MyDraw( 1, j, "middleblue" ) ; endfor ;
3285 for i = 1 upto 10 : MyDraw( i, -10, "middlegreen" ) ; endfor ;
3286 for i = 1 upto 10 : MyDraw( i, -1, "middleyellow") ; endfor ;
3287 for j = 1 upto 10 : MyDraw(10, j, "middlecyan" ) ; endfor ;
3288 for j = 1 upto 10 : MyDraw( 5, j, "middlegray" ) ; endfor ;
3289 for j = 1 upto 10 : MyDraw( j, j, "middlegray" ) ; endfor ;
3290
3291 draw image ( for i = 1 upto 10 : for j = 1 upto 10 :
3292 draw textext(decimal (ij)) ysized .25 shifted (i,j) ;
3293 endfor ; endfor ; ) withcolor white ;
3294
3295 currentpicture := currentpicture ysized 10cm ;
3296\stopMPcode \stop
3297\stopbuffer
3298
3299\typebuffer
3300
3301\startplacefigure[title=Overlapping glyphs,reference=fig:tentable]
3302 \getbuffer
3303\stopplacefigure
3304
3305Here we use both transparency and colors to stress the reduction of cases. The
3306named colors resolve to ones defined at the \TEX\ end. We see the redndering
3307\in {figure} [fig:tentable].
3308
3309\startsection[title=Glyph magic]
3310
3311The next example is the result of a tread on the mailing list. After Henri Menke
3312posted a some glyph overlap code, I made a variant that more suited the way we do
3313it in \METAFUN. In the meantime Floris van Maanen had found out that some glyphs
3314need a more inventive solution so after that the code evolved. By then the \type
3315{outlinetext}, \type {drawoutlinetext} and \type {filloutlinetext} helpers had
3316been added to the code base.
3317
3318Because this is complicated stuff, we just show the two solutions. The first one
3319is a relative simple one, the second one uses an approach suggested by Alan
3320Braslau and therefore uses some of the code that can be found in the \type
3321{crossingunder} macro.
3322
3323\startbuffer
3324\startMPdefinitions
3325def ShowOverlapInOutlinesA(expr first, second) =
3326 path p_i, p_j, s_i, s_j ;
3327 numeric n_i, n_j, index ;
3328 pair found ;
3329 index := 0 ;
3330 for i within first :
3331 for j within second :
3332 p_i := pathpart i ; n_i := length(p_i) ;
3333 p_j := pathpart j ; n_j := length(p_j) ;
3334 for ii = 0 upto n_i 1 :
3335 s_i := subpath(ii,ii+1) of p_i ;
3336 for jj = 0 upto n_j 1 :
3337 s_j := subpath(jj,jj+1) of p_j ;
3338 found := s_i intersection_point s_j ;
3339 if intersection_found :
3340 index := index 1 ;
3341 drawdot found
3342 withpen pencircle scaled 4 withcolor white ;
3343 draw textext("\strut\ttbf " & decimal index) ysized 3
3344 shifted found ;
3345 fi ;
3346 endfor ;
3347 endfor ;
3348 endfor ;
3349 endfor ;
3350enddef ;
3351\stopMPdefinitions
3352\stopbuffer
3353
3354\typebuffer \getbuffer
3355
3356This is the solution based on \type {crossingunder}, a macro that has been
3357introduced as part of Alans neat node module.
3358
3359\startbuffer
3360\startMPdefinitions
3361def ShowOverlapInOutlinesB(expr first, second) =
3362 begingroup ;
3363 save p, q, n, t, a, b, c, bcuttings, hold, found ;
3364 path p, q ;
3365 numeric n, hold ;
3366 path a, b, c, bcuttings ;
3367 pair found ;
3368 c := makepath(currentpen scaled crossingscale) ;
3369 for f within first :
3370 numeric t[];
3371 path hold[];
3372 t[0] := n := hold := 0 ;
3373 for s within second :
3374 p := pathpart f ;
3375 q := pathpart s ;
3376 a := p ;
3377 for i=1 upto crossingnumbermax :
3378 clearxy ; z = a intersectiontimes q ;
3379 if x < 0 :
3380 exitif hold < 1 ;
3381 a := hold[hold] ; hold := hold 1 ;
3382 clearxy ; z = a intersectiontimes q ;
3383 fi
3384 (t[incr n], whatever) = p intersectiontimes point x of a ;
3385 if x = 0 :
3386 a := a cutbefore c shifted point x of a ;
3387 elseif x = length a :
3388 a := a cutafter c shifted point x of a ;
3389 else :
3390 b := subpath (0,x) of a cutafter c shifted point x of a ;
3391 bcuttings := cuttings ;
3392 a := subpath (x,length a) of a cutbefore c shifted point x of a ;
3393 clearxy ; z = a intersectiontimes q ;
3394 if x < 0 :
3395 a := b ;
3396 cuttings := bcuttings ;
3397 elseif length bcuttings > 0 :
3398 clearxy ; z = b intersectiontimes q ;
3399 if x >= 0 :
3400 hold[incr hold] := b ;
3401 fi
3402 fi
3403 fi
3404 if length cuttings = 0 :
3405 exitif hold < 1 ;
3406 a := hold[hold] ; hold := hold 1 ;
3407 fi
3408 endfor ;
3409 endfor ;
3410 t[incr n] = length p ;
3411 for i=1 upto n :
3412 found := point t[i] of p ;
3413 drawdot found
3414 withpen pencircle scaled 4 withcolor white ;
3415 draw textext("\strut\ttbf " & decimal i) ysized 3
3416 shifted found ;
3417 endfor ;
3418 endfor ;
3419 endgroup ;
3420enddef ;
3421\stopMPdefinitions
3422\stopbuffer
3423
3424\typebuffer \getbuffer
3425
3426We demonstrate the differences with an example. The result can be seen in
3427\in {figure} [fig:overlapping:a].
3428
3429\startbuffer
3430\startcombination
3431 {\startMPcode
3432 picture first, second ;
3433 first := outlinetext.p("N") ; first := first scaled 10 ;
3434 second := outlinetext.p("T") ; second := second scaled 10 ;
3435 second := second rotatedaround(center second, 5) shifted (1,-1) ;
3436 filloutlinetext(first ) withcolor .5[darkblue,white] ;
3437 filloutlinetext(second) withcolor .5[darkred,white] ;
3438 drawoutlinetext(first ) ;
3439 drawoutlinetext(second) ;
3440 ShowOverlapInOutlinesA(first, second) ;
3441 addbackground withcolor darkgray ;
3442 currentpicture := currentpicture scaled 2.5 ;
3443 \stopMPcode} {Method A}
3444 {\startMPcode
3445 picture first, second ;
3446 first := outlinetext.p("N") ; first := first scaled 10 ;
3447 second := outlinetext.p("T") ; second := second scaled 10 ;
3448 second := second rotatedaround(center second, 5) shifted (1,-1) ;
3449 filloutlinetext(first ) withcolor .5[darkgreen,white] ;
3450 filloutlinetext(second) withcolor .5[darkyellow,white] ;
3451 drawoutlinetext(first ) ;
3452 drawoutlinetext(second) ;
3453 ShowOverlapInOutlinesB(first, second) ;
3454 addbackground withcolor darkgray ;
3455 currentpicture := currentpicture scaled 2.5 ;
3456 \stopMPcode} {Method B}
3457\stopcombination
3458\stopbuffer
3459
3460\typebuffer
3461
3462\startplacefigure[title=Overlapping glyphs,reference=fig:overlapping:a]
3463 \getbuffer
3464\stopplacefigure
3465
3466We duplicate some code because the pictures will change in the process of
3467analyzing. Lets make a helper for that:
3468
3469\startbuffer
3470\startMPdefinitions
3471def ShowOverlap(expr f, s, m) =
3472 picture first, second ;
3473 first := outlinetext.p(f) ; first := first scaled 10 ;
3474 second := outlinetext.p(s) ; second := second scaled 10 ;
3475
3476 filloutlinetext(first ) withcolor darkblue ;
3477 drawoutlinetext(first ) ;
3478
3479 filloutlinetext(second) withcolor darkred ;
3480 drawoutlinetext(second) ;
3481
3482 if m == 2 :
3483 ShowOverlapInOutlinesB
3484 else :
3485 ShowOverlapInOutlinesA
3486 fi (first, second) ;
3487
3488 addbackground withcolor darkgray ;
3489 currentpicture := currentpicture ysized 4cm ;
3490enddef ;
3491\stopMPdefinitions
3492\stopbuffer
3493
3494\typebuffer \getbuffer
3495
3496Again we demonstrate the differences with some examples. The result can be seen in
3497\in {figure} [fig:overlapping:b].
3498
3499\startbuffer
3500 \startcombination[nx=3,ny=2]
3501 {\startMPcode ShowOverlap("N","T",1) ; \stopMPcode} {Method 1}
3502 {\startMPcode ShowOverlap("\$","Q",1) ; \stopMPcode} {Method 1}
3503 {\startMPcode ShowOverlap("\tttf ABC","\tttf PQR",1) ; \stopMPcode} {Method 1}
3504 {\startMPcode ShowOverlap("N","T",2) ; \stopMPcode} {Method 2}
3505 {\startMPcode ShowOverlap("\$","Q",2) ; \stopMPcode} {Method 2}
3506 {\startMPcode ShowOverlap("\tttf ABC","\tttf PQR",2) ; \stopMPcode} {Method 2}
3507 \stopcombination
3508\stopbuffer
3509
3510\typebuffer
3511
3512\startplacefigure[title=Overlapping glyphs,reference=fig:overlapping:b]
3513 \getbuffer
3514\stopplacefigure
3515
3516\stopsection
3517
3518\startsection[title=Hidden beauty]
3519
3520\index {hiding}
3521
3522The \type {hide} wraps its (text) argument in a group in such a way that whatever
3523happens is not interfering with its surrounding. This is comparable with what
3524\type {vardef} does except that there is nothing of value produced.
3525
3526\startbuffer
3527def mfuncurvetoa = hide(let connect = mfuncurvetob ;) enddef ; def mfuncurvetob = .. enddef ;
3528def mfunlinetoa = hide(let connect = mfunlinetob ;) enddef ; def mfunlinetob = enddef ;
3529
3530def forcurve = hide(let connect = mfuncurvetoa ;) for enddef;
3531def forline = hide(let connect = mfunlinetoa ;) for enddef;
3532
3533draw image (
3534 draw forline i = 0 upto 25 : connect(i,sin(i)) endfor
3535 withcolor darkblue
3536 withtransparency (1,.5) ;
3537 draw forcurve i = 0 upto 25 : connect(i,sin(i)) endfor
3538 withcolor darkyellow
3539 withtransparency (1,.5) ;
3540) xysized (TextWidth, 3cm) ;
3541\stopbuffer
3542
3543\typebuffer
3544
3545In this example we hide the assignment in the loop and not only define the \type
3546{connect} macro but also adept it after its first expansion. Usage is shown \in
3547{in } [fig:hide].
3548
3549\startplacefigure[title=Hiding assignments in a loop,reference=fig:hide]
3550 \processMPbuffer
3551\stopplacefigure
3552
3553\stopsection
3554
3555\stopchapter
3556
3557\stopcomponent
3558 |