meta-imp-experiments.mkxl /size: 10 Kb    last modification: 2021-10-28 13:51
1%D \module
2%D   [       file=meta-imp-experiments,
3%D        version=2020.03.18,
4%D          title=\METAPOST\ Graphics,
5%D       subtitle=Experimental Graphics,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14%D This library implements some experimental functionality that eventually
15%D might end up someplace else.
16
17\startluacode
18
19    -- I decided to play with this kind of graphics after I read an article in a Dutch
20    -- popular science journal. It was an article about virusus in the mids of the time
21    -- when covid raged, so it also was a distraction.
22
23    -- This is a typical case of first prototyping it in Lua using the 'context' command
24    -- to generate MetaPost code and when that worked okay do it the following way. The
25    -- way data is passed is a bit messy.
26
27    do
28
29        local function sum(t)
30            local n = 0
31            for i=1,#t do
32                n = n + t[i]
33            end
34            return n
35        end
36
37        local function checked(data)
38            if data then
39                local n = #data
40                for i=1,n do
41                    data[i][i] = 0
42                end
43                for i=1,n do
44                    local di = data[i]
45                    for j=i+1,n do
46                        local dj = data[j]
47                        local dji = dj[i]
48                        if not dji then
49                            dji = 0
50                            dj[i] = 0
51                        end
52                        di[j] = dji
53                    end
54                end
55                return data
56            else
57                return { }
58            end
59        end
60
61        local data, list = nil, nil
62
63        function mp.lmt_overlap_prepare()
64            data = checked(metapost.getparameter { "data" })
65            list = { }
66            for i=1,#data do
67                list[i] = sum(data[i])
68            end
69        end
70
71        function mp.lmt_overlap_reset()
72            data = nil
73            list = nil
74        end
75
76        function mp.lmt_overlap_n()
77            return #list
78        end
79
80        function mp.lmt_overlap_data(i, j)
81            if j then
82                return data[i][j]
83            else
84                return list[i]
85            end
86        end
87
88        local injectstring = mp.inject.string
89
90        function mp.lmt_overlap_text(i, j)
91            injectstring(data[i][j] or "")
92        end
93
94        function mp.lmt_overlap_label(i)
95            local labels = metapost.getparameter { "labels" }
96            injectstring(labels and labels[i] or "")
97        end
98
99        function mp.lmt_overlap_color(i)
100            local colors = metapost.getparameter { "colors" }
101            injectstring(colors and colors[i] or "darkgray")
102        end
103
104        function mp.lmt_overlap_total()
105            return sum(list)
106        end
107
108    end
109\stopluacode
110
111\startMPextensions
112
113presetparameters "overlap" [
114    options     = "paths,lines",
115    gap         = 4,
116    subgap      = 2,
117    offset      = 8,
118    color       = "darkgray",
119    alternative = "circular",
120    colors      = {
121        "darkred",
122        "darkgreen",
123        "darkblue",
124        "darkyellow",
125        "darkmagenta",
126        "darkcyan"
127    },
128] ;
129
130def lmt_overlap = applyparameters "overlap" "lmt_do_overlap" enddef ;
131
132vardef lmt_do_overlap_circular =
133
134    astep  := 360 / steps ;
135
136    p := fullcircle scaled steps ;
137    r := origin -- (2*steps,0) ;
138
139    start := 0 ;
140    stop  := 0 ;
141    for i=1 upto n:
142        stop  := start + lua.mp.lmt_overlap_data(i) ;
143        first := start ;
144        last  := stop ;
145        for j=1 upto n:
146            if i <> j :
147                last := first + lua.mp.lmt_overlap_data(i,j) ;
148                a := p intersectionpoint (r rotated (first * astep + 0.1)) ; % the 0.1 is somehow needed, why
149                b := p intersectionpoint (r rotated (last  * astep - 0.1)) ; % the 0.1 is somehow needed, why
150                qq[i][j] := (p cutafter b) cutbefore a ;
151                first := last + subgap ;
152            fi ;
153        endfor ;
154        start := stop + gap + (n - 1) * subgap ;
155    endfor ;
156
157    if hasoption "options" "paths" :
158
159        for i=1 upto n :
160            for j=1 upto n :
161                if i <> j :
162                    q := qq[i][j] ;
163                    freelabeloffset := getparameter "offset" ;
164                    freelabel(lua.mp.lmt_overlap_text(i,j), point .5 along q, origin) ;
165                    if i < j :
166                        s := qq[j][i] ;
167                        a := point length(q) of q ;
168                        b := point 0 of s ;
169                        c := point length(s) of s ;
170                        d := point 0 of q ;
171                        q := q & a .. controls origin and origin .. b & s & c .. controls origin and origin .. d -- cycle ;
172                        fill q withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ;
173                    fi ;
174                fi ;
175            endfor ;
176        endfor ;
177
178    fi ;
179
180    if hasoption "options" "lines" :
181
182        start := 0 ;
183        stop  := 0 ;
184        for i=1 upto n:
185            stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ;
186            a := p intersectionpoint (r rotated (start * astep)) ;
187            b := p intersectionpoint (r rotated (stop  * astep)) ;
188            q := (p cutbefore a) cutafter b ;
189            freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ;
190            freelabel(lua.mp.lmt_overlap_label(i), point .5 along q, origin) ;
191            draw q withcolor white withpen pencircle scaled 5 ;
192            draw q withcolor getparameter "color" withpen pencircle scaled 5 ;
193            start := stop + gap + subgap ;
194        endfor ;
195
196    fi ;
197
198enddef ;
199
200vardef lmt_do_overlap_linear =
201    astep  := 1 ; % 1.25
202
203    p := origin -- (astep * steps,0) ;
204    r := origin -- (0,astep * steps) ;
205
206    start := 0 ;
207    stop  := 0 ;
208    for i=1 upto n:
209        stop  := start + lua.mp.lmt_overlap_data(i) ;
210        first := start ;
211        last  := stop ;
212        for j=1 upto n:
213            if i <> j :
214                last := first + lua.mp.lmt_overlap_data(i,j) ;
215                qq[i][j] := (first * astep,0) -- (last  * astep,0) ;
216                first := last + subgap ;
217            fi ;
218        endfor ;
219        start := stop + gap + (n - 1) * subgap ;
220    endfor ;
221
222    if hasoption "options" "paths" :
223
224        for i=1 upto n :
225            for j=1 upto n :
226                if i < j :
227                    qq[i][j] := qq[i][j] { up } .. { down } qq[j][i] { up } .. { down} cycle ;
228                    fill qq[i][j] withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ;
229                fi ;
230            endfor ;
231        endfor ;
232
233        for i=1 upto n :
234            for j=1 upto n :
235                if i < j :
236                    t := thelabel(lua.mp.lmt_overlap_text(i,j), (center topboundary qq[i][j]) ) ;
237                    fill boundingbox t enlarged (ExHeight/2) withcolor white ;
238                    draw t ;
239                fi ;
240            endfor ;
241        endfor ;
242
243    fi ;
244
245    if hasoption "options" "lines" :
246
247        start := 0 ;
248        stop  := 0 ;
249        for i=1 upto n:
250            stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ;
251            q := (start * astep,0) -- (stop  * astep,0) ;
252            freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ;
253            label.bot(lua.mp.lmt_overlap_label(i), (point .5 along q) shifted (0,- freelabeloffset/4)) ;
254            draw q withcolor white withpen pencircle scaled 5 ;
255            draw q withcolor getparameter "color" withpen pencircle scaled 5 ;
256            start := stop + gap + subgap ;
257        endfor ;
258
259    fi ;
260
261enddef ;
262
263vardef lmt_do_overlap =
264    image (
265
266        pushparameters "overlap" ;
267
268            save p, q, r, s, qq, a, b, c, d, t, n, gap, subgap, steps, astep, start, stop, first, last ;
269            path p, q, r, s, qq[][] ;
270            pair a, b, c, d ;
271            picture t ;
272            numeric n, gap, subgap, steps, astep, start, stop, first, last ;
273            save freelabeloffset; freelabeloffset := 8 ;
274            interim linecap := butt;
275            interim linejoin := squared;
276
277            lua.mp.lmt_overlap_prepare() ;
278
279                n      := lua.mp.lmt_overlap_n();
280                gap    := getparameter "gap" ;
281                subgap := getparameter "subgap" ;
282                steps  := lua.mp.lmt_overlap_total() + (n * gap) + n * (n - 1) * subgap ;
283
284                if ((getparameter "alternative") = "linear") or ((getparameter "alternative") = "line") :
285                    lmt_do_overlap_linear ;
286                else :
287                    lmt_do_overlap_circular ;
288                fi ;
289
290            lua.mp.lmt_overlap_reset() ;
291
292        popparameters ;
293    )
294enddef ;
295
296\stopMPextensions
297
298\continueifinputfile{meta-imp-experiments.mkxl}
299
300% \useMPlibrary[experiments]
301
302\usemodule[article-basic]
303
304\starttext
305
306% todo: datafile: { data = { }, labels = { } }
307
308%   % data   = {
309%   %     {  0, 10, 30, 10, 20 },
310%   %     { 10,  0, 10, 20, 10 },
311%   %     { 30, 10,  0,  5, 15 },
312%   %     { 10, 20,  5,  0,  5 },
313%   %     { 20, 10, 15,  5,  0 }
314%   % },
315%   % data = {
316%   %     {  0 },
317%   %     { 10,  0 },
318%   %     { 30, 10,  0 },
319%   %     { 10, 20,  5,  0 },
320%   %     { 20, 10, 15,  5,  0 }
321%   % },
322%     data = {
323%         { },
324%         { 10 },
325%         { 30, 10 },
326%         { 10, 20,  5 },
327%         { 20, 10, 15,  5 }
328%     },
329
330\startbuffer
331\startMPcode{doublefun}
332    draw lmt_overlap [
333        alternative = "circular",
334        data        = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } },
335        labels      = { "one", "two", "three", "four", "five" }
336    ] ;
337\stopMPcode
338\stopbuffer
339
340\startlinecorrection[blank]
341    \getbuffer
342\stoplinecorrection
343
344\startbuffer
345\startMPcode{doublefun}
346    draw lmt_overlap [
347        alternative = "linear",
348        data        = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } },
349        labels      = { "one", "two", "three", "four", "five" }
350    ] ;
351\stopMPcode
352\stopbuffer
353
354\startlinecorrection[blank]
355    \getbuffer
356\stoplinecorrection
357
358\stoptext
359