Logo + Raum-Schedule angefangen
BIN
ffnord-con-2015-2/logo/logo.png
Normal file
After Width: | Height: | Size: 91 KiB |
130
ffnord-con-2015-2/logo/logo.svg
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="1280"
|
||||||
|
height="720"
|
||||||
|
viewBox="0 0 1280 720"
|
||||||
|
id="svg3363"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
inkscape:export-filename="/home/baldo/projects/ffhh/ffnord-info-beamer/ffnord-con-2015-2/logo/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<defs
|
||||||
|
id="defs3365" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="606.70395"
|
||||||
|
inkscape:cy="189.53381"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g3425"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1278"
|
||||||
|
inkscape:window-height="1415"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="23"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3368">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-332.36224)">
|
||||||
|
<g
|
||||||
|
id="g3413"
|
||||||
|
transform="matrix(0.95474973,0,0,0.95474973,31.067475,31.857036)">
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#e44081;fill-opacity:1;fill-rule:nonzero;stroke:#dc0067;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="M 728.68638,350.92954 A 220.49287,220.49287 0 0 0 520.99106,498.10728 150.31792,150.31792 0 0 0 476.72349,491.27916 150.31792,150.31792 0 0 0 326.40513,641.59752 150.31792,150.31792 0 0 0 476.72349,791.91587 150.31792,150.31792 0 0 0 588.71763,741.58384 220.49287,220.49287 0 0 0 728.68638,791.91587 220.49287,220.49287 0 0 0 949.18052,571.42369 220.49287,220.49287 0 0 0 728.68638,350.92954 Z m 0,30.35352 A 190.13927,190.13927 0 0 1 918.827,571.42369 190.13927,190.13927 0 0 1 728.68638,761.56236 190.13927,190.13927 0 0 1 606.50083,717.07994 150.31792,150.31792 0 0 0 616.68442,695.5565 167.38731,167.38731 0 0 0 728.68638,738.81041 167.38731,167.38731 0 0 0 896.07505,571.42369 167.38731,167.38731 0 0 0 728.68638,404.03502 167.38731,167.38731 0 0 0 568.63364,522.92955 150.31792,150.31792 0 0 0 548.96567,509.9608 190.13927,190.13927 0 0 1 728.68638,381.28306 Z m 0,28.45703 a 161.68246,161.68246 0 0 1 161.68359,161.6836 161.68246,161.68246 0 0 1 -161.68359,161.68164 161.68246,161.68246 0 0 1 -109.74219,-43.22266 150.31792,150.31792 0 0 0 8.0957,-48.28515 150.31792,150.31792 0 0 0 -53.64062,-114.7168 161.68246,161.68246 0 0 1 155.28711,-117.14063 z m -251.96289,86.27735 a 145.57964,145.57964 0 0 1 42.82031,6.45117 220.49287,220.49287 0 0 0 -11.34961,68.95508 220.49287,220.49287 0 0 0 76.98828,167.05468 A 145.57964,145.57964 0 0 1 476.72349,787.17759 145.57964,145.57964 0 0 1 331.14341,641.59752 145.57964,145.57964 0 0 1 476.72349,496.01744 Z m 70.63671,18.33789 a 145.57964,145.57964 0 0 1 19.78711,13.41406 167.38731,167.38731 0 0 0 -5.84765,43.6543 167.38731,167.38731 0 0 0 51.62695,120.83203 145.57964,145.57964 0 0 1 -9.95117,21.59375 190.13927,190.13927 0 0 1 -64.42774,-142.42578 190.13927,190.13927 0 0 1 8.8125,-57.06836 z m 24.78711,17.42578 a 145.57964,145.57964 0 0 1 50.1543,109.81641 145.57964,145.57964 0 0 1 -7.19336,44.71093 161.68246,161.68246 0 0 1 -48.10351,-114.88476 161.68246,161.68246 0 0 1 5.14257,-39.64258 z"
|
||||||
|
id="circle5678" />
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:0.1418799;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
points="31,56 37,50 31,44 26,44 30,48 22,48 22,52 30,52 26,56 "
|
||||||
|
transform="matrix(7.3084349,-6.920364e-8,-2.0120062e-8,6.9273802,224.1884,303.86994)"
|
||||||
|
id="polygon5543-0" />
|
||||||
|
<polygon
|
||||||
|
id="polygon5747"
|
||||||
|
transform="matrix(-13.127626,-1.2430561e-7,3.6140246e-8,12.443163,1116.7329,-46.888118)"
|
||||||
|
points="31,56 37,50 31,44 26,44 30,48 22,48 22,52 30,52 26,56 "
|
||||||
|
style="fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:0.07898763;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g3425"
|
||||||
|
transform="matrix(0.93158948,0,0,0.93158948,45.877567,41.852256)">
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text4174"
|
||||||
|
y="954.60046"
|
||||||
|
x="394.6301"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:147.46354675px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#40b6e7;fill-opacity:1;stroke:#009ee0;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#40b6e7;fill-opacity:1;stroke:#009ee0;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;"
|
||||||
|
y="954.60046"
|
||||||
|
x="394.6301"
|
||||||
|
id="tspan4176"
|
||||||
|
sodipodi:role="line">FFNORD</tspan></text>
|
||||||
|
<g
|
||||||
|
id="g3418">
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text4212"
|
||||||
|
y="1032.0195"
|
||||||
|
x="478.35355"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:82.68903351px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
dx="0 0 0"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;"
|
||||||
|
y="1032.0195"
|
||||||
|
x="478.35355"
|
||||||
|
id="tspan4214"
|
||||||
|
sodipodi:role="line">CON</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:82.68903351px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#e44081;fill-opacity:1;stroke:#dc0067;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;"
|
||||||
|
x="620.80359"
|
||||||
|
y="1032.0195"
|
||||||
|
id="text4274"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan4276"
|
||||||
|
x="620.80359"
|
||||||
|
y="1032.0195"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#e44081;fill-opacity:1;stroke:#dc0067;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;"
|
||||||
|
dx="5.5911546">2015.2</tspan></text>
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:0.18134066;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
points="30,48 22,48 22,52 30,52 26,56 31,56 37,50 31,44 26,44 "
|
||||||
|
transform="matrix(4.0051378,-4.0051379,3.796314,3.7963139,132.57633,923.99276)"
|
||||||
|
id="polygon5543" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.4 KiB |
8
ffnord-con-2015-2/logo/node.lua
Normal file
|
@ -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
|
BIN
ffnord-con-2015-2/logo/path3482.png
Normal file
After Width: | Height: | Size: 37 KiB |
|
@ -1,6 +1,6 @@
|
||||||
gl.setup(1280, 720)
|
gl.setup(1280, 720)
|
||||||
|
|
||||||
function node.render()
|
function node.render()
|
||||||
resource.render_child("31c3"):draw(0, 0, 1280, 720)
|
resource.render_child("room"):draw(0, 0, 1280, 720)
|
||||||
resource.render_child("29c3"):draw(0, 0, 1280, 720)
|
-- resource.render_child("logo"):draw(0, 0, 1280, 720)
|
||||||
end
|
end
|
||||||
|
|
BIN
ffnord-con-2015-2/room/.node.lua.swp
Normal file
BIN
ffnord-con-2015-2/room/.service.swp
Normal file
BIN
ffnord-con-2015-2/room/Oswald-Medium.ttf
Normal file
14
ffnord-con-2015-2/room/README.txt
Normal file
|
@ -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
|
BIN
ffnord-con-2015-2/room/clock.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
15
ffnord-con-2015-2/room/config.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"devices": {
|
||||||
|
"2429607372": "attraktor"
|
||||||
|
},
|
||||||
|
"rooms": {
|
||||||
|
"attraktor": {
|
||||||
|
"dect": "",
|
||||||
|
"translation": "",
|
||||||
|
"twitter": "#ffnord",
|
||||||
|
"irc": "#ffnord",
|
||||||
|
"texture": "marble",
|
||||||
|
"speed": 70
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
ffnord-con-2015-2/room/g3527.png
Normal file
After Width: | Height: | Size: 11 KiB |
377
ffnord-con-2015-2/room/json.lua
Normal file
|
@ -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
|
||||||
|
|
BIN
ffnord-con-2015-2/room/logo.png
Normal file
After Width: | Height: | Size: 18 KiB |
114
ffnord-con-2015-2/room/logo.svg
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="410"
|
||||||
|
height="210"
|
||||||
|
viewBox="0 0 410 210"
|
||||||
|
id="svg3363"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
inkscape:export-filename="/home/baldo/projects/ffhh/ffnord-info-beamer/ffnord-con-2015-2/room/logo.png"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<defs
|
||||||
|
id="defs3365" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="3.959798"
|
||||||
|
inkscape:cx="184.07151"
|
||||||
|
inkscape:cy="90.89919"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g3418"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1417"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="23"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3368">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-842.36224)">
|
||||||
|
<g
|
||||||
|
id="g3527"
|
||||||
|
transform="matrix(1.7382264,0,0,1.7382264,-46.776992,-857.16085)"
|
||||||
|
inkscape:export-xdpi="90"
|
||||||
|
inkscape:export-ydpi="90">
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text4174"
|
||||||
|
y="1033.4785"
|
||||||
|
x="23.347813"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:67.51537323px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#40b6e7;fill-opacity:1;stroke:#009ee0;stroke-width:0.4578445;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#40b6e7;fill-opacity:1;stroke:#009ee0;stroke-width:0.4578445;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
y="1033.4785"
|
||||||
|
x="23.347813"
|
||||||
|
id="tspan4176"
|
||||||
|
sodipodi:role="line">FFNORD</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(0.45784451,0,0,0.45784451,-157.33141,596.41996)"
|
||||||
|
id="g3418">
|
||||||
|
<text
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
id="text4212"
|
||||||
|
y="1032.0195"
|
||||||
|
x="478.35355"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:82.68903351px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
dx="0 0 0"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;"
|
||||||
|
y="1032.0195"
|
||||||
|
x="478.35355"
|
||||||
|
id="tspan4214"
|
||||||
|
sodipodi:role="line">CON</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:82.68903351px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#e44081;fill-opacity:1;stroke:#dc0067;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;"
|
||||||
|
x="620.80359"
|
||||||
|
y="1032.0195"
|
||||||
|
id="text4274"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan4276"
|
||||||
|
x="620.80359"
|
||||||
|
y="1032.0195"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Oswald;-inkscape-font-specification:'Oswald Ultra-Bold';fill:#e44081;fill-opacity:1;stroke:#dc0067;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;"
|
||||||
|
dx="5.5911546">2015.2</tspan></text>
|
||||||
|
<polygon
|
||||||
|
style="fill:#ffc640;fill-opacity:1;stroke:#ffb400;stroke-width:0.18134066;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
points="31,44 26,44 30,48 22,48 22,52 30,52 26,56 31,56 37,50 "
|
||||||
|
transform="matrix(4.0051378,-4.0051379,3.796314,3.7963139,132.57633,923.99276)"
|
||||||
|
id="polygon5543" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.2 KiB |
BIN
ffnord-con-2015-2/room/marble.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
ffnord-con-2015-2/room/marble_lol.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
318
ffnord-con-2015-2/room/node.lua
Normal file
|
@ -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
|
BIN
ffnord-con-2015-2/room/place.png
Normal file
After Width: | Height: | Size: 51 KiB |
16
ffnord-con-2015-2/room/progress.frag
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
121
ffnord-con-2015-2/room/schedule.json
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
55
ffnord-con-2015-2/room/service
Executable file
|
@ -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()
|
32
ffnord-con-2015-2/room/trichter.frag
Normal file
|
@ -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;
|
||||||
|
}
|
BIN
ffnord-con-2015-2/room/trichter_grid.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
ffnord-con-2015-2/room/trichter_map.png
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
ffnord-con-2015-2/room/white.png
Normal file
After Width: | Height: | Size: 184 B |