diff options
Diffstat (limited to 'macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua')
-rw-r--r-- | macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua b/macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua new file mode 100644 index 0000000000..049644d291 --- /dev/null +++ b/macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua @@ -0,0 +1,233 @@ +function init(opts) + tex.print([[\newwrite\timetableATdataOutput \immediate\openout\timetableATdataOutput=\jobname-data.dat]]) + tex.print([[\immediate\write\timetableATdataOutput{return \iftrue\string{\else}\fi}]]) + if(not checkKeys(opts, {"days", "min", "max", "dayse"})) then + error("missing argument") + end + -- clean up first + -- global variables + EVENTS={} + DAYS = prepareDays(opts.days) -- header with names of the days set from tex currently + DAYSE = prepareDays(opts.dayse) -- day representation in source code + MIN = 25*60 -- bigger than any allowed value could be + MAX = 0 + MIN_BYPASS = false -- weather min is fixed by the user + MAX_BYPASS = false -- weather max is fixed by the user + + if(opts.min == "") then + else + assert(opts.min:match("^%d+"), "start time has to be an integer representing the HH*60+MM of the desired start time") + MIN = tonumber(opts.min) + MIN_BYPASS = true + end + + if(opts.max == "") then + else + assert(opts.max:match("^%d+"), "end time has to be an integer representing the HH*60+MM of the desired end time") + MAX = tonumber(opts.max) + MAX_BYPASS = true + end +end + +function defaultFormatter(opts) + local ret = "" + for k,v in pairs(opts) do + if type(k) == "string" then k = k:gsub("[_^]", "") end + if type(v) == "string" then v = v:gsub("[_^]", "") end + ret = string.format("%s, %s: %s", ret, tostring(k), tostring(v)) + end + -- print(ret) + return ret +end + +function timetableformatter(opts) + return string.format( + [[\textcolor{%s}{\textbf{%s}\\[.2em]\raggedright{%s}\\[0.5em]\raggedright{%s}\hfil\raggedright{%s}\\[0.5em]\raggedright{%s}}]], + opts.textcolor, opts.title, opts.speaker, opts.prio, opts.location, opts.time) +end +-- result are the global variables EVENTS, MIN and MAX +function addEvent(opts) + -- print("Reading event on line ", tex.inputlineno) + opts.inputlineno = tex.inputlineno + if(not checkKeys(opts, {"time", "day", "tikz"})) then + error("missing argument") + end + + if opts.content == nil then + if opts.formatter == nil then + opts.content = defaultFormatter(opts) + else + opts.content = opts.formatter(opts) + end + end + + opts.from,opts.to = dur2Int(opts.time) + + tex.print(string.format( + [[\immediate\write\timetableATdataOutput{\unexpanded{{["start"]=%q, ["end"]=%q, ["title"]=%q, ["location"]=%q, ["password"]=%q, ["type"]=%q},}}]], + opts.from + 24*60*day2Int(opts.day), + opts.to + 24*60*day2Int(opts.day), + opts.title, + opts.location:match([[\href{(.*)}{.*}]]) or opts.location, + opts.password, + opts.type + )) + + if(not MIN_BYPASS and opts.from < MIN) then MIN = opts.from end + if(not MAX_BYPASS and opts.to > MAX) then MAX = opts.to end + assert(opts.from < opts.to, "From has to be before to") + + table.insert(EVENTS, opts) +end +-- parameters are all global variables +function draw(length, width) + -- copy relevant variables for working on local copies + local events = copy_array(EVENTS) + local days = copy_array(DAYS) + local min, minH, max, maxH = prepareMinMax(MIN, MAX) + + assert(length:match("%d*%.?%d*"), "Length must be a valid length measured in cm") + length = tonumber(length) + + textwidth = width + + tex.print([[\begin{tikzpicture}]]) + tex.print([[\tikzset{defStyle/.style={font=\tiny,anchor=north west,fill=blue!50,draw=black,rectangle}}]]) + -- print the tabular with the weekday headers + tex.print(string.format( + [[\foreach \week [count=\x from 0, evaluate=\x as \y using \x+0.5] in {%s}{ ]], + table.concat(days, ",") + ) + ) + tex.print(string.format( + [[\node[anchor=south] at (\y/%d* %s, 0) {\week};]], #days, textwidth)) + tex.print(string.format( + [[\draw (\x/%d * %s, 0cm) -- (\x/%d * %s, %dcm);]], + #days, + textwidth, + #days, + textwidth, -length + ) + ) + tex.print("}") + tex.print(string.format( + [[\draw (%s, 0) -- (%s,%dcm);]], + textwidth, + textwidth, + -length + ) + ) + + for i=minH,maxH do + tex.print(string.format( + [[\node[anchor=east] at (0,%fcm ) {%d:00};]], + minuteToFrac(i*60,min,max)*-length, i + ) + ) + tex.print(string.format( + [[\draw (0,%fcm ) -- (%s,%fcm );]], + minuteToFrac(i*60,min,max)*-length, + textwidth, + minuteToFrac(i*60,min,max)*-length + ) + ) + end + + local d + local red = 0.3333 -- calculated in em from inner sep + local red_y = 0.25 -- calculated in em + for _,e in ipairs(events) do + if e.from < max and e.to > min then -- only draw if event is in scope (part of the comp is done in addEvent from < to + if e.to > max then e.to = max end + if e.from < min then e.from = min end + -- print("Drawing event on line ", e.inputlineno) + d = day2Int(e.day) + tex.print(string.format( + [[\node[defStyle,text width=-%fem+%f%s/%d, text depth=%fcm-%fem, text height=%fem, %s] at (%f*%s,%fcm) {%s};]], + 2*red, -- text width + e.scale_width, -- text width + textwidth, + #days, -- text width + length*(e.to-e.from)/(max-min), -- text depth + 2*red+red_y, -- text depth + red_y, -- text height + e.tikz, -- free tikz code + (d+e.offset)/#days, -- xcoord + textwidth, + minuteToFrac(e.from,min,max)*-length, -- ycoord + e.content -- content + ) + ) + end + end + tex.print([[\end{tikzpicture}]]) + tex.print([[\immediate\write\timetableATdataOutput{\iffalse{\else\string}\fi}]]) +end +function search_array(t, s) + for k,v in ipairs(t) do + if(v == s) then return k end + end + return nil +end + +function minuteToFrac(minute, min, max) + return (minute-min)/(max-min) +end +function prepareMinMax(min, max) + local minH = math.floor(min/60) + local maxH = math.ceil(max/60) + local min = minH*60 + local max = maxH*60 + return min, minH, max, maxH +end +function checkKeys(t, k) + for _,x in ipairs(k) do + if(t[x] == nil) then + return false + end + end + return true +end +function dur2Int(clk) + local f1,f2, t1,t2 = clk:match("^(%d%d?):(%d%d)-(%d%d?):(%d%d)$") + if(f1 ~= nil and f2 ~= nil and t1 ~= nil and t2 ~= nil) then + f1 = tonumber(f1) f2 = tonumber(f2) + t1 = tonumber(t1) t2 = tonumber(t2) + assert(f1 >= 0 and f1 < 24, "Hours have to be >= 0 && < 24") + assert(f2 >= 0 and f2 < 60, "Mins have to be >= 0 && < 60") + assert(t1 >= 0 and t1 < 24, "Hours have to be >= 0 && < 24") + assert(t2 >= 0 and t2 < 60, "Mins have to be >= 0 && < 60") + return f1*60 + f2, t1*60 + t2 + else + error("clk string \"" .. clk .. "\" was no valid clock string") + end +end +function prepareDays(days) + local ret = {} + for m in days:gmatch("[^,]+") do + table.insert(ret, m) + end + return ret +end +function day2Int(day) + return search_array(DAYSE, day) - 1 +end + +function copy_array(obj) + if type(obj) ~= 'table' then return obj end + local res = {} + for k, v in pairs(obj) do + local c = copy_array(v) + res[copy_array(k)] = c + end + return res +end + +semesterplannerLua = { + init = init, + addEvent = addEvent, + draw = draw, + day2Int = day2Int, +} +return semesterplannerLua + |