summaryrefslogtreecommitdiff
path: root/macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua
diff options
context:
space:
mode:
Diffstat (limited to 'macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua')
-rw-r--r--macros/luatex/latex/semesterplannerlua/semesterplannerlua_timetable.lua233
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
+