colors-metafun.tex /size: 19 Kb    last modification: 2021-10-28 13:50
1% language=us runpath=texruns:manuals/colors
2
3\startcomponent colors-basics
4
5\environment colors-environment
6
7\startchapter[title=Metafun][color=darkyellow]
8
9\startsection[title=Defining and using]
10
11In \METAPOST\ itself colors are defined as numbers or sets:
12
13\starttyping
14color     red  ; red  := (1,0,0) ;
15cmykcolor cyan ; cyan := (1,0,0,0) ;
16numeric   gray ; gray := 0.5 ;
17\stoptyping
18
19You don't need much fantasy to see that this fits well in the data model of
20\METAPOST. In fact, transparency could be represented as a \type {pair}. The
21disadvantage of having no generic color type is that you cannot mix them. In case
22you need to manipulate them, you can check the type:
23
24\starttyping
25if cmykcolor cyan : ... fi ;
26\stoptyping
27
28because \METAFUN\ is tightly integrated in \CONTEXT\ you can refer to colors
29known at the \TEX\ end by string. So,
30
31\starttyping
32string mycolor ; mycolor := "red" ;
33\stoptyping
34
35and then:
36
37\starttyping
38fill fullcircle scaled 4cm withcolor mycolor ;
39\stoptyping
40
41is quite okay. For completeness we also have \type {namedcolor} but it's not
42really needed:
43
44\starttyping
45fill fullcircle scaled 4cm withcolor namedcolor("red");
46\stoptyping
47
48You can define spot colors too but normally you will refer to colors
49defined at the \TEX\ end.
50
51\startbuffer[spot]
52\startMPcode
53    fill fullcircle scaled 3cm withcolor
54        .5 * spotcolor("whatever",(.3,.4,.5)) ;
55    fill fullcircle scaled 2cm withcolor
56             spotcolor("whatever",(.3,.4,.5)) ;
57    fill fullcircle scaled 1cm withcolor
58             spotcolor("whatever",(.3,.4,.5)/2) ;
59\stopMPcode
60\stopbuffer
61
62\startbuffer[multi]
63\startMPcode
64    fill fullcircle scaled 3cm withcolor
65        .5 * multitonecolor("whatever",(.3,.4,.5),(.5,.3,.4)) ;
66    fill fullcircle scaled 2cm withcolor
67             multitonecolor("whatever",(.3,.4,.5),(.5,.3,.4)) ;
68    fill fullcircle scaled 1cm withcolor
69             multitonecolor("whatever",(.3,.4,.5)/2,(.5,.3,.4)/2) ;
70\stopMPcode
71\stopbuffer
72
73\typebuffer[spot]
74
75Multitones are defined as:
76
77\typebuffer[multi]
78
79Some \PDF\ renderers have problems with fractions of such colors and even display
80the wrong colors. So, in these examples the third alternative in the sets of
81three might be more robust than the first. The result is shown in \in {figure}
82[fig:mpspot].
83
84\startplacefigure[reference=fig:mpspot,title={Spot and multitones directly defined in \METAFUN.}]
85    \startcombination[2*1]
86        {\getbuffer[spot]}  {}
87        {\getbuffer[multi]} {}
88    \stopcombination
89\stopplacefigure
90
91\stopsection
92
93\startsection[title=Passing colors]
94
95Originally \TEX\ and \METAPOST\ were separated processes and even in \LUATEX\
96they still are. There can be many independent \METAPOST\ instances present, but
97always there is \LUA\ as glue between them. In the early days of \LUATEX\ this
98was a one way channel: the \METAPOST\ output is available at the \TEX\ end in
99\LUA\ as a table and properties are used to communicate extensions. In today's
100\LUATEX\ the \METAPOST\ library has access to \LUA\ itself so that gives us a
101channel to \TEX, although with some limitations.
102
103Say that we have a color defined as follows:
104
105\startbuffer
106\definecolor[MyColor][r=.25,g=.50,b=.75]
107\stopbuffer
108
109\typebuffer \getbuffer
110
111We can apply this to a rule:
112
113\startbuffer
114\blackrule[color=MyColor,width=3cm,height=1cm,depth=0cm]
115\stopbuffer
116
117\typebuffer
118
119From this we get:
120
121\startlinecorrection
122\getbuffer
123\stoplinecorrection
124
125In \TEX\ (code) we can do this:
126
127\startbuffer
128\startMPcode
129    fill unitsquare xyscaled (3cm,1cm) withcolor \MPcolor {MyColor} ;
130\stopMPcode
131\stopbuffer
132
133\typebuffer
134
135When the code is pushed to \METAPOST\ the color gets expanded, in this case to
136\typ {(0.25, 0.50, 0.75)} because we specified an \RGB\ color but the other
137colorspaces are supported too.
138
139\startlinecorrection
140\getbuffer
141\stoplinecorrection
142
143Equally valid code is:
144
145\starttyping
146\startMPcode
147    fill unitsquare xyscaled (3cm,1cm) withcolor "MyColor" ;
148\stopMPcode
149\stoptyping
150
151This is very un-\METAPOST\ as naturally it can only deal with numerics for gray
152scales, triplets for \RGB\ colors, and quadruplets for \CMYK\ colors. In
153\METAFUN\ (as present in \CONTEXT\ MKIV) the \type {withcolor} operator also
154accepts a string, which is resolved to a color specification.
155
156For the record we note that when you use transparent colors, a more complex
157specification gets passed with \type {\MPcolor} or resolved (via the string). The
158same is true for spot and multitone colors. It will be clear that when you want
159to assign a color to a variable you have to make sure the type matches. A rather
160safe way to define colors is:
161
162\starttyping
163def MyColor =
164    \MPcolor{MyColor}
165enddef ;
166\stoptyping
167
168and because we can use strings, string variables are also an option.
169
170\stopsection
171
172\startsection[title=Grouping]
173
174The reason for discussing these details is that there is a rather fundamental
175concept of grouping in \TEX\ which can lead to unexpected side effects. The
176reason is that there is no grouping at the \LUA\ end, unless one uses a kind of
177stack, and that in \METAPOST\ grouping is an explicit feature.
178
179\starttyping
180\scratchcounter=123
181\bgroup
182    \scratchcounter=456
183\egroup
184\stoptyping
185
186After this \TEX\ code is expanded the counter has value 123. In \METAPOST\ you
187can do the following:
188
189\starttyping
190scratchcounter := 123 ;
191\begingroup
192    scratchcounter := 456 ;
193\endgroup
194\stoptyping
195
196but here the counter is 456 afterwards! You explicitly need to save a value:
197
198\starttyping
199scratchcounter := 123 ;
200\begingroup
201    save scratchcounter ;
202    numeric scratchcounter ; % variables are numeric by default anyway
203    scratchcounter := 456 ;
204\endgroup
205\stoptyping
206
207The difference perfectly makes sense if you think about the kind of applications
208\TEX\ and \METAPOST\ are used for. In \LUA\ you can do this:
209
210
211\starttyping
212scratchcounter = 123
213do
214    local scratchcounter = 456
215end
216\stoptyping
217
218and in fact, a \type {then}, \type {else}, \type {while}, \type {repeat}, \type
219{do} and function body also behave this way.
220
221So, what is the impact on colors? Imagine that you do this:
222
223\startbuffer
224\bgroup
225    \definecolor[MyColor][s=.5]
226    \startMPcode
227        pickup pencircle scaled 4mm ;
228        draw fullcircle scaled 30mm withcolor \MPcolor{MyColor} ;
229        draw fullcircle scaled 15mm withcolor "MyColor" ;
230    \stopMPcode
231\egroup
232\quad
233\startMPcode
234    pickup pencircle scaled 4mm ;
235    draw fullcircle scaled 30mm withcolor \MPcolor{MyColor} ;
236    draw fullcircle scaled 15mm withcolor "MyColor" ;
237\stopMPcode
238\stopbuffer
239
240\typebuffer
241
242We get the following colors:
243
244\startlinecorrection
245\hbox{\getbuffer}
246\stoplinecorrection
247
248Because \type {\MPcolor} is a \TEX\ macro, its value gets expanded when the
249graphic is calculated. After the group (first graphic) the color is restored.
250But, in order to access the colors defined at the \TEX\ end in \METAPOST\ by name
251(using \LUA) we need to make sure that a defined color is registered at that end.
252Before we could use the string accessor in \METAPOST\ colors, this was never a
253real issue. We kept the values in a (global) \LUA\ table which was good enough
254for the cases where we wanted to access color specifications, for instance for
255tracing. Such colors never changed in a document. But with the more dynamic
256\METAPOST\ graphics we cannot do that: there is no way that \METAPOST\ (or \LUA)
257later on can know that the color was defined inside a group as clone. For daily
258usage it's enough to know that we have found a way around it in \CONTEXT\ at
259neglectable overhead. In the rare case this mechanism fails, you can always
260revert to the \type {\MPcolor} method.
261
262\startbuffer
263\definecolor[DemoOne][red]
264\definecolor[DemoTwo][s=.8,t=0.5,a=1]
265
266%definepalet[DemoPalet][NumberColor={g=1}]
267\definepalet[DemoPalet][NumberColor=red,red=cyan]
268\definepalet[DemoPalet][NumberColor=red]
269
270\setuppalet[DemoPalet]
271
272\bgroup
273    \definecolor[red]    [b=.8]
274    \definecolor[DemoOne][yellow]
275    \startMPcode
276        fill fullcircle scaled 10 withcolor "NumberColor" ;
277        fill fullcircle scaled  7 withcolor "red" ;
278        fill fullcircle scaled  6 withcolor .5\MPcolor{red} ;
279        fill fullcircle scaled  4 shifted (-4,0) withcolor \MPcolor{DemoTwo} ;
280        fill fullcircle scaled  4 shifted ( 4,0) withcolor "DemoTwo" ;
281        fill fullcircle scaled  2 withcolor "DemoOne" ;
282        fill fullcircle scaled  1 withcolor \MPcolor{NumberColor} ;
283        currentpicture := currentpicture xysized(5cm,3cm) ;
284    \stopMPcode
285\egroup
286\hskip1cm
287\startMPcode
288    fill fullcircle scaled 10 withcolor "NumberColor" ;
289    fill fullcircle scaled  7 withcolor "red" ;
290    fill fullcircle scaled  6 withcolor .5\MPcolor{red} ;
291    fill fullcircle scaled  4 shifted (-4,0) withcolor \MPcolor{DemoTwo} ;
292    fill fullcircle scaled  4 shifted ( 4,0) withcolor "DemoTwo" ;
293    fill fullcircle scaled  2 withcolor "DemoOne" ;
294    fill fullcircle scaled  1 withcolor \MPcolor{NumberColor} ;
295        currentpicture := currentpicture xysized(5cm,3cm) ;
296\stopMPcode
297\stopbuffer
298
299The following example was used when developing the string based color resolver.
300The complication was in getting the color palets resolved right without too much
301overhead. Again we demonstrate this because border cases might occur that are not
302catched (yet).
303
304\startlinecorrection
305    \hbox {\getbuffer}
306\stoplinecorrection
307
308\stopsection
309
310\startsection[title=Transparency]
311
312Transparency is supported at the \TEX\ end: either or not bound to colors. We
313already saw how to use colors, here's how to apply transparency:
314
315\startbuffer
316\startMPcode
317    fill fullsquare xyscaled (4cm,2cm) randomized 5mm
318        withcolor "darkred" ;
319    fill fullsquare xyscaled (2cm,4cm) randomized 5mm
320        withcolor "darkblue" withtransparency ("normal",0.5) ;
321
322    fill fullsquare xyscaled (4cm,2cm) randomized 5mm shifted (45mm,0)
323        withcolor "darkred"  withtransparency ("normal",0.5) ;
324    fill fullsquare xyscaled (2cm,4cm) randomized 5mm shifted (45mm,0)
325        withcolor "darkblue" withtransparency ("normal",0.5) ;
326
327    fill fullsquare xyscaled (4cm,2cm) randomized 5mm shifted (90mm,0)
328        withcolor "darkred"  withtransparency ("normal",0.5) ;
329    fill fullsquare xyscaled (2cm,4cm) randomized 5mm shifted (90mm,0)
330        withcolor "darkblue" ;
331\stopMPcode
332\stopbuffer
333
334\typebuffer
335
336We get a mixture of normal and transparent colors. Instead of \type {normal}
337you can also pass a number (with \type {1} being \type {normal}).
338
339\startlinecorrection
340    \getbuffer
341\stoplinecorrection
342
343\stopsection
344
345\startsection[title=Shading]
346
347Shading is available too. This mechanism is a bit more complex deep down because
348it needs resources as well as shading vectors that adapt themselves to the current
349scale. We will not go into detail about the shading properties here.
350
351\startbuffer
352\startMPcode
353    comment("two shades with mp colors");
354    fill fullcircle scaled 5cm
355        withshademethod "circular"
356        withshadevector (2cm,1cm)
357        withshadecenter (.1,.5)
358        withshadedomain (.2,.6)
359        withshadefactor 1.2
360        withshadecolors (red,white)
361        ;
362    fill fullcircle scaled 5cm shifted (6cm,0)
363        withshademethod "circular"
364        withcolor "red" shadedinto "blue"
365    ;
366\stopMPcode
367\stopbuffer
368
369\typebuffer
370
371You can use normal \METAPOST\ colors as well as named colors.
372
373\startlinecorrection
374    \getbuffer
375\stoplinecorrection
376
377\startbuffer
378\startMPcode
379    comment("two shades with named colors");
380    fill fullcircle scaled 5cm
381        withshademethod "circular"
382        withshadecolors ((1,0,0),(0,0,1,0))
383    ;
384    fill fullcircle scaled 5cm shifted (6cm,0)
385        withshademethod "circular"
386        withcolor (1,0,0,0) shadedinto "blue"
387    ;
388\stopMPcode
389\stopbuffer
390
391The color space of the first color determines if the second one needs
392to be converted, so this is valid:
393
394\typebuffer
395
396The first circle is in \RGB\ colors and the second in \CMYK.
397
398\startlinecorrection
399    \getbuffer
400\stoplinecorrection
401
402You cannot use spot colors but you can use transparency, so with:
403
404\startbuffer
405\startMPcode
406    comment("three transparent shades");
407    fill fullcircle scaled 5cm
408        withshademethod "circular"
409        withshadecolors ("red","green")
410        withtransparency ("normal",0.5)
411    ;
412    fill fullcircle scaled 5cm shifted (30mm,0)
413        withshademethod "circular"
414        withshadecolors ("green","blue")
415        withtransparency ("normal",0.5)
416    ;
417    fill fullcircle scaled 5cm shifted (60mm,0)
418        withshademethod "circular"
419        withshadecolors ("blue","yellow")
420        withtransparency ("normal",0.5)
421    ;
422\stopMPcode
423\stopbuffer
424
425\typebuffer
426
427we get:
428
429\startlinecorrection
430    \getbuffer
431\stoplinecorrection
432
433You can define a shade and use it later on, for example:
434
435\startbuffer
436\startMPcode
437    defineshade myshade
438        withshademethod "circular"
439        withshadefactor 1
440        withshadedomain (0,1)
441        withshadecolors (black,white)
442        withtransparency (1,.5)
443    ;
444
445    fill fullcircle xyscaled(.75TextWidth,4cm)
446        shaded myshade ;
447    fill fullcircle xyscaled(.75TextWidth,4cm) shifted (.125TextWidth,0)
448        shaded myshade ;
449    fill fullcircle xyscaled(.75TextWidth,4cm) shifted (.25TextWidth,0)
450        shaded myshade ;
451\stopMPcode
452\stopbuffer
453
454\typebuffer
455
456This gives three transparent shaded shapes:
457
458\startlinecorrection
459    \getbuffer
460\stoplinecorrection
461
462A very special shade is the following:
463
464\startbuffer
465\startMPcode
466    fill fullsquare yscaled 5ExHeight xscaled TextWidth
467        withshademethod "linear"
468        withshadevector (0,1)
469        withshadestep (
470            withshadefraction .3
471            withshadecolors (red,green)
472        )
473        withshadestep (
474            withshadefraction .5
475            withshadecolors (green,blue)
476        )
477        withshadestep (
478            withshadefraction .7
479            withshadecolors (blue,red)
480        )
481        withshadestep (
482            withshadefraction 1
483            withshadecolors (red,yellow)
484        )
485    ;
486\stopMPcode
487\stopbuffer
488
489\typebuffer
490
491The result is a colorful band:
492
493\startlinecorrection
494    \getbuffer
495\stoplinecorrection
496
497\stopsection
498
499\startsection[title=Text]
500
501The text typeset with \type {textext} is processed in \TEX\ using the
502current settings. A text can of course have color directives embedded.
503
504\startbuffer
505\startMPcode
506numeric u ; u := 8mm ;
507draw thetextext("RED",                     (0,0u)) withcolor darkred ;
508draw thetextext("\darkgreen GREEN",        (0,1u)) ;
509draw thetextext("\darkblue   BLUE",        (0,2u)) withcolor darkred ;
510draw thetextext("BLACK {\darkgreen GREEN}",(0,3u)) ;
511draw thetextext("RED   {\darkblue   BLUE}",(0,4u)) withcolor darkred ;
512\stopMPcode
513\stopbuffer
514
515\typebuffer
516
517In this example we demonstrate that you can also apply a color to the
518resulting picture.
519
520\startlinecorrection
521\tttfd \getbuffer
522\stoplinecorrection
523
524\stopsection
525
526\startsection[title=Helpers]
527
528\stopsection
529
530There are several color related macros in \METAFUN\ and these are discussed
531in the \METAFUN\ manual, so we only mention a few here.
532
533\startbuffer
534\startMPcode
535    fill fullsquare xyscaled(TextWidth,4cm)
536        withcolor darkred ;
537    fill fullsquare xyscaled(TextWidth,3cm)
538        withcolor complementary darkred ;
539    fill fullsquare xyscaled(TextWidth,2cm)
540        withcolor complemented darkred ;
541    fill fullsquare xyscaled(TextWidth,1cm)
542        withcolor grayed darkred ;
543\stopMPcode
544\stopbuffer
545
546\typebuffer
547
548This example code is shown in \in {figure} [fig:complemen-1]. The \type
549{complementary} operator subtracts the given color from white, the \type
550{complemented} operator calculates its values from opposites (so a zero becomes a
551one). In \in {figure} [fig:complemen-2] a more extensive example is shown.
552
553\startplacefigure
554    [reference=fig:complemen-1,
555     title={The \type {complementary}, \type {complemented} and \type
556     {grayed} methodscompared.}]
557    \getbuffer
558\stopplacefigure
559
560\startMPdefinitions
561    % This is an old example I had laying around since 2005. The original was just
562    % a framed text with the graphic as background but here I use textext instead.
563    def MyCompare (text method) =
564
565        picture p ; p := textext("\quad \bf
566            I don't understand about complementary colors\quad
567            And what they say\quad
568            Side by side they both get bright\quad
569            Together they both get gray\quad"
570        ) ;
571
572        numeric w ; w := bbwidth  p ;
573        numeric h ; h := bbheight p ;
574
575        for i = 1 upto 10 :
576            fill fullsquare
577                xscaled (w/10)
578                yscaled 5h
579                shifted (-w/2-w/20+i*w/10,-3h/2)
580                withcolor (i*red/10)
581                withtransparency(1,.5) ;
582            fill fullsquare
583                xscaled (w/10)
584                yscaled 5h
585                shifted (-w/2-w/20+i*w/10,3h/2)
586                withcolor method (i*red/10)
587                withtransparency(1,.5) ;
588        endfor ;
589        addbackground withcolor .75white ;
590
591        draw p withcolor white ;
592
593        currentpicture := currentpicture xsized TextWidth ;
594    enddef ;
595\stopMPdefinitions
596
597\startplacefigure[reference=fig:complemen-2,title={Two methods to complement colors compared (text: Fiona Apple).}]
598    \startcombination[1*2]
599        {\startMPcode MyCompare(complemented)  ; \stopMPcode} {complemented}
600        {\startMPcode MyCompare(complementary) ; \stopMPcode} {complementary}
601    \stopcombination
602\stopplacefigure
603
604As we discussed before, the different color models in \METAPOST\ cannot be mixed
605in expressions. We therefore have two macros that expand into white or black
606in the right colorspace.
607
608\typebuffer
609
610\startbuffer
611\startMPcode
612    fill fullsquare xyscaled(TextWidth,4cm)
613        withcolor .5[(.5,0,0),   whitecolor (.5,0,0)] ;
614    fill fullsquare xyscaled(TextWidth,3cm)
615        withcolor .5[(.5,0,0),   blackcolor (.5,0,0)] ;
616    fill fullsquare xyscaled(TextWidth,2cm)
617        withcolor .5[(.5,0,0,0), whitecolor (.5,0,0,0)] ;
618    fill fullsquare xyscaled(TextWidth,1cm)
619        withcolor .5[(.5,0,0,0), blackcolor (.5,0,0,0)] ;
620\stopMPcode
621\stopbuffer
622
623\typebuffer
624
625\startlinecorrection
626\getbuffer
627\stoplinecorrection
628
629There are two macros that can be used to resolve string to colors: \type
630{resolvedcolor} and \type {namedcolor}. A resolved color is expanded via \LUA\
631while a named color is handled in the backend, when the result is converted to
632\PDF. The resolved approach is more recent and is the same as a string color
633specification.
634
635\startbuffer
636\startMPcode
637    fill fullcircle scaled 4cm withcolor .5 resolvedcolor "darkred" ;
638    fill fullcircle scaled 3cm withcolor .5 resolvedcolor "gray" ;
639    fill fullcircle scaled 2cm withcolor .5 namedcolor    "darkblue" ;
640    fill fullcircle scaled 1cm withcolor .5 namedcolor    "gray" ;
641\stopMPcode
642\stopbuffer
643
644\typebuffer
645
646\startlinecorrection
647\getbuffer
648\stoplinecorrection
649
650There is a \type {drawoptions} macro that can be used to define properties in one go.
651
652\startbuffer
653\startMPcode
654    drawoptions(withcolor "darkgreen");
655    fill fullcircle scaled 4cm  ;
656    fill fullcircle scaled 3cm withcolor white ;
657    fill fullcircle scaled 2cm ;
658\stopMPcode
659\stopbuffer
660
661\typebuffer
662
663We get:
664
665\startlinecorrection
666\getbuffer
667\stoplinecorrection
668
669The drawback of this approach is that, because we use so called pre- and
670postscripts for achieving special effects (like spotcolors and shading)
671successive \type {withcolor} calls can interfere in a way that unexpected results
672turn up. A way out is to use properties:
673
674\startbuffer
675\startMPcode
676    property p[] ;
677    p1 = properties(withcolor "darkred") ;
678    p2 = properties(withcolor "white") ;
679    fill fullcircle scaled 4cm withproperties p1 ;
680    fill fullcircle scaled 3cm withproperties p2 ;
681    fill fullcircle scaled 2cm withproperties p1 ;
682\stopMPcode
683\stopbuffer
684
685\typebuffer
686
687This results in:
688
689\startlinecorrection
690\getbuffer
691\stoplinecorrection
692
693\stopchapter
694
695\stopcomponent
696