diff --git a/ffnord-con-2015-2/logo/logo.png b/ffnord-con-2015-2/logo/logo.png new file mode 100644 index 0000000..ff977e8 Binary files /dev/null and b/ffnord-con-2015-2/logo/logo.png differ diff --git a/ffnord-con-2015-2/logo/logo.svg b/ffnord-con-2015-2/logo/logo.svg new file mode 100644 index 0000000..96aafee --- /dev/null +++ b/ffnord-con-2015-2/logo/logo.svg @@ -0,0 +1,130 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + FFNORD + + CON + 2015.2 + + + + + diff --git a/ffnord-con-2015-2/logo/node.lua b/ffnord-con-2015-2/logo/node.lua new file mode 100644 index 0000000..af63f77 --- /dev/null +++ b/ffnord-con-2015-2/logo/node.lua @@ -0,0 +1,8 @@ +gl.setup(1280, 720) + +background = resource.load_image("logo.png") + +function node.render() + gl.clear(0.1, 0.1, 0.1, 1) + background:draw(0, 0, WIDTH, HEIGHT) +end diff --git a/ffnord-con-2015-2/logo/path3482.png b/ffnord-con-2015-2/logo/path3482.png new file mode 100644 index 0000000..82fb501 Binary files /dev/null and b/ffnord-con-2015-2/logo/path3482.png differ diff --git a/ffnord-con-2015-2/node.lua b/ffnord-con-2015-2/node.lua index c3eec37..b430822 100644 --- a/ffnord-con-2015-2/node.lua +++ b/ffnord-con-2015-2/node.lua @@ -1,6 +1,6 @@ gl.setup(1280, 720) function node.render() - resource.render_child("31c3"):draw(0, 0, 1280, 720) - resource.render_child("29c3"):draw(0, 0, 1280, 720) + resource.render_child("room"):draw(0, 0, 1280, 720) +-- resource.render_child("logo"):draw(0, 0, 1280, 720) end diff --git a/ffnord-con-2015-2/room/.node.lua.swp b/ffnord-con-2015-2/room/.node.lua.swp new file mode 100644 index 0000000..638fa26 Binary files /dev/null and b/ffnord-con-2015-2/room/.node.lua.swp differ diff --git a/ffnord-con-2015-2/room/.service.swp b/ffnord-con-2015-2/room/.service.swp new file mode 100644 index 0000000..0e89840 Binary files /dev/null and b/ffnord-con-2015-2/room/.service.swp differ diff --git a/ffnord-con-2015-2/room/Oswald-Medium.ttf b/ffnord-con-2015-2/room/Oswald-Medium.ttf new file mode 100644 index 0000000..8210fc3 Binary files /dev/null and b/ffnord-con-2015-2/room/Oswald-Medium.ttf differ diff --git a/ffnord-con-2015-2/room/README.txt b/ffnord-con-2015-2/room/README.txt new file mode 100644 index 0000000..df38ef1 --- /dev/null +++ b/ffnord-con-2015-2/room/README.txt @@ -0,0 +1,14 @@ +Room visualization used at 30c3. + +This is almost the code that ran at 30c3. I had to remove the font used +as the font used at 30c3 cannot be redistributed. The replacement font +is "Roboto light". See http://www.fontsquirrel.com/license/roboto + +Installation: + + Nothing to do + +Usage: + + start info-beamer for this directory. + Then run ./service diff --git a/ffnord-con-2015-2/room/clock.png b/ffnord-con-2015-2/room/clock.png new file mode 100644 index 0000000..8a97e16 Binary files /dev/null and b/ffnord-con-2015-2/room/clock.png differ diff --git a/ffnord-con-2015-2/room/config.json b/ffnord-con-2015-2/room/config.json new file mode 100644 index 0000000..14da937 --- /dev/null +++ b/ffnord-con-2015-2/room/config.json @@ -0,0 +1,15 @@ +{ + "devices": { + "2429607372": "attraktor" + }, + "rooms": { + "attraktor": { + "dect": "", + "translation": "", + "twitter": "#ffnord", + "irc": "#ffnord", + "texture": "marble", + "speed": 70 + } + } +} diff --git a/ffnord-con-2015-2/room/g3527.png b/ffnord-con-2015-2/room/g3527.png new file mode 100644 index 0000000..c8a9603 Binary files /dev/null and b/ffnord-con-2015-2/room/g3527.png differ diff --git a/ffnord-con-2015-2/room/json.lua b/ffnord-con-2015-2/room/json.lua new file mode 100644 index 0000000..d95241a --- /dev/null +++ b/ffnord-con-2015-2/room/json.lua @@ -0,0 +1,377 @@ +----------------------------------------------------------------------------- +-- JSON4Lua: JSON encoding / decoding support for the Lua language. +-- json Module. +-- Author: Craig Mason-Jones +-- Homepage: http://json.luaforge.net/ +-- Version: 0.9.40 +-- This module is released under the MIT License (MIT). +-- Please see LICENCE.txt for details. +-- +-- USAGE: +-- This module exposes two functions: +-- encode(o) +-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. +-- decode(json_string) +-- Returns a Lua object populated with the data encoded in the JSON string json_string. +-- +-- REQUIREMENTS: +-- compat-5.1 if using Lua 5.0 +-- +-- CHANGELOG +-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). +-- Fixed Lua 5.1 compatibility issues. +-- Introduced json.null to have null values in associative arrays. +-- encode() performance improvement (more than 50%) through table.concat rather than .. +-- Introduced decode ability to ignore /**/ comments in the JSON string. +-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Imports and dependencies +----------------------------------------------------------------------------- +local math = require('math') +local string = require("string") +local table = require("table") + +local base = _G + +----------------------------------------------------------------------------- +-- Module declaration +----------------------------------------------------------------------------- +module("json") + +-- Public functions + +-- Private functions +local decode_scanArray +local decode_scanComment +local decode_scanConstant +local decode_scanNumber +local decode_scanObject +local decode_scanString +local decode_scanWhitespace +local encodeString +local isArray +local isEncodable + +----------------------------------------------------------------------------- +-- PUBLIC FUNCTIONS +----------------------------------------------------------------------------- +--- Encodes an arbitrary Lua object / variable. +-- @param v The Lua object / variable to be JSON encoded. +-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) +function encode (v) + -- Handle nil values + if v==nil then + return "null" + end + + local vtype = base.type(v) + + -- Handle strings + if vtype=='string' then + return '"' .. encodeString(v) .. '"' -- Need to handle encoding in string + end + + -- Handle booleans + if vtype=='number' or vtype=='boolean' then + return base.tostring(v) + end + + -- Handle tables + if vtype=='table' then + local rval = {} + -- Consider arrays separately + local bArray, maxCount = isArray(v) + if bArray then + for i = 1,maxCount do + table.insert(rval, encode(v[i])) + end + else -- An object, not an array + for i,j in base.pairs(v) do + if isEncodable(i) and isEncodable(j) then + table.insert(rval, '"' .. encodeString(i) .. '":' .. encode(j)) + end + end + end + if bArray then + return '[' .. table.concat(rval,',') ..']' + else + return '{' .. table.concat(rval,',') .. '}' + end + end + + -- Handle null values + if vtype=='function' and v==null then + return 'null' + end + + base.assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. base.tostring(v)) +end + + +--- Decodes a JSON string and returns the decoded value as a Lua data structure / value. +-- @param s The string to scan. +-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. +-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, +-- and the position of the first character after +-- the scanned JSON object. +function decode(s, startPos) + startPos = startPos and startPos or 1 + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') + local curChar = string.sub(s,startPos,startPos) + -- Object + if curChar=='{' then + return decode_scanObject(s,startPos) + end + -- Array + if curChar=='[' then + return decode_scanArray(s,startPos) + end + -- Number + if string.find("+-0123456789.e", curChar, 1, true) then + return decode_scanNumber(s,startPos) + end + -- String + if curChar==[["]] or curChar==[[']] then + return decode_scanString(s,startPos) + end + if string.sub(s,startPos,startPos+1)=='/*' then + return decode(s, decode_scanComment(s,startPos)) + end + -- Otherwise, it must be a constant + return decode_scanConstant(s,startPos) +end + +--- The null function allows one to specify a null value in an associative array (which is otherwise +-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } +function null() + return null -- so json.null() will also return null ;-) +end +----------------------------------------------------------------------------- +-- Internal, PRIVATE functions. +-- Following a Python-like convention, I have prefixed all these 'PRIVATE' +-- functions with an underscore. +----------------------------------------------------------------------------- + +--- Scans an array from JSON into a Lua object +-- startPos begins at the start of the array. +-- Returns the array and the next starting position +-- @param s The string being scanned. +-- @param startPos The starting position for the scan. +-- @return table, int The scanned array as a table, and the position of the next character to scan. +function decode_scanArray(s,startPos) + local array = {} -- The return value + local stringLen = string.len(s) + base.assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) + startPos = startPos + 1 + -- Infinite loop for array elements + repeat + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') + local curChar = string.sub(s,startPos,startPos) + if (curChar==']') then + return array, startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + base.assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') + object, startPos = decode(s,startPos) + table.insert(array,object) + until false +end + +--- Scans a comment and discards the comment. +-- Returns the position of the next character following the comment. +-- @param string s The JSON string to scan. +-- @param int startPos The starting position of the comment +function decode_scanComment(s, startPos) + base.assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) + local endPos = string.find(s,'*/',startPos+2) + base.assert(endPos~=nil, "Unterminated comment in string at " .. startPos) + return endPos+2 +end + +--- Scans for given constants: true, false or null +-- Returns the appropriate Lua type, and the position of the next character to read. +-- @param s The string being scanned. +-- @param startPos The position in the string at which to start scanning. +-- @return object, int The object (true, false or nil) and the position at which the next character should be +-- scanned. +function decode_scanConstant(s, startPos) + local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } + local constNames = {"true","false","null"} + + for i,k in base.pairs(constNames) do + --print ("[" .. string.sub(s,startPos, startPos + string.len(k) -1) .."]", k) + if string.sub(s,startPos, startPos + string.len(k) -1 )==k then + return consts[k], startPos + string.len(k) + end + end + base.assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) +end + +--- Scans a number from the JSON encoded string. +-- (in fact, also is able to scan numeric +- eqns, which is not +-- in the JSON spec.) +-- Returns the number, and the position of the next character +-- after the number. +-- @param s The string being scanned. +-- @param startPos The position at which to start scanning. +-- @return number, int The extracted number and the position of the next character to scan. +function decode_scanNumber(s,startPos) + local endPos = startPos+1 + local stringLen = string.len(s) + local acceptableChars = "+-0123456789.e" + while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) + and endPos<=stringLen + ) do + endPos = endPos + 1 + end + local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) + local stringEval = base.loadstring(stringValue) + base.assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) + return stringEval(), endPos +end + +--- Scans a JSON object into a Lua object. +-- startPos begins at the start of the object. +-- Returns the object and the next starting position. +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return table, int The scanned object as a table and the position of the next character to scan. +function decode_scanObject(s,startPos) + local object = {} + local stringLen = string.len(s) + local key, value + base.assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) + startPos = startPos + 1 + repeat + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') + local curChar = string.sub(s,startPos,startPos) + if (curChar=='}') then + return object,startPos+1 + end + if (curChar==',') then + startPos = decode_scanWhitespace(s,startPos+1) + end + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') + -- Scan the key + key, startPos = decode(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + startPos = decode_scanWhitespace(s,startPos) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + base.assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) + startPos = decode_scanWhitespace(s,startPos+1) + base.assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) + value, startPos = decode(s,startPos) + object[key]=value + until false -- infinite loop while key-value pairs are found +end + +--- Scans a JSON string from the opening inverted comma or single quote to the +-- end of the string. +-- Returns the string extracted as a Lua string, +-- and the position of the next non-string character +-- (after the closing inverted comma or single quote). +-- @param s The string being scanned. +-- @param startPos The starting position of the scan. +-- @return string, int The extracted string as a Lua string, and the next character to parse. +function decode_scanString(s,startPos) + base.assert(startPos, 'decode_scanString(..) called without start position') + local startChar = string.sub(s,startPos,startPos) + base.assert(startChar==[[']] or startChar==[["]],'decode_scanString called for a non-string') + local escaped = false + local endPos = startPos + 1 + local bEnded = false + local stringLen = string.len(s) + repeat + local curChar = string.sub(s,endPos,endPos) + -- Character escaping is only used to escape the string delimiters + if not escaped then + if curChar==[[\]] then + escaped = true + else + bEnded = curChar==startChar + end + else + -- If we're escaped, we accept the current character come what may + escaped = false + end + endPos = endPos + 1 + base.assert(endPos <= stringLen+1, "String decoding failed: unterminated string at position " .. endPos) + until bEnded + local stringValue = 'return ' .. string.sub(s, startPos, endPos-1) + local stringEval = base.loadstring(stringValue) + base.assert(stringEval, 'Failed to load string [ ' .. stringValue .. '] in JSON4Lua.decode_scanString at position ' .. startPos .. ' : ' .. endPos) + return stringEval(), endPos +end + +--- Scans a JSON string skipping all whitespace from the current start position. +-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. +-- @param s The string being scanned +-- @param startPos The starting position where we should begin removing whitespace. +-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string +-- was reached. +function decode_scanWhitespace(s,startPos) + local whitespace=" \n\r\t" + local stringLen = string.len(s) + while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do + startPos = startPos + 1 + end + return startPos +end + +--- Encodes a string to be JSON-compatible. +-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) +-- @param s The string to return as a JSON encoded (i.e. backquoted string) +-- @return The string appropriately escaped. +function encodeString(s) + s = string.gsub(s,'\\','\\\\') + s = string.gsub(s,'"','\\"') + s = string.gsub(s,"'","\\'") + s = string.gsub(s,'\n','\\n') + s = string.gsub(s,'\t','\\t') + return s +end + +-- Determines whether the given Lua type is an array or a table / dictionary. +-- We consider any table an array if it has indexes 1..n for its n items, and no +-- other data in the table. +-- I think this method is currently a little 'flaky', but can't think of a good way around it yet... +-- @param t The table to evaluate as an array +-- @return boolean, number True if the table can be represented as an array, false otherwise. If true, +-- the second returned value is the maximum +-- number of indexed elements in the array. +function isArray(t) + -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable + -- (with the possible exception of 'n') + local maxIndex = 0 + for k,v in base.pairs(t) do + if (base.type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair + if (not isEncodable(v)) then return false end -- All array elements must be encodable + maxIndex = math.max(maxIndex,k) + else + if (k=='n') then + if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements + else -- Else of (k=='n') + if isEncodable(v) then return false end + end -- End of (k~='n') + end -- End of k,v not an indexed pair + end -- End of loop across all pairs + return true, maxIndex +end + +--- Determines whether the given Lua object / table / variable can be JSON encoded. The only +-- types that are JSON encodable are: string, boolean, number, nil, table and json.null. +-- In this implementation, all other types are ignored. +-- @param o The object to examine. +-- @return boolean True if the object should be JSON encoded, false if it should be ignored. +function isEncodable(o) + local t = base.type(o) + return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) +end + diff --git a/ffnord-con-2015-2/room/logo.png b/ffnord-con-2015-2/room/logo.png new file mode 100644 index 0000000..181b8cf Binary files /dev/null and b/ffnord-con-2015-2/room/logo.png differ diff --git a/ffnord-con-2015-2/room/logo.svg b/ffnord-con-2015-2/room/logo.svg new file mode 100644 index 0000000..1d26fa8 --- /dev/null +++ b/ffnord-con-2015-2/room/logo.svg @@ -0,0 +1,114 @@ + + + + + + + + + + image/svg+xml + + + + + + + + FFNORD + + CON + 2015.2 + + + + + diff --git a/ffnord-con-2015-2/room/marble.jpg b/ffnord-con-2015-2/room/marble.jpg new file mode 100644 index 0000000..53a4290 Binary files /dev/null and b/ffnord-con-2015-2/room/marble.jpg differ diff --git a/ffnord-con-2015-2/room/marble_lol.jpg b/ffnord-con-2015-2/room/marble_lol.jpg new file mode 100644 index 0000000..8d870b7 Binary files /dev/null and b/ffnord-con-2015-2/room/marble_lol.jpg differ diff --git a/ffnord-con-2015-2/room/node.lua b/ffnord-con-2015-2/room/node.lua new file mode 100644 index 0000000..91e035f --- /dev/null +++ b/ffnord-con-2015-2/room/node.lua @@ -0,0 +1,318 @@ +gl.setup(1280, 720) + +font = resource.load_font("Oswald-Medium.ttf") + +node.alias("room") + +local json = require "json" + +-- Configure room here +local SAAL = "attraktor" + +util.auto_loader(_G) + +util.file_watch("schedule.json", function(content) + print("reloading schedule") + talks = json.decode(content) +end) + +util.file_watch("config.json", function(content) + local config = json.decode(content) + if sys.get_env then + saal = config.devices[sys.get_env("SERIAL")] + end + if not saal then + print("using statically configured saal identifier") + saal = SAAL + end + print(saal) + rooms = config.rooms + room = config.rooms[saal] +end) + +local base_time = N.base_time or 0 +local current_talk +local all_talks = {} +local day = 0 + +function get_now() + return base_time + sys.now() +end + +function check_next_talk() + local now = get_now() + local room_next = {} + for idx, talk in ipairs(talks) do + if rooms[talk.place] and not room_next[talk.place] and talk.unix + 25 * 60 > now then + room_next[talk.place] = talk + end + end + + for room, talk in pairs(room_next) do + talk.lines = wrap(talk.title, 30) + end + + if room_next[saal] then + current_talk = room_next[saal] + else + current_talk = nil + end + + all_talks = {} + for room, talk in pairs(room_next) do + if current_talk and room ~= current_talk.place then + all_talks[#all_talks + 1] = talk + end + end + table.sort(all_talks, function(a, b) + if a.unix < b.unix then + return true + elseif a.unix > b.unix then + return false + else + return a.place < b.place + end + end) +end + +function wrap(str, limit, indent, indent1) + limit = limit or 72 + local here = 1 + local wrapped = str:gsub("(%s+)()(%S+)()", function(sp, st, word, fi) + if fi-here > limit then + here = st + return "\n"..word + end + end) + local splitted = {} + for token in string.gmatch(wrapped, "[^\n]+") do + splitted[#splitted + 1] = token + end + return splitted +end + +local clock = (function() + local base_time = N.base_time or 0 + + local function set(time) + base_time = tonumber(time) - sys.now() + end + + util.data_mapper{ + ["clock/midnight"] = function(since_midnight) + set(since_midnight) + end; + } + + local left = 0 + + local function get() + local time = (base_time + sys.now()) % 86400 + return string.format("%d:%02d", math.floor(time / 3600), math.floor(time % 3600 / 60)) + end + + return { + get = get; + set = set; + } +end)() + +check_next_talk() + +util.data_mapper{ + ["clock/set"] = function(time) + base_time = tonumber(time) - sys.now() + N.base_time = base_time + check_next_talk() + print("UPDATED TIME", base_time) + end; + ["clock/day"] = function(new_day) + print("DAY", new_day) + day = new_day + end; +} + +function switcher(screens) + local current_idx = 1 + local current = screens[current_idx] + local switch = sys.now() + current.time + local switched = sys.now() + + local blend = 0.5 + + local function draw() + local now = sys.now() + + local percent = ((now - switched) / (switch - switched)) * 3.14129 * 2 - 3.14129 + progress:use{percent = percent} + white:draw(WIDTH-50, HEIGHT-50, WIDTH-10, HEIGHT-10) + progress:deactivate() + + if now - switched < blend then + local delta = (switched - now) / blend + gl.pushMatrix() + gl.translate(WIDTH/2, 0) + gl.rotate(270-90 * delta, 0, 1, 0) + gl.translate(-WIDTH/2, 0) + current.draw() + gl.popMatrix() + elseif now < switch - blend then + current.draw(now - switched) + elseif now < switch then + local delta = 1 - (switch - now) / blend + gl.pushMatrix() + gl.translate(WIDTH/2, 0) + gl.rotate(90 * delta, 0, 1, 0) + gl.translate(-WIDTH/2, 0) + current.draw() + gl.popMatrix() + else + current_idx = current_idx + 1 + if current_idx > #screens then + current_idx = 1 + end + current = screens[current_idx] + switch = now + current.time + switched = now + end + end + return { + draw = draw; + } +end + +content = switcher{ +--[[ { + time = 10; + draw = function() + font:write(400, 200, "Other rooms", 80, 1,1,1,1) + white:draw(0, 300, WIDTH, 302, 0.6) + y = 320 + local time_sep = false + if #all_talks > 0 then + for idx, talk in ipairs(all_talks) do + if not time_sep and talk.unix > get_now() then + if idx > 1 then + y = y + 5 + white:draw(0, y, WIDTH, y+2, 0.6) + y = y + 20 + end + time_sep = true + end + + local alpha = 1 + if not time_sep then + alpha = 0.3 + end + font:write(30, y, talk.start, 50, 1,1,1,alpha) + font:write(190, y, talk.place, 50, 1,1,1,alpha) + font:write(400, y, talk.lines[math.floor((sys.now()/2) % #talk.lines)+1], 50, 1,1,1,alpha) + y = y + 60 + end + else + font:write(400, 330, "No other talks.", 50, 1,1,1,1) + end + end + }, --]] { + time = 30; + draw = function() + if not current_talk then + redU(400, 200, "Next session...", 80, 1,1,1,1) + white:draw(0, 300, WIDTH, 302, 0.6) + redU(400, 330, "Nope. That's it.", 80) + + else + local delta = current_talk.unix - get_now() + if delta > 0 then + redU(400, 200, "Next session", 80) + else + redU(400, 200, "This session", 80) + end + white:draw(0, 300, WIDTH, 302, 0.6) + + blue(130, 330, current_talk.start, 50, 1,1,1,1) + if delta > 0 then + blue(130, 330 + 60, string.format("in %d min", math.floor(delta/60)+1), 50) + end + for idx, line in ipairs(current_talk.lines) do + if idx >= 5 then + break + end + yellow(400, 330 - 60 + 60 * idx, line, 50) + end + for i, speaker in ipairs(current_talk.speakers) do + blue(400, 510 + 50 * i, speaker, 50) + end + end + end + }, --[[ { + time = 10; + draw = function(t) + font:write(400, 200, "Room info", 80, 1,1,1,1) + white:draw(0, 300, WIDTH, 302, 0.6) + font:write(30, 320, "Audio", 50, 1,1,1,1) + font:write(400, 320, "Dial " .. room.dect, 50, 1,1,1,1) + + font:write(30, 380, "Translation", 50, 1,1,1,1) + font:write(400, 380, "Dial " .. room.translation, 50, 1,1,1,1) + + font:write(30, 480, "IRC", 50, 1,1,1,1) + font:write(400, 480, room.irc, 50, 1,1,1,1) + + font:write(30, 540, "Twitter", 50, 1,1,1,1) + font:write(400, 540, room.twitter, 50, 1,1,1,1) + + if t then + local banner = -4 + t + if banner > 0 and banner < 3.1412 then + local alpha = math.pow(math.sin(banner), 2) + local x = 400 - math.pow(banner / math.pi - 0.5, 5) * WIDTH * 2 + font:write(x, 640, "http://info-beamer.org/", 30, 1,1,1,alpha) + end + end + end + }, --]] +} + +function redU(x, y, text, size) + red(x, y, string.upper(text), size) +end + +function red(x, y, text, size) + font:write(x, y, text, size, 0.894, 0.251, 0.506, 1) +end + +function yellowU(x, y, text, size) + yellow(x, y, string.upper(text), size) +end + +function yellow(x, y, text, size) + font:write(x, y, text, size, 1.0, 0.776, 0.251, 1) +end + +function blueU(x, y, text, size) + blue(x, y, string.upper(text), size) +end + +function blue(x, y, text, size) + font:write(x, y, text, size, 0.251, 0.714, 0.906, 1) +end + +function node.render() + gl.clear(0.1, 0.1, 0.1, 1) + + if base_time == 0 then + return + end + + util.draw_correct(logo, 20, 20, 300, 120) + + yellowU(310, 20, saal, 80) + blueU(730, 20, clock.get(), 80) + redU(WIDTH-300, 20, string.format("Day %d", day), 80) + + local fov = math.atan2(HEIGHT, WIDTH*2) * 360 / math.pi + gl.perspective(fov, WIDTH/2, HEIGHT/2, -WIDTH, + WIDTH/2, HEIGHT/2, 0) + content.draw() +end diff --git a/ffnord-con-2015-2/room/place.png b/ffnord-con-2015-2/room/place.png new file mode 100644 index 0000000..8458b4f Binary files /dev/null and b/ffnord-con-2015-2/room/place.png differ diff --git a/ffnord-con-2015-2/room/progress.frag b/ffnord-con-2015-2/room/progress.frag new file mode 100644 index 0000000..1c04e89 --- /dev/null +++ b/ffnord-con-2015-2/room/progress.frag @@ -0,0 +1,16 @@ +uniform sampler2D Texture; +varying vec2 TexCoord; +uniform float percent; + +void main() { + vec2 pos = TexCoord; + float angle = atan(pos.x - 0.5, pos.y - 0.5); + float dist = distance(pos, vec2(0.5, 0.5)); + if (dist >= 0.5) { + gl_FragColor = vec4(0.0); + } else if (angle < percent) { + gl_FragColor = vec4(0.894, 0.251, 0.506, 1); + } else { + gl_FragColor = vec4(1.0, 0.776, 0.251, 1); + } +} diff --git a/ffnord-con-2015-2/room/schedule.json b/ffnord-con-2015-2/room/schedule.json new file mode 100644 index 0000000..d864a4a --- /dev/null +++ b/ffnord-con-2015-2/room/schedule.json @@ -0,0 +1,121 @@ +[ + { + "duration": 60, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "9:30", + "title": "„Einlass“ und Frühstück", + "type": "talk", + "unix": 1441956600, + "unix_end": 1441960200 + }, + + { + "duration": 30, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "11:00", + "title": "Eröffnungsplenum", + "type": "talk", + "unix": 1441962000, + "unix_end": 1441963800 + }, + + { + "duration": 60, + "lang": "de", + "place": "attraktor", + "speakers": [ + "t.b.a." + ], + "start": "12:00", + "title": "Vortrags-Slot #1 mit folgender Diskussion und Kaffeepause", + "type": "talk", + "unix": 1441965600, + "unix_end": 1441969200 + }, + + { + "duration": 60, + "lang": "de", + "place": "attraktor", + "speakers": [ + "t.b.a." + ], + "start": "14:00", + "title": "Vortrags-Slot #2 mit folgender Diskussion und Kaffeepause", + "type": "talk", + "unix": 1441972800, + "unix_end": 1441976400 + }, + + { + "duration": 240, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "16:00", + "title": "Workshop und Hacking Time", + "type": "talk", + "unix": 1441980000, + "unix_end": 1441994400 + }, + + { + "duration": 60, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "9:30", + "title": "„Einlass“ und Frühstück", + "type": "talk", + "unix": 1442043000, + "unix_end": 1442046600 + }, + + { + "duration": 60, + "lang": "de", + "place": "attraktor", + "speakers": [ + "t.b.a." + ], + "start": "11:00", + "title": "Vortrags-Slot #3 mit folgender Diskussion und Kaffeepause", + "type": "talk", + "unix": 1442048400, + "unix_end": 1442052000 + }, + + { + "duration": 150, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "13:00", + "title": "Workshop und Hacking Time", + "type": "talk", + "unix": 1442055600, + "unix_end": 1442064600 + }, + + { + "duration": 30, + "lang": "de", + "place": "attraktor", + "speakers": [ + ], + "start": "16:00", + "title": "Abschlussplenum und Diskussion", + "type": "talk", + "unix": 1442066400, + "unix_end": 1442068200 + } +] diff --git a/ffnord-con-2015-2/room/service b/ffnord-con-2015-2/room/service new file mode 100755 index 0000000..4b3cc4a --- /dev/null +++ b/ffnord-con-2015-2/room/service @@ -0,0 +1,55 @@ +#!/usr/bin/python +import sys +import time +import pytz +import socket +from calendar import timegm +from datetime import datetime, date + +diff = None + +# Fake time in UTC +diff = datetime(2015,9,11,8,30,00) - datetime.utcnow() + +met = pytz.timezone("Europe/Berlin") +def current_time(): + now = datetime.utcnow() + if diff: + now += diff + timestamp = timegm(now.timetuple()) + now.microsecond / 1000000. + now = now.replace(tzinfo=pytz.utc) + now = now.astimezone(met) + now = now.replace(tzinfo=None) + return now, timestamp + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +def send(data): + sock.sendto(data, ('127.0.0.1', 4444)) + print >>sys.stderr, "SENT >>> ", data + +def send_clock(now, ts): + day = (now.date() - date(2015, 9, 11)).days + since_midnight = ( + now - + now.replace(hour=0, minute=0, second=0, microsecond=0) + ) + since_midnight = since_midnight.seconds + since_midnight.microseconds / 1000000. + + send('room/clock/day:%d' % day) + send('room/clock/set:%f' % ts) + send('room/clock/midnight:%f' % since_midnight) + + +def main(): + while 1: + now, ts = current_time() + if now.year < 2000: + print >>sys.stderr, "too soon" + time.sleep(1) + continue + send_clock(now, ts) + time.sleep(2) + +if __name__ == "__main__": + main() diff --git a/ffnord-con-2015-2/room/trichter.frag b/ffnord-con-2015-2/room/trichter.frag new file mode 100644 index 0000000..3489d91 --- /dev/null +++ b/ffnord-con-2015-2/room/trichter.frag @@ -0,0 +1,32 @@ +uniform sampler2D Texture; +uniform sampler2D Grid; +uniform sampler2D Overlay; +varying vec2 TexCoord; +uniform float time; + +const float segments = 9.0; + +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +void main() { + vec2 pos = TexCoord; + + vec2 coord = texture2D(Texture, pos).st; + coord.x = mod(coord.x + time, 1.0); + coord.y = coord.y + 0.0175; + coord += rand(pos) * 0.008; + vec3 col = texture2D(Overlay, coord).rgb; + + float foo = floor(coord.y * segments); + col *= (foo / segments) * 0.6 + 0.4; + + if (pos.y > 0.82) { + vec3 bar = texture2D(Overlay, pos).rgb; + col = col * 0.3 + bar * 0.4; //mix(col, bar, 0.3) * 0.8; + } + + vec4 grid = texture2D(Grid, pos); + gl_FragColor = vec4(col, 1.0) + grid * 0.09; +} diff --git a/ffnord-con-2015-2/room/trichter_grid.png b/ffnord-con-2015-2/room/trichter_grid.png new file mode 100644 index 0000000..ac8c986 Binary files /dev/null and b/ffnord-con-2015-2/room/trichter_grid.png differ diff --git a/ffnord-con-2015-2/room/trichter_map.png b/ffnord-con-2015-2/room/trichter_map.png new file mode 100644 index 0000000..f596552 Binary files /dev/null and b/ffnord-con-2015-2/room/trichter_map.png differ diff --git a/ffnord-con-2015-2/room/white.png b/ffnord-con-2015-2/room/white.png new file mode 100644 index 0000000..5e03498 Binary files /dev/null and b/ffnord-con-2015-2/room/white.png differ