diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/m-chart.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/m-chart.lua | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/m-chart.lua b/Master/texmf-dist/tex/context/base/m-chart.lua new file mode 100644 index 00000000000..32de8a5ba57 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/m-chart.lua @@ -0,0 +1,877 @@ +if not modules then modules = { } end modules ['x-flow'] = { + version = 1.001, + comment = "companion to m-flow.mkvi", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- when we can resolve mpcolor at the lua end we will +-- use metapost.graphic(....) directly + +-- todo: labels + +moduledata.charts = moduledata.charts or { } + +local gsub, match, find, format, lower = string.gsub, string.match, string.find, string.format, string.lower +local setmetatableindex = table.setmetatableindex + +local P, S, C, Cc = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc +local lpegmatch = lpeg.match + +local points = number.points + +local variables = interfaces.variables + +local v_yes = variables.yes +local v_no = variables.no +local v_none = variables.none +local v_standard = variables.standard +local v_start = variables.start +local v_overlay = variables.overlay +local v_round = variables.round +local v_test = variables.test + +local defaults = { + chart = { + name = "", + option = "", + backgroundcolor = "", + width = 100*65436, + height = 50*65436, + dx = 30*65436, + dy = 30*65436, + offset = 0, + bodyfont = "", + dot = "", + hcompact = variables_no, + vcompact = variables_no, + autofocus = "", + focus = "", + labeloffset = 5*65436, + commentoffset = 5*65436, + exitoffset = 0, + + }, + shape = { -- FLOS + rulethickness = 65436, + default = "", + framecolor = "darkblue", + backgroundcolor = "lightgray", + }, + focus = { -- FLOF + rulethickness = 65436, + framecolor = "darkred", + backgroundcolor = "gray", + }, + line = { -- FLOL + rulethickness = 65436, + radius = 10*65436, + color = "darkgreen", + corner = "", + dash = "", + arrow = "", + offset = "", + }, + set = { -- FLOX + }, + split = { + nx = 3, + ny = 3, + command = "", + marking = "", + before = "", + after = "", + } +} + +local validshapes = { + ["node"] = { kind = "shape", number = 0 }, + ["action"] = { kind = "shape", number = 24 }, + ["procedure"] = { kind = "shape", number = 5 }, + ["product"] = { kind = "shape", number = 12 }, + ["decision"] = { kind = "shape", number = 14 }, + ["archive"] = { kind = "shape", number = 19 }, + ["loop"] = { kind = "shape", number = 35 }, + ["wait"] = { kind = "shape", number = 6 }, + ["subprocedure"] = { kind = "shape", number = 20 }, + ["singledocument"] = { kind = "shape", number = 32 }, + ["multidocument"] = { kind = "shape", number = 33 }, + + ["right"] = { kind = "line", number = 66 }, + ["left"] = { kind = "line", number = 67 }, + ["up"] = { kind = "line", number = 68 }, + ["down"] = { kind = "line", number = 69 }, +} + +local validlabellocations = { + l = "l", left = "l", + r = "r", right = "r", + t = "t", top = "t", + b = "b", bottom = "b", + lt = "lt", + rt = "rt", + lb = "lb", + rb = "rb", + tl = "tl", + tr = "tr", + bl = "bl", + br = "br", +} + +local validcommentlocations = { + l = "l", left = "l", + r = "r", right = "r", + t = "t", top = "t", + b = "b", bottom = "b", + lt = "lt", + rt = "rt", + lb = "lb", + rb = "rb", + tl = "tl", + tr = "tr", + bl = "bl", + br = "br", +} + +local validtextlocations = { + l = "l", left = "l", + r = "r", right = "r", + t = "t", top = "t", + b = "b", bottom = "b", + c = "c", center = "c", + m = "c", middle = "m", + lt = "lt", + rt = "rt", + lb = "lb", + rb = "rb", + tl = "lt", + tr = "rt", + bl = "lb", + br = "rb", +} + +setmetatableindex(validshapes,function(t,k) + local l = gsub(lower(k)," ","") + local v = rawget(t,l) + if not v then + local n = tonumber(k) + if n then + v = { kind = "shape", number = n } + else + v = rawget(t,"action") + end + end + t[k] = v + return v +end) + +local charts = { } + +local data, hash, temp, last_x, last_y, name + +function commands.flow_start_chart(chartname) + data = { } + hash = { } + last_x, last_y = 0, 0 + name = chartname +end + +function commands.flow_stop_chart() + charts[name] = { + data = data, + hash = hash, + last_x = last_x, + last_y = last_y, + } + data, hash, temp = nil, nil, nil +end + +-- function commands.flow_set(chartname,chartdata) +-- local hash = { } +-- local data = { } +-- charts[name] = { +-- data = data, +-- hash = hash, +-- } +-- for i=1,#chartdata do +-- local di = data[i] +-- local name = di.name or "" +-- if name then +-- data[#data+1] = { +-- name = name, +-- labels = di.labels or { }, +-- comments = di.comments or { }, +-- exits = di.exits or { }, +-- connections = di.connections or { }, +-- settings = di.settings or { }, +-- x = di.x or 1, +-- y = di.y or 1, +-- } +-- hash[name] = i +-- end +-- end +-- end + +function commands.flow_reset(chartname) + charts[name] = nil +end + +function commands.flow_set_current_cell(n) + temp = data[tonumber(n)] or { } +end + +function commands.flow_start_cell(settings) + temp = { + texts = { }, + labels = { }, + exits = { }, + connections = { }, + settings = settings, + x = 1, + y = 1, + name = "", + } +end + +function commands.flow_stop_cell() + data[#data+1] = temp + hash[temp.name or #data] = temp +end + +function commands.flow_set_name(str) + temp.name = str +end + +function commands.flow_set_shape(str) + temp.shape = str +end + +function commands.flow_set_destination(str) + temp.destination = str +end + +function commands.flow_set_text(align,str) + temp.texts[#temp.texts+1] = { + location = align, + text = str, + } +end + +function commands.flow_set_overlay(str) + temp.overlay = str +end + +function commands.flow_set_focus(str) + temp.focus = str +end + +function commands.flow_set_figure(str) + temp.figure = str +end + +function commands.flow_set_label(location,text) + temp.labels[#temp.labels+1] = { + location = location, + text = text, + } +end + +function commands.flow_set_comment(location,text) + local connections = temp.connections + if connections then + local connection = connections[#connections] + if connection then + local comments = connection.comments + if comments then + comments[#comments+1] = { + location = location, + text = text, + } + end + end + end +end + +function commands.flow_set_exit(location,text) + temp.exits[#temp.exits+1] = { + location = location, + text = text, + } +end + +function commands.flow_set_include(name,x,y,settings) + data[#data+1] = { + include = name, + x = x, + y = y, + -- settings = settings, + } +end + +local function inject(includedata,data,hash) + local subchart = charts[includedata.include] + if not subchart then + return + end + local subdata = subchart.data + if not subdata then + return + end + local xoffset = (includedata.x or 1) - 1 + local yoffset = (includedata.y or 1) - 1 + local settings = includedata.settings + for i=1,#subdata do + local si = subdata[i] + if si.include then + inject(si,data,hash) + else + local t = { + x = si.x + xoffset, + y = si.y + yoffset, + settings = settings, + } + setmetatableindex(t,si) + data[#data+1] = t + hash[si.name or #data] = t + end + end +end + +local function pack(data,field) + local list, max = { }, 0 + for e=1,#data do + local d = data[e] + local f = d[field] + list[f] = true + if f > max then + max = f + end + end + for i=1,max do + if not list[i] then + for e=1,#data do + local d = data[e] + local f = d[field] + if f > i then + d[field] = f - 1 + end + end + end + end +end + +local function expanded(chart,chartsettings) + local expandeddata = { } + local expandedhash = { } + local expandedchart = { + data = expandeddata, + hash = expandedhash, + } + setmetatableindex(expandedchart,chart) + local data = chart.data + local hash = chart.hash + for i=1,#data do + local di = data[i] + if di.include then + inject(di,expandeddata,expandedhash) + else + expandeddata[#expandeddata+1] = di + expandedhash[di.name or #expandeddata] = di + end + end + -- + expandedchart.settings = chartsettings or { } + -- make locals + chartsettings.shape = chartsettings.shape or { } + chartsettings.focus = chartsettings.focus or { } + chartsettings.line = chartsettings.line or { } + chartsettings.set = chartsettings.set or { } + chartsettings.split = chartsettings.split or { } + chartsettings.chart = chartsettings.chart or { } + setmetatableindex(chartsettings.shape,defaults.shape) + setmetatableindex(chartsettings.focus,defaults.focus) + setmetatableindex(chartsettings.line ,defaults.line ) + setmetatableindex(chartsettings.set ,defaults.set ) + setmetatableindex(chartsettings.split,defaults.split) + setmetatableindex(chartsettings.chart,defaults.chart) + -- + if chartsettings.chart.vcompact == v_yes then + pack(expandeddata,"y") + end + if chartsettings.chart.hcompact == v_yes then + pack(expandeddata,"x") + end + -- + for i=1,#expandeddata do + local cell = expandeddata[i] + local settings = cell.settings + if not settings then + cell.settings = chartsettings + else + settings.shape = settings.shape or { } + settings.focus = settings.focus or { } + settings.line = settings.line or { } + setmetatableindex(settings.shape,chartsettings.shape) + setmetatableindex(settings.focus,chartsettings.focus) + setmetatableindex(settings.line ,chartsettings.line) + end + end + return expandedchart +end + +local splitter = lpeg.splitat(",") + +function commands.flow_set_location(x,y) + if type(x) == "string" and not y then + x, y = lpegmatch(splitter,x) + end + if not x or x == "" then + x = last_x + elseif type(x) == "number" then + -- ok + elseif x == "+" then + x = last_x + 1 + elseif x == "-" then + x = last_x - 1 + elseif find(x,"^[%+%-]") then + x = last_x + (tonumber(x) or 0) + else + x = tonumber(x) + end + if not y or y == "" then + y = last_y + elseif type(y) == "number" then + -- ok + elseif y == "+" then + y = last_y + 1 + elseif x == "-" then + y = last_y - 1 + elseif find(y,"^[%+%-]") then + y = last_y + (tonumber(y) or 0) + else + y = tonumber(y) + end + temp.x = x or 1 + temp.y = y or 1 + last_x = x or last_x + last_y = y or last_y +end + +function commands.flow_set_connection(location,displacement,name) + local dx, dy = lpegmatch(splitter,displacement) + dx = tonumber(dx) + dy = tonumber(dy) + temp.connections[#temp.connections+1] = { + location = location, + dx = dx or 0, + dy = dy or 0, + name = name, + comments = { }, + } +end + +local function visible(chart,cell) + local x, y = cell.x, cell.y + return + x >= chart.from_x and x <= chart.to_x and + y >= chart.from_y and y <= chart.to_y and cell +end + +local function process_cells(chart,xoffset,yoffset) + local data = chart.data + if not data then + return + end + local focus = utilities.parsers.settings_to_hash(chart.settings.chart.focus or "") + for i=1,#data do + local cell = visible(chart,data[i]) + if cell then + local settings = cell.settings + local shapesettings = settings.shape + local shape = cell.shape + if not shape or shape == "" then + shape = shapesettings.default or "none" + end + if shape ~= v_none then + local shapedata = validshapes[shape] + context("flow_begin_sub_chart ;") -- when is this needed + if shapedata.kind == "line" then + local linesettings = settings.line + context("flow_shape_line_color := \\MPcolor{%s} ;", linesettings.color) + context("flow_shape_fill_color := \\MPcolor{%s} ;", linesettings.backgroundcolor) + context("flow_shape_line_width := %s ; ", points(linesettingsrulethickness)) + elseif focus[cell.focus] or focus[cell.name] then + local focussettings = settings.focus + context("flow_shape_line_color := \\MPcolor{%s} ;", focussettings.framecolor) + context("flow_shape_fill_color := \\MPcolor{%s} ;", focussettings.backgroundcolor) + context("flow_shape_line_width := %s ; ", points(focussettings.rulethickness)) + else + local shapesettings = settings.shape + context("flow_shape_line_color := \\MPcolor{%s} ;", shapesettings.framecolor) + context("flow_shape_fill_color := \\MPcolor{%s} ;", shapesettings.backgroundcolor) + context("flow_shape_line_width := %s ; " , points(shapesettings.rulethickness)) + end + context("flow_peepshape := false ;") -- todo + context("flow_new_shape(%s,%s,%s) ;",cell.x+xoffset,cell.y+yoffset,shapedata.number) + context("flow_end_sub_chart ;") + end + end + end +end + +-- todo : make lpeg for splitter + +local sign = S("+p") / "1" + + S("-m") / "-1" + +local full = C(P("left")) + + C(P("right")) + + C(P("top")) + + C(P("bottom")) + +local char = P("l") / "left" + + P("r") / "right" + + P("t") / "top" + + P("b") / "bottom" + +local space = P(" ")^0 + +local what = space + * (sign + Cc("0")) + * space + * (full + char) + * space + * (sign + Cc("0")) + * space + * (full + char) + * space + * P(-1) + +-- print(lpegmatch(what,"lr")) +-- print(lpegmatch(what,"+l+r")) +-- print(lpegmatch(what,"+l")) +-- print(lpegmatch(what,"+ left+r ")) + +local function process_connections(chart,xoffset,yoffset) + local data = chart.data + local hash = chart.hash + if not data then + return + end + local settings = chart.settings + for i=1,#data do + local cell = visible(chart,data[i]) + if cell then + local connections = cell.connections + for j=1,#connections do + local connection = connections[j] + local othername = connection.name + local othercell = hash[othername] + if othercell then -- and visible(chart,data[i]) then + local cellx, celly = cell.x, cell.y + local otherx, othery, location = othercell.x, othercell.y, connection.location + if otherx > 0 and othery > 0 and cellx > 0 and celly > 0 and connection.location then + local what_cell, where_cell, what_other, where_other = lpegmatch(what,location) + if what_cell and where_cell and what_other and where_other then + local linesettings = settings.line + context("flow_smooth := %s ;", linesettings.corner == v_round and "true" or "false") + context("flow_dashline := %s ;", linesettings.dash == v_yes and "true" or "false") + context("flow_arrowtip := %s ;", linesettings.arrow == v_yes and "true" or "false") + context("flow_touchshape := %s ;", linesettings.offset == v_none and "true" or "false") + context("flow_dsp_x := %s ; flow_dsp_y := %s ;",connection.dx or 0, connection.dy or 0) + context("flow_connection_line_color := \\MPcolor{%s} ;",linesettings.color) + context("flow_connection_line_width := 2pt ;",points(linesettings.rulethickness)) + context("flow_connect_%s_%s (%s) (%s,%s,%s) (%s,%s,%s) ;",where_cell,where_other,j,cellx,celly,what_cell,otherx,othery,what_other) + context("flow_dsp_x := 0 ; flow_dsp_y := 0 ;") + end + end + end + end + end + end +end + +local texttemplate = "\\setvariables[flowcell:text][x=%s,y=%s,text={%s},align={%s},figure={%s},destination={%s}]" + +local splitter = lpeg.splitat(":") + +local function process_texts(chart,xoffset,yoffset) + local data = chart.data + local hash = chart.hash + if not data then + return + end + for i=1,#data do + local cell = visible(chart,data[i]) + if cell then + local x = cell.x or 1 + local y = cell.y or 1 + local texts = cell.texts + for i=1,#texts do + local text = texts[i] + local data = text.text + local align = validlabellocations[text.align or ""] or text.align or "" + local figure = i == 1 and cell.figure or "" + local destination = i == 1 and cell.destination or "" + context('flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,format(texttemplate,x,y,data,align,figure,destination)) + end + local labels = cell.labels + for i=1,#labels do + local label = labels[i] + local text = label.text + local location = validlabellocations[label.location or ""] or label.location or "" + if text and location then + context('flow_chart_draw_label(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text) + end + end + local exits = cell.exits + for i=1,#exits do + local exit = exits[i] + local text = exit.text + local location = validlabellocations[exit.location or ""] + if text and location then + -- maybe make autoexit an option + if location == "l" and x == chart.from_x + 1 or + location == "r" and x == chart.to_x - 1 or + location == "t" and y == chart.to_y - 1 or + location == "b" and y == chart.from_y + 1 then + context('flow_chart_draw_exit(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text) + end + end + end + local connections = cell.connections + for i=1,#connections do + local comments = connections[i].comments + for j=1,#comments do + local comment = comments[j] + local text = comment.text + local location = comment.location or "" + local length = 0 + -- "tl" "tl:*" "tl:0.5" + local loc, len = lpegmatch(splitter,location) -- do the following in lpeg + if len == "*" then + location = validcommentlocations[loc] or "" + if location == "" then + location = "*" + else + location = location .. ":*" + end + elseif loc then + location = validcommentlocations[loc] or "*" + length = tonumber(len) or 0 + else + location = validcommentlocations[location] or "" + end + if text and location then + context('flow_chart_draw_comment(%s,%s,%s,"%s",%s,textext("\\strut %s")) ;',x,y,i,location,length,text) + end + end + end + end + end +end + +local function getchart(settings) + if not settings then + print("no settings given") + return + end + local chartname = settings.chart.name + if not chartname then + print("no name given") + return + end + local chart = charts[chartname] + if not chart then + print("no such chart",chartname) + return + end + chart = expanded(chart,settings) + local cc_settings = chart.settings.chart + local autofocus = chart.settings.chart.autofocus + if autofocus then + autofocus = utilities.parsers.settings_to_hash(autofocus) + if not next(autofocus) then + autofocus = false + end + end + -- check natural window + local x = tonumber(cc_settings.x) + local y = tonumber(cc_settings.y) + local nx = tonumber(cc_settings.nx) + local ny = tonumber(cc_settings.ny) + -- + local minx, miny, maxx, maxy = 0, 0, 0, 0 + local data = chart.data + for i=1,#data do + local cell = data[i] + if not autofocus or autofocus[cell.name] then -- offsets probably interfere with autofocus + local x = cell.x + local y = cell.y + if minx == 0 or x < minx then minx = x end + if miny == 0 or y < miny then miny = y end + if minx == 0 or x > maxx then maxx = x end + if miny == 0 or y > maxy then maxy = y end + end + end +-- print("1>",x,y,nx,ny) +-- print("2>",minx, miny, maxx, maxy) + -- check of window should be larger (maybe autofocus + nx/ny?) + if autofocus then + -- x and y are ignored + if nx and nx > 0 then + maxx = minx + nx - 1 + end + if ny and ny > 0 then + maxy = miny + ny - 1 + end + else + if x and x > 0 then + minx = x + end + if y and y > 0 then + miny = y + end + if nx and nx > 0 then + maxx = minx + nx - 1 + end + if ny and ny > 0 then + maxy = miny + ny - 1 + end + end +-- print("3>",minx, miny, maxx, maxy) + -- + local nx = maxx - minx + 1 + local ny = maxy - miny + 1 + -- relocate cells + for i=1,#data do + local cell = data[i] + cell.x = cell.x - minx + 1 + cell.y = cell.y - miny + 1 + end + chart.from_x = 1 + chart.from_y = 1 + chart.to_x = nx + chart.to_y = ny + chart.nx = nx + chart.ny = ny + -- + -- inspect(chart) + return chart +end + +local function makechart(chart) + local settings = chart.settings + context.begingroup() + context.forgetall() + -- + context.startMPcode() + context("if unknown context_flow : input mp-char.mpiv ; fi ;") + context("flow_begin_chart(0,%s,%s);",chart.nx,chart.ny) + -- + if settings.chart.option == v_test or settings.chart.dot == v_yes then + context("flow_show_con_points := true ;") + context("flow_show_mid_points := true ;") + context("flow_show_all_points := true ;") + elseif settings.chart.dot ~= "" then -- no checking done, private option + context("flow_show_%s_points := true ;",settings.chart.dot) + end + -- + local backgroundcolor = settings.chart.backgroundcolor + if backgroundcolor and backgroundcolor ~= "" then + context("flow_chart_background_color := \\MPcolor{%s} ;",backgroundcolor) + end + -- + local shapewidth = settings.chart.width + local gridwidth = shapewidth + 2*settings.chart.dx + local shapeheight = settings.chart.height + local gridheight = shapeheight + 2*settings.chart.dy + local chartoffset = settings.chart.offset + local labeloffset = settings.chart.labeloffset + local exitoffset = settings.chart.exitoffset + local commentoffset = settings.chart.commentoffset + context("flow_grid_width := %s ;", points(gridwidth)) + context("flow_grid_height := %s ;", points(gridheight)) + context("flow_shape_width := %s ;", points(shapewidth)) + context("flow_shape_height := %s ;", points(shapeheight)) + context("flow_chart_offset := %s ;", points(chartoffset)) + context("flow_label_offset := %s ;", points(labeloffset)) + context("flow_exit_offset := %s ;", points(exitoffset)) + context("flow_comment_offset := %s ;", points(commentoffset)) + -- + local radius = settings.line.radius + local rulethickness = settings.line.rulethickness + local dx = settings.chart.dx + local dy = settings.chart.dy + if radius < rulethickness then + radius = 2.5*rulethickness + if radius > dx then + radius = dx + end + if radius > dy then + radius = dy + end + end + context("flow_connection_line_width := %s ;", points(rulethickness)) + context("flow_connection_smooth_size := %s ;", points(radius)) + context("flow_connection_arrow_size := %s ;", points(radius)) + context("flow_connection_dash_size := %s ;", points(radius)) + -- + local offset = settings.chart.offset -- todo: pass string + if offset == v_none or offset == v_overlay or offset == "" then + offset = -2.5 * radius -- or rulethickness? + elseif offset == v_standard then + offset = radius -- or rulethickness? + end + context("flow_chart_offset := %s ;",points(offset)) + -- + context("flow_reverse_y := true ;") + process_cells(chart,0,0) + process_connections(chart,0,0) + process_texts(chart,0,0) + -- context("clip_chart(%s,%s,%s,%s) ;",x,y,nx,ny) -- todo: draw lines but not shapes + context("flow_end_chart ;") + context.stopMPcode() + context.endgroup() +end + +function commands.flow_make_chart(settings) + local chart = getchart(settings) + if chart then + local settings = chart.settings +-- if settings.split.state == v_start then +-- local nx = chart.settings.split.nx +-- local ny = chart.settings.split.ny +-- local x = 1 +-- while true do +-- local y = 1 +-- while true do +-- -- FLOTbefore +-- -- doif @@FLOTmarking on -> cuthbox +-- -- @@FLOTcommand +-- chart.from_x = x +-- chart.from_y = y +-- chart.to_x = math.min(x + nx - 1,chart.nx) +-- chart.to_y = math.min(x + ny - 1,chart.ny) +-- makechart(chart) +-- -- FLOTafter +-- y = y + ny +-- if y > chart.max_y then +-- break +-- else +-- y = y - dy +-- end +-- end +-- x = x + nx +-- if x > chart.max_x then +-- break +-- else +-- x = x - dx +-- end +-- end +-- else + makechart(chart) +-- end + end +end |