cld-graphics.tex /size: 9688 b    last modification: 2021-10-28 13:50
1% language=us runpath=texruns:manuals/cld
2
3\startcomponent cld-graphics
4
5\environment cld-environment
6
7\startchapter[title=Graphics]
8
9\startsection[title=The regular interface]
10
11If you are familiar with \CONTEXT, which by now probably is the case, you will
12have noticed that it integrates the \METAPOST\ graphic subsystem. Drawing a
13graphic is not that complex:
14
15\startbuffer
16context.startMPcode()
17context [[
18  draw
19    fullcircle scaled 1cm
20    withpen pencircle scaled 1mm
21    withcolor .5white
22    dashed dashpattern (on 2mm off 2mm) ;
23 ]]
24context.stopMPcode()
25\stopbuffer
26
27\typebuffer
28
29We get a gray dashed circle rendered with an one millimeter thick line:
30
31\startlinecorrection
32\ctxluabuffer
33\stoplinecorrection
34
35So, we just use the regular commands and pass the drawing code as strings.
36Although \METAPOST\ is a rather normal language and therefore offers loops and
37conditions and the lot, you might want to use \LUA\ for anything else than the
38drawing commands. Of course this is much less efficient, but it could be that you
39don't care about speed. The next example demonstrates the interface for building
40graphics piecewise.
41
42\startbuffer
43context.resetMPdrawing()
44
45context.startMPdrawing()
46context([[fill fullcircle scaled 5cm withcolor (0,0,.5) ;]])
47context.stopMPdrawing()
48
49context.MPdrawing("pickup pencircle scaled .5mm ;")
50context.MPdrawing("drawoptions(withcolor white) ;")
51
52for i=0,50,5 do
53  context.startMPdrawing()
54  context("draw fullcircle scaled %smm ;",i)
55  context.stopMPdrawing()
56end
57
58for i=0,50,5 do
59  context.MPdrawing("draw fullsquare scaled " .. i .. "mm ;")
60end
61
62context.MPdrawingdonetrue()
63
64context.getMPdrawing()
65\stopbuffer
66
67\typebuffer
68
69This gives:
70
71\startlinecorrection
72\ctxluabuffer
73\stoplinecorrection
74
75I the first loop we can use the format options associated with the simple \type
76{context} call. This will not work in the second case. Even worse, passing more
77than one argument will definitely give a faulty graphic definition. This is why
78we have a special interface for \METAFUN. The code above can also be written as:
79
80\startbuffer
81local metafun = context.metafun
82
83metafun.start()
84
85metafun("fill fullcircle scaled 5cm withcolor %s ;",
86    metafun.color("darkblue"))
87
88metafun("pickup pencircle scaled .5mm ;")
89metafun("drawoptions(withcolor white) ;")
90
91for i=0,50,5 do
92  metafun("draw fullcircle scaled %smm ;",i)
93end
94
95for i=0,50,5 do
96  metafun("draw fullsquare scaled %smm ;",i)
97end
98
99metafun.stop()
100\stopbuffer
101
102\typebuffer
103
104Watch the call to \type {color}, this will pass definitions at the \TEX\ end to
105\METAPOST. Of course you really need to ask yourself \quotation {Do I want to use
106\METAPOST\ this way?}. Using \LUA\ loops instead of \METAPOST\ ones makes much
107more sense in the following case:
108
109\startbuffer
110local metafun = context.metafun
111
112function metafun.barchart(t)
113  metafun.start()
114  local t = t.data
115  for i=1,#t do
116    metafun("draw unitsquare xyscaled(%s,%s) shifted (%s,0);",
117      10, t[i]*10, i*10)
118  end
119  metafun.stop()
120end
121
122local one = { 1, 4, 6, 2, 3, }
123local two = { 8, 1, 3, 5, 9, }
124
125context.startcombination()
126  context.combination(metafun.delayed.barchart { data = one }, "one")
127  context.combination(metafun.delayed.barchart { data = two }, "two")
128context.stopcombination()
129\stopbuffer
130
131\typebuffer
132
133We get two barcharts alongside:
134
135\startlinecorrection
136\ctxluabuffer
137\stoplinecorrection
138
139\startbuffer
140local template = [[
141  path p, q ; color c[] ;
142  c1 := \MPcolor{darkblue} ;
143  c2 := \MPcolor{darkred} ;
144  p := fullcircle scaled 50 ;
145  l := length p ;
146  n := %s ;
147  q := subpath (0,%s/n*l) of p ;
148  draw q withcolor c2 withpen pencircle scaled 1 ;
149  fill fullcircle scaled 5 shifted point length q of q withcolor c1 ;
150  setbounds currentpicture to unitsquare shifted (-0.5,-0.5) scaled 60 ;
151  draw boundingbox currentpicture withcolor c1 ;
152  currentpicture := currentpicture xsized(1cm) ;
153]]
154
155local function steps(n)
156  for i=0,n do
157    context.metafun.start()
158      context.metafun(template,n,i)
159    context.metafun.stop()
160    if i < n then
161        context.quad()
162    end
163  end
164end
165
166context.hbox(function() steps(10) end)
167\stopbuffer
168
169\typebuffer
170
171\startlinecorrection
172\ctxluabuffer
173\stoplinecorrection
174
175Using a template is quite convenient but at some point you can loose track of the
176replacement values. Also, adding an extra value can force you to adapt the
177following ones which enlarges the change for making an error. An alternative is
178to use the template mechanism. Although this mechanism was originally made for
179other purposes, you can use it for whatever you like.
180
181\startbuffer
182local template = [[
183    path p ; p := fullcircle scaled 4cm ;
184    draw p withpen pencircle scaled .5mm withcolor red ;
185    freedotlabel ("%lefttop%",    point 1 of p,origin) ;
186    freedotlabel ("%righttop%",   point 3 of p,origin) ;
187    freedotlabel ("%leftbottom%", point 5 of p,origin) ;
188    freedotlabel ("%rightbottom%",point 7 of p,origin) ;
189]]
190
191local variables = {
192  lefttop     = "one",
193  righttop    = "two",
194  leftbottom  = "three",
195  rightbottom = "four" ,
196}
197
198context.metafun.start()
199  context.metafun(utilities.templates.replace(template,variables))
200context.metafun.stop()
201\stopbuffer
202
203\typebuffer
204
205Here we use named placeholders and pass a table with associated values to the
206replacement function. Apart from convenience it's also more readable. And the
207overhead is rather minimal.
208
209\startlinecorrection
210\ctxluabuffer
211\stoplinecorrection
212
213To some extent we fool ourselves with this kind of \LUA fication of \METAPOST\
214code. Of course we can make a nice \METAPOST\ library and put the code in a macro
215instead. In that sense, doing this in \CONTEXT\ directly often gives better and
216more efficient code.
217
218Of course you can use all relevant commands in the \LUA\ interface, like:
219
220\starttyping
221context.startMPpage()
222  context("draw origin")
223  for i=0,100,10 do
224    context("..{down}(%d,0)",i)
225  end
226  context(" withcolor \\MPcolor{darkred} ;")
227context.stopMPpage()
228\stoptyping
229
230to get a graphic that has its own page. Don't use the \type {metafun} namespace
231here, as it will not work here. This drawing looks like:
232
233\startlinecorrection
234\startluacode
235context.startMPcode()
236  context("draw origin")
237  for i=0,100,10 do
238    context("..{down}(%d,0)",i)
239  end
240  context(" withcolor red ;")
241context.stopMPcode()
242\stopluacode
243\stoplinecorrection
244
245\stopsection
246
247\startsection[title=The \LUA\ interface]
248
249Messing around with graphics is normally not needed and if you do it, you'd
250better know what you're doing. For \TEX\ a graphic is just a black box: a
251rectangle with dimensions. You specify a graphic, in a format that the backend
252can deal with, either or not apply some scaling and from then on a reference to
253that graphic, normally wrapped in a normal \TEX\ box, enters the typesetting
254machinery. Because the backend, the part that is responsible for translating
255typeset content onto a viewable or printable format like \PDF, is built into
256\LUATEX, at some point the real image has to be injected and the backend can only
257handle a few image formats: \PNG, \JPG, \JBIG\ and \PDF.
258
259In \CONTEXT\ some more image formats are supported but in practice this boils
260down to converting the image to a format that the backend can handle. Such a
261conversion depends on an external programs and in order not to redo the
262conversion each run \CONTEXT\ keeps track of the need to redo it.
263
264Some converters are built in, for example one that deals with \GIF\ images. This
265is normally not a preferred format, but it happens that we have to deal with it
266in cases where organizations use that format (if only because they use the web).
267Here is how this works at the \LUA\ end:
268
269\starttyping
270figures.converters.gif = {
271  pdf = function(oldname,newname)
272    os.execute(string.format("gm convert %s %s",oldname,newname))
273  end
274}
275\stoptyping
276
277We use \type {gm} (Graphic Magic) for the conversion and pass the old and new
278names. Given this definition at the \TEX\ end we can say:
279
280\starttyping
281\externalfigure[whatever.gif][width=4cm]
282\stoptyping
283
284Here is a another one:
285
286\starttyping
287figures.converters.bmp = {
288  pdf = function(oldname,newname)
289      os.execute(string.format("gm convert %s %s",oldname,newname))
290  end
291}
292\stoptyping
293
294In both examples we convert to \PDF\ because including this filetype is quite
295fast. But you can also go to other formats:
296
297\starttyping
298figures.converters.png = {
299  png = function(oldname,newname,resolution)
300    local command = string.format('gm convert -depth 1 "%s" "%s"',oldname,newname)
301    logs.report(string.format("running command %s",command))
302    os.execute(command)
303  end
304}
305\stoptyping
306
307Instead of directly defining such a table, you can better do this:
308
309\starttyping
310figures.converters.png = figures.converters.png or { }
311
312figures.converters.png.png = function(oldname,newname,resolution)
313  local command = string.format('gm convert -depth 1 "%s" "%s"',oldname,newname)
314  logs.report(string.format("running command %s",command))
315  os.execute(command)
316end
317\stoptyping
318
319Here we check if a table exists and if not we extend the table. Such converters
320work out of the box if you specify the suffix, but you can also opt for a simple:
321
322\starttyping
323\externalfigure[whatever][width=4cm]
324\stoptyping
325
326In this case \CONTEXT\ will check for all known supported formats, which is not
327that efficient when no graphic can be found. In order to let for instance files
328with suffix \type {bmp} can be included you have to register it as follows. The
329second argument is the target.
330
331\starttyping
332figures.registersuffix("bmp","bmp")
333\stoptyping
334
335At some point more of the graphic inclusion helpers will be opened up for general
336use but for now this is what you have available.
337
338\stopsection
339
340\stopchapter
341
342\stopcomponent
343