%D \module %D [ file=meta-imp-experiments, %D version=2020.03.18, %D title=\METAPOST\ Graphics, %D subtitle=Experimental Graphics, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. %D This library implements some experimental functionality that eventually %D might end up someplace else. \startluacode -- I decided to play with this kind of graphics after I read an article in a Dutch -- popular science journal. It was an article about virusus in the mids of the time -- when covid raged, so it also was a distraction. -- This is a typical case of first prototyping it in Lua using the 'context' command -- to generate MetaPost code and when that worked okay do it the following way. The -- way data is passed is a bit messy. do local function sum(t) local n = 0 for i=1,#t do n = n + t[i] end return n end local function checked(data) if data then local n = #data for i=1,n do data[i][i] = 0 end for i=1,n do local di = data[i] for j=i+1,n do local dj = data[j] local dji = dj[i] if not dji then dji = 0 dj[i] = 0 end di[j] = dji end end return data else return { } end end local data, list = nil, nil function mp.lmt_overlap_prepare() data = checked(metapost.getparameter { "data" }) list = { } for i=1,#data do list[i] = sum(data[i]) end end function mp.lmt_overlap_reset() data = nil list = nil end function mp.lmt_overlap_n() return #list end function mp.lmt_overlap_data(i, j) if j then return data[i][j] else return list[i] end end local injectstring = mp.inject.string function mp.lmt_overlap_text(i, j) injectstring(data[i][j] or "") end function mp.lmt_overlap_label(i) local labels = metapost.getparameter { "labels" } injectstring(labels and labels[i] or "") end function mp.lmt_overlap_color(i) local colors = metapost.getparameter { "colors" } injectstring(colors and colors[i] or "darkgray") end function mp.lmt_overlap_total() return sum(list) end end \stopluacode \startMPextensions presetparameters "overlap" [ options = "paths,lines", gap = 4, subgap = 2, offset = 8, color = "darkgray", alternative = "circular", colors = { "darkred", "darkgreen", "darkblue", "darkyellow", "darkmagenta", "darkcyan" }, ] ; def lmt_overlap = applyparameters "overlap" "lmt_do_overlap" enddef ; vardef lmt_do_overlap_circular = astep := 360 / steps ; p := fullcircle scaled steps ; r := origin -- (2*steps,0) ; start := 0 ; stop := 0 ; for i=1 upto n: stop := start + lua.mp.lmt_overlap_data(i) ; first := start ; last := stop ; for j=1 upto n: if i <> j : last := first + lua.mp.lmt_overlap_data(i,j) ; a := p intersectionpoint (r rotated (first * astep + 0.1)) ; % the 0.1 is somehow needed, why b := p intersectionpoint (r rotated (last * astep - 0.1)) ; % the 0.1 is somehow needed, why qq[i][j] := (p cutafter b) cutbefore a ; first := last + subgap ; fi ; endfor ; start := stop + gap + (n - 1) * subgap ; endfor ; if hasoption "options" "paths" : for i=1 upto n : for j=1 upto n : if i <> j : q := qq[i][j] ; freelabeloffset := getparameter "offset" ; freelabel(lua.mp.lmt_overlap_text(i,j), point .5 along q, origin) ; if i < j : s := qq[j][i] ; a := point length(q) of q ; b := point 0 of s ; c := point length(s) of s ; d := point 0 of q ; q := q & a .. controls origin and origin .. b & s & c .. controls origin and origin .. d -- cycle ; fill q withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ; fi ; fi ; endfor ; endfor ; fi ; if hasoption "options" "lines" : start := 0 ; stop := 0 ; for i=1 upto n: stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ; a := p intersectionpoint (r rotated (start * astep)) ; b := p intersectionpoint (r rotated (stop * astep)) ; q := (p cutbefore a) cutafter b ; freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ; freelabel(lua.mp.lmt_overlap_label(i), point .5 along q, origin) ; draw q withcolor white withpen pencircle scaled 5 ; draw q withcolor getparameter "color" withpen pencircle scaled 5 ; start := stop + gap + subgap ; endfor ; fi ; enddef ; vardef lmt_do_overlap_linear = astep := 1 ; % 1.25 p := origin -- (astep * steps,0) ; r := origin -- (0,astep * steps) ; start := 0 ; stop := 0 ; for i=1 upto n: stop := start + lua.mp.lmt_overlap_data(i) ; first := start ; last := stop ; for j=1 upto n: if i <> j : last := first + lua.mp.lmt_overlap_data(i,j) ; qq[i][j] := (first * astep,0) -- (last * astep,0) ; first := last + subgap ; fi ; endfor ; start := stop + gap + (n - 1) * subgap ; endfor ; if hasoption "options" "paths" : for i=1 upto n : for j=1 upto n : if i < j : qq[i][j] := qq[i][j] { up } .. { down } qq[j][i] { up } .. { down} cycle ; fill qq[i][j] withcolor lua.mp.lmt_overlap_color(i) withtransparency (1,.8) ; fi ; endfor ; endfor ; for i=1 upto n : for j=1 upto n : if i < j : t := thelabel(lua.mp.lmt_overlap_text(i,j), (center topboundary qq[i][j]) ) ; fill boundingbox t enlarged (ExHeight/2) withcolor white ; draw t ; fi ; endfor ; endfor ; fi ; if hasoption "options" "lines" : start := 0 ; stop := 0 ; for i=1 upto n: stop := start + lua.mp.lmt_overlap_data(i) + (n - 2) * subgap ; q := (start * astep,0) -- (stop * astep,0) ; freelabeloffset := getparameterdefault "textoffset" (4 * getparameter "offset") ; label.bot(lua.mp.lmt_overlap_label(i), (point .5 along q) shifted (0,- freelabeloffset/4)) ; draw q withcolor white withpen pencircle scaled 5 ; draw q withcolor getparameter "color" withpen pencircle scaled 5 ; start := stop + gap + subgap ; endfor ; fi ; enddef ; vardef lmt_do_overlap = image ( pushparameters "overlap" ; save p, q, r, s, qq, a, b, c, d, t, n, gap, subgap, steps, astep, start, stop, first, last ; path p, q, r, s, qq[][] ; pair a, b, c, d ; picture t ; numeric n, gap, subgap, steps, astep, start, stop, first, last ; save freelabeloffset; freelabeloffset := 8 ; interim linecap := butt; interim linejoin := squared; lua.mp.lmt_overlap_prepare() ; n := lua.mp.lmt_overlap_n(); gap := getparameter "gap" ; subgap := getparameter "subgap" ; steps := lua.mp.lmt_overlap_total() + (n * gap) + n * (n - 1) * subgap ; if ((getparameter "alternative") = "linear") or ((getparameter "alternative") = "line") : lmt_do_overlap_linear ; else : lmt_do_overlap_circular ; fi ; lua.mp.lmt_overlap_reset() ; popparameters ; ) enddef ; \stopMPextensions \continueifinputfile{meta-imp-experiments.mkxl} % \useMPlibrary[experiments] \usemodule[article-basic] \starttext % todo: datafile: { data = { }, labels = { } } % % data = { % % { 0, 10, 30, 10, 20 }, % % { 10, 0, 10, 20, 10 }, % % { 30, 10, 0, 5, 15 }, % % { 10, 20, 5, 0, 5 }, % % { 20, 10, 15, 5, 0 } % % }, % % data = { % % { 0 }, % % { 10, 0 }, % % { 30, 10, 0 }, % % { 10, 20, 5, 0 }, % % { 20, 10, 15, 5, 0 } % % }, % data = { % { }, % { 10 }, % { 30, 10 }, % { 10, 20, 5 }, % { 20, 10, 15, 5 } % }, \startbuffer \startMPcode{doublefun} draw lmt_overlap [ alternative = "circular", data = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } }, labels = { "one", "two", "three", "four", "five" } ] ; \stopMPcode \stopbuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection \startbuffer \startMPcode{doublefun} draw lmt_overlap [ alternative = "linear", data = { { }, { 10 }, { 30, 10 }, { 10, 20, 5 }, { 20, 10, 15, 5 } }, labels = { "one", "two", "three", "four", "five" } ] ; \stopMPcode \stopbuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection \stoptext