Post sonoptik commit

This commit is contained in:
Sam 2020-10-25 20:18:56 +01:00
commit ab868905b3
109 changed files with 22885 additions and 0 deletions

0
README.md Normal file
View File

70
clitools/README.md Normal file
View File

@ -0,0 +1,70 @@
# Chaining Lasers In Submission Tools for LJ
Alright everybody, ready for some fun? Here comes The Piping And Plumbing Your Way To The Top Show!
You're going to push so many points to this laser it will hog and cry...
BOOM | WIIIIIZ :: PHHHHHRACKRACKRACK ~~ WOOP ~~###~~ WIIT
## The basic loop
```
python3 generators/dummy.py -f 2 | python3 filters/kaleidoscope.py | python3 exports/toRedis.py -v
------------------------------ --------------------- -------------------
\/ \/ \/
Generator Filter Export
```
### 1. The Generator
Use it to produce some points in any manner, orderly or total chaos.
Don't be that boring Sinusoids bugger! Flash Maps of Dooms, Disbitnic sprites, Dismorphic HexaFonts all over the walls!
### 2. The Filter(s)
These babies will modify data on the wire by passing around the points and modifying them in sequence.
Want your Double Heavy Laser Cannons to Bounce Together Like They Been Drinking Jagerbombs For Two Hours? That's the place.
### 3. The Exporter
Now, this IS the most boring part. Send your points to whatever output system. Yawn. Are we there yet?
## Hacking around
Say what!? Why, this is exactly the place for that!
Take a seat and copy paste the "dummy.py" files, they present the basic structure you need to play around.
Just be cautious to use the `debug` method if you're the kind of miss that debugs by outputing data structures (who does not, yah know, sometimes?). Or you'll break the chain.
### Generators
They must send list of points to standard out. Don't forget the "flush" argument, or the piping will be breaking, ain't no Mario lazering.
* dummy.py : sends always the same list of points. The Monomaniac.
* @todo : read texts from redis and others
### Filters
These do listen and read on STDIN and do the same print'n'flush on STDOUT.
* kaleidoscope.py : mirrors the points based on a pivot
* @todo : fourier analysis and other realtime reaction
### Export
Read from STDIN and send to redis mostly
* toRedis.py : provide a key, server IP, etc.
### Common parameters
Every command can be called with a `-h/--help` flag to get some help
Every command has a `-v/--verbose` flag to send debug info to STDERR.
Generators have a `-f/--fps` param for FPS, to be fast but not so furious on your machine
Filters and Exports are their own beasts

Binary file not shown.

11
clitools/_run.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
killexit(){
pkill -9 -s $$
}
trap killexit SIGTERM SIGINT SIGKILL SIGSTOP
bash -c "$@"
killbill(){ pkill -9 -s $(awk '{print $6}' /proc/$1/stat) }

BIN
clitools/exports/.DS_Store vendored Normal file

Binary file not shown.

46
clitools/exports/toNull.py Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
The exporter that drops all traffic !
v0.1.0
A basic exporter
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import sys
import os
import argparse
import redis
import time
argsparser = argparse.ArgumentParser(description="Null exporter LJ")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
verbose=args.verbose
name = "exports::toNull"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
try:
while True:
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
debug(name,"dumping: "+line)
except EOFError:
debug("break")# no more information

View File

@ -0,0 +1,63 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
redis exporter
v0.1.0
A basic exporter
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import sys
import os
import argparse
import redis
import time
argsparser = argparse.ArgumentParser(description="Redis exporter LJ")
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
ip = args.ip
port = args.port
key = args.key
verbose=args.verbose
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
r=redis.StrictRedis(host=ip, port=port, db=0)
try:
while True:
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
continue
line = line.rstrip('\n')
line=line[1:-1]
line = line.replace("[",'(')
line = line.replace("]",')')
line = "[{}]".format(line)
if line == "[]":
line="[(400.0,400.0,0),(400.0,400.0,0),(400.0,400.0,0),(400.0,400.0,0)]"
continue
if r.set(key,line)==True:
debug("exports::redis set("+str(key)+") to "+line)
except EOFError:
debug("break")# no more information

84
clitools/exports/toUDP.py Normal file
View File

@ -0,0 +1,84 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
toUDP
v0.1.0
A basic exporter
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import sys
import os
import argparse
import time
import socket
import ast
argsparser = argparse.ArgumentParser(description="toUDP v0.1b help mode")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str)
argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)",default="9003",type=str)
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = int(args.port)
verbose = args.verbose
name = "exports::toUDP"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def ClientStart(ip, port):
global sockclient
sockclient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
def ClientSend(msgFromClient):
bytesToSend = str.encode(str(msgFromClient))
serverAddressPort = (ip, port)
bufferSize = 1024
# Send to server using created UDP socket
sockclient.sendto(bytesToSend, serverAddressPort)
'''
# If reply :
msgFromServer = sockclient.recvfrom(bufferSize)
msg = "Message from Server {}".format(msgFromServer[0])
print(msg)
'''
try:
ClientStart(ip, port)
while True:
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
#pointsList = ast.literal_eval(line)
debug(name,": "+line)
ClientSend(line)
except EOFError:
debug("break")# no more information

115
clitools/exports/toWS.py Normal file
View File

@ -0,0 +1,115 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
toWS
exporter to LJ via WebSocket
v0.1b
'''
from __future__ import print_function
import websocket
import time
import argparse
import traceback
import sys
try:
import thread
except ImportError:
import _thread as thread
print("")
print("toWS v0.1b")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="toWS v0.1b help mode")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-s","--server",help="WS server IP (127.0.0.1 by default)", type=str)
argsparser.add_argument("-p","--port",help="WS port to bind to (9001 by default)", type=str)
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
args = argsparser.parse_args()
key = args.key
verbose=args.verbose
name = "exports::toWS"
# Server name
if args.server:
serverIP = args.server
else:
serverIP = "127.0.0.1"
# ORCA destination device
if args.port:
wsPORT = args.port
else:
wsPORT = 9001
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def GetTime():
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
def on_message(ws, message):
debug(message)
def on_error(ws, error):
debug(error)
def on_close(ws):
debug("### closed ###")
def on_open(ws):
def run(*args):
try:
while True:
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
line=line[1:-1]
line = line.replace("[",'(')
line = line.replace("]",')')
#debug(line)
line = "[{}]".format(line)
ws.send(str(key)+' "'+line+'"')
debug("exports::ws "+str(key)+" "+line)
except EOFError:
debug("break")# no more information
finally:
ws.close()
print("sendWS terminating...")
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()

157
clitools/exports/tonano.py Normal file
View File

@ -0,0 +1,157 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
tonano
exporter to LJ nano
v0.1b
'''
from __future__ import print_function
import websocket
import time
import argparse
import traceback
import sys
import random
from websocket_server import WebsocketServer
from socket import *
try:
import thread
except ImportError:
import _thread as thread
name = "exports::tonano"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="tonano v0.1b help mode")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-s","--server",help="WS server IP (127.0.0.1 by default)", type=str)
argsparser.add_argument("-p","--port",help="WS port to bind to (9001 by default)", type=str)
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
args = argsparser.parse_args()
key = args.key
if args.verbose:
verbose = True
else:
verbose = False
if args.server:
serverIP = args.server
else:
serverIP = "127.0.0.1"
if args.port:
wsPORT = args.port
else:
wsPORT = 9001
debug("")
debug("tonano v0.1b")
points0 = "[(150.0, 230.0, 65280), (170.0, 170.0, 65280), (230.0, 170.0, 65280), (210.0, 230.0, 65280), (150.0, 230.0, 65280)]"
points1 = "[(180.0, 230.0, 65280), (200.0, 170.0, 65280), (180.0, 230.0, 65280)]"
points2 = "[(170.0, 190.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (230.0, 200.0, 65280), (170.0, 230.0, 65280), (230.0, 230.0, 65280)]"
points3 = "[(170.0, 170.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (200.0, 200.0, 65280), (230.0, 210.0, 65280), (200.0, 230.0, 65280), (170.0, 230.0, 65280)]"
points = [points0, points1, points2, points3]
LaserNumber = 1
SceneNumber = 0
Laser = 0
def sendbroadcast():
debug("Sending broadcast")
cs = socket(AF_INET, SOCK_DGRAM)
cs.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
cs.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
cs.sendto("LJ tonano 0.1".encode(), ("255.255.255.255", 54545))
#
# CLI websocket client -> WS server (nanoLJ) -> webpage
#
def on_message(ws, message):
if random.randint(0,100)>95:
sendbroadcast()
#debug("CLI WS client received and dropped "+message)
def on_error(ws, error):
debug("CLI WS client got error :"+error)
def on_close(ws):
debug("### CLI WS client WS closed ###")
def on_open(ws):
def run(*args):
try:
while True:
line = sys.stdin.readline()
if line == "":
time.sleep(0.005)
#debug("CLI string", line)
line = line.rstrip('\n')
line=line[1:-1]
line = line.replace("[",'(')
line = line.replace("]",')')
#debug(line)
line = "[{}]".format(line)
debug("CLI proccess sending : /simul" +" "+ line)
#sendWSall("/simul" +" "+ str(points[laserid].decode('ascii')))
ws.send("/simul "+line)
#debug("exports::tosimuCLIent "+str(key)+" "+line)
except EOFError:
debug("tonano break")# no more information
finally:
ws.close()
debug("tonano WS terminating...")
thread.start_new_thread(run, ())
def handle_timeout(self):
self.timed_out = True
#
# Launch WS CLI client
#
if __name__ == "__main__":
try:
# CLI Websocket client
debug("Launching tosimu CLI websocket client...")
#websocket.enableTrace(True)
websocket.enableTrace(False)
ws = websocket.WebSocketApp("ws://"+str(serverIP)+":"+str(wsPORT),
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
except Exception:
debug("tonano Exception")
traceback.print_exc()

View File

@ -0,0 +1,388 @@
# Author: Johan Hanssen Seferidis
# License: MIT
'''
Custom version
with clients_list()
For 2 clients :
[{'id': 1, 'handler': <websocket_server.WebSocketHandler object at 0x114d35880>, 'address': ('127.0.0.1', 62718)}, {'id': 2, 'handler': <websocket_server.WebSocketHandler object at 0x114d35d60>, 'address': ('127.0.0.1', 62720)}]
def client_list():
clients = wserver.clients()
for client in clients:
print(client['id'])
'''
import sys
import struct
from base64 import b64encode
from hashlib import sha1
import logging
from socket import error as SocketError
import errno
if sys.version_info[0] < 3:
from SocketServer import ThreadingMixIn, TCPServer, StreamRequestHandler
else:
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
logger = logging.getLogger(__name__)
logging.basicConfig()
'''
+-+-+-+-+-------+-+-------------+-------------------------------+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| Payload Data continued ... |
+---------------------------------------------------------------+
'''
FIN = 0x80
OPCODE = 0x0f
MASKED = 0x80
PAYLOAD_LEN = 0x7f
PAYLOAD_LEN_EXT16 = 0x7e
PAYLOAD_LEN_EXT64 = 0x7f
OPCODE_CONTINUATION = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE_CONN = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xA
# -------------------------------- API ---------------------------------
class API():
def run_forever(self):
try:
logger.info("Listening on port %d for clients.." % self.port)
self.serve_forever()
except KeyboardInterrupt:
self.server_close()
logger.info("Server terminated.")
except Exception as e:
logger.error(str(e), exc_info=True)
exit(1)
def new_client(self, client, server):
pass
def client_left(self, client, server):
pass
def message_received(self, client, server, message):
pass
def set_fn_new_client(self, fn):
self.new_client = fn
def set_fn_client_left(self, fn):
self.client_left = fn
def set_fn_message_received(self, fn):
self.message_received = fn
def send_message(self, client, msg):
self._unicast_(client, msg)
def send_message_to_all(self, msg):
self._multicast_(msg)
def clients_list(self):
return self.clients
# ------------------------- Implementation -----------------------------
class WebsocketServer(ThreadingMixIn, TCPServer, API):
"""
A websocket server waiting for clients to connect.
Args:
port(int): Port to bind to
host(str): Hostname or IP to listen for connections. By default 127.0.0.1
is being used. To accept connections from any client, you should use
0.0.0.0.
loglevel: Logging level from logging module to use for logging. By default
warnings and errors are being logged.
Properties:
clients(list): A list of connected clients. A client is a dictionary
like below.
{
'id' : id,
'handler' : handler,
'address' : (addr, port)
}
"""
allow_reuse_address = True
daemon_threads = True # comment to keep threads alive until finished
clients = []
id_counter = 0
def __init__(self, port, host='127.0.0.1', loglevel=logging.WARNING):
logger.setLevel(loglevel)
TCPServer.__init__(self, (host, port), WebSocketHandler)
self.port = self.socket.getsockname()[1]
def _message_received_(self, handler, msg):
self.message_received(self.handler_to_client(handler), self, msg)
def _ping_received_(self, handler, msg):
handler.send_pong(msg)
def _pong_received_(self, handler, msg):
pass
def _new_client_(self, handler):
self.id_counter += 1
client = {
'id': self.id_counter,
'handler': handler,
'address': handler.client_address
}
self.clients.append(client)
self.new_client(client, self)
def _client_left_(self, handler):
client = self.handler_to_client(handler)
self.client_left(client, self)
if client in self.clients:
self.clients.remove(client)
def _unicast_(self, to_client, msg):
to_client['handler'].send_message(msg)
def _multicast_(self, msg):
for client in self.clients:
self._unicast_(client, msg)
def handler_to_client(self, handler):
for client in self.clients:
if client['handler'] == handler:
return client
class WebSocketHandler(StreamRequestHandler):
def __init__(self, socket, addr, server):
self.server = server
StreamRequestHandler.__init__(self, socket, addr, server)
def setup(self):
StreamRequestHandler.setup(self)
self.keep_alive = True
self.handshake_done = False
self.valid_client = False
def handle(self):
while self.keep_alive:
if not self.handshake_done:
self.handshake()
elif self.valid_client:
self.read_next_message()
def read_bytes(self, num):
# python3 gives ordinal of byte directly
bytes = self.rfile.read(num)
if sys.version_info[0] < 3:
return map(ord, bytes)
else:
return bytes
def read_next_message(self):
try:
b1, b2 = self.read_bytes(2)
except SocketError as e: # to be replaced with ConnectionResetError for py3
if e.errno == errno.ECONNRESET:
logger.info("Client closed connection.")
print("Error: {}".format(e))
self.keep_alive = 0
return
b1, b2 = 0, 0
except ValueError as e:
b1, b2 = 0, 0
fin = b1 & FIN
opcode = b1 & OPCODE
masked = b2 & MASKED
payload_length = b2 & PAYLOAD_LEN
if opcode == OPCODE_CLOSE_CONN:
logger.info("Client asked to close connection.")
self.keep_alive = 0
return
if not masked:
logger.warn("Client must always be masked.")
self.keep_alive = 0
return
if opcode == OPCODE_CONTINUATION:
logger.warn("Continuation frames are not supported.")
return
elif opcode == OPCODE_BINARY:
logger.warn("Binary frames are not supported.")
return
elif opcode == OPCODE_TEXT:
opcode_handler = self.server._message_received_
elif opcode == OPCODE_PING:
opcode_handler = self.server._ping_received_
elif opcode == OPCODE_PONG:
opcode_handler = self.server._pong_received_
else:
logger.warn("Unknown opcode %#x." % opcode)
self.keep_alive = 0
return
if payload_length == 126:
payload_length = struct.unpack(">H", self.rfile.read(2))[0]
elif payload_length == 127:
payload_length = struct.unpack(">Q", self.rfile.read(8))[0]
masks = self.read_bytes(4)
message_bytes = bytearray()
for message_byte in self.read_bytes(payload_length):
message_byte ^= masks[len(message_bytes) % 4]
message_bytes.append(message_byte)
opcode_handler(self, message_bytes.decode('utf8'))
def send_message(self, message):
self.send_text(message)
def send_pong(self, message):
self.send_text(message, OPCODE_PONG)
def send_text(self, message, opcode=OPCODE_TEXT):
"""
Important: Fragmented(=continuation) messages are not supported since
their usage cases are limited - when we don't know the payload length.
"""
# Validate message
if isinstance(message, bytes):
message = try_decode_UTF8(message) # this is slower but ensures we have UTF-8
if not message:
logger.warning("Can\'t send message, message is not valid UTF-8")
return False
elif sys.version_info < (3,0) and (isinstance(message, str) or isinstance(message, unicode)):
pass
elif isinstance(message, str):
pass
else:
logger.warning('Can\'t send message, message has to be a string or bytes. Given type is %s' % type(message))
return False
header = bytearray()
payload = encode_to_UTF8(message)
payload_length = len(payload)
# Normal payload
if payload_length <= 125:
header.append(FIN | opcode)
header.append(payload_length)
# Extended payload
elif payload_length >= 126 and payload_length <= 65535:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT16)
header.extend(struct.pack(">H", payload_length))
# Huge extended payload
elif payload_length < 18446744073709551616:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT64)
header.extend(struct.pack(">Q", payload_length))
else:
raise Exception("Message is too big. Consider breaking it into chunks.")
return
self.request.send(header + payload)
def read_http_headers(self):
headers = {}
# first line should be HTTP GET
http_get = self.rfile.readline().decode().strip()
assert http_get.upper().startswith('GET')
# remaining should be headers
while True:
header = self.rfile.readline().decode().strip()
if not header:
break
head, value = header.split(':', 1)
headers[head.lower().strip()] = value.strip()
return headers
def handshake(self):
headers = self.read_http_headers()
try:
assert headers['upgrade'].lower() == 'websocket'
except AssertionError:
self.keep_alive = False
return
try:
key = headers['sec-websocket-key']
except KeyError:
logger.warning("Client tried to connect but was missing a key")
self.keep_alive = False
return
response = self.make_handshake_response(key)
self.handshake_done = self.request.send(response.encode())
self.valid_client = True
self.server._new_client_(self)
@classmethod
def make_handshake_response(cls, key):
return \
'HTTP/1.1 101 Switching Protocols\r\n'\
'Upgrade: websocket\r\n' \
'Connection: Upgrade\r\n' \
'Sec-WebSocket-Accept: %s\r\n' \
'\r\n' % cls.calculate_response_key(key)
@classmethod
def calculate_response_key(cls, key):
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
hash = sha1(key.encode() + GUID.encode())
response_key = b64encode(hash.digest()).strip()
return response_key.decode('ASCII')
def finish(self):
self.server._client_left_(self)
def encode_to_UTF8(data):
try:
return data.encode('UTF-8')
except UnicodeEncodeError as e:
logger.error("Could not encode data to UTF-8 -- %s" % e)
return False
except Exception as e:
raise(e)
return False
def try_decode_UTF8(data):
try:
return data.decode('utf-8')
except UnicodeDecodeError:
return False
except Exception as e:
raise(e)

BIN
clitools/filters/.DS_Store vendored Normal file

Binary file not shown.

200
clitools/filters/anaglyph.py Executable file
View File

@ -0,0 +1,200 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
anaglyph
v0.1.0
Attempts to create a valid 3D-glasses structure
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import argparse
import ast
import math
import os
import random
import sys
import time
name = "filters::cycle"
maxDist = 300
argsparser = argparse.ArgumentParser(description="Redis exporter LJ")
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int)
argsparser.add_argument("-m","--min",help="Minimal displacement (default:2) ",default=1,type=int)
argsparser.add_argument("-M","--max",help="Maximal displacement (default:20) ",default=5,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
fps = args.fps
minVal = args.min
maxVal = args.max
centerX = args.centerX
centerY = args.centerY
verbose = args.verbose
optimal_looptime = 1 / fps
name = "filters::anaglyph"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def rgb2int(rgb):
#debug(name,"::rgb2int rbg:{}".format(rgb))
return int('0x%02x%02x%02x' % tuple(rgb),0)
def isValidColor( color, intensityColThreshold ):
if color[0] + color[1] + color[2] > intensityColThreshold:
return True
return False
# These are paper colors
red = (41,24,24)
white = (95,95,95)
blue = (0,41,64)
red = (127,0,0)
blue = (0,128,128)
white = (128,128,128)
def anaglyph( pl ):
debug(name,'--------------- new loop ------------------')
# We will send one list after the other to optimize color change
blueList = list()
redList = list()
whiteList = list()
out = []
out1 = []
out2 = []
out3 = []
# The anaglyphic effect will be optained by :
# * having close objects appear as white
# * having distant objects appear as blue + red
# * having in between objects appear as distanceDecreased(white) + blue + red
for i, point in enumerate(pl):
ref_x = point[0]-centerX
ref_y = point[1]-centerY
ref_color = point[2]
angle = math.atan2( ref_x , ref_y )
dist = ref_y / math.cos(angle)
white_rvb = (0,0,0)
blue_rvb = (0,0,0)
red_rvb = (0,0,0)
# Calculate the point's spread factor (0.0 to 1.0)
# The spread is high if the point is close to center
"""
dist = 0 : spread = 1.0
dist = maxDist spread = 0.0
"""
if dist == 0:
spread = 1.0
else :
spread =( maxDist - dist ) / maxDist
if spread < 0.0:
spread = 0.0
#debug(name,"dist:{} spread:{}".format(dist,spread))
# White color is high if spread is low, i.e. point away from center
"""
spread = 1.0 : white_c = 0.0
spread = 0.0 : whice_c = 1.0
"""
if point[2] == 0:
white_color = 0
else:
white_factor = 1.0 - math.pow(spread,0.5)
white_rvb = tuple(map( lambda a: int(white_factor* a), white))
white_color = rgb2int( white_rvb)
#debug(name,"spread:{}\t white_rvb:{}\t white_color:{}".format(spread, white_rvb, white_color))
# Blue and Red colors are high if spread is high, i.e. close to center
"""
spread = 1.0 : red_c = 1.0
spread = 0.0 : red_c = 0.0
"""
color_factor = math.pow(spread,1)
if point[2] == 0:
blue_color = 0
red_color = 0
else:
blue_rvb = tuple(map( lambda a: int(color_factor * a), blue))
blue_color = rgb2int( blue_rvb)
red_rvb = tuple(map( lambda a: int(color_factor * a), red))
red_color = rgb2int( red_rvb)
#debug(name,"color_factor:{}\t\t blue_color:{}\t\t red_color:{}".format(color_factor,blue_color,red_color))
# Blue-to-Red spatial spread is high when spread is high, i.e. point close to center
"""
spread = 1.0 : spatial_spread = maxVal
spread = 0.0 : spatial_spread = minVal
"""
spatial_spread = minVal + spread * (maxVal - minVal)
#debug(name,"spatial_spread:{}".format(spatial_spread))
red_x = int(point[0] + spatial_spread)
blue_x = int(point[0] - spatial_spread )
red_y = int(point[1] )
blue_y = int(point[1])
white_point = [point[0], point[1], white_color]
blue_point = [blue_x,blue_y,blue_color]
red_point = [red_x,red_y,red_color]
#debug(name,"white[x,y,c]:{}".format(white_point))
#debug(name,"blue[x,y,c]:{}".format(blue_point))
#debug(name,"red[x,y,c]:{}".format(red_point))
# Do not append "black lines" i.e. a color where each composent is below X
# if isValidColor(white_rvb, 150):
# out1.append(white_point)
# if isValidColor(blue_rvb, 50):
# out2.append(blue_point)
# if isValidColor(red_rvb, 30):
# out3.append(red_point)
out1.append(white_point)
out2.append(blue_point)
out3.append(red_point)
#debug(name,"source pl:{}".format(pl))
debug(name,"whiteList:{}".format(out1))
debug(name,"blueList:{}".format(out2))
debug(name,"redList:{}".format(out3))
return out1 + out3 + out2
#return out1 + out2 + out3
try:
while True:
start = time.time()
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
pointsList = ast.literal_eval(line)
# Do the filter
result = anaglyph( pointsList )
print( result, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

108
clitools/filters/colorcycle.py Executable file
View File

@ -0,0 +1,108 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
colorcycle
v0.1.0
A simple effect : cycle colors
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import sys
import ast
import os
import argparse
import random
import time
name = "filters::cycle"
argsparser = argparse.ArgumentParser(description="Redis exporter LJ")
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=300,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=300,type=int)
argsparser.add_argument("-m","--min",help="Lowest value in the range 0-255",default=10,type=int)
argsparser.add_argument("-M","--max",help="Highest value in the range 0-255",default=255,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
fps = args.fps
minVal = args.min
maxVal = args.max
centerX = args.centerX
centerY = args.centerY
verbose = args.verbose
optimal_looptime = 1 / fps
UP = 5
DOWN = -5
currentColor = [0,0,0]
composant = 0
currentDirection = UP
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
def cycleColor( pl ):
global composant
global currentDirection
# debug(name,"pl:{}".format(pl))
value = currentColor[composant]
if currentDirection == UP:
target = maxVal
else:
target = minVal
value += currentDirection
currentColor[composant] = value
debug(name,"currentColor:{}".format(currentColor))
for i in range( 0, len(pl)):
if pl[i][2] != 0:
pl[i][2] = rgb2int( currentColor)
# change the composant if target reached
if value <= target and currentDirection == DOWN or value >= target and currentDirection == UP :
composant = random.randint( 0,2)
value = currentColor[composant]
if value == 0 :
currentDirection = UP
else:
currentDirection = DOWN
#debug( "pl:{}".format(pl))
return pl
try:
while True:
start = time.time()
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
pointsList = ast.literal_eval(line)
# Do the filter
result = cycleColor( pointsList )
print( result, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

174
clitools/filters/kaleidoscope.py Executable file
View File

@ -0,0 +1,174 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
kaleidoscop
v0.1.0
A simple effect : mirror a quadrant of the input
LICENCE : CC
by Sam Neurohack
'''
from __future__ import print_function
import sys
import ast
import os
import argparse
ljpath = r'%s' % os.getcwd().replace('\\','/')
sys.path.append(ljpath +'/../libs/')
sys.path.append(ljpath +'/libs/')
import time
name = "filters::kaleidoscope"
argsparser = argparse.ArgumentParser(description="Redis exporter LJ")
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
fps = args.fps
centerX = args.centerX
centerY = args.centerY
verbose = args.verbose
optimal_looptime = 1 / fps
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def kaleidoscope( pl ):
# Stage 1: Crop points in single quadrant
quad1 = []
# Iterate trough the segments
for i in range( 0, len(pl) ):
#debug(name+" point #", i)
currentpoint = cp = pl[i]
cx,cy,cc = [cp[0],cp[1],cp[2]]
# Exception: escape early if last point
if i == len(pl) - 1:
if cx >= centerX and cy >= centerY :
quad1.append( currentpoint )
break
# Search for the couple of points
nextpoint = pl[i+1]
nx,ny,nc = [nextpoint[0],nextpoint[1],nextpoint[2]]
rect=[[cx,cy],[cx,ny],[nx,ny],[nx,cy]]
right = wrong = 0
#debug(name+" rect: ", rect,"curr",currentpoint,"next",nextpoint )
# Enumerate the points in rectangle to see
# how many right / wrong there are
# either to add or skip early
for iterator, p in enumerate(rect):
if p[0] >= centerX and p[1] >= centerY:
right += 1
else:
#if p[0] <= centerX and p[1] <= centerY:
wrong += 1
# If all rectangle points are in the right quadrant, Add and Skip
if right == 4:
quad1.append(pl[i])
#debug(name+" found valid point", pl[i])
continue
# If all rectangle points in wrong quadrant, Skip
if wrong == 4:
#debug(name+" found bad point", pl[i])
continue
# Find the (x,y) intersections
#
#debug(name+" Looking for crossing point between ("+str(cx)+","+str(cy)+") and ("+str(nx)+","+str(ny)+")")
delta=[ nx - cx, ny - cy ]
#debug(name+" delta:",delta)
crossX = None
crossY = None
absnewX = 0
absnewY = 0
# If one point has negative x, search y axis crossing
if cx < centerX or nx < centerX:
if delta[0] == 0 :
delta[0] = 0.0000001
v=[ delta[0]/abs(delta[0]), delta[1]/abs(delta[0]) ]
absnewX = abs( centerX - cx )
#print("on y axis, v=",str(v)," and absnewX=",str(absnewX))
crossX = [( absnewX*v[0] + cx ),( absnewX*v[1]+cy ), nc]
# If one point has negative y, search x axis crossing
if cy < centerY or ny < centerY:
if delta[1] == 0 :
delta[1] = 0.0000001
v=[ delta[0]/abs(delta[1]), delta[1]/abs(delta[1])]
absnewY = abs( centerY - cy )
#print("on x axis, v=",str(v)," and absnewY=",str(absnewY))
crossY = [( absnewY*v[0] + cy ),( absnewY*v[1]+cy ), nc]
# Inject in order
# If current point is the quadrant, add it
if cx >= centerX and cy >= centerY :
quad1.append( currentpoint )
# If absnewX smaller, it is closest to currentPoint
if absnewX < absnewY:
if None != crossX : quad1.append( crossX )
if None != crossY : quad1.append( crossY )
else :
if None != crossY : quad1.append( crossY )
if None != crossX : quad1.append( crossX )
# Add a black point at the end
#lastQuad1Point = quad1[-1]
#quad1.append( [lastQuad1Point[0],lastQuad1Point[1],0] )
## Stage 2 : Mirror points
#
quad2 = []
# quad2 = vertical symetric of quad1
for iterator in range( len(quad1) -1 , -1, -1):
point = quad1[iterator]
quad2.append([ point[0], 2*centerY - point[1], point[2] ])
# quad3 is the merge of 1 and 2
quad3 = quad1 + quad2
# quad4 is the horizontal symetric of quad3
quad4 = []
for iterator in range( len(quad3) -1, -1, -1):
point = quad3[iterator]
quad4.append([ 2*centerX - point[0], point[1], point[2] ])
#debug(name+" quad1:",quad1)
#debug(name+" quad2:", quad2 )
#debug(name+" quad3:", quad3 )
#debug(name+" quad4:", quad4 )
return quad3+quad4
try:
while True:
start = time.time()
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
pointsList = ast.literal_eval(line)
# Do the filter
result = kaleidoscope( pointsList )
print( result, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

300
clitools/filters/redilysis.py Executable file
View File

@ -0,0 +1,300 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
redilysis
v0.1.0
A complex effect that depends on redis keys for audio analysis
see https://git.interhacker.space/teamlase/redilysis for more informations
about the redilysis project
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import argparse
import ast
import os
import math
import random
import redis
import sys
import time
name = "filters::redilysis"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def msNow():
return time.time()
# The list of available modes => redis keys each requires to run
oModeList = {
"rms_noise": ["rms"],
"rms_size": ["rms"],
"bpm_size": ["bpm"],
"bpm_detect_size": ["bpm","bpm_delay","bpm_sample_interval","beats"]
}
CHAOS = 1
REDISLATENCY = 30
REDIS_FREQ = 300
# General Args
argsparser = argparse.ArgumentParser(description="Redilysis filter")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
# Redis Args
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-s","--redis-freq",help="Query Redis every x (in milliseconds). Default:{}".format(REDIS_FREQ),default=REDIS_FREQ,type=int)
# General args
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
# Modes And Common Modes Parameters
argsparser.add_argument("-l","--redisLatency",help="Latency in ms to substract. Default:{}".format(REDISLATENCY),default=REDISLATENCY,type=float)
argsparser.add_argument("-m","--modelist",required=True,help="Comma separated list of modes to use from: {}".format("i, ".join(oModeList.keys())),type=str)
argsparser.add_argument("--chaos",help="How much disorder to bring. High value = More chaos. Default {}".format(CHAOS), default=CHAOS, type=str)
args = argsparser.parse_args()
ip = args.ip
port = args.port
redisFreq = args.redis_freq / 1000
verbose = args.verbose
fps = args.fps
centerX = args.centerX
centerY = args.centerY
redisLatency = args.redisLatency
chaos = float(args.chaos)
optimal_looptime = 1 / fps
modeList = args.modelist.split(",")
redisKeys = []
for mode in modeList:
if not mode in oModeList:
print("Mode '{}' is invalid. Exiting.".format(mode))
sys.exit(2)
redisKeys += oModeList[mode]
redisKeys = list(set(redisKeys))
debug(name,"Redis Keys:{}".format(redisKeys))
redisData = {}
redisLastHit = msNow() - 99999
r = redis.Redis(
host=ip,
port=port)
# Records the last bpm
tsLastBeat = time.time()
def gauss(x, mu, sigma):
return( math.exp(-math.pow((x-mu),2)/(2*math.pow(sigma,2))/math.sqrt(2*math.pi*math.pow(sigma,2))))
previousPTTL = 0
tsNextBeatsList = []
def bpmDetect( ):
"""
An helper to compute the next beat time in milliseconds
Returns True if the cache was updated
"""
global tsNextBeatsList
global previousPTTL
global redisLastHit
global redisLatency
# Get the redis PTTL value for bpm
PTTL = redisData["bpm_pttl"]
# Skip early if PTTL < 0
if PTTL < 0 :
debug(name,"bpmDetect skip detection : PTTL expired for 'bpm' key")
return False
# Skip early if the record hasn't been rewritten
if PTTL <= previousPTTL :
previousPTTL = PTTL
#debug(name,"bpmDetect skip detection : {} <= {}".format(PTTL, previousPTTL))
return False
debug(name,"bpmDetect running detection : {} > {}".format(PTTL, previousPTTL))
previousPTTL = PTTL
# Skip early if beat list is empty
beatsList = ast.literal_eval(redisData["beats"])
tsNextBeatsList = []
if( len(beatsList) == 0 ):
return True
# Read from redis
bpm = float(redisData["bpm"])
msBpmDelay = float(redisData["bpm_delay"])
samplingInterval = float(redisData["bpm_sample_interval"])
# Calculate some interpolations
lastBeatTiming = float(beatsList[len(beatsList) - 1])
msPTTLDelta = 2 * samplingInterval - float(PTTL)
sPerBeat = 60 / bpm
lastBeatDelay = msBpmDelay - lastBeatTiming*1000 + msPTTLDelta
countBeatsPast = math.floor( (lastBeatDelay / 1000) / sPerBeat)
#debug(name,"bpmDetect lastBeatTiming:{}\tmsPTTLDelta:{}\tsPerBeat:{}".format(lastBeatTiming,msPTTLDelta,sPerBeat))
#debug(name,"lastBeatDelay:{}\t countBeatsPast:{}".format(lastBeatDelay, countBeatsPast))
for i in range( countBeatsPast, 1000):
beatTime = i * sPerBeat - lastBeatTiming
if beatTime < 0:
continue
if beatTime * 1000 > 2 * samplingInterval :
break
#debug(name, "bpmDetect beat add beatTime:{} redisLastHit:{}".format(beatTime, redisLastHit))
tsNextBeatsList.append( redisLastHit + beatTime - redisLatency/1000)
debug(name, "bpmDetect new tsNextBeatsList:{}".format(tsNextBeatsList))
return True
def bpm_detect_size( pl ):
bpmDetect()
# Find the next beat in the list
tsNextBeat = 0
now = time.time()
msNearestBeat = None
msRelativeNextBTList = list(map( lambda a: abs(now - a) * 1000, tsNextBeatsList))
msToBeat = min( msRelativeNextBTList)
#debug(name,"bpm_detect_size msRelativeNextBTList:{} msToBeat:{}".format(msRelativeNextBTList,msToBeat))
# Calculate the intensity based on bpm coming/leaving
# The curb is a gaussian
mu = 15
intensity = gauss( msToBeat, 0 , mu)
#debug(name,"bpm_size","mu:{}\t msToBeat:{}\tintensity:{}".format(mu, msToBeat, intensity))
if msToBeat < 20:
debug(name,"bpm_detect_size kick:{}".format(msToBeat))
pass
for i, point in enumerate(pl):
ref_x = point[0]-centerX
ref_y = point[1]-centerY
#debug(name,"In new ref x:{} y:{}".format(point[0]-centerX,point[1]-centerY))
angle=math.atan2( point[0] - centerX , point[1] - centerY )
l = ref_y / math.cos(angle)
new_l = l * intensity
#debug(name,"bpm_size","angle:{} l:{} new_l:{}".format(angle,l,new_l))
new_x = math.sin(angle) * new_l + centerX
new_y = math.cos(angle) * new_l + centerY
#debug(name,"x,y:({},{}) x',y':({},{})".format(point[0],point[1],new_x,new_y))
pl[i][0] = new_x
pl[i][1] = new_y
#debug( name,"bpm_detect_size output:{}".format(pl))
return( pl );
def bpm_size( pl ):
global tsLastBeat
bpm = float(redisData["bpm"])
# msseconds ber beat
msPerBeat = int(60 / bpm * 1000)
# Calculate the intensity based on bpm coming/leaving
# The curb is a gaussian
mu = math.sqrt(msPerBeat)
msTimeToLastBeat = (time.time() - tsLastBeat) * 1000
msTimeToNextBeat = (msPerBeat - msTimeToLastBeat)
intensity = gauss( msTimeToNextBeat, 0 , mu)
debug(name,"bpm_size","msPerBeat:{}\tmu:{}".format(msPerBeat, mu))
debug(name,"bpm_size","msTimeToLastBeat:{}\tmsTimeToNextBeat:{}\tintensity:{}".format(msTimeToLastBeat, msTimeToNextBeat, intensity))
if msTimeToNextBeat <= 0 :
tsLastBeat = time.time()
for i, point in enumerate(pl):
ref_x = point[0]-centerX
ref_y = point[1]-centerY
#debug(name,"In new ref x:{} y:{}".format(point[0]-centerX,point[1]-centerY))
angle=math.atan2( point[0] - centerX , point[1] - centerY )
l = ref_y / math.cos(angle)
new_l = l * intensity
#debug(name,"bpm_size","angle:{} l:{} new_l:{}".format(angle,l,new_l))
new_x = math.sin(angle) * new_l + centerX
new_y = math.cos(angle) * new_l + centerY
#debug(name,"x,y:({},{}) x',y':({},{})".format(point[0],point[1],new_x,new_y))
pl[i][0] = new_x
pl[i][1] = new_y
#debug( name,"bpm_noise output:{}".format(pl))
return pl
def rms_size( pl ):
rms = float(redisData["rms"])
for i, point in enumerate(pl):
ref_x = point[0]-centerX
ref_y = point[1]-centerY
debug(name,"In new ref x:{} y:{}".format(point[0]-centerX,point[1]-centerY))
angle=math.atan2( point[0] - centerX , point[1] - centerY )
l = ref_y / math.cos(angle)
debug(name,"angle:{} l:{}".format(angle,l))
new_l = l + rms * chaos
new_x = math.sin(angle) * new_l + centerX
new_y = math.cos(angle) * new_l + centerY
debug(name,"x,y:({},{}) x',y':({},{})".format(point[0],point[1],new_x,new_y))
pl[i][0] = new_x
pl[i][1] = new_y
#debug( name,"rms_noise output:{}".format(pl))
return pl
def rms_noise( pl ):
rms = float(redisData["rms"])
debug(name, "pl:{}".format(pl))
for i, point in enumerate(pl):
#debug(name,"rms_noise chaos:{} rms:{}".format(chaos, rms))
xRandom = random.uniform(-1,1) * rms * chaos
yRandom = random.uniform(-1,1) * rms * chaos
#debug(name,"rms_noise xRandom:{} yRandom:{}".format(xRandom, yRandom))
pl[i][0] += xRandom
pl[i][1] += yRandom
#debug( name,"rms_noise output:{}".format(pl))
return pl
def refreshRedis():
global redisLastHit
global redisData
# Skip if cache is sufficent
diff = msNow() - redisLastHit
if diff < redisFreq :
#debug(name, "refreshRedis not updating redis, {} < {}".format(diff, redisFreq))
pass
else:
#debug(name, "refreshRedis updating redis, {} > {}".format(diff, redisFreq))
redisLastHit = msNow()
for key in redisKeys:
redisData[key] = r.get(key).decode('ascii')
#debug(name,"refreshRedis key:{} value:{}".format(key,redisData[key]))
# Only update the TTLs
if 'bpm' in redisKeys:
redisData['bpm_pttl'] = r.pttl('bpm')
#debug(name,"refreshRedis key:bpm_ttl value:{}".format(redisData["bpm_pttl"]))
#debug(name,"redisData:{}".format(redisData))
return True
try:
while True:
refreshRedis()
start = time.time()
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
pointsList = ast.literal_eval(line)
# Do the filter
for mode in modeList:
pointsList = locals()[mode](pointsList)
print( pointsList, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

View File

@ -0,0 +1,186 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
redilysis colors
v0.1.0
A complex effect that depends on redis keys for audio analysis
see https://git.interhacker.space/teamlase/redilysis for more informations
about the redilysis project
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import argparse
import ast
import os
import math
import random
import redis
import sys
import time
name = "filters::redilysis_colors"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def msNow():
return time.time()
# The list of available modes => redis keys each requires to run
oModeList = {
}
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
def int2rgb(intcode):
#hexcode = hex(intcode)[2:]
hexcode = '{0:06X}'.format(intcode)
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
#return tuple(map(ord,hexcode[1:].decode('hex')))
CHAOS = 1
REDIS_FREQ = 100
# General Args
argsparser = argparse.ArgumentParser(description="Redilysis filter")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
# Redis Args
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-s","--redis-freq",help="Query Redis every x (in milliseconds). Default:{}".format(REDIS_FREQ),default=REDIS_FREQ,type=int)
# Modes And Common Modes Parameters
#argsparser.add_argument("-m","--modelist",required=False,help="Comma separated list of modes to use from: {}".format("i, ".join(oModeList.keys())),type=str)
argsparser.add_argument("-c","--chaos",help="How much disorder to bring. High value = More chaos. Default {}".format(CHAOS), default=CHAOS, type=float)
args = argsparser.parse_args()
fps = args.fps
ip = args.ip
port = args.port
redisFreq = args.redis_freq / 1000
verbose = args.verbose
chaos = float(args.chaos)
optimal_looptime = 1 / fps
max_width = 800
max_height = 800
redisKeys = ["rms","spectrum_10","spectrum_120"]
debug(name,"Redis Keys:{}".format(redisKeys))
redisData = {}
redisLastHit = msNow() - 99999
r = redis.Redis(
host=ip,
port=port)
def refreshRedis():
global redisData
for key in redisKeys:
try:
redisData[key] = ast.literal_eval(r.get(key).decode('ascii'))
except :
debug("Error when reading redis key '{}".format(key))
def gauss(x, mu, sigma):
return( math.exp(-math.pow((x-mu),2)/(2*math.pow(sigma,2))/math.sqrt(2*math.pi*math.pow(sigma,2))))
spect10Correct = [
6.0,
1.5,
1.0,
1.0,
1.0,
1.0,
1.0,
0.8,
0.6,
0.5,
]
def default( pl ):
global redisData
spect = redisData["spectrum_10"]
debug(name, "spect:{}".format(spect))
new_list = []
# We want to color points that are on left and right when high is strong
# i.e. the farther the distance from spectrum, the higher notes have influence
# power = 0-1
# x = 800 spec[2]= 6.0 spec[7]=0.0 power=0.0
# x = 0 spec[2]= 6.0 spec[7]=0.0 power=0.0
# x = 0 spec[2]= 1.0 spec[7]=0.5 power=1.0
# dist 0 = 1
# 400 - 400 : maxW/2 -x
# 399 = -1 : x - 400
# 401 = 1
# x = 400 spec[2]= 6.0 spec[7]=0.0 power=1.0
# x = 400 spec[2]= 1.0 spec[7]=0.5 power=0.0
for i, point in enumerate(pl):
ocolor = pl[i][2]
if ocolor == 0 :
new_list.append(point)
continue
colorTuple = int2rgb(ocolor)
x = point[0]
dist = abs(x - max_width/2)
key = int(2* dist / max_width * 8)
power = spect[key] / spect10Correct[key] * chaos
color = []
for i in colorTuple:
new_color = int(i * power)
if new_color > 255 :
new_color = 255
if new_color < 0 :
new_color = 0
color.append( new_color )
color = rgb2int(tuple(color))
point[2] = color
new_list.append(point)
#debug(name,"x:{}\t dist:{}\t key:{}\t power:{}\t ocolor:{}\t color:{}".format(point[0], dist, key,power, ocolor, pl[i][2]))
debug( name,"rms_noise output:{}".format(new_list))
return new_list
try:
while True:
refreshRedis()
start = time.time()
line = sys.stdin.readline()
if line == "":
time.sleep(0.01)
line = line.rstrip('\n')
pointsList = ast.literal_eval(line)
# Do the filter
pointsList = default(pointsList)
print( pointsList, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

BIN
clitools/generators/.DS_Store vendored Normal file

Binary file not shown.

2407
clitools/generators/159.gml Executable file

File diff suppressed because it is too large Load Diff

2791
clitools/generators/160.gml Executable file

File diff suppressed because it is too large Load Diff

2873
clitools/generators/OSC3.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

53
clitools/generators/blank.py Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Send only black points
v0.1.0
Use it to test your filters and outputs
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import time
import argparse
import sys
name="generator::dummy"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="dummy generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
shape = [[400,400,0],[400,400,64],[400,400,0]]
while True:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))

BIN
clitools/generators/book2.ild Executable file

Binary file not shown.

54
clitools/generators/brmlab1.svg Executable file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
xml:space="preserve"
height="53"
width="299.96875"
version="1.1"
id="svg2"
inkscape:version="0.48.0 r9654"
sodipodi:docname="brmlab1.svg"
inkscape:export-filename="/home/prusnak/work/scm/laserdisplay/svglaser/templates/template3.svg.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200"><defs
id="defs2988" /><metadata
id="metadata78"><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><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1024"
inkscape:window-height="689"
id="namedview76"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="-2.5851173"
inkscape:cy="-55.600814"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:snap-global="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
id="path20"
style="fill:none;stroke:#000000;stroke-opacity:1"
d="m 137.17375,22.4425 c 0,-1.815 5.24,-4.345 13.73375,-4.345 3.94875,0 7.73,0.59 10.37875,1.62 2.085,0.81125 3.32875,1.83 3.32875,2.725 0,9.0725 0.0188,30.05375 0.0188,30.05375 l 5.87245,-0.004 c 0,-10e-4 -0.0175,-20.97875 -0.0175,-30.05 0,-2.035 -0.9175,-5.805 -7.0725,-8.2 -3.35125,-1.3025 -7.7925,-2.01875 -12.50875,-2.01875 -4.71625,0 -9.15875,0.71625 -12.50875,2.01875 -1.7975,0.7 -3.16375,1.50625 -4.175,2.3625 -1.01,-0.8475 -2.3675,-1.66875 -4.15,-2.3625 -3.34875,-1.3025 -7.79125,-2.01875 -12.50875,-2.01875 -4.715,0 -9.15625,0.71625 -12.50625,2.01875 -6.15625,2.395 -7.075,6.165 -7.075,8.2 0,9.8775 -0.0163,30.04875 -0.0163,30.04875 l 5.87375,0.005 c 0,0 0.0175,-20.17375 0.0175,-30.05375 0,-1.815 5.215,-4.345 13.70625,-4.345 3.94875,0 7.73125,0.59 10.38,1.62 2.08375,0.81125 3.32875,1.83 3.32875,2.725 0,9.0725 0.0175,30.0575 0.0175,30.0575 l 5.87375,-0.005 c 0,0 0.009,-20.98125 0.009,-30.0525 M 93.61125,18.3922 c -1.0075,-1.47125 -2.7675,-2.98 -5.79125,-4.15625 -3.35,-1.3025 -7.79375,-2.02 -12.50875,-2.02 -4.715,0 -9.15875,0.7175 -12.5075,2.02 C 56.64875,16.63 55.73,20.4 55.73,22.435 c 0,9.87875 -0.0175,30.04875 -0.0175,30.04875 l 5.87375,0.005 c 0,0 0.0175,-20.17375 0.0175,-30.05375 0,-1.81375 5.21375,-4.345 13.7075,-4.345 3.9475,0 7.73,0.59 10.37875,1.62125 1.58875,0.6175 2.6875,1.355 3.1225,2.0675 l 4.79875,-3.38625 z m 86.2125,34.1 5.874,0 0,-52.0075 -5.874,0 0,52.0075 z m 24.45,-33.325 c -0.89625,0 -1.91375,1.24375 -2.725,3.32875 -1.03,2.64875 -1.62125,6.43125 -1.62125,10.37875 0,8.4925 2.53125,13.7075 4.34625,13.7075 7.19625,0 24.94,0.0225 31.975,0.0313 l 0,-27.44625 -31.975,0 z m 37.84875,33.32875 -2.94,-0.004 c 0,0 -25.83625,-0.035 -34.90875,-0.035 -2.035,0 -5.80625,-0.91875 -8.2,-7.07375 -1.3025,-3.35125 -2.02,-7.7925 -2.02,-12.50875 0,-4.715 0.7175,-9.1575 2.02,-12.5075 2.39375,-6.155 6.165,-7.075 8.2,-7.075 l 34.9125,0 2.91979,0.006 0.0165,39.198 z m 49.85125,-9.24125 c -0.80875,2.085 -1.82875,3.32875 -2.7225,3.32875 -7.1975,0 -24.94125,0.0225 -31.97625,0.0313 l 0,-27.4475 31.97625,0.001 c 1.81375,0 4.345,5.215 4.345,13.7075 0,3.94625 -0.5925,7.73 -1.6225,10.37875 M 297.45,20.3687 c -2.39375,-6.155 -6.165,-7.075 -8.19875,-7.075 l -31.97625,-0.002 0,-12.80625 -5.87375,0 0,52.0125 2.94125,-0.004 c 0,0 25.83625,-0.0362 34.90875,-0.0362 2.03375,0 5.805,-0.9175 8.19875,-7.0725 1.30375,-3.35125 2.02,-7.7925 2.02,-12.50875 0,-4.715 -0.71625,-9.1575 -2.02,-12.5075 M 41.07375,43.255 c -0.80875,2.085 -1.82875,3.32875 -2.725,3.32875 -7.195,0 -24.94,0.0225 -31.97375,0.0313 l 0,-27.4475 31.97375,0.001 c 1.815,0 4.34625,5.215 4.34625,13.7075 0,3.94625 -0.5925,7.73 -1.62125,10.37875 m 5.475,-22.88625 c -2.39375,-6.155 -6.16375,-7.075 -8.2,-7.075 l -31.97375,-0.002 0,-12.80625 -5.875,0 0,52.0125 2.9425,-0.004 c 0,0 25.835,-0.0362 34.90625,-0.0362 2.03625,0 5.80625,-0.9175 8.2,-7.0725 1.30375,-3.35125 2.02,-7.7925 2.02,-12.50875 0,-4.715 -0.71625,-9.1575 -2.02,-12.5075"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

53
clitools/generators/brmlab2.svg Executable file
View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
xml:space="preserve"
height="78.96875"
width="78.96875"
version="1.1"
id="svg2"
inkscape:version="0.48.0 r9654"
sodipodi:docname="brmlab2.svg"
inkscape:export-filename="/home/prusnak/work/scm/laserdisplay/svglaser/templates/template4.svg.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200"><defs
id="defs2994" /><metadata
id="metadata78"><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><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1024"
inkscape:window-height="689"
id="namedview76"
showgrid="false"
inkscape:zoom="1"
inkscape:cx="25.043829"
inkscape:cy="5.6960585"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:snap-bbox="true"
inkscape:snap-global="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
id="path3237"
style="fill:none;stroke:#000000;stroke-width:1"
d="m 20.26276,45.39193 c -1.816875,0 -3.293437,-1.4775 -3.293437,-3.29344 0,-1.81593 1.476562,-3.29437 3.293437,-3.29437 1.816875,0 3.294375,1.47844 3.294375,3.29437 0,1.81594 -1.4775,3.29344 -3.294375,3.29344 M 39.480011,0.50605 C 17.985519,0.50605 0.5,17.99425 0.5,39.48874 c 0,10.41205 4.0546519,20.20071 11.416975,27.56304 7.363668,7.36232 17.15099,11.41697 27.563036,11.41697 21.494491,0 38.982697,-17.48686 38.982697,-38.98001 C 78.461408,17.99291 60.974502,0.50605 39.480011,0.50605 m -0.09362,68.75171 c -5.945327,0 -11.681286,-1.74473 -16.588197,-5.04528 l -0.998709,-0.67022 0.765276,-0.92531 c 1.473999,-1.78685 1.350062,-4.36785 -0.289986,-6.0079 -0.844692,-0.84469 -1.96854,-1.31035 -3.163381,-1.31035 -1.037214,0 -2.046752,0.36218 -2.843313,1.01916 l -0.926513,0.76407 -0.670218,-0.9963 C 6.757481,44.32012 8.3000651,28.49238 18.341303,18.45235 c 4.199391,-4.2018 9.490142,-7.02345 15.299501,-8.16174 l 1.179198,-0.22982 0.115514,1.19484 c 0.220197,2.30425 2.13459,4.04176 4.452076,4.04176 2.317487,0 4.23188,-1.73751 4.45328,-4.04176 l 0.115513,-1.19484 1.177996,0.22982 c 5.806952,1.13829 11.098906,3.95994 15.299501,8.15933 10.040034,10.04124 11.583822,25.87018 3.668751,37.63569 l -0.670218,0.9963 -0.92531,-0.76527 c -0.798967,-0.65698 -1.807302,-1.01796 -2.845719,-1.01796 -1.194841,0 -2.317486,0.46446 -3.163381,1.30915 -1.638845,1.64005 -1.762781,4.22225 -0.289986,6.0091 l 0.764072,0.92531 -0.996302,0.67022 c -4.90691,3.30055 -10.644073,5.04528 -16.5894,5.04528 m -7.130784,-26.72605 14.281498,0 0,-6.00112 -14.281498,0 0,6.00112 z M 24.122448,30.11568 c -1.816875,0 -3.294375,-1.4775 -3.294375,-3.29344 0,-1.81593 1.4775,-3.29437 3.294375,-3.29437 1.815937,0 3.293437,1.47844 3.293437,3.29437 0,1.81594 -1.4775,3.29344 -3.293437,3.29344 m 34.387656,15.27625 c -1.815937,0 -3.294375,-1.4775 -3.294375,-3.29344 0,-1.81593 1.478438,-3.29437 3.294375,-3.29437 1.816875,0 3.294375,1.47844 3.294375,3.29437 0,1.81594 -1.4775,3.29344 -3.294375,3.29344 m -26.905937,15.1875 c -1.815938,0 -3.292501,-1.4775 -3.292501,-3.29344 0,-1.81593 1.476563,-3.29437 3.292501,-3.29437 1.816875,0 3.294374,1.47844 3.294374,3.29437 0,1.81594 -1.477499,3.29344 -3.294374,3.29344 M 54.647291,30.11568 c -1.816874,0 -3.294374,-1.4775 -3.294374,-3.29344 0,-1.81593 1.4775,-3.29437 3.294374,-3.29437 1.815938,0 3.292501,1.47844 3.292501,3.29437 0,1.81594 -1.476563,3.29344 -3.292501,3.29344 m -7.474843,30.46375 c -1.816875,0 -3.294375,-1.4775 -3.294375,-3.29344 0,-1.81593 1.4775,-3.29437 3.294375,-3.29437 1.815937,0 3.293437,1.47844 3.293437,3.29437 0,1.81594 -1.4775,3.29344 -3.293437,3.29344 m -7.775,-30.46375 c -1.816875,0 -3.294375,-1.4775 -3.294375,-3.29344 0,-1.81593 1.4775,-3.29437 3.294375,-3.29437 1.815937,0 3.293437,1.47844 3.293437,3.29437 0,1.81594 -1.4775,3.29344 -3.293437,3.29344"
inkscape:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

85
clitools/generators/dummy.py Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This is the most basic generator you can imagine: straight up static!
v0.1.0
Use it to test your filters and outputs
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import time
import argparse
import sys
name="generator::dummy"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="dummy generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-s","--speed",help="point per frame progress",default=3,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
color = 16777215
square = [[100.0, 100.0, color], [100.0, 500.0, color], [500.0, 500.0, color], [500.0, 100.0, color], [100.0, 100.0, color]]
line =[]
for i in range(00,800,int(800/120)):
line.append([i, 400, color])
square = [[100.0, 100.0, color], [100.0, 500.0, color], [500.0, 500.0, color], [500.0, 100.0, color], [100.0, 100.0, color]]
mire = [
[600,600,0],
[600,600,color],
[700,600,color],
[700,700,color],
[600,700,color],
[600,600,color],
[100,100,0],
[100,100,color],
[200,100,color],
[200,200,color],
[100,200,color],
[100,100,color],
[0,0,0],
[0,0,color],
[800,0,color],
[800,800,color],
[0,800,color],
[0,0,color],
[350,400,0],
[350,400,color],
[450,400,color],
[400,350,0],
[400,350,color],
[400,450,color],
]
shape = mire
while True:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))

182
clitools/generators/example.py Executable file
View File

@ -0,0 +1,182 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
example, based on custom
v0.1.0
A copy of square.py you can modify to code your plugin.
custom1 has necessary hooks in LJ.conf, webui and so on.
LICENCE : CC
by Sam Neurohack
'''
import sys
import os
ljpath = r'%s' % os.getcwd().replace('\\','/')
# import from shell
sys.path.append(ljpath +'/../../libs/')
#import from LJ
sys.path.append(ljpath +'/libs/')
print(ljpath+'/../libs/')
import lj23layers as lj
sys.path.append('../libs')
import math
import time
import argparse
print ("")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="Custom1 example for LJ")
argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",default=0,type=int)
args = argsparser.parse_args()
# Useful variables init.
white = lj.rgb2int(255,255,255)
red = lj.rgb2int(255,0,0)
blue = lj.rgb2int(0,0,255)
green = lj.rgb2int(0,255,0)
width = 800
height = 600
centerX = width / 2
centerY = height / 2
# 3D to 2D projection parameters
fov = 256
viewer_distance = 2.2
# Anaglyph computation parameters for right and left eyes.
# algorythm come from anaglyph geo maps
eye_spacing = 100
nadir = 0.5
observer_altitude = 30000
map_layerane_altitude = 0.0
# square coordinates : vertices that compose each of the square.
vertices = [
(- 1.0, 1.0,- 1.0),
( 1.0, 1.0,- 1.0),
( 1.0,- 1.0,- 1.0),
(- 1.0,- 1.0,- 1.0)
]
face = [0,1,2,3]
#
# LJ inits
#
layer = 0
# Define properties for each drawn "element" : name, intensity, active, xy, color, red, green, blue, layer , closed
Leftsquare = lj.FixedObject('Leftsquare', True, 255, [], red, 255, 0, 0, layer , True)
Rightsquare = lj.FixedObject('Rightsquare', True, 255, [], green, 0, 255, 0, layer , True)
# 'Destination' for given layer : name, number, active, layer , scene, laser
Dest0 = lj.DestObject('0', 0, True, 0 , 0, 0) # Dest0 will send layer 0 points to scene 0, laser 0
#
# Anaglyph computation : different X coordinate for each eye
#
def LeftShift(elevation):
diff = elevation - map_layerane_altitude
return nadir * eye_spacing * diff / (observer_altitude - elevation)
def RightShift(elevation):
diff = map_layerane_altitude - elevation
return (1 - nadir) * eye_spacing * diff / (observer_altitude - elevation)
def Proj(x,y,z,angleX,angleY,angleZ):
rad = angleX * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
y2 = y
y = y2 * cosa - z * sina
z = y2 * sina + z * cosa
rad = angleY * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
z2 = z
z = z2 * cosa - x * sina
x = z2 * sina + x * cosa
rad = angleZ * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
x2 = x
x = x2 * cosa - y * sina
y = x2 * sina + y * cosa
""" Transforms this 3D point to 2D using a perspective projection. """
factor = fov / (viewer_distance + z)
x = x * factor + centerX
y = - y * factor + centerY
return (x,y)
#
# Main
#
def Run():
Left = []
Right = []
counter =0
try:
while True:
Left = []
Right = []
x = vertices[0][0]
y = vertices[0][1]
z = vertices[0][2]
# lj tracers will "move" the laser to this first point in black, then move to the next with second point color.
# for more accuracy in dac emulator, repeat this first point.
# generate all points in square.
for point in face:
x = vertices[point][0]
y = vertices[point][1]
z = vertices[point][2]
left.append(proj(x+leftshift(z*25),y,z,0,counter,0))
right.append(proj(x+rightshift(z*25),y,z,0,counter,0))
lj.polylineonecolor(left, c = leftsquare.color , layer = leftsquare.layer, closed = leftsquare.closed)
lj.polylineonecolor(right, c = rightsquare.color , layer = rightsquare.layer, closed = rightsquare.closed)
lj.drawdests()
time.sleep(0.1)
counter += 1
if counter > 360:
counter = 0
except KeyboardInterrupt:
pass
# Gently stop on CTRL C
finally:
lj.ClosePlugin()
Run()

View File

@ -0,0 +1,406 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
fromGML
v0.1.0
Display a GML file
See GML specs at the end.
Support the gml spec="1.0 (minimum)"
and header/client/name
and maybe one day drawing/brush/color
LICENCE : CC
by cocoa and Sam Neurohack
Heavy use of : https://github.com/kgn/pygml
'''
from __future__ import print_function
import time
import struct
import argparse
import sys
import xml.etree.ElementTree as etree
#import urllib
from datetime import datetime
import math, random
import ast
name="generator::fromgml"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="GML file frame generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-g","--gml",help=".gml file",default="147.gml",type=str)
argsparser.add_argument("-t","--total",help="Total time",default=32,type=int)
argsparser.add_argument("-m","--mode",help="once or anim mode",default="anim",type=str)
argsparser.add_argument("-s","--skip",help="% of points to skip",default="0.4",type=float)
argsparser.add_argument("-r","--rot",help="(angleX, angleY, angleZ) in degree",default="(0,0,270)",type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
mode = args.mode
optimal_looptime = 1 / fps
angles = ast.literal_eval(args.rot)
debug(name+" optimal frame time "+str(optimal_looptime))
TOTAL_TIME=float(args.total)
TIME_STRETCH = 1
ZOOM=1.0
DELTA = 7
width = 500
height = 500
centerX = width / 2
centerY = height / 2
# 3D to 2D projection parameters
fov = 200
viewer_distance = 2.2
skip = args.skip
#skip is the percentage of points that we ignore in order to render
# faster in the laser display. Unfortunately we are not able to render too
# complex content in our display without resulting in a lot of blinking.
# return a list with all points
def readGML(filename):
outputData = []
tree = etree.parse(filename)
root = tree.getroot()
'''
if (root.tag.lower() != "gml"):
print("Not a GML file.")
return
'''
#~
tag = root.find("tag")
header = tag.find("header")
if header != None:
client = header.find("client")
if client != None:
debug("Graffiti name :", client.find("name").text)
drawing = tag.find("drawing")
environment = header.find("environment")
if not environment:
environment = tag.find("environment")
#screenBounds = environment.find("screenBounds")
#globalScale = (1.0,1.0,1.0)
#dim = (float(screenBounds.find("x").text) * globalScale[0], float(screenBounds.find("y").text) * globalScale[1], float(screenBounds.find("z").text) * globalScale[2])
#dim = (40.0,40.0,40.0)
#~
strokes = drawing.findall("stroke")
for stroke in strokes:
pointsEl = stroke.findall("pt")
for pointEl in pointsEl:
x = float(pointEl.find("x").text) - 0.5
y = float(pointEl.find("y").text) - 0.5
z = float(pointEl.find("z").text) - 0.5
transpoint = Rot(x,y,z,angles[0],angles[1],angles[2])
x = (transpoint[0]*ZOOM*width/2) + (width/2)
y = (transpoint[1]*ZOOM*height/2) + (height/2)
z = transpoint[2]
# WIDTH/2 + ZOOM*point[0]*WIDTH/2, HEIGHT/2 + ZOOM*point[1]*HEIGHT/2
time = float(pointEl.find("time").text)
outputData.append([x,y,z,time])
#print(outputData)
return outputData
def Rot(x, y, z, angleX, angleY, angleZ):
rad = angleX * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
y2 = y
y = y2 * cosa - z * sina
z = y2 * sina + z * cosa
rad = angleY * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
z2 = z
z = z2 * cosa - x * sina
x = z2 * sina + x * cosa
rad = angleZ * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
x2 = x
x = x2 * cosa - y * sina
y = x2 * sina + y * cosa
return (x,y,z)
#[x,y,z,time]
def iterPoints():
for point in gml:
yield point
# Play once during total time arg
def Once():
debug(name,"play once mode")
shape = []
for point in gml:
shape.append([point[0],point[1], 65535])
debug(name + str(shape))
t0=datetime.now()
deltat=0
while deltat<TOTAL_TIME:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
delta = datetime.now() - t0
deltat = delta.seconds + delta.microseconds/1000000.0
deltat = float(deltat)/TIME_STRETCH
# Anim
def Anim():
debug(name+" anim mode")
t0=datetime.now()
deltat = 0
while deltat<TOTAL_TIME:
delta = datetime.now() - t0
deltat = delta.seconds + delta.microseconds/1000000.0
deltat = float(deltat)/TIME_STRETCH
if deltat > TOTAL_TIME:
t0=datetime.now()
first=True
shape = []
for point in iterPoints():
if point[3] <= deltat and deltat <= point[3]+DELTA and random.random()<(1-skip):
if first:
first=False
else:
#LD.draw_point(WIDTH/2 + ZOOM*point.x*WIDTH/2, HEIGHT/2 + ZOOM*point.y*HEIGHT/2)
shape.append([point[0], point[1], 65535])
print(shape, flush=True);
debug(name + " Reading : "+args.gml+" in "+mode+" mode.")
gml = readGML(args.gml)
debug(name + " total points : "+ str(len(gml)))
if mode =="once":
Once()
else:
Anim()
debug(name + " ends.")
exit()
'''
<gml spec="1.0 (minimum)">
<tag>
<drawing>
<stroke>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
</stroke>
</drawing>
</tag>
</gml>
<gml spec="1.0">
<tag>
<header>
<client> <!-- how, who, what and where -->
<name>Laser Tag</name> <!-- application name -->
<version>2.0</version> <!-- application version -->
<username>MyUserName</username> <!-- user name on 000000book.com, optional -->
<permalink>http://000000book.com/data/156/</permalink> <!-- URL to .gml data on 000000book.com, optional -->
<keywords>katsu,paris,2010</keywords> <!-- comma-separated -->
<uniquekey>28sks922ks992</uniquekey> <!-- iPhone uuid, MAC address, etc -->
<ip>192.168.1.1</ip>
<time>1928372722</time> <!-- unixtime -->
<location>
<lon>-39.392922</lon>
<lat>53.29292</lat>
</location>
</client>
<!-- This is all stuff that relates to the orientation and dimensions of the client -->
<!-- So that we know how to re-map the 0.0-1.0 coordinates that come in for each point -->
<!-- Also for figuring out the down vector for devices with accelerometers and how that effects drips -->
<!-- All numbers should be between 0.0 - 1.0 -->
<environment>
<offset>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z>
</offset>
<rotation>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z>
</rotation>
<up>
<x>0.0</x> <!-- commonly up for iphone apps -->
<y>-1.0</y> <!-- most common -->
<z>0.0</z>
</up>
<screenbounds> <!-- use this as your multipler to get 0.0 to 1.0 back to right size - pts should never go off 0.0 to 1.0 -->
<x>1024</x>
<y>768</y>
<z>0</z>
</screenbounds>
<origin>
<x>0</x>
<y>0</y>
<z>0</z>
</origin>
<realscale> <!-- how these units relate to real world units - good for laser tag -->
<x>1000</x>
<y>600</y>
<z>0</z>
<unit>cm</unit>
</realscale>
<audio>youraudio.mp3</audio> <!-- path to audio file -->
<background>yourimage.jpg</background> <!-- path to image file -->
</environment>
</header>
<drawing>
<!-- for all stroke and movement stuff it helps to have everything inside the stroke tag -->
<!-- this way it is easy to get a sense of order to events -->
<stroke isdrawing="false"> <!-- for non drawing mouse movements -->
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.013</t> <!-- time is optional too -->
<!-- NOTE: older versions of GML use <time> instead of <t> -->
</pt>
</stroke>
<stroke> <!-- by default stroke drawing is true -->
<!-- each stroke could be drawn with a different brush -->
<!-- if no brush tag is found for a stroke then it inherits the previous settings -->
<brush>
<mode>0</mode> <!-- same as uniqueStyleID but an internal reference -->
<uniquestyleid>LaserTagArrowLetters</uniquestyleid> <!-- unique blackbook string for your style -->
<!-- see note about spec at the bottom - like unique style but with extra info -->
<spec>http://aurltodescribethebrushspec.com/someSpec.xml</spec>
<width>10</width>
<speedtowidthratio>1.5</speedtowidthratio> <!-- put 0 for fixed width -->
<dripamnt>1.0</dripamnt>
<dripspeed>1.0</dripspeed>
<layerabsolute>0</layerabsolute> <!--Think photoshop layers-->
<color>
<r>255</r>
<g>255</g>
<b>255</b>
<a>255</a> <!-- optional -->
</color>
<dripvecrelativetoup> <!-- what angle do our drips go in relation to our up vector -->
<x>0</x>
<y>1</y>
<z>0</z>
</dripvecrelativetoup>
</brush>
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.013</t> <!-- time is optional too -->
</pt>
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.023</t> <!-- time is optional too -->
</pt>
</stroke>
<!-- this stroke inherits the previous stroke properties -->
<!-- but changes color and draws on the layer below -->
<stroke>
<info> <!-- optional info - more stuff soon-->
<curved>true</curved>
</info>
<brush>
<color>
<r>255</r>
<g>255</g>
<b>0</b>
</color>
<layerrelative> <!-- this means one layer bellow the previous layer -->
-1
</layerrelative>
</brush>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
</stroke>
<stroke>
<pt>
<pres>0.5</pres> <!-- Optional. Preasure range from 0 to 1 -->
<rot>0.5</rot> <!-- Optional. Rotation range from 0 to 1 for 0 to 2*PI -->
<dir> <!-- Optional Direction -->
<x></x> <!-- range from 0 to 1 -->
<y></y> <!-- range from 0 to 1 -->
<z></z> <!-- Optional inside direction. Range from 0 to 1 -->
</dir>
</pt>
</stroke>
</drawing>
</tag>
</gml>
'''

View File

@ -0,0 +1,126 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Forward /pl pointlist to cli
input OSC in END points format : (x,y,color)
output CLI in CLI points format : [x,y,color]
/pl "[(150.0, 230.0, 255), (170.0, 170.0, 255), (230.0, 170.0, 255), (210.0, 230.0, 255), (150.0, 230.0, 255)]"
v0.1.0
LICENCE : CC
by Cocoa, Sam Neurohack
'''
from __future__ import print_function
from OSC3 import OSCServer, OSCClient, OSCMessage
import sys
from time import sleep
import argparse
import ast
argsparser = argparse.ArgumentParser(description="fromOSC generator")
argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str)
argsparser.add_argument("-p","--port",help="OSC port to bind to (9002 by default)",default=9002,type=str)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = int(args.port)
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
oscserver = OSCServer( (ip, port) )
oscserver.timeout = 0
run = True
# this method of reporting timeouts only works by convention
# that before calling handle_request() field .timed_out is
# set to False
def handle_timeout(self):
self.timed_out = True
# funny python's way to add a method to an instance of a class
import types
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
# RAW OSC Frame available ?
def OSC_frame():
# clear timed_out flag
oscserver.timed_out = False
# handle all pending requests then return
while not oscserver.timed_out:
oscserver.handle_request()
# default handler
def OSChandler(oscpath, tags, args, source):
oscaddress = ''.join(oscpath.split("/"))
debug("fromOSC Default OSC Handler got oscpath", oscpath, "from" + str(source[0]), ":", args)
#print("OSC address", path)
#print("find.. /bhoreal ?", path.find('/bhoreal'))
if oscpath == "/pl" and len(args)==1:
debug("correct OSC type :'/pl")
if validate(args[0]) == True:
debug("new pl : ", args[0])
line = args[0].replace("(",'[')
line = line.replace(")",']')
line = "[{}]".format(line)
print(line, flush=True);
else:
debug("Bad pointlist -> msg trapped.")
else:
debug("BAD OSC Message : " + oscpath +" " +args[0])
oscserver.addMsgHandler( "default", OSChandler )
def validate(pointlist):
state = True
if len(pointlist)<9:
debug("Not enough characters :", pointlist)
state = False
if pointlist.find("(") == -1:
debug("Bad format : use () not [] for points", pointlist)
state = False
try:
pl = bytes(pointlist, 'ascii')
check = ast.literal_eval(pl.decode('ascii'))
except:
debug("BAD POINTLIST :", pointlist)
state = False
return state
# simulate a "game engine"
while run:
# do the game stuff:
sleep(0.01)
# call user script
OSC_frame()
oscserver.close()

View File

@ -0,0 +1,72 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This generator reads a frame from redis
v0.1.0
Use it to create feedback loops by writing to the same frame
or to copy the frame from someone else
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import ast
import argparse
import json
import redis
import sys
import time
name="generator::fromRedis"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="Dummy generator")
argsparser.add_argument("-k","--key",required=True,help="Redis key to look after",default=30,type=str)
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
args = argsparser.parse_args()
fps = args.fps
verbose = args.verbose
key = args.key
ip = args.ip
port = args.port
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
r = redis.Redis(
host=ip,
port=port)
while True:
start = time.time()
# Read from Redis
line = r.get(key)
# Decode as list of tuples
pointsList = ast.literal_eval(line.decode('ascii'))
# convert to list of lists
pointsList = [list(elem) for elem in pointsList]
# Convert to JSON string
line = json.dumps( pointsList )
debug(name,"Key:{} line:{}".format(key,line))
print(line, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))

View File

@ -0,0 +1,84 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
fromUDP
Udp server to cli
v0.1b
'''
from __future__ import print_function
import traceback, time
import argparse
import socket
import _thread
import sys
name="generator::fromUDP"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="fromUDP v0.1b help mode")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str)
argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)",default=9000,type=str)
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = int(args.port)
verbose = args.verbose
def udp_thread():
while True:
payload, client_address = sock.recvfrom(1024)
udpath = payload.decode('utf_8')
debug(udpath[0:])
print(udpath[0:], flush=True);
'''
# Reply to client
bytesToSend = str.encode("ACK :"+str(payload))
serverAddressPort = (client_address, port)
bufferSize = 1024
#sock.sendto(bytesToSend, serverAddressPort)
sock.sendto(bytesToSend, client_address)
'''
def StartUDP(serverIP, UDPORT):
global sock
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server = ( serverIP,UDPORT)
sock.bind(server)
_thread.start_new_thread(udp_thread, ())
StartUDP(ip, port)
# Do something else
try:
while True:
time.sleep(0.005)
except Exception:
traceback.print_exc()

View File

@ -0,0 +1,319 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
fromild
v0.1.0
Read/display once an .ild animation file and quit ??
LICENCE : CC
by cocoa and Sam Neurohack
Heavy u-se of :
ILDA.py
Python module for dealing with the ILDA Image Data Transfer Format,
an interchange format for laser image frames.
Copyright (c) 2008 Micah Dowty
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
from __future__ import print_function
import time
import struct
import argparse
import sys
name="generator::fromild"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description=".ild file frame generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-i","--ild",help=".ild file",default="book2.ild",type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
# Format codes
FORMAT_3D = 0
FORMAT_2D = 1
FORMAT_COLOR_TABLE = 2
# Mapping from FORMAT_* codes to struct format strings
formatTable = (
'>hhhH',
'>hhH',
'>BBB',
)
# Header values
HEADER_MAGIC = b"ILDA\0\0\0"
HEADER_RESERVED = 0
HEADER_FORMAT = ">7sB16sHHHBB"
HEADER_LEN = struct.calcsize(HEADER_FORMAT)
# 64 default colors table : use rgb2int(colors64[ildacolor])
colors64 = [[255, 0, 0], [255, 17, 0], [255, 34, 0], [255, 51, 0], [255, 68, 0], [255, 85, 0], [255, 102, 0], [255, 119, 0], [255, 136, 0], [255, 153, 0], [255, 170, 0], [255, 187, 0], [255, 204, 0], [255, 221, 0], [255, 238, 0], [255, 255, 0], [255, 255, 0], [238, 255, 0], [204, 255, 0], [170, 255, 0], [136, 255, 0], [102, 255, 0], [68, 255, 0], [34, 255, 0], [0, 255, 0], [0, 255, 34], [0, 255, 68], [0, 255, 102], [0, 255, 136], [0, 255, 170], [0, 255, 204], [0, 255, 238], [0, 136, 255], [0, 119, 255], [0, 102, 255], [0, 102, 255], [0, 85, 255], [0, 68, 255], [0, 68, 255], [0, 34, 255], [0, 0, 255], [34, 0, 255], [68, 0, 255], [102, 0, 255], [136, 0, 255], [170, 0, 255], [204, 0, 255], [238, 0, 255], [255, 0, 255], [255, 34, 255], [255, 68, 255], [255, 102, 255], [255, 136, 255], [255, 170, 255], [255, 204, 255], [255, 238, 255], [255, 255, 255], [255, 238, 238], [255, 204, 204], [255, 170, 170], [255, 136, 136], [255, 102, 102], [255, 68, 68], [0, 34, 34]]
# 256 default colors table
colors256 = [[0, 0, 0], [255, 255, 255], [255, 0, 0], [255, 255, 0], [0, 255, 0], [0, 255, 255], [0, 0, 255], [255, 0, 255], [255, 128, 128], [255, 140, 128], [255, 151, 128], [255, 163, 128], [255, 174, 128], [255, 186, 128], [255, 197, 128], [255, 209, 128], [255, 220, 128], [255, 232, 128], [255, 243, 128], [255, 255, 128], [243, 255, 128], [232, 255, 128], [220, 255, 128], [209, 255, 128], [197, 255, 128], [186, 255, 128], [174, 255, 128], [163, 255, 128], [151, 255, 128], [140, 255, 128], [128, 255, 128], [128, 255, 140], [128, 255, 151], [128, 255, 163], [128, 255, 174], [128, 255, 186], [128, 255, 197], [128, 255, 209], [128, 255, 220], [128, 255, 232], [128, 255, 243], [128, 255, 255], [128, 243, 255], [128, 232, 255], [128, 220, 255], [128, 209, 255], [128, 197, 255], [128, 186, 255], [128, 174, 255], [128, 163, 255], [128, 151, 255], [128, 140, 255], [128, 128, 255], [140, 128, 255], [151, 128, 255], [163, 128, 255], [174, 128, 255], [186, 128, 255], [197, 128, 255], [209, 128, 255], [220, 128, 255], [232, 128, 255], [243, 128, 255], [255, 128, 255], [255, 128, 243], [255, 128, 232], [255, 128, 220], [255, 128, 209], [255, 128, 197], [255, 128, 186], [255, 128, 174], [255, 128, 163], [255, 128, 151], [255, 128, 140], [255, 0, 0], [255, 23, 0], [255, 46, 0], [255, 70, 0], [255, 93, 0], [255, 116, 0], [255, 139, 0], [255, 162, 0], [255, 185, 0], [255, 209, 0], [255, 232, 0], [255, 255, 0], [232, 255, 0], [209, 255, 0], [185, 255, 0], [162, 255, 0], [139, 255, 0], [116, 255, 0], [93, 255, 0], [70, 255, 0], [46, 255, 0], [23, 255, 0], [0, 255, 0], [0, 255, 23], [0, 255, 46], [0, 255, 70], [0, 255, 93], [0, 255, 116], [0, 255, 139], [0, 255, 162], [0, 255, 185], [0, 255, 209], [0, 255, 232], [0, 255, 255], [0, 232, 255], [0, 209, 255], [0, 185, 255], [0, 162, 255], [0, 139, 255], [0, 116, 255], [0, 93, 255], [0, 70, 255], [0, 46, 255], [0, 23, 255], [0, 0, 255], [23, 0, 255], [46, 0, 255], [70, 0, 255], [93, 0, 255], [116, 0, 255], [139, 0, 255], [162, 0, 255], [185, 0, 255], [209, 0, 255], [232, 0, 255], [255, 0, 255], [255, 0, 232], [255, 0, 209], [255, 0, 185], [255, 0, 162], [255, 0, 139], [255, 0, 116], [255, 0, 93], [255, 0, 70], [255, 0, 46], [255, 0, 23], [128, 0, 0], [128, 12, 0], [128, 23, 0], [128, 35, 0], [128, 47, 0], [128, 58, 0], [128, 70, 0], [128, 81, 0], [128, 93, 0], [128, 105, 0], [128, 116, 0], [128, 128, 0], [116, 128, 0], [105, 128, 0], [93, 128, 0], [81, 128, 0], [70, 128, 0], [58, 128, 0], [47, 128, 0], [35, 128, 0], [23, 128, 0], [12, 128, 0], [0, 128, 0], [0, 128, 12], [0, 128, 23], [0, 128, 35], [0, 128, 47], [0, 128, 58], [0, 128, 70], [0, 128, 81], [0, 128, 93], [0, 128, 105], [0, 128, 116], [0, 128, 128], [0, 116, 128], [0, 105, 128], [0, 93, 128], [0, 81, 128], [0, 70, 128], [0, 58, 128], [0, 47, 128], [0, 35, 128], [0, 23, 128], [0, 12, 128], [0, 0, 128], [12, 0, 128], [23, 0, 128], [35, 0, 128], [47, 0, 128], [58, 0, 128], [70, 0, 128], [81, 0, 128], [93, 0, 128], [105, 0, 128], [116, 0, 128], [128, 0, 128], [128, 0, 116], [128, 0, 105], [128, 0, 93], [128, 0, 81], [128, 0, 70], [128, 0, 58], [128, 0, 47], [128, 0, 35], [128, 0, 23], [128, 0, 12], [255, 192, 192], [255, 64, 64], [192, 0, 0], [64, 0, 0], [255, 255, 192], [255, 255, 64], [192, 192, 0], [64, 64, 0], [192, 255, 192], [64, 255, 64], [0, 192, 0], [0, 64, 0], [192, 255, 255], [64, 255, 255], [0, 192, 192], [0, 64, 64], [192, 192, 255], [64, 64, 255], [0, 0, 192], [0, 0, 64], [255, 192, 255], [255, 64, 255], [192, 0, 192], [64, 0, 64], [255, 96, 96], [255, 255, 255], [245, 245, 245], [235, 235, 235], [224, 224, 224], [213, 213, 213], [203, 203, 203], [192, 192, 192], [181, 181, 181], [171, 171, 171], [160, 160, 160], [149, 149, 149], [139, 139, 139], [128, 128, 128], [117, 117, 117], [107, 107, 107], [96, 96, 96], [85, 85, 85], [75, 75, 75], [64, 64, 64], [53, 53, 53], [43, 43, 43], [32, 32, 32], [21, 21, 21], [11, 11, 11], [0, 0, 0]]
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
class Table(object):
"""Container object for one ILDA table: either a frame (table of points)
or a palette (table of colors).
The 'items' list contains the data within this table. Each item
is a tuple, corresponding to the raw values within that row of the
table.
2D frame: (x, y, status)
3D frame: (x, y, z, status)
Color: (r, g, b)
"""
def __init__(self, format=FORMAT_2D, name="",
length=0, number=0, total=0, scanHead=0):
self.__dict__.update(locals())
self.items = []
self.itemsproducer = None
def __repr__(self):
return ("<ILDA.Table format=%d name=%r "
"length=%d number=%d total=%d scanHead=%d>" %
(self.format, self.name, self.length, self.number,
self.total, self.scanHead))
def unpackHeader(self, data):
magic, self.format, self.name, self.length, \
self.number, self.total, self.scanHead, \
reserved = struct.unpack(HEADER_FORMAT, data)
print(magic, HEADER_MAGIC)
if magic != HEADER_MAGIC:
raise ValueError("Bad ILDA header magic. Not an ILDA file?")
if reserved != HEADER_RESERVED:
raise ValueError("Reserved ILDA field is not zero.")
def packHeader(self):
return struct.pack(HEADER_FORMAT, HEADER_MAGIC, self.format,
self.name, self.length, self.number,
self.total, self.scanHead, HEADER_RESERVED)
def readHeader(self, stream):
self.unpackHeader(stream.read(HEADER_LEN))
def writeHeader(self, stream):
stream.write(self.packHeader())
def _getItemFormat(self):
try:
return formatTable[self.format]
except IndexError:
raise ValueError("Unsupported format code")
def read_stream(self, stream):
"""Read the header, then read all items in this table."""
self.readHeader(stream)
if self.length:
fmt = self._getItemFormat()
itemSize = struct.calcsize(fmt)
self.items = [struct.unpack(fmt, stream.read(itemSize))
for i in range(self.length)]
self.itemsproducer = self.produce()
def write(self, stream):
"""Write the header, then write all items in this table."""
self.writeHeader(stream)
if self.length:
fmt = self._getItemFormat()
itemSize = struct.calcsize(fmt)
stream.write(''.join([struct.pack(fmt, *item)
for item in self.items]))
def iterPoints(self):
"""Iterate over Point instances for each item in this table.
Only makes sense if this is a 2D or 3D point table.
"""
for item in self.items:
p = Point()
p.decode(item)
yield p
def produce(self):
"""Iterate over Point instances for each item in this table.
Only makes sense if this is a 2D or 3D point table.
"""
while True:
for item in self.items:
p = Point()
p.decode(item)
yield p.encode()
#yield (p.x, p.y, p.z, p.color, p.blanking)
def read(self, cap):
"""yields what dac.play_stream() needs (x, y, z, ?, ?)
"""
return [next(self.itemsproducer) for i in range(cap)]
class Point:
"""Abstraction for one vector point. The Table object, for
completeness and efficiency, stores raw tuples for each
point. This is a higher level interface that decodes the status
bits and represents coordinates in floating point.
"""
def __init__(self, x=0.0, y=0.0, z=0.0, color=0, blanking=False):
self.__dict__.update(locals())
def __repr__(self):
return "%s, %s, %s, %s, %s" % (
self.x, self.y, self.z, self.color, self.blanking)
#return "<ILDA.Point (%s, %s, %s) color=%s blanking=%s>" % (
# self.x, self.y, self.z, self.color, self.blanking)
def encode(self):
status = self.color & 0xFF
if self.blanking:
status |= 1 << 14
return (
int( min(0x7FFF, max(-0x7FFF, self.x * 0x7FFF)) ),
int( min(0x7FFF, max(-0x7FFF, self.y * 0x7FFF)) ),
int( min(0x7FFF, max(-0x7FFF, self.z * 0x7FFF)) ),
int( min(0x7FFF, max(-0x7FFF, self.color * 0x7FFF)) ),
int( min(0x7FFF, max(-0x7FFF, self.blanking * 0x7FFF)) )
)
def decode(self, t):
#print "~~ Decoding, t of len "+ str(len(t)) +" is: " + str(t)
self.x = t[0] / 0x7FFF
self.y = t[1] / 0x7FFF
if len(t) > 3:
self.z = t[2] / 0x7FFF
# self.color = t[3] & 0xFF
# self.blanking = (t[3] & (1 << 14)) != 0
else:
self.z = 0.0
self.color = t[-1] & 0xFF
self.blanking = (t[-1] & (1 << 14)) != 0
def read(stream):
"""Read ILDA data from a stream until we hit the
end-of-stream marker. Yields a sequence of Table objects.
"""
while True:
t = Table()
t.read_stream(stream)
if not t.length:
# End-of-stream
break
yield t
def write(stream, tables):
"""Write a sequence of tables in ILDA format,
terminated by an end-of-stream marker.
"""
for t in tables:
t.write(stream)
Table().write(stream)
def readFrames(stream):
"""Read ILDA data from a stream, and ignore
all non-frame tables. Yields only 2D or 3D
point tables.
"""
for t in read(stream):
if t.format in (FORMAT_2D, FORMAT_3D):
yield t
def readFirstFrame(stream):
"""Read only a single frame from an ILDA stream."""
for frame in readFrames(stream):
return frame
#
f = open(args.ild, 'rb')
myframe = readFirstFrame(f)
while myframe.number +1< myframe.total:
start = time.time()
shape =[]
if myframe is None:
f.close()
break
debug(name,"Frame", myframe.number, "/",myframe.total, "length", myframe.length)
for p in myframe.iterPoints():
p2 = str(p)
point = p2.split(',')
x = float(point[0])
y = float(point[1])
z = float(point[2])
color = int(point[3])
blanking = point[4][1:]
if blanking == "True":
shape.append([300+(x*300),300+(-y*300),0])
else:
shape.append([300+(x*300),300+(-y*300),rgb2int(colors64[color])])
print(shape, flush=True);
myframe = readFirstFrame(f)
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
f.close()
debug(name + " end of .ild animation")

View File

@ -0,0 +1,131 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Forward pointlist to redis key
END POINT Format : (x,y,color)
/pl/0/0 "[(150.0, 230.0, 255), (170.0, 170.0, 255), (230.0, 170.0, 255), (210.0, 230.0, 255), (150.0, 230.0, 255)]"
v0.1.0
LICENCE : CC
by Cocoa, Sam Neurohack
'''
from OSC3 import OSCServer, OSCClient, OSCMessage
import sys
from time import sleep
import argparse
import ast
import redis
argsparser = argparse.ArgumentParser(description="osc2redis generator")
argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str)
argsparser.add_argument("-p","--port",help="OSC port to bind to (9002 by default)",default=9002,type=str)
argsparser.add_argument("-r","--rip",help="Redis server IP (127.0.0.1 by default)",default="127.0.0.1",type=str)
argsparser.add_argument("-o","--rout",help="Redis port (6379 by default)",default=6379,type=str)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = int(args.port)
rip = args.rip
rport = int(args.rout)
r = redis.StrictRedis(host=rip, port=rport, db=0)
def debug(msg):
if( verbose == False ):
return
print(msg)
oscserver = OSCServer( (ip, port) )
oscserver.timeout = 0
run = True
# this method of reporting timeouts only works by convention
# that before calling handle_request() field .timed_out is
# set to False
def handle_timeout(self):
self.timed_out = True
# funny python's way to add a method to an instance of a class
import types
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
def validate(pointlist):
state = True
if len(pointlist)<9:
state = False
try:
pl = bytes(pointlist, 'ascii')
check = ast.literal_eval(pl.decode('ascii'))
except:
state = False
return state
# RAW OSC Frame available ?
def OSC_frame():
# clear timed_out flag
oscserver.timed_out = False
# handle all pending requests then return
while not oscserver.timed_out:
oscserver.handle_request()
# default handler
def OSChandler(oscpath, tags, args, source):
oscaddress = ''.join(oscpath.split("/"))
print("fromOSC Default OSC Handler got oscpath :", oscpath, "from :" + str(source[0]), "args :", args)
print(oscpath.find("/pl/"), len(oscpath))
if oscpath.find("/pl/") ==0 and len(args)==1:
print("correct OSC type :'/pl/")
if validate(args[0]) == True and len(oscpath) == 7:
print("new pl for key ", oscpath, ":", args[0])
if r.set(oscpath,args[0])==True:
debug("exports::redis set("+str(oscpath)+") to "+args[0])
else:
print("Bad pointlist -> msg trapped.")
else:
print("BAD OSC Message :", oscpath)
oscserver.addMsgHandler( "default", OSChandler )
# simulate a "game engine"
while run:
# do the game stuff:
sleep(0.01)
# call user script
OSC_frame()
oscserver.close()

View File

@ -0,0 +1,174 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
redilysis_lines
v0.1.0
Add a line on every frame and scroll
see https://git.interhacker.space/teamlaser/redilysis for more informations
about the redilysis project
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import argparse
import ast
import os
import math
import random
import redis
import sys
import time
name = "generator::redilysis_lines"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def msNow():
return time.time()
CHAOS = 1
REDIS_FREQ = 33
# General Args
argsparser = argparse.ArgumentParser(description="Redilysis filter")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
# Redis Args
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-F","--redis-freq",help="Query Redis every x (in milliseconds). Default:{}".format(REDIS_FREQ),default=REDIS_FREQ,type=int)
# General args
argsparser.add_argument("-n","--nlines",help="number of lines on screen",default=60,type=int)
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int)
argsparser.add_argument("-W","--max-width",help="geometrical max width",default=800,type=int)
argsparser.add_argument("-H","--max-height",help="geometrical max height",default=800,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = args.port
fps = args.fps
centerX = args.centerX
centerY = args.centerY
redisFreq = args.redis_freq / 1000
maxWidth = args.max_width
maxHeight = args.max_height
nlines = args.nlines
optimal_looptime = 1 / fps
redisKeys = ["spectrum_120","spectrum_10"]
debug(name,"Redis Keys:{}".format(redisKeys))
redisData = {}
redisLastHit = msNow() - 99999
r = redis.Redis(
host=ip,
port=port)
white = 16777215
lineList = []
scroll_speed = int(maxHeight / nlines )
line_length = int(maxWidth / 10)
line_pattern = []
def rgb2int(rgb):
#debug(name,"::rgb2int rbg:{}".format(rgb))
return int('0x%02x%02x%02x' % tuple(rgb),0)
def spectrum_10( ):
delList = []
spectrum = ast.literal_eval(redisData["spectrum_10"])
debug( name, "spectrum:{}".format(spectrum))
# scroll lines
for i,line in enumerate(lineList):
skip_line = False
new_y = int(line[0][1] + scroll_speed)
if( new_y >= maxHeight ):
debug(name,"{} > {}".format(new_y,maxHeight))
debug(name,"delete:{}".format(i))
delList.append(i)
continue
for j,point in enumerate(line):
line[j][1] = new_y
lineList[i] = line
for i in delList:
del lineList[i]
# new line
currentLine = []
for i in range(0,10):
x = int(i * line_length)
y = 0
# get frequency level
level = spectrum[i]
# get color
comp = int(255*level)
color = rgb2int( (comp,comp,comp))
# new point
currentLine.append( [x,y,color] )
# add line to list
lineList.append( currentLine)
def refreshRedis():
global redisLastHit
global redisData
# Skip if cache is sufficent
diff = msNow() - redisLastHit
if diff < redisFreq :
#debug(name, "refreshRedis not updating redis, {} < {}".format(diff, redisFreq))
pass
else:
#debug(name, "refreshRedis updating redis, {} > {}".format(diff, redisFreq))
redisLastHit = msNow()
for key in redisKeys:
redisData[key] = r.get(key).decode('ascii')
#debug(name,"refreshRedis key:{} value:{}".format(key,redisData[key]))
# Only update the TTLs
if 'bpm' in redisKeys:
redisData['bpm_pttl'] = r.pttl('bpm')
#debug(name,"refreshRedis key:bpm_ttl value:{}".format(redisData["bpm_pttl"]))
#debug(name,"redisData:{}".format(redisData))
return True
def linelistToPoints( lineList ):
pl = []
for i,line in enumerate(lineList):
# add a blank point
pl.append([ line[0][0], line[0][1], 0 ])
# append all the points of the line
pl += line
#debug(name,"pl:{}".format(pl))
debug(name,"pl length:{}".format(len(pl)))
return pl
try:
while True:
refreshRedis()
start = time.time()
# Do the thing
pointsList = spectrum_10()
print( linelistToPoints(lineList), flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
# debug(name+" micro sleep:"+str( optimal_looptime - looptime))
except EOFError:
debug(name+" break")# no more information

View File

@ -0,0 +1,288 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
v0.1.0
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import math
import random
import sys
import os
import time
import redis
import ast
import argparse
MAX_PARTICLES = 50
MAX_TIME = 500
argsparser = argparse.ArgumentParser(description="Dummy generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
# Redis Args
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
#
argsparser.add_argument("-M","--max-particles",help="Max Particles. Default:{}".format(MAX_PARTICLES),default=MAX_PARTICLES,type=int)
argsparser.add_argument("-m","--max-time",help="Max Particles. Default:{}".format(MAX_TIME),default=MAX_TIME,type=int)
args = argsparser.parse_args()
verbose = args.verbose
ip = args.ip
port = args.port
max_particles = args.max_particles
max_time = args.max_time
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def rgb2int(rgb):
#debug(name,"::rgb2int rbg:{}".format(rgb))
return int('0x%02x%02x%02x' % tuple(rgb),0)
def spectrum_120( ):
return ast.literal_eval(redisData["spectrum_10"])
def rgb2int(rgb):
#debug(name,"::rgb2int rbg:{}".format(rgb))
return int('0x%02x%02x%02x' % tuple(rgb),0)
def msNow():
return time.time()
def refreshRedis():
global redisData
for key in redisKeys:
redisData[key] = ast.literal_eval(r.get(key).decode('ascii'))
name="generator::redilisys_particles"
class UnpreparedParticle(Exception):
pass
class Particle(object):
def __init__(self, x, y, m):
self.x = x
self.y = y
self.m = m
self.dx = 0
self.dy = 0
self.connectedTo = []
self.decay = random.randint(10,max_time)
self.color = (random.randint(128,256) - int(12.8 * self.m),
random.randint(128,256) - int(12.8 * self.m),
random.randint(128,256) - int(12.8 * self.m))
self.color = (255,255,255)
#debug( self.color )
def interact(self, bodies):
self.connectedTo = []
spec = redisData["spectrum_10"]
power = int(sum(spec[4:6]))
for other in bodies:
if other is self:
continue
dx = other.x - self.x
dy = other.y - self.y
dist = math.sqrt(dx*dx + dy*dy)
if dist == 0:
dist = 1
if dist < 100 and random.randint(0,power) > 0.5 :
self.connectedTo.append(other)
self.decay += 2
factor = other.m / dist**2
high_power = sum(spec[8:9]) if sum(spec[8:9]) != 0 else 0.01
self.dx += (dx * factor * self.m)
self.dy += (dy * factor * self.m)
#print "factor %f" % (factor,)
def move(self):
spec = redisData["spectrum_10"]
x_friction = (2.2-(1+spec[7]/2))
y_friction = (2.2-(1+spec[7]/2))
#x_friction = 1.02
#y_friction = 1.04
self.dx /= x_friction if x_friction != 0 else 0.01
self.dy /= y_friction if y_friction != 0 else 0.01
self.x += self.dx
self.y += self.dy
if self.x > max_width:
self.dx = - self.dx /8
self.x = max_width
if self.x < 1:
self.dx = - self.dx /8
self.x = 1
if self.y > max_height:
self.dy = - self.dy /4
self.y = max_height
if self.y < 1:
self.dy = - self.dy /4
self.y = 1
#print "(%.2f,%.2f) -> (%.2f,%.2f)" % (ox, oy, self.x, self.y)
def attractor(self,attractor):
spec = redisData["spectrum_10"]
power = sum(spec[0:4])/3
# If we're going in the direction of center, reverse
next_x = self.x + self.dx
next_y = self.y + self.dy
next_dx = attractor["x"] - self.x
next_dy = attractor["y"] - self.y
next_dist = math.sqrt(next_dx*next_dx + next_dy*next_dy)
dx = attractor["x"] - self.x
dy = attractor["y"] - self.y
dist = math.sqrt(dx*dx + dy*dy)
if dist == 0:
dist = 1
factor = power/ dist**2
x_acceleration = (dx * factor * power * power)
y_acceleration = (dx * factor * power * power)
if next_dist > dist:
self.dx -= x_acceleration * power
self.dy -= y_acceleration * power
else:
self.dx += x_acceleration
self.dy += y_acceleration
class Attractor(Particle):
def move(self):
pass
class ParticleViewer(object):
def __init__(self, particles, size=(800,800)):
(self.width, self.height) = size
self.size = size
self.particles = particles
self.xoff = 0
self.yoff = 0
self.scalefactor = 1
def redraw(self):
pl = []
drawnVectors = []
for p in self.particles:
x = int(self.scalefactor * p.x) - self.xoff
y = int(self.scalefactor * p.y) - self.yoff
if x > max_width:
x = max_width
if x < 1:
x = 1
if y > max_height:
y = max_height
if y < 1:
y = 1
color = rgb2int(p.color)
pl.append([x+1,y+1,0])
pl.append([x+1,y+1,color])
pl.append([x,y,color])
for other in p.connectedTo:
if [other,self] in drawnVectors:
continue
drawnVectors.append([other,self])
pl.append([x,y,0])
pl.append([x,y,color])
pl.append([other.x,other.y,color])
print(pl,flush = True)
def decayParticles(self):
for i,p in enumerate(self.particles):
# Handle positional decay
if p.decay == 0:
del self.particles[i]
continue
p.decay = p.decay - 1
# Handle color decay
n = int(255 * (p.decay / max_time ))
p.color = (n,n,n)
def emitParticles(self):
spec = redisData["spectrum_10"]
power = sum(spec[6:])
if len(self.particles ) > math.sqrt(max_particles):
if len(self.particles) > max_particles:
return
if random.random() > power:
return
# x is either left or right
d = 600
rx = 100 if random.randint(0,1) else 700
#rx = random.randint(1,max_width)
ry = random.randint(1,max_height)
spec = redisData["spectrum_10"]
m = random.randint(1,1+int(10*spec[7]))
particles.append(Particle(rx, ry, m))
def tick(self):
self.decayParticles()
self.emitParticles()
for p in self.particles:
p.interact(self.particles)
p.attractor({
"x":max_width/2,
"y":max_height/2
})
for p in particles:
p.move()
self.redraw()
def scale(self, factor):
self.scalefactor += factor
max_width = 800
max_height = 800
redisKeys = ["spectrum_120","spectrum_10"]
redisData = {}
redisLastHit = msNow() - 99999
r = redis.Redis(
host=ip,
port=port)
white = 16777215
refreshRedis()
if __name__ == "__main__":
particles = []
# particles.append(Attractor(320, 200, 10))
# particles.append(Attractor(100, 100, 10))
win = ParticleViewer(particles)
try:
while True:
win.tick()
refreshRedis()
time.sleep(.03)
except KeyboardInterrupt:
pass

View File

@ -0,0 +1,93 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
A text generators using Hershey fonts
https://pypi.org/project/Hershey-Fonts/
pip3 install Hershey-Fonts
v0.1.0
Font list :
'futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram', 'gothgbt', 'gothgrt',
'gothiceng', 'gothicger', 'gothicita', 'gothitt', 'greek', 'greekc', 'greeks', 'japanese',
'markers', 'mathlow', 'mathupp', 'meteorology', 'music', 'rowmand', 'rowmans', 'rowmant',
'scriptc', 'scripts', 'symbolic', 'timesg', 'timesi', 'timesib', 'timesr', 'timesrb'
LICENCE : CC
by cocoa and Sam Neurohack
'''
from __future__ import print_function
import time
import argparse
import sys
from HersheyFonts import HersheyFonts
name="generator::text"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="Text generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-t","--text",help="Text to display",default="hello",type=str)
argsparser.add_argument("-p","--police",help="Herschey font to use",default="futural",type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
text = args.text
fontname = args.police
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
# Useful variables init.
white = rgb2int((255,255,255))
red = rgb2int((255,0,0))
blue = rgb2int((0,0,255))
green = rgb2int((0,255,0))
color = 65280
shape =[]
Allfonts = ['futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram', 'gothgbt', 'gothgrt', 'gothiceng', 'gothicger', 'gothicita', 'gothitt', 'greek', 'greekc', 'greeks', 'japanese', 'markers', 'mathlow', 'mathupp', 'meteorology', 'music', 'rowmand', 'rowmans', 'rowmant', 'scriptc', 'scripts', 'symbolic', 'timesg', 'timesi', 'timesib', 'timesr', 'timesrb']
thefont = HersheyFonts()
#thefont.load_default_font()
thefont.load_default_font(fontname)
thefont.normalize_rendering(120)
for (x1, y1), (x2, y2) in thefont.lines_for_text(text):
shape.append([x1, -y1+400, color])
shape.append([x2 ,-y2+400, color])
while True:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
#[[14.285714285714286, 100.0, 14.285714285714286, 25.0, 65280], [64.28571428571429, 100.0, 64.28571428571429, 25.0, 65280], [14.285714285714286, 64.28571428571429, 64.28571428571429, 64.28571428571429, 65280], [89.28571428571428, 53.57142857142858, 132.14285714285714, 53.57142857142858, 65280], [132.14285714285714, 53.57142857142858, 132.14285714285714, 60.714285714285715, 65280], [132.14285714285714, 60.714285714285715, 128.57142857142856, 67.85714285714286, 65280], [128.57142857142856, 67.85714285714286, 125.0, 71.42857142857143, 65280], [125.0, 71.42857142857143, 117.85714285714286, 75.0, 65280], [117.85714285714286, 75.0, 107.14285714285714, 75.0, 65280], [107.14285714285714, 75.0, 100.0, 71.42857142857143, 65280], [100.0, 71.42857142857143, 92.85714285714286, 64.28571428571429, 65280], [92.85714285714286, 64.28571428571429, 89.28571428571428, 53.57142857142858, 65280], [89.28571428571428, 53.57142857142858, 89.28571428571428, 46.42857142857143, 65280], [89.28571428571428, 46.42857142857143, 92.85714285714286, 35.714285714285715, 65280], [92.85714285714286, 35.714285714285715, 100.0, 28.571428571428573, 65280], [100.0, 28.571428571428573, 107.14285714285714, 25.0, 65280], [107.14285714285714, 25.0, 117.85714285714286, 25.0, 65280], [117.85714285714286, 25.0, 125.0, 28.571428571428573, 65280], [125.0, 28.571428571428573, 132.14285714285714, 35.714285714285715, 65280]]

View File

@ -0,0 +1,175 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
A Face tracker
v0.1.0
Get all points fom redis /trckr/frame/WSclientID points
LICENCE : CC
by cocoa and Sam Neurohack
'''
from __future__ import print_function
import time
import argparse
import sys
import redis
import ast
name="generator::trckr"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="Face tracking generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-i","--id",help="Trckr client ID",default="0",type=str)
argsparser.add_argument("-s","--server",help="redis server IP (127.0.0.1 by default)", type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
idclient = args.id
if args.server:
redisIP = args.server
else:
redisIP = "127.0.0.1"
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
color = 65280
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
# Useful variables init.
white = rgb2int((255,255,255))
red = rgb2int((255,0,0))
blue = rgb2int((0,0,255))
green = rgb2int((0,255,0))
#
# Redis functions
#
r = redis.StrictRedis(host=redisIP , port=6379, db=0)
# read from redis key
def fromKey(keyname):
return r.get(keyname)
# Write to redis key
def toKey(keyname,keyvalue):
return r.set(keyname,keyvalue)
#
# Trckr faces
#
TrckrPts = [[159.39, 137.68], [155.12, 159.31], [155.56, 180.13], [159.81, 201.6], [170.48, 220.51], [187.46, 234.81], [208.4, 244.68], [229.46, 248.21], [246.44, 244.91], [259.69, 234.83], [270.95, 221.51], [278.54, 204.66], [283.53, 185.63], [286.27, 165.79], [284.72, 144.84], [280.06, 125.01], [274.35, 118.7], [260.71, 117.23], [249.52, 118.86], [182.04, 121.5], [193.63, 114.79], [210.24, 114.77], [222.35, 117.57], [190.6, 137.49], [203.59, 132.42], [214.75, 137.58], [203.04, 140.46], [203.32, 136.53], [272.45, 141.57], [263.33, 135.42], [250.31, 138.89], [262.15, 143.27], [261.99, 139.37], [235.82, 131.74], [221.87, 156.09], [213.66, 165.88], [219.28, 173.53], [236.3, 175.25], [249.02, 174.4], [254.22, 167.81], [248.83, 157.39], [237.94, 147.51], [227.01, 168.39], [245.68, 170.02], [204.94, 197.32], [217.56, 192.77], [228.27, 190.55], [234.66, 192.19], [240.47, 191.09], [247.96, 193.87], [254.52, 199.19], [249.35, 204.25], [242.74, 207.16], [233.2, 207.87], [222.13, 206.52], [212.44, 203.09], [220.34, 198.74], [233.31, 200.04], [244.0, 199.6], [244.27, 197.8], [233.81, 197.44], [220.88, 196.99], [239.57, 162.69], [196.52, 133.86], [210.2, 133.98], [209.43, 139.41], [196.59, 139.47], [268.99, 137.59], [256.36, 136.02], [255.95, 141.5], [267.9, 142.85]]
toKey('/trckr/frame/0',str(TrckrPts))
# get absolute face position points
def getPART(TrckrPts, pose_points):
dots = []
#debug(pose_points)
#debug(TrckrPts)
for dot in pose_points:
dots.append((TrckrPts[dot][0], TrckrPts[dot][1],0))
#debug(dots)
return dots
# Face keypoints
def face(TrckrPts):
pose_points = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
return getPART(TrckrPts, pose_points)
def browL(TrckrPts):
pose_points = [15,16,17,18]
return getPART(TrckrPts, pose_points)
def browR(TrckrPts):
pose_points = [22,21,20,19]
return getPART(TrckrPts, pose_points)
def eyeR(TrckrPts):
pose_points = [25,64,24,63,23,66,26,65,25]
return getPART(TrckrPts, pose_points)
def eyeL(TrckrPts):
pose_points = [28,67,29,68,30,69,31,28]
return getPART(TrckrPts, pose_points)
def pupR(TrckrPts):
pose_points = [27]
return getPART(TrckrPts, pose_points)
def pupL(TrckrPts):
pose_points = [32]
return getPART(TrckrPts, pose_points)
def nose1(TrckrPts):
pose_points = [62,41,33]
return getPART(TrckrPts, pose_points)
def nose2(TrckrPts):
pose_points = [40,39,38,43,37,42,36,35,34]
return getPART(TrckrPts, pose_points)
def mouth(TrckrPts):
pose_points = [50,49,48,47,46,45,44,55,54,53,52,51,50]
return getPART(TrckrPts, pose_points)
def mouthfull(TrckrPts):
pose_points = [50,49,48,47,46,45,44,55,54,53,52,51,50,59,60,61,44,56,57,58,50]
return getPART(TrckrPts, pose_points)
while True:
start = time.time()
shape =[]
points = ast.literal_eval(fromKey('/trckr/frame/'+idclient).decode('ascii'))
shape.append(browL(points))
shape.append(eyeL(points))
shape.append(browR(points))
shape.append(eyeR(points))
shape.append(pupL(points))
shape.append(pupR(points))
shape.append(nose1(points))
shape.append(nose2(points))
shape.append(mouthfull(points))
line = str(shape)
line = line.replace("(",'[')
line = line.replace(")",']')
line = "[{}]".format(line)
print(line, flush=True);
#debug(shape)
#print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))

194
clitools/generators/tunnel.py Executable file
View File

@ -0,0 +1,194 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
Woooh! I'm progressing in a tunnel !
v0.1.0
Use it to test your filters and outputs
LICENCE : CC
by cocoa
'''
from __future__ import print_function
import argparse
import math
import random
import sys
import time
name="generator::tunnel"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="tunnel generator")
argsparser.add_argument("-c","--color",help="Color",default=65280,type=int)
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-i","--interval",help="point per shape interval",default=30,type=int)
argsparser.add_argument("-m","--max-size",help="maximum size for objects",default=400,type=int)
argsparser.add_argument("-r","--randomize",help="center randomization",default=5,type=int)
argsparser.add_argument("-s","--speed",help="point per frame progress",default=3,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int)
argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int)
args = argsparser.parse_args()
centerX = args.centerX
centerY = args.centerY
color = args.color
fps = args.fps
interval = args.interval
max_size = args.max_size
randomize = args.randomize
speed = args.speed
verbose = args.verbose
origSpeed = speed
optimal_looptime = 1 / fps
square = [
[-1,1],
[1,1],
[1,-1],
[-1,-1],
[-1,1]
]
circle = [[1,0],
[0.9238795325112867,0.3826834323650898],
[0.7071067811865476,0.7071067811865475],
[0.38268343236508984,0.9238795325112867],
[0,1.0],
[-0.3826834323650897,0.9238795325112867],
[-0.7071067811865475,0.7071067811865476],
[-0.9238795325112867,0.3826834323650899],
[-1.0,0],
[-0.9238795325112868,-0.38268343236508967],
[-0.7071067811865477,-0.7071067811865475],
[-0.38268343236509034,-0.9238795325112865],
[0,-1.0],
[0.38268343236509,-0.9238795325112866],
[0.707106781186548,-0.707106781186547],
[0.9238795325112872,-0.3826834323650887],
[1,0]]
shape = circle
currentCenter = [centerX, centerY]
centerVector= [0,0]
# tweak random basis
if randomize % 2 == 1:
randomize += 1
debug(name,"randomize:{}".format(randomize))
centerRand = int(math.sqrt(randomize) / 4 ) + 1
debug( name, "centerRand:{}".format(centerRand ) )
class polylineGenerator( object ):
def __init__( self ):
self.polylineList = [[0,[currentCenter[0],currentCenter[1]]]]
self.buf = []
def init(self):
finished = False
while not finished:
finished = self.increment()
debug(name,"init done:{}".format(self.polylineList))
def draw( self ):
self.buf = []
for it_pl, infoList in enumerate(self.polylineList):
size = infoList[0]
center = infoList[1]
for it_sqr, point in enumerate(shape):
x = int( center[0] + point[0]*size )
y = int( center[1] + point[1]*size )
# Add an invisible point in first location
if 0 == it_sqr:
self.buf.append([x,y,0])
self.buf.append([x,y,color])
#debug( name, "buf size:", str(len(self.buf)) )
return self.buf
def increment(self):
global speed
self.buffer = []
min_size = 9999
delList = []
if randomize :
# Change the vector
centerVector[0] += random.randrange( -centerRand,centerRand )
centerVector[1] += random.randrange( -centerRand,centerRand )
# Modify the vector if it is over the limit
if currentCenter[0] + centerVector[0] >= centerX + randomize or currentCenter[0] + centerVector[0] <= centerX - randomize:
centerVector[0] = 0
if currentCenter[1] + centerVector[1] >= centerY + randomize or currentCenter[1] +centerVector[1] <= centerY - randomize:
centerVector[1] = 0
currentCenter[0] += centerVector[0]
currentCenter[1] += centerVector[1]
# Change speed
speed += int( random.randrange( int(-origSpeed),origSpeed ) )
if speed < origSpeed :
speed = origSpeed
elif speed > (origSpeed + randomize / 2) :
speed = origSpeed + randomize / 2
#debug(name, "currentCenter:{} speed:{}".format(currentCenter,speed))
for i, shapeInfo in enumerate(self.polylineList):
size = shapeInfo[0]
# Augment speed with size
"""
size = 0 : += sqrt(speed)
size = half max size : +=speed
"""
if size < max_size / 4:
size += math.pow(speed, 0.1)
elif size < max_size / 3:
size += math.pow(speed, 0.25)
elif size < max_size / 2:
size += math.pow(speed, 0.5)
else:
size += math.pow(speed, 1.25)
if size < min_size : min_size = size
if size > max_size : delList.append(i)
self.polylineList[i][0] = size
for i in delList:
del self.polylineList[i]
#debug(name, "polyline:",self.polylineList)
if min_size >= interval:
debug(name, "new shape")
self.polylineList.append([0,[currentCenter[0],currentCenter[1]]])
# Return True if we delete a shape
if len(delList):
return True
return False
pgen = polylineGenerator()
pgen.init()
while True:
start = time.time()
# Generate
pgen.increment()
# send
pl = pgen.draw()
print(pl, flush=True)
#debug(name,"output:{}".format(pl))
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
#debug(name+" micro sleep:"+str( optimal_looptime - looptime))

126
clitools/runner.py Executable file
View File

@ -0,0 +1,126 @@
#!/usr/bin/python3
import sys
import os
import signal
import subprocess
import time
import tty,termios
import re
import json
from pathlib import Path
import runner_lib as runner
def action_help():
global bindings
print("\nKey\tAction\n--------------------------------------")
for i in bindings:
print(" {}\t{}".format(bindings[i],i))
print("--------------------------------------\n")
bindings={
"Show playlist" : "l",
"Launch [0-x] cmd" : "0-x",
"Previous command" : "p",
"Next command" : "o",
"New command" : "a",
"Edit command" : "e",
"Delete command" : "d",
"Load playlist" : "L",
"Save playlist" : "S",
"Save as new" : "A",
"New playlist" : "N",
"Command help" : "H",
"Kill process Id" : "K",
"Edit Laser Id" : "i",
"Edit Laser Scene" : "s",
"Information" : "I",
"Help" : "h",
"Quit" : "q",
}
## Init user contact
# Main Loop
runner.action_info()
action_help()
print("\n\nLoad a playlist? [Y/n]: ")
if "y" == runner.inkey() :
runner.action_loadPlaylist()
while True:
# Fuck zombies
runner._killBill()
runner._ok("> Next Action?")
k = runner.inkey()
if bindings["Next command"] == k:
runner.action_changeCommand( 1 )
runner.action_runCommand()
elif bindings["Previous command"] == k:
runner.action_changeCommand( -1 )
runner.action_runCommand()
elif re.match( r'^\d+$',k):
runner.action_match(k)
runner.action_runCommand()
elif bindings["New command"] == k:
runner.action_newCommand()
continue
elif bindings["Show playlist"] == k:
runner.action_listAll()
continue
elif bindings["Delete command"] == k:
runner.action_deleteCommand()
continue
elif bindings["Edit command"] == k:
runner.action_listAll()
runner.action_edit()
continue
elif bindings["Load playlist"] == k:
if runner.action_loadPlaylist():
runner.action_listAll()
continue
elif bindings["Save playlist"] == k:
runner.action_savePlaylist()
continue
elif bindings["Save as new"] == k:
runner.action_savePlaylist()
continue
elif bindings["New playlist"] == k:
runner.action_newPlaylist()
continue
elif bindings["Command help"] == k:
runner.action_commandHelp()
continue
elif bindings["Edit Laser Id"] == k:
runner.action_laserId()
continue
elif bindings["Edit Laser Scene"] == k:
runner.action_laserScene()
continue
elif bindings["Kill process Id"] == k:
runner.action_killPid()
continue
elif bindings["Help"] == k:
action_help()
continue
elif bindings["Information"] == k:
runner.action_info()
continue
elif bindings["Quit"] == k:
runner.action_quit()
else:
runner._err("Unexpected key:{}".format(k))
continue

379
clitools/runner_lib.py Normal file
View File

@ -0,0 +1,379 @@
import sys
import os
import signal
import subprocess
import time
import tty,termios
import re
import json
from pathlib import Path
import redis
environ = {
# "REDIS_IP" : "127.0.0.1",
"REDIS_IP" : "192.168.2.44",
"REDIS_PORT" : "6379",
"REDIS_KEY" : "/pl/0/0",
"REDIS_SCENE" : "0",
"REDIS_LASER" : "0"
}
class bcolors:
HL = '\033[31m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class _Getch:
def __call__(self):
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
inkey = _Getch()
def intkey():
try:
i = int( inkey() )
return(i)
except ValueError:
print("Error.")
current_id = 0
current_cmd = ""
process = None
current_filename = ""
currentPlayList = []
playlistsDir = Path("./playlists")
if not playlistsDir.is_dir() : playlistsDir.mkdir()
def ask(q):
print(q)
return inkey()
def _ok(msg):
print( bcolors.BOLD+bcolors.OKBLUE+ msg + bcolors.ENDC)
def _err(msg):
print( bcolors.HL + msg + bcolors.ENDC)
def _kill(process):
if process :
try:
pid = os.getpgid(process.pid)
os.killpg(pid, signal.SIGTERM)
os.killpg(pid, signal.SIGKILL)
os.kill(pid, signal.SIGTERM)
os.kill(pid, signal.SIGKILL)
process.terminate()
process.kill()
except Exception as e:
print("woops:{}".format(e))
def _killBill():
subprocess.run("ps --ppid 1 -fo pid,sess,ppid,cmd | grep 'toRedis.py' | while read pid sid other; do pkill -9 -s $sid; done", shell=True,executable='/bin/bash')
def action_info():
print("""
Welcome to LJ playlist manager
Currently running on
IP : {}
Port : {}
Key : {}
Scene : {}
Laser : {}
""".format(
environ["REDIS_IP"],
environ["REDIS_PORT"],
environ["REDIS_KEY"],
environ["REDIS_SCENE"],
environ["REDIS_LASER"]
))
def action_changeCommand( inc ):
global currentPlayList
global current_id
if 0 == len(currentPlayList):
_err("Empty playlist")
return False
current_id = (current_id + 1) % len(currentPlayList)
return True
def action_match( k ):
global current_id, currentPlayList
if int(k) > (len(currentPlayList) - 1):
print( bcolors.HL + "This key does not exist" + bcolors.ENDC )
return False
else :
_ok("Changed action id to {}.".format(k))
current_id = int(k)
def action_runCommand():
global currentPlayList
global current_id
global process
# Get new command
try:
current_cmd = currentPlayList[current_id]
except IndexError as e:
_err("woops:{}".format(e))
return False
print("\n[!]New command:'{}'\n".format(current_cmd))
# Start subprocess
try :
_kill(process)
process = subprocess.Popen("./_run.sh '"+current_cmd+" | exports/toRedis.py -i $REDIS_IP -k $REDIS_KEY'", shell=True, executable='/bin/bash', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=environ, preexec_fn=os.setsid)
except Exception as e:
print("woops:{}".format(e))
def action_newCommand():
global currentPlayList
print("Enter new command or e(x)it.")
k = input()
# Exit early
if "x" == k:
return(False)
currentPlayList.append(k)
print(bcolors.OKBLUE + "Command added" + bcolors.ENDC)
return True
def action_deleteCommand():
global currentPlayList
print("Select sequence to delete or e(x)it.")
action_listAll()
key = int(input())
# Exit early
if "x" == key:
return(False)
del currentPlayList[key]
return True
def action_listAll():
global currentPlayList, current_cmd, current_id
print("\n--------------------------------------")
for i,seq in enumerate(currentPlayList):
pre=""
suf=""
if current_cmd == seq :
pre = bcolors.HL
suf = bcolors.ENDC
print( pre + "{}\t{}".format(i,seq) + suf )
print("--------------------------------------\n")
def action_edit():
print("Enter the command number to edit, or 'x' to abort.")
k = intkey()
if 'x' == k:
return
print("Enter the next value, or 'x' to abort.")
value = input()
if 'x' == value:
return
currentPlayList[k] = value
def action_loadPlaylist():
global playlistsDir
global currentPlayList
global current_playlist_name
# list files
i=0
file_list = [x for x in playlistsDir.glob("*json")]
if 0 == len(file_list ):
print( bcolors.HL + "Error. No file in path '{}'\n".format(playlistsDir.name))
return False
print("\n Id\tName")
for k,name in enumerate(file_list) :
print(" {}\t{}".format(k,name),flush=True)
# ask file
print("\nChoose a file or e(x)it:")
k = intkey()
if '' == k:
print("Invalid choice: '{}'".format(k))
return
# Exit early
if "x" == k: return(False)
# todo : helper for detecting invalid keys
try:
if k > (len(file_list) - 1):
print( bcolors.HL + "This key '{}' does not exist".format(k) + bcolors.ENDC )
return False
except TypeError:
print( bcolors.HL + "This key '{}' is not valid".format(k) + bcolors.ENDC )
return False
# @todo replace with _loadPlaylist
playlistFile = Path("./playlists/"+file_list[k].name)
currentPlayList = json.loads(playlistFile.read_text())
current_playlist_name = file_list[k].name
current_id = 0
print( bcolors.OKBLUE + "Playlist loaded: {}\n".format(current_playlist_name)+ bcolors.ENDC)
return True
def _loadPlaylist( filename ):
global currentPlayList, current_playlist_name, current_id
try:
playlistFile = Path(filename)
currentPlayList = json.loads(playlistFile.read_text())
current_playlist_name = filename
current_id = 0
_ok("Playlist loaded: {}\n".format(current_playlist_name))
return True
except Exception as e:
_err("_loadPlaylist error when loading '{}':{}".format(filename,e))
def action_newPlaylist():
global playlistsDir
global currentPlayList
# ask for name
print("Enter new playlist name (without.json) or e(x)it question?")
k = input()
# Exit early
if "x" == k:
return(False)
# save file
currentPlayList = []
_savePlaylist( k+".json" )
currentPlayList = []
pass
def _savePlaylist( playlistname ):
global currentPlayList
filepath = Path("playlists/{}".format(playlistname))
with filepath.open("w", encoding ="utf-8") as f:
f.write(json.dumps(currentPlayList, indent=4, sort_keys=True))
return(True)
def action_savePlaylist( name=False ):
global current_playlist_name
playlist_name = name if name else current_playlist_name
if not playlist_name :
_err("No name found.")
return False
try:
_savePlaylist(playlist_name)
print( bcolors.OKBLUE + "\nSaved as '{}'.\n".format(playlist_name) + bcolors.ENDC)
except Exception as e:
print("woops:{}".format(e))
return False
def action_commandHelp():
global playlistsDir
# iterate through files
file_list=[]
for folder in ["generators","filters","exports"]:
p = Path("./"+folder)
for plFile in Path("./"+folder).iterdir() :
if re.match("^.*py$",plFile.name):
file_list.append(os.path.join(folder,plFile.name))
print("\n Id\tFile")
for k,filename in enumerate(file_list):
print(" {}\t{}".format(k,filename))
print("\nChoose a file:")
k = int(input())
print("\n-----------------------------------------------\n")
subprocess.run("python3 "+file_list[k]+" -h", shell=True, executable='/bin/bash')
print("\n-----------------------------------------------\n")
def _setKey( laser=0, scene=0 ):
global environ
laser = laser if laser else environ["REDIS_LASER"]
scene = scene if scene else environ["REDIS_SCENE"]
new_key = "/pl/{}/{}".format(scene,laser)
environ["REDIS_KEY"] = new_key
print("Sending new key '{}'".format(new_key))
def action_laserId():
k = int(ask("Enter the LJ Laser id [0-3]"))
_setKey( laser = k )
def action_laserScene():
k = int(ask("Enter the LJ Scene id [0-3]"))
_setKey( scene = k )
def action_killPid():
print("Enter pid to kill")
kill_pid = input()
subprocess.run("pkill -9 -s $(awk '{print $6}' /proc/$kill_pid/stat)", shell=True,executable='/bin/bash', env={"kill_pid":kill_pid})
def action_quit():
print("Quit [Y/n]?")
global process
quit = inkey()
if quit != "n":
_kill(process)
sys.exit(1)

87
clitools/runner_midi.py Executable file
View File

@ -0,0 +1,87 @@
#!/usr/bin/python3
import argparse
import re
import redis
import runner_lib as runner
import time
novationRows = [
[ 0, 1, 2, 3, 4, 5, 6, 7 ],
[ *range(16,24)],
[ *range(32,40)],
[ *range(48,56)]
]
argsparser = argparse.ArgumentParser(description="Playlist midi")
argsparser.add_argument("playlist",help="JSON playlist file ",type=str)
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
argsparser.add_argument("-r","--row",help="Row of Novation pad. Default:1 ",default=1,type=str)
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
argsparser.add_argument("-l","--laser",help="Laser number. Default:0 ",default=0,type=int)
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
argsparser.add_argument("-s","--scene",help="Laser scene. Default:0 ",default=0,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
args = argsparser.parse_args()
ip = args.ip
port = args.port
key = args.key
verbose=args.verbose
laser = args.laser
scene = args.scene
playlist = args.playlist
row = args.row - 1
rowKeys = novationRows[row]
# Subscriber
r = redis.StrictRedis(host=ip, port=port, db=0)
p = r.pubsub()
p.subscribe('/midi/last_event')
runner._killBill()
# Set Laser and scene
runner._setKey( laser = laser, scene = scene)
# Load playlist
runner._loadPlaylist( playlist )
print("Loaded playlist : {}".format(runner.currentPlayList))
runner.action_info()
runner.current_id = -1
while True:
runner._killBill()
message = p.get_message()
if message:
#runner._ok ("Subscriber: %s" % message['data'])
# b'/midi/noteon/0/19/127'
match = re.match(".*/([0-9]+)/[0-9]+",str(message['data']))
if not match:
continue
key = int(match.group(1))
# Check if the event is for us
if key not in rowKeys:
print("key {} not in {} ".format(key,rowKeys))
continue
try:
command_id = rowKeys.index(key)
cmd = runner.currentPlayList[command_id]
if command_id != runner.current_id :
runner._ok("Launching command #{}\n Previous was {}\n Cmd:{}".format(command_id,runner.current_id,cmd))
runner.action_match(command_id)
runner.action_runCommand()
else :
runner._err("Not running {} : already running.".format(command_id))
except Exception as e :
print("Woops.",e)

273
nano.py Normal file
View File

@ -0,0 +1,273 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJnano
v0.1b
Open localhost websocket 9001
Write Redis keys :
- '/trckr/frame/WSclientid' : TrckrPts
- 'WSclients' : [{'id': 1, 'handler': <websocket_server.WebSocketHandler object at 0x114d35880>, 'address': ('127.0.0.1', 62718)}, {'id': 2, 'handler': <websocket_server.WebSocketHandler object at 0x114d35d60>, 'address': ('127.0.0.1', 62720)}]
'''
from __future__ import print_function
import websocket
import time
import argparse
import traceback
import sys
import random
from websocket_server import WebsocketServer
import redis
from socket import *
try:
import thread
except ImportError:
import _thread as thread
name = "exports::ljnano"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="ljnano v0.1b help mode")
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-s","--server",help="WS server IP (127.0.0.1 by default)", type=str)
argsparser.add_argument("-p","--port",help="WS port to bind to (9001 by default)", type=str)
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
args = argsparser.parse_args()
key = args.key
if args.verbose:
verbose = True
else:
verbose = False
if args.server:
serverIP = args.server
else:
serverIP = "127.0.0.1"
if args.port:
wsPORT = args.port
else:
wsPORT = 9001
debug("")
debug("LJnano v0.1b")
points0 = "[(150.0, 230.0, 65280), (170.0, 170.0, 65280), (230.0, 170.0, 65280), (210.0, 230.0, 65280), (150.0, 230.0, 65280)]"
points1 = "[(180.0, 230.0, 65280), (200.0, 170.0, 65280), (180.0, 230.0, 65280)]"
points2 = "[(170.0, 190.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (230.0, 200.0, 65280), (170.0, 230.0, 65280), (230.0, 230.0, 65280)]"
points3 = "[(170.0, 170.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (200.0, 200.0, 65280), (230.0, 210.0, 65280), (200.0, 230.0, 65280), (170.0, 230.0, 65280)]"
points = [points0, points1, points2, points3]
LaserNumber = 1
SceneNumber = 0
Laser = 0
#
# Redis
#
r = redis.StrictRedis(host=serverIP , port=6379, db=0)
# read from redis key
def fromKey(keyname):
return r.get(keyname)
# Write to redis key
def toKey(keyname,keyvalue):
#print(keyname,keyvalue)
# Store encoded data in Redis
return r.set(keyname,keyvalue)
# event key can be like "/midi/last_event"
# Send en event to eventkey (publish)
def toKeyevent(eventkey,eventname):
print("redis midi event key :", eventname)
r.publish(eventkey, eventname)
# examples
#if toKey("/clientkey","/pl/"+str(gstt.SceneNumber)+"/")==True:
# print("sent clientkey : /pl/"+str(gstt.SceneNumber)+"/")
# toKeyevent("/midi/noteon/"+str(MidiChannel)+"/"+str(MidiNote)+"/"+str(MidiVel))
def custom_handler(message):
# do_something with the message
print(message)
'''
p.psubscribe(**{'hello*':custom_handler})
thread = p.run_in_thread(sleep_time=0.001)
'''
# Change current laser.
def NoteOn(note):
debug("NoteOn", note)
# Change laser client
if note < 8:
Laser = note
debug("Current Laser switched to", Laser)
sendWSall("/status Laser " + str(Laser))
sendWSall("/simul" +" "+ points[Laser])
# Change PL displayed on webui
if note > 23 and note < 32:
if note - 24 > LaserNumber -1:
debug("Only",LaserNumber,"lasers asked, you dum ass !")
sendWSall("/redstatus No Laser"+str(note-24))
sendWSall("/laser "+str(LaserNumber-1))
sendWSall("/simul" +" "+ points[LaserNumber-1])
else:
Laser = note -24
debug("Current Laser switched to", Laser)
sendWSall("/status Laser " + str(Laser))
sendWSall("/simul" +" "+ points[Laser])
def sendbroadcast():
debug("Sending broadcast")
cs = socket(AF_INET, SOCK_DGRAM)
cs.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
cs.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
cs.sendto("LJ nano 0.1".encode(), ("255.255.255.255", 54545))
#
# Websocket server
#
def client_list():
clients = []
for client in wserver.clients_list():
clients.append(client['id'])
#print(clients)
return str(clients)
# Called for every WS client connecting (after handshake)
def new_client(client, wserver):
debug("WS server got new client connected and was given id %d" % client['id'])
toKey('WSclients', client_list())
sendWSall("/status Hello " + str(client['id']))
sendWSall("/laser "+str(0))
sendWSall("/lack/" + str(0) + " 3")
sendWSall("/lstt/" + str(0) + " 3")
sendWSall("/simul" +" "+ points[0])
# Called for every WS client disconnecting
def client_left(client, wserver):
try:
debug("WS server had client(%d) disconnected" % client['id'])
except:
debug("Something weird if coming from",client,"on the wire...")
pass
# Called for each WS received message.
def message_received(client, wserver, message):
#if len(message) > 200:
# message = message[:200]+'..'
oscpath = message.split(" ")
#print("WS Client", client['id'], "said :", message, "splitted in an oscpath :", oscpath)
#if( verbose == True ):
# debug("WS server's client", client['id'], "said :", message)
if message[0:5] == "/simu":
debug("WS server sending to simu page :",message)
sendWSall(message)
if message[0:7] == "/noteon":
oscpath = message.split(" ")
debug("WS server got new laser", int(oscpath[1]))
NoteOn(int(oscpath[1]))
# Get WS /aurora/trckr/frame layernumber framenumber points
# Set redis /trckr/frame/WSclientID points
if message.find('trckr/frame') > 0:
args = message.split(" ")
TrckrPts = []
for dot in range(3,len(args)-1,2):
TrckrPts.append([float(args[dot]), float(args[dot+1])])
debug('/trckr/frame/'+str(client['id'])+" "+str(TrckrPts))
toKey('/trckr/frame/'+str(client['id']),str(TrckrPts))
def sendWSall(message):
if ( verbose == True ):
debug("WS server is sending to all : %s" % (message))
wserver.send_message_to_all(message)
def LaunchServer(*args):
global wserver
try:
# Websocket server
wserver = WebsocketServer(wsPORT,host=serverIP)
debug("Launching Websocket server...")
debug("at", serverIP, "port",wsPORT)
wserver.set_fn_new_client(new_client)
wserver.set_fn_client_left(client_left)
wserver.set_fn_message_received(message_received)
debug("LJ local server running...")
debug("")
# websocket server loop
wserver.run_forever()
except EOFError:
debug("break")# no more information
finally:
debug("sendWS terminating...")
#
# Launch WS server
#
if __name__ == "__main__":
try:
LaunchServer()
except Exception:
debug("nano Exception")
traceback.print_exc()

374
websocket_server.py Normal file
View File

@ -0,0 +1,374 @@
# Author: Johan Hanssen Seferidis
# License: MIT
import sys
import struct
from base64 import b64encode
from hashlib import sha1
import logging
from socket import error as SocketError
import errno
if sys.version_info[0] < 3:
from SocketServer import ThreadingMixIn, TCPServer, StreamRequestHandler
else:
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
logger = logging.getLogger(__name__)
logging.basicConfig()
'''
+-+-+-+-+-------+-+-------------+-------------------------------+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| Payload Data continued ... |
+---------------------------------------------------------------+
'''
FIN = 0x80
OPCODE = 0x0f
MASKED = 0x80
PAYLOAD_LEN = 0x7f
PAYLOAD_LEN_EXT16 = 0x7e
PAYLOAD_LEN_EXT64 = 0x7f
OPCODE_CONTINUATION = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE_CONN = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xA
# -------------------------------- API ---------------------------------
class API():
def run_forever(self):
try:
logger.info("Listening on port %d for clients.." % self.port)
self.serve_forever()
except KeyboardInterrupt:
self.server_close()
logger.info("Server terminated.")
except Exception as e:
logger.error(str(e), exc_info=True)
exit(1)
def new_client(self, client, server):
pass
def client_left(self, client, server):
pass
def message_received(self, client, server, message):
pass
def set_fn_new_client(self, fn):
self.new_client = fn
def set_fn_client_left(self, fn):
self.client_left = fn
def set_fn_message_received(self, fn):
self.message_received = fn
def send_message(self, client, msg):
self._unicast_(client, msg)
def send_message_to_all(self, msg):
self._multicast_(msg)
def clients_list(self):
return self.clients
# ------------------------- Implementation -----------------------------
class WebsocketServer(ThreadingMixIn, TCPServer, API):
"""
A websocket server waiting for clients to connect.
Args:
port(int): Port to bind to
host(str): Hostname or IP to listen for connections. By default 127.0.0.1
is being used. To accept connections from any client, you should use
0.0.0.0.
loglevel: Logging level from logging module to use for logging. By default
warnings and errors are being logged.
Properties:
clients(list): A list of connected clients. A client is a dictionary
like below.
{
'id' : id,
'handler' : handler,
'address' : (addr, port)
}
"""
allow_reuse_address = True
daemon_threads = True # comment to keep threads alive until finished
clients = []
id_counter = 0
def __init__(self, port, host='127.0.0.1', loglevel=logging.WARNING):
logger.setLevel(loglevel)
TCPServer.__init__(self, (host, port), WebSocketHandler)
self.port = self.socket.getsockname()[1]
def _message_received_(self, handler, msg):
self.message_received(self.handler_to_client(handler), self, msg)
def _ping_received_(self, handler, msg):
handler.send_pong(msg)
def _pong_received_(self, handler, msg):
pass
def _new_client_(self, handler):
self.id_counter += 1
client = {
'id': self.id_counter,
'handler': handler,
'address': handler.client_address
}
self.clients.append(client)
self.new_client(client, self)
def _client_left_(self, handler):
client = self.handler_to_client(handler)
self.client_left(client, self)
if client in self.clients:
self.clients.remove(client)
def _unicast_(self, to_client, msg):
to_client['handler'].send_message(msg)
def _multicast_(self, msg):
for client in self.clients:
self._unicast_(client, msg)
def handler_to_client(self, handler):
for client in self.clients:
if client['handler'] == handler:
return client
class WebSocketHandler(StreamRequestHandler):
def __init__(self, socket, addr, server):
self.server = server
StreamRequestHandler.__init__(self, socket, addr, server)
def setup(self):
StreamRequestHandler.setup(self)
self.keep_alive = True
self.handshake_done = False
self.valid_client = False
def handle(self):
while self.keep_alive:
if not self.handshake_done:
self.handshake()
elif self.valid_client:
self.read_next_message()
def read_bytes(self, num):
# python3 gives ordinal of byte directly
bytes = self.rfile.read(num)
if sys.version_info[0] < 3:
return map(ord, bytes)
else:
return bytes
def read_next_message(self):
try:
b1, b2 = self.read_bytes(2)
except SocketError as e: # to be replaced with ConnectionResetError for py3
if e.errno == errno.ECONNRESET:
logger.info("Client closed connection.")
print("Error: {}".format(e))
self.keep_alive = 0
return
b1, b2 = 0, 0
except ValueError as e:
b1, b2 = 0, 0
fin = b1 & FIN
opcode = b1 & OPCODE
masked = b2 & MASKED
payload_length = b2 & PAYLOAD_LEN
if opcode == OPCODE_CLOSE_CONN:
logger.info("Client asked to close connection.")
self.keep_alive = 0
return
if not masked:
logger.warn("Client must always be masked.")
self.keep_alive = 0
return
if opcode == OPCODE_CONTINUATION:
logger.warn("Continuation frames are not supported.")
return
elif opcode == OPCODE_BINARY:
logger.warn("Binary frames are not supported.")
return
elif opcode == OPCODE_TEXT:
opcode_handler = self.server._message_received_
elif opcode == OPCODE_PING:
opcode_handler = self.server._ping_received_
elif opcode == OPCODE_PONG:
opcode_handler = self.server._pong_received_
else:
logger.warn("Unknown opcode %#x." % opcode)
self.keep_alive = 0
return
if payload_length == 126:
payload_length = struct.unpack(">H", self.rfile.read(2))[0]
elif payload_length == 127:
payload_length = struct.unpack(">Q", self.rfile.read(8))[0]
masks = self.read_bytes(4)
message_bytes = bytearray()
for message_byte in self.read_bytes(payload_length):
message_byte ^= masks[len(message_bytes) % 4]
message_bytes.append(message_byte)
opcode_handler(self, message_bytes.decode('utf8'))
def send_message(self, message):
self.send_text(message)
def send_pong(self, message):
self.send_text(message, OPCODE_PONG)
def send_text(self, message, opcode=OPCODE_TEXT):
"""
Important: Fragmented(=continuation) messages are not supported since
their usage cases are limited - when we don't know the payload length.
"""
# Validate message
if isinstance(message, bytes):
message = try_decode_UTF8(message) # this is slower but ensures we have UTF-8
if not message:
logger.warning("Can\'t send message, message is not valid UTF-8")
return False
elif sys.version_info < (3,0) and (isinstance(message, str) or isinstance(message, unicode)):
pass
elif isinstance(message, str):
pass
else:
logger.warning('Can\'t send message, message has to be a string or bytes. Given type is %s' % type(message))
return False
header = bytearray()
payload = encode_to_UTF8(message)
payload_length = len(payload)
# Normal payload
if payload_length <= 125:
header.append(FIN | opcode)
header.append(payload_length)
# Extended payload
elif payload_length >= 126 and payload_length <= 65535:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT16)
header.extend(struct.pack(">H", payload_length))
# Huge extended payload
elif payload_length < 18446744073709551616:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT64)
header.extend(struct.pack(">Q", payload_length))
else:
raise Exception("Message is too big. Consider breaking it into chunks.")
return
self.request.send(header + payload)
def read_http_headers(self):
headers = {}
# first line should be HTTP GET
http_get = self.rfile.readline().decode().strip()
assert http_get.upper().startswith('GET')
# remaining should be headers
while True:
header = self.rfile.readline().decode().strip()
if not header:
break
head, value = header.split(':', 1)
headers[head.lower().strip()] = value.strip()
return headers
def handshake(self):
headers = self.read_http_headers()
try:
assert headers['upgrade'].lower() == 'websocket'
except AssertionError:
self.keep_alive = False
return
try:
key = headers['sec-websocket-key']
except KeyError:
logger.warning("Client tried to connect but was missing a key")
self.keep_alive = False
return
response = self.make_handshake_response(key)
self.handshake_done = self.request.send(response.encode())
self.valid_client = True
self.server._new_client_(self)
@classmethod
def make_handshake_response(cls, key):
return \
'HTTP/1.1 101 Switching Protocols\r\n'\
'Upgrade: websocket\r\n' \
'Connection: Upgrade\r\n' \
'Sec-WebSocket-Accept: %s\r\n' \
'\r\n' % cls.calculate_response_key(key)
@classmethod
def calculate_response_key(cls, key):
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
hash = sha1(key.encode() + GUID.encode())
response_key = b64encode(hash.digest()).strip()
return response_key.decode('ASCII')
def finish(self):
self.server._client_left_(self)
def encode_to_UTF8(data):
try:
return data.encode('UTF-8')
except UnicodeEncodeError as e:
logger.error("Could not encode data to UTF-8 -- %s" % e)
return False
except Exception as e:
raise(e)
return False
def try_decode_UTF8(data):
try:
return data.decode('utf-8')
except UnicodeDecodeError:
return False
except Exception as e:
raise(e)

316
www/LJ.js Normal file
View File

@ -0,0 +1,316 @@
//
// LJ.js v0.7.1
//
// LJ websocket address. IP will be updated at LJ startup according to LJ.conf wwwIP
var LJ = websocket_uri
//
// Central horizontal menu
//
function noMenu() {
var x = document.getElementById("align");
x.value = 0 ;
var x = document.getElementById("run");
x.value = 0 ;
var x = document.getElementById("simu");
x.value = 0 ;
var x = document.getElementById("live");
x.value = 0 ;
// Hide all possible main central grids.
var x = document.getElementById("mgalign");
x.style.display = "none";
var x = document.getElementById("mgsimu");
x.style.display = "none";
var x = document.getElementById("mgrun");
x.style.display = "none";
var x = document.getElementById("mglive");
x.style.display = "none";
}
function showAlign() {
noMenu();
var x = document.getElementById("mgalign");
x.style.display = "grid";
var x = document.getElementById("align");
x.value = 1 ;
}
function showRun() {
noMenu();
var x = document.getElementById("mgrun");
x.style.display = "grid";
var x = document.getElementById("run");
x.value = 1 ;
}
function showCanvas() {
noMenu();
var x = document.getElementById("mgsimu");
x.style.display = "grid";
var x = document.getElementById("cnvbuttons");
x.style.display = "grid";
var x = document.getElementById("simu");
x.value = 1 ;
}
function showLive() {
noMenu();
var x = document.getElementById("mglive");
x.style.display = "grid";
var x = document.getElementById("live");
x.value = 1 ;
}
//
// SimuUIs
//
function nosimuUI() {
// Hide all possible main central grids.
var x = document.getElementById("planetUI");
x.style.display = "none";
var x = document.getElementById("nozoidUI");
x.style.display = "none";
var x = document.getElementById("aiUI");
x.style.display = "none";
var x = document.getElementById("lissaUI");
x.style.display = "none";
var x = document.getElementById("vjUI");
x.style.display = "none";
var x = document.getElementById("poseUI");
x.style.display = "none";
var x = document.getElementById("wordsUI");
x.style.display = "none";
var x = document.getElementById("pluginsUI");
x.style.display = "none";
}
function showplanetUI() {
nosimuUI();
var x = document.getElementById("planetUI");
x.style.display = "grid";
_WS.send("/planet/ping");
}
function shownozoidUI() {
nosimuUI();
var x = document.getElementById("nozoidUI");
x.style.display = "grid";
_WS.send("/nozoid/ping");
}
function showaiUI() {
nosimuUI();
var x = document.getElementById("aiUI");
x.style.display = "grid";
}
function showlissaUI() {
nosimuUI();
var x = document.getElementById("lissaUI");
x.style.display = "grid";
}
function showvjUI() {
nosimuUI();
var x = document.getElementById("vjUI");
x.style.display = "grid";
_WS.send("/bank0/ping");
}
function showposeUI() {
nosimuUI();
var x = document.getElementById("poseUI");
x.style.display = "grid";
_WS.send("/pose/ping");
}
function showwordsUI() {
nosimuUI();
var x = document.getElementById("wordsUI");
x.style.display = "grid";
_WS.send("/words/ping");
}
function showpluginsUI() {
nosimuUI();
var x = document.getElementById("pluginsUI");
x.style.display = "grid";
//_WS.send("/words/ping");
}
//
// Button clicked
//
function buttonClicked(clicked_id) {
_WS.send("/" + clicked_id);
// update Canvas right part of maingrid
if (clicked_id === "planet/planetUI") {
showplanetUI();
}
if (clicked_id === "nozoid/nozoidUI") {
shownozoidUI();
}
if (clicked_id === "ai/aiUI") {
showaiUI();
}
if (clicked_id === "lissa/lissaUI") {
showlissaUI();
}
if (clicked_id === "bank0/vjUI") {
showvjUI();
}
if (clicked_id === "pose/poseUI") {
showposeUI();
}
if (clicked_id === "words/wordsUI") {
showwordsUI();
}
if (clicked_id === "lj/pluginsUI") {
showpluginsUI();
}
if (clicked_id === "nozoid/down 50") {
var x = document.getElementById("nozoid/down 50");
x.value = 0 ;
}
}
//
// Matrix selection
//
function MatrixSelect(clicked_value) {
console.log("/" + clicked_value);
_WS.send("/" + clicked_value);
}
//
// Forms submits
//
function onSubmit(clicked_id) {
var input = document.getElementById(clicked_id);
console.log("/" + clicked_id + " " + input.value);
_WS.send("/" + clicked_id + " " + input.value);
_WS.showout("/" + clicked_id + " " + input.value);
}
//
// Websocket handler
//
var pl = "";
var pl2 = new Array();
var _WS = {
uri: LJ,
ws: null,
init : function (e) {
_WS.s = new WebSocket(_WS.uri);
_WS.s.onopen = function (e) { _WS.onOpen(e); };
_WS.s.onclose = function (e) { _WS.onClose(e); };
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
_WS.s.onerror = function (e) { _WS.onError(e); };
},
onOpen: function () {
_WS.showout(_WS.uri);
_WS.showout('CONNECTED');
document.getElementById("on").value = 1;
},
onClose: function () {
_WS.showout('DISCONNECTED');
document.getElementById("on").value = 0;
document.getElementById("lstt/0").value = 0;
document.getElementById("lstt/1").value = 0;
document.getElementById("lstt/2").value = 0;
document.getElementById("lstt/3").value = 0;
document.getElementById("lack/0").value = 0;
document.getElementById("lack/1").value = 0;
document.getElementById("lack/2").value = 0;
document.getElementById("lack/3").value = 0;
},
onMessage: function (e) {
var res = e.data.split(" ");
//console.log(e.data)
//console.log(res[0].substring(0,6))
//console.log(res)
//console.log(res[0].slice(1))
switch (res[0].substring(0,6)) {
case "/statu":
_WS.showstatus(e.data.slice(8));
break;
case "/simul":
pl = e.data.slice(7);
//console.log(pl)
pl2 = eval(pl.replace(/[()]/g, ''));
break;
case "/plpoi":
//console.log("plpoint");
break;
default:
//console.log(e);
document.getElementById(res[0].slice(1)).value = res[1];
_WS.showin(e.data);
}
},
onError: function (e) {
_WS.showin('<span style="color: red;">ERROR:</span> ' + e.data);
},
showin: function (message) {
var divtext = document.getElementById('showin');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showout: function (message) {
var divtext = document.getElementById('showout');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showstatus: function (message) {
var divtext = document.getElementById('showstatus');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
send: function (message) {
if (!message.length) {
alert('Empty message not allowed !');
} else {
_WS.showout(message);
_WS.s.send(message);
}
},
close: function () {
_WS.showout('GOODBYE !');
_WS.s.close();
}
};
window.addEventListener('load', _WS.init, false);

BIN
www/android-chrome-192x192.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
www/android-chrome-512x512.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
www/apple-touch-icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

1
www/config.js Normal file
View File

@ -0,0 +1 @@
websocket_uri = "ws://192.168.2.43:9001/"

Binary file not shown.

View File

@ -0,0 +1,828 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg>
<metadata>
Created by FontForge 20120731 at Sat Jun 6 06:35:01 2020
By www
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License (click below for details).
The font was created by Michal Sulik using Fony, bdfresize, potrace, autotrace and Fontforge.
If you use this font in any public work, please be so nice and leave a link in comments on http://msulik.deviantart.com/.
</metadata>
<defs>
<font id="BusLedDisplaySmall" horiz-adv-x="458" >
<font-face
font-family="Bus Led Display Small"
font-weight="500"
font-variant="small-caps"
font-stretch="normal"
units-per-em="1024"
panose-1="2 0 6 3 0 0 0 0 0 0"
ascent="819"
descent="-205"
x-height="461"
cap-height="461"
bbox="34 -154 716 682"
underline-thickness="50"
underline-position="-101"
unicode-range="U+0001-02DD"
/>
<missing-glyph horiz-adv-x="374"
d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
<glyph glyph-name=".notdef" horiz-adv-x="374"
d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
<glyph glyph-name=".null" horiz-adv-x="0"
/>
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="341"
/>
<glyph glyph-name="$000" horiz-adv-x="49"
/>
<glyph glyph-name="$000" horiz-adv-x="49"
/>
<glyph glyph-name="$001" horiz-adv-x="49"
/>
<glyph glyph-name="$002" horiz-adv-x="49"
/>
<glyph glyph-name="$003" horiz-adv-x="49"
/>
<glyph glyph-name="$004" horiz-adv-x="49"
/>
<glyph glyph-name="$005" horiz-adv-x="49"
/>
<glyph glyph-name="$006" horiz-adv-x="49"
/>
<glyph glyph-name="$007" horiz-adv-x="49"
/>
<glyph glyph-name="$008" horiz-adv-x="49"
/>
<glyph glyph-name="$009" unicode="&#x9;" horiz-adv-x="49"
/>
<glyph glyph-name="$010" unicode="&#xa;" horiz-adv-x="49"
/>
<glyph glyph-name="$011" horiz-adv-x="49"
/>
<glyph glyph-name="$012" horiz-adv-x="49"
/>
<glyph glyph-name="$013" unicode="&#xd;" horiz-adv-x="49"
/>
<glyph glyph-name="$014" horiz-adv-x="49"
/>
<glyph glyph-name="$015" horiz-adv-x="49"
/>
<glyph glyph-name="$016" horiz-adv-x="49"
/>
<glyph glyph-name="$017" horiz-adv-x="49"
/>
<glyph glyph-name="$018" horiz-adv-x="49"
/>
<glyph glyph-name="$019" horiz-adv-x="49"
/>
<glyph glyph-name="$020" horiz-adv-x="49"
/>
<glyph glyph-name="$021" horiz-adv-x="49"
/>
<glyph glyph-name="$022" horiz-adv-x="49"
/>
<glyph glyph-name="$023" horiz-adv-x="49"
/>
<glyph glyph-name="$024" horiz-adv-x="49"
/>
<glyph glyph-name="$025" horiz-adv-x="49"
/>
<glyph glyph-name="$026" horiz-adv-x="49"
/>
<glyph glyph-name="$027" horiz-adv-x="49"
/>
<glyph glyph-name="$028" horiz-adv-x="49"
/>
<glyph glyph-name="$029" horiz-adv-x="49"
/>
<glyph glyph-name="$030" horiz-adv-x="49"
/>
<glyph glyph-name="$031" horiz-adv-x="49"
/>
<glyph glyph-name="$032" unicode=" " horiz-adv-x="199"
/>
<glyph glyph-name="$033" unicode="!" horiz-adv-x="150"
d="M50 0v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$034" unicode="&#x22;" horiz-adv-x="355"
d="M204 307v51h51v-51h-51zM50 307v51h51v-51h-51zM255 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$035" unicode="#" horiz-adv-x="662"
d="M357 0v51h52v-51h-52zM153 0v51h51v-51h-51zM460 102v52h51v-52h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h51v-52h-51zM409 205v51h51v-51h-51zM204 205v51h51v-51h-51zM562 307v51h51v-51h-51zM460 307v51h51v-51h-51z
M357 307v51h52v-51h-52zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM460 410v51h51v-51h-51zM255 410v51h51v-51h-51z" />
<glyph glyph-name="$036" unicode="$"
d="M204 -102v51h51v-51h-51zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM204 102v52h51v-52h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM204 307v51h51v-51h-51z
M357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM204 512v51h51v-51h-51z" />
<glyph glyph-name="$037" unicode="%" horiz-adv-x="560"
d="M409 0v51h51v-51h-51zM153 0v51h51v-51h-51zM306 51v51h51v-51h-51zM460 102v52h51v-52h-51zM204 102v52h51v-52h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 256v51h51v-51h-51zM306 307v51h51v-51h-51zM50 307v51h51v-51h-51zM204 358v52h51v-52h-51z
M357 410v51h52v-51h-52zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$038" unicode="&#x26;" horiz-adv-x="611"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM460 51v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM409 154v51h51v-51h-51zM50 154v51h51v-51h-51zM153 205v51h51v-51h-51zM511 256v51h51v-51h-51zM409 256v51h51v-51h-51zM306 256v51h51v-51h-51zM50 256
v51h51v-51h-51zM409 358v52h51v-52h-51zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="$039" unicode="'" horiz-adv-x="150"
d="M50 358v52h51v-52h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$040" unicode="(" horiz-adv-x="202"
d="M101 0v51h52v-51h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$041" unicode=")" horiz-adv-x="202"
d="M50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$042" unicode="*" horiz-adv-x="560"
d="M255 51v51h51v-51h-51zM460 154v51h51v-51h-51zM255 154v51h51v-51h-51zM50 154v51h51v-51h-51zM357 205v51h52v-51h-52zM153 205v51h51v-51h-51zM255 256v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM460 358v52h51v-52h-51zM255 358v52h51v-52h-51z
M50 358v52h51v-52h-51zM255 461v51h51v-51h-51z" />
<glyph glyph-name="$043" unicode="+" horiz-adv-x="355"
d="M153 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM153 307v51h51v-51h-51z" />
<glyph glyph-name="$044" unicode="," horiz-adv-x="202"
d="M50 -51v51h51v-51h-51zM101 51v51h52v-51h-52z" />
<glyph glyph-name="$045" unicode="-" horiz-adv-x="355"
d="M255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51z" />
<glyph glyph-name="$046" unicode="." horiz-adv-x="150"
d="M50 0v51h51v-51h-51z" />
<glyph glyph-name="$047" unicode="/" horiz-adv-x="406"
d="M50 -51v51h51v-51h-51zM101 51v51h52v-51h-52zM153 154v51h51v-51h-51zM204 256v51h51v-51h-51zM255 358v52h51v-52h-51zM306 461v51h51v-51h-51z" />
<glyph glyph-name="$048" unicode="0"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM153 154v51h51v-51h-51zM50 154v51h51v-51h-51zM357 256v51h52v-51h-52zM255 256v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358
v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="$049" unicode="1" horiz-adv-x="304"
d="M204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM153 102v52h51v-52h-51zM153 205v51h51v-51h-51zM153 307v51h51v-51h-51zM50 358v52h51v-52h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="$050" unicode="2"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM306 154v51h51v-51h-51zM204 154v51h51v-51h-51zM357 256v51h52v-51h-52zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51z" />
<glyph glyph-name="$051" unicode="3" horiz-adv-x="406"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$052" unicode="4"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM255 102v52h51v-52h-51zM357 154v51h52v-51h-52zM153 154v51h51v-51h-51zM50 154v51h51v-51h-51zM255 205v51h51v-51h-51zM101 256v51h52v-51h-52zM255 307v51h51v-51h-51zM101 358v52h52v-52h-52zM255 410v51h51v-51h-51z
" />
<glyph glyph-name="$053" unicode="5"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM255 256v51h51v-51h-51zM153 256v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$054" unicode="6"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM255 256v51h51v-51h-51zM153 256v51h51v-51h-51zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51z
M204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$055" unicode="7"
d="M153 0v51h51v-51h-51zM204 102v52h51v-52h-51zM255 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$056" unicode="8"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM306 205v51h51v-51h-51zM204 205v51h51v-51h-51zM101 205v51h52v-51h-52zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51z
M204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$057" unicode="9"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51z
M255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="$058" unicode=":" horiz-adv-x="150"
d="M50 102v52h51v-52h-51zM50 307v51h51v-51h-51z" />
<glyph glyph-name="$059" unicode=";" horiz-adv-x="202"
d="M50 -51v51h51v-51h-51zM101 51v51h52v-51h-52zM101 256v51h52v-51h-52z" />
<glyph glyph-name="$060" unicode="&#x3c;" horiz-adv-x="304"
d="M204 -102v51h51v-51h-51zM153 0v51h51v-51h-51zM101 102v52h52v-52h-52zM50 205v51h51v-51h-51zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM204 512v51h51v-51h-51z" />
<glyph glyph-name="$061" unicode="=" horiz-adv-x="355"
d="M255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 256v51h51v-51h-51zM153 256v51h51v-51h-51zM50 256v51h51v-51h-51z" />
<glyph glyph-name="$062" unicode="&#x3e;" horiz-adv-x="304"
d="M50 -102v51h51v-51h-51zM101 0v51h52v-51h-52zM153 102v52h51v-52h-51zM204 205v51h51v-51h-51zM153 307v51h51v-51h-51zM101 410v51h52v-51h-52zM50 512v51h51v-51h-51z" />
<glyph glyph-name="$063" unicode="?" horiz-adv-x="406"
d="M153 -51v51h51v-51h-51zM153 102v52h51v-52h-51zM255 205v51h51v-51h-51zM306 307v51h51v-51h-51zM50 358v52h51v-52h-51zM306 410v51h51v-51h-51zM204 461v51h51v-51h-51zM101 461v51h52v-51h-52z" />
<glyph glyph-name="$064" unicode="@" horiz-adv-x="560"
d="M409 0v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM409 102v52h51v-52h-51zM306 102v52h51v-52h-51zM204 102v52h51v-52h-51zM50 102v52h51v-52h-51zM460 205v51h51v-51h-51zM357 205v51h52v-51h-52zM153 205v51h51v-51h-51zM50 205
v51h51v-51h-51zM460 307v51h51v-51h-51zM357 307v51h52v-51h-52zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51zM409 410v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$065" unicode="A"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51z" />
<glyph glyph-name="$066" unicode="B"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 102v52h51v-52h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358
v52h52v-52h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$067" unicode="C"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$068" unicode="D"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410
v51h51v-51h-51z" />
<glyph glyph-name="$069" unicode="E"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$070" unicode="F"
d="M50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$071" unicode="G"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM255 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52z" />
<glyph glyph-name="$072" unicode="H"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52z
M50 410v51h51v-51h-51z" />
<glyph glyph-name="$073" unicode="I" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$074" unicode="J"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM357 307v51h52v-51h-52zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$075" unicode="K"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM255 307v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$076" unicode="L"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$077" unicode="M"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$078" unicode="N"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$079" unicode="O"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52z" />
<glyph glyph-name="$080" unicode="P"
d="M50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$081" unicode="Q" horiz-adv-x="509"
d="M409 0v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410
v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$082" unicode="R"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$083" unicode="S"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51z" />
<glyph glyph-name="$084" unicode="T"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$085" unicode="U"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$086" unicode="V"
d="M204 0v51h51v-51h-51zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM306 205v51h51v-51h-51zM101 205v51h52v-51h-52zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$087" unicode="W" horiz-adv-x="662"
d="M409 0v51h51v-51h-51zM204 0v51h51v-51h-51zM460 102v52h51v-52h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM511 205v51h51v-51h-51zM306 205v51h51v-51h-51zM101 205v51h52v-51h-52zM562 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M562 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$088" unicode="X"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM101 102v52h52v-52h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$089" unicode="Y"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$090" unicode="Z"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$091" unicode="[" horiz-adv-x="253"
d="M153 -51v51h51v-51h-51zM50 -51v51h51v-51h-51zM50 51v51h51v-51h-51zM50 154v51h51v-51h-51zM50 256v51h51v-51h-51zM50 358v52h51v-52h-51zM153 461v51h51v-51h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$092" unicode="\" horiz-adv-x="406"
d="M306 -51v51h51v-51h-51zM255 51v51h51v-51h-51zM204 154v51h51v-51h-51zM153 256v51h51v-51h-51zM101 358v52h52v-52h-52zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$093" unicode="]" horiz-adv-x="253"
d="M153 -51v51h51v-51h-51zM50 -51v51h51v-51h-51zM153 51v51h51v-51h-51zM153 154v51h51v-51h-51zM153 256v51h51v-51h-51zM153 358v52h51v-52h-51zM153 461v51h51v-51h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$094" unicode="^" horiz-adv-x="355"
d="M255 358v52h51v-52h-51zM50 358v52h51v-52h-51zM153 461v51h51v-51h-51z" />
<glyph glyph-name="$095" unicode="_"
d="M357 -51v51h52v-51h-52zM255 -51v51h51v-51h-51zM153 -51v51h51v-51h-51zM50 -51v51h51v-51h-51z" />
<glyph glyph-name="$096" unicode="`" horiz-adv-x="253"
d="M153 358v52h51v-52h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$097" unicode="a"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51z" />
<glyph glyph-name="$098" unicode="b"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 102v52h51v-52h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358
v52h52v-52h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$099" unicode="c"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$100" unicode="d"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410
v51h51v-51h-51z" />
<glyph glyph-name="$101" unicode="e"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$102" unicode="f"
d="M50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$103" unicode="g"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM255 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52z" />
<glyph glyph-name="$104" unicode="h"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52z
M50 410v51h51v-51h-51z" />
<glyph glyph-name="$105" unicode="i" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$106" unicode="j"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM357 307v51h52v-51h-52zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$107" unicode="k"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM255 307v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$108" unicode="l"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$109" unicode="m"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$110" unicode="n"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$111" unicode="o"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52z" />
<glyph glyph-name="$112" unicode="p"
d="M50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$113" unicode="q" horiz-adv-x="509"
d="M409 0v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410
v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="$114" unicode="r"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$115" unicode="s"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51z" />
<glyph glyph-name="$116" unicode="t"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$117" unicode="u"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$118" unicode="v"
d="M204 0v51h51v-51h-51zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM306 205v51h51v-51h-51zM101 205v51h52v-51h-52zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$119" unicode="w" horiz-adv-x="662"
d="M409 0v51h51v-51h-51zM204 0v51h51v-51h-51zM460 102v52h51v-52h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM511 205v51h51v-51h-51zM306 205v51h51v-51h-51zM101 205v51h52v-51h-52zM562 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M562 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$120" unicode="x"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM101 102v52h52v-52h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$121" unicode="y"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$122" unicode="z"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="$123" unicode="{" horiz-adv-x="304"
d="M204 -51v51h51v-51h-51zM153 51v51h51v-51h-51zM153 154v51h51v-51h-51zM50 205v51h51v-51h-51zM153 256v51h51v-51h-51zM153 358v52h51v-52h-51zM204 461v51h51v-51h-51z" />
<glyph glyph-name="$124" unicode="|" horiz-adv-x="150"
d="M50 -51v51h51v-51h-51zM50 51v51h51v-51h-51zM50 154v51h51v-51h-51zM50 256v51h51v-51h-51zM50 358v52h51v-52h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$125" unicode="}" horiz-adv-x="304"
d="M50 -51v51h51v-51h-51zM101 51v51h52v-51h-52zM101 154v51h52v-51h-52zM204 205v51h51v-51h-51zM101 256v51h52v-51h-52zM101 358v52h52v-52h-52zM50 461v51h51v-51h-51z" />
<glyph glyph-name="$126" unicode="~"
d="M306 154v51h51v-51h-51zM50 154v51h51v-51h-51zM204 205v51h51v-51h-51zM357 256v51h52v-51h-52zM101 256v51h52v-51h-52z" />
<glyph glyph-name="$127" horiz-adv-x="49"
/>
<glyph glyph-name="uni0080" horiz-adv-x="49"
/>
<glyph glyph-name="uni0081" horiz-adv-x="49"
/>
<glyph glyph-name="uni0082" horiz-adv-x="49"
/>
<glyph glyph-name="uni0083" horiz-adv-x="49"
/>
<glyph glyph-name="uni0084" horiz-adv-x="49"
/>
<glyph glyph-name="uni0085" unicode="&#x85;" horiz-adv-x="49"
/>
<glyph glyph-name="uni0086" horiz-adv-x="49"
/>
<glyph glyph-name="uni0087" horiz-adv-x="49"
/>
<glyph glyph-name="uni0088" horiz-adv-x="49"
/>
<glyph glyph-name="uni0089" horiz-adv-x="49"
/>
<glyph glyph-name="uni008A" horiz-adv-x="49"
/>
<glyph glyph-name="uni008B" horiz-adv-x="49"
/>
<glyph glyph-name="uni008C" horiz-adv-x="49"
/>
<glyph glyph-name="uni008D" horiz-adv-x="49"
/>
<glyph glyph-name="uni008E" horiz-adv-x="49"
/>
<glyph glyph-name="uni008F" horiz-adv-x="49"
/>
<glyph glyph-name="uni0090" horiz-adv-x="49"
/>
<glyph glyph-name="uni0091" horiz-adv-x="49"
/>
<glyph glyph-name="uni0092" horiz-adv-x="49"
/>
<glyph glyph-name="uni0093" horiz-adv-x="49"
/>
<glyph glyph-name="uni0094" horiz-adv-x="49"
/>
<glyph glyph-name="uni0095" horiz-adv-x="49"
/>
<glyph glyph-name="uni0096" horiz-adv-x="49"
/>
<glyph glyph-name="uni0097" horiz-adv-x="49"
/>
<glyph glyph-name="uni0098" horiz-adv-x="49"
/>
<glyph glyph-name="uni0099" horiz-adv-x="49"
/>
<glyph glyph-name="uni009A" horiz-adv-x="49"
/>
<glyph glyph-name="uni009B" horiz-adv-x="49"
/>
<glyph glyph-name="uni009C" horiz-adv-x="49"
/>
<glyph glyph-name="uni009D" horiz-adv-x="49"
/>
<glyph glyph-name="uni009E" horiz-adv-x="49"
/>
<glyph glyph-name="uni009F" horiz-adv-x="49"
/>
<glyph glyph-name="uni00A0" unicode="&#xa0;" horiz-adv-x="49"
/>
<glyph glyph-name="currency" unicode="&#xa4;" horiz-adv-x="560"
d="M460 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM357 205v51h52v-51h-52zM153 205v51h51v-51h-51zM357 307v51h52v-51h-52zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM460 410v51h51v-51h-51z
M50 410v51h51v-51h-51z" />
<glyph glyph-name="section" unicode="&#xa7;"
d="M255 -102v51h51v-51h-51zM153 -102v51h51v-51h-51zM357 -51v51h52v-51h-52zM50 -51v51h51v-51h-51zM306 51v51h51v-51h-51zM204 102v52h51v-52h-51zM306 154v51h51v-51h-51zM101 154v51h52v-51h-52zM306 256v51h51v-51h-51zM101 256v51h52v-51h-52zM204 307v51h51v-51
h-51zM101 358v52h52v-52h-52zM357 461v51h52v-51h-52zM50 461v51h51v-51h-51zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51z" />
<glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="304"
d="M204 461v51h51v-51h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="662"
d="M460 -51v51h51v-51h-51zM357 -51v51h52v-51h-52zM255 -51v51h51v-51h-51zM153 -51v51h51v-51h-51zM562 51v51h51v-51h-51zM306 51v51h51v-51h-51zM204 51v51h51v-51h-51zM50 51v51h51v-51h-51zM409 102v52h51v-52h-51zM562 154v51h51v-51h-51zM153 154v51h51v-51h-51z
M50 154v51h51v-51h-51zM562 256v51h51v-51h-51zM153 256v51h51v-51h-51zM50 256v51h51v-51h-51zM409 307v51h51v-51h-51zM562 358v52h51v-52h-51zM306 358v52h51v-52h-51zM204 358v52h51v-52h-51zM50 358v52h51v-52h-51zM460 461v51h51v-51h-51zM357 461v51h52v-51h-52z
M255 461v51h51v-51h-51zM153 461v51h51v-51h-51z" />
<glyph glyph-name="guillemotleft" unicode="&#xab;" horiz-adv-x="355"
d="M255 154v51h51v-51h-51zM101 154v51h52v-51h-52zM204 256v51h51v-51h-51zM50 256v51h51v-51h-51zM255 358v52h51v-52h-51zM101 358v52h52v-52h-52zM255 154v51h51v-51h-51zM101 154v51h52v-51h-52zM204 256v51h51v-51h-51zM50 256v51h51v-51h-51zM255 358v52h51v-52h-51z
M101 358v52h52v-52h-52z" />
<glyph glyph-name="logicalnot" unicode="&#xac;"
d="M357 51v51h52v-51h-52zM357 154v51h52v-51h-52zM357 256v51h52v-51h-52zM255 256v51h51v-51h-51zM153 256v51h51v-51h-51zM50 256v51h51v-51h-51zM357 51v51h52v-51h-52zM357 154v51h52v-51h-52zM357 256v51h52v-51h-52zM255 256v51h51v-51h-51zM153 256v51h51v-51h-51z
M50 256v51h51v-51h-51z" />
<glyph glyph-name="uni00AD" unicode="&#xad;" horiz-adv-x="253"
d="M153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51z" />
<glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="765"
d="M562 -102v51h51v-51h-51zM460 -102v51h51v-51h-51zM357 -102v51h52v-51h-52zM255 -102v51h51v-51h-51zM153 -102v51h51v-51h-51zM613 0v51h52v-51h-52zM101 0v51h52v-51h-52zM460 51v51h51v-51h-51zM255 51v51h51v-51h-51zM665 102v52h51v-52h-51zM50 102v52h51v-52h-51z
M357 154v51h52v-51h-52zM255 154v51h51v-51h-51zM665 205v51h51v-51h-51zM460 205v51h51v-51h-51zM50 205v51h51v-51h-51zM255 256v51h51v-51h-51zM665 307v51h51v-51h-51zM460 307v51h51v-51h-51zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 358v52h51v-52h-51z
M613 410v51h52v-51h-52zM101 410v51h52v-51h-52zM562 512v51h51v-51h-51zM460 512v51h51v-51h-51zM357 512v51h52v-51h-52zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51z" />
<glyph glyph-name="degree" unicode="&#xb0;" horiz-adv-x="304"
d="M101 154v51h52v-51h-52zM204 205v51h51v-51h-51zM50 256v51h51v-51h-51zM153 307v51h51v-51h-51z" />
<glyph glyph-name="plusminus" unicode="&#xb1;" horiz-adv-x="355"
d="M255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h51v-52h-51zM153 205v51h51v-51h-51zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51zM153 410v51h51v-51h-51zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h51v-52h-51z
M153 205v51h51v-51h-51zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="uni00B2" unicode="&#xb2;" horiz-adv-x="49"
/>
<glyph glyph-name="acute" unicode="&#xb4;" horiz-adv-x="202"
d="M50 358v52h51v-52h-51zM101 461v51h52v-51h-52z" />
<glyph glyph-name="uni00B5" unicode="&#xb5;"
d="M50 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM306 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51
h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM306 205v51h51v-51h-51zM50 205v51h51v-51h-51z" />
<glyph glyph-name="paragraph" unicode="&#xb6;" horiz-adv-x="560"
d="M460 -102v51h51v-51h-51zM255 -102v51h51v-51h-51zM460 0v51h51v-51h-51zM255 0v51h51v-51h-51zM460 102v52h51v-52h-51zM255 102v52h51v-52h-51zM460 205v51h51v-51h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM460 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 358v52h51v-52h-51zM460 410v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 461v51h51v-51h-51zM460 512v51h51v-51h-51zM357 512v51h52v-51h-52zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51z
M460 -102v51h51v-51h-51zM255 -102v51h51v-51h-51zM460 0v51h51v-51h-51zM255 0v51h51v-51h-51zM460 102v52h51v-52h-51zM255 102v52h51v-52h-51zM460 205v51h51v-51h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM460 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 358v52h51v-52h-51zM460 410v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 461v51h51v-51h-51zM460 512v51h51v-51h-51zM357 512v51h52v-51h-52zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51z
" />
<glyph glyph-name="periodcentered" unicode="&#xb7;" horiz-adv-x="150"
d="M50 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="cedilla" unicode="&#xb8;" horiz-adv-x="355"
d="M153 -154v52h51v-52h-51zM50 -154v52h51v-52h-51zM255 -102v51h51v-51h-51z" />
<glyph glyph-name="guillemotright" unicode="&#xbb;" horiz-adv-x="355"
d="M204 154v51h51v-51h-51zM50 154v51h51v-51h-51zM255 256v51h51v-51h-51zM101 256v51h52v-51h-52zM204 358v52h51v-52h-51zM50 358v52h51v-52h-51zM204 154v51h51v-51h-51zM50 154v51h51v-51h-51zM255 256v51h51v-51h-51zM101 256v51h52v-51h-52zM204 358v52h51v-52h-51z
M50 358v52h51v-52h-51z" />
<glyph glyph-name="Agrave" unicode="&#xc0;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM50 0v50h50v-50h-50zM350 100v50h50v-50h-50zM50 100v50h50v-50h-50zM250 150v50h50v-50h-50zM150 150v50h50v-50h-50zM350 200v50h50v-50h-50zM50 200v50h50v-50h-50zM300 300v50h50v-50h-50zM100 300v50h50v-50h-50zM250 400v50h50v-50h-50z
M150 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50z" />
<glyph glyph-name="Aacute" unicode="&#xc1;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Acircumflex" unicode="&#xc2;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="Adieresis" unicode="&#xc4;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="765"
d="M665 0v51h51v-51h-51zM562 0v51h51v-51h-51zM460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM562 205v51h51v-51h-51zM460 205v51h51v-51h-51zM357 205
v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM101 307v51h52v-51h-52zM665 410v51h51v-51h-51zM562 410v51h51v-51h-51zM460 410v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM665 0v51h51v-51h-51zM562 0v51h51
v-51h-51zM460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM562 205v51h51v-51h-51zM460 205v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51z
M357 307v51h52v-51h-52zM101 307v51h52v-51h-52zM665 410v51h51v-51h-51zM562 410v51h51v-51h-51zM460 410v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="Ccedilla" unicode="&#xc7;"
d="M255 -154v52h51v-52h-51zM357 -102v51h52v-51h-52zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51z
M204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="Egrave" unicode="&#xc8;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400
v50h50v-50h-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50
h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100
h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50zM350 0h50v50
h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50z
M50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50
h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50z
M250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0
v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50
v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50z" />
<glyph glyph-name="Eacute" unicode="&#xc9;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Ecircumflex" unicode="&#xca;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400
v50h50v-50h-50zM50 400v50h50v-50h-50zM300 500v50h50v-50h-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50
v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50z
M150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500
v50h50v-50h-50zM100 500h50v50h-50v-50zM200 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50
h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50z
M50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500v50h50v-50h-50zM100 500h50v50h-50v-50z
M200 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50
v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50z
M150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500v50h50v-50h-50zM100 500h50v50h-50v-50zM200 550h50v50h-50v-50z" />
<glyph glyph-name="Edieresis" unicode="&#xcb;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Iacute" unicode="&#xcd;" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="Icircumflex" unicode="&#xce;" horiz-adv-x="355"
d="M204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM153 102v52h51v-52h-51zM153 205v51h51v-51h-51zM153 307v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="Idieresis" unicode="&#xcf;" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 563v51h51v-51h-51zM50 563v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102
v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 563v51h51v-51h-51zM50 563v51h51v-51h-51z" />
<glyph glyph-name="Eth" unicode="&#xd0;" horiz-adv-x="560"
d="M357 0v51h51v-51h-51zM255 0v51h51v-51h-51zM152 0v51h52v-51h-52zM460 102v52h51v-52h-51zM152 102v52h52v-52h-52zM460 205v51h51v-51h-51zM152 205v51h52v-51h-52zM460 307v51h51v-51h-51zM255 307v51h51v-51h-51zM152 307v51h52v-51h-52zM50 307v51h51v-51h-51z
M357 410v51h51v-51h-51zM255 410v51h51v-51h-51zM152 410v51h52v-51h-52zM357 0v51h51v-51h-51zM255 0v51h51v-51h-51zM152 0v51h52v-51h-52zM460 102v52h51v-52h-51zM152 102v52h52v-52h-52zM460 205v51h51v-51h-51zM152 205v51h52v-51h-52zM460 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM152 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h51v-51h-51zM255 410v51h51v-51h-51zM152 410v51h52v-51h-52z" />
<glyph glyph-name="Ntilde" unicode="&#xd1;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM357 563v51h52v-51h-52zM153 563v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205
v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM357 563v51h52v-51h-52zM153 563v51
h51v-51h-51z" />
<glyph glyph-name="Ograve" unicode="&#xd2;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM204 512v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Oacute" unicode="&#xd3;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51z" />
<glyph glyph-name="Ocircumflex" unicode="&#xd4;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="Odieresis" unicode="&#xd6;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="multiply" unicode="&#xd7;" horiz-adv-x="355"
d="M255 51v51h51v-51h-51zM50 51v51h51v-51h-51zM153 154v51h51v-51h-51zM255 256v51h51v-51h-51zM50 256v51h51v-51h-51z" />
<glyph glyph-name="Oslash" unicode="&#xd8;"
d="M50 -102v51h52v-51h-52zM306 0v51h52v-51h-52zM204 0v51h51v-51h-51zM102 0v51h51v-51h-51zM358 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h52v-52h-52zM358 205v51h51v-51h-51zM204 205v51h51v-51h-51zM50 205v51h52v-51h-52zM358 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM50 307v51h52v-51h-52zM306 410v51h52v-51h-52zM204 410v51h51v-51h-51zM102 410v51h51v-51h-51zM358 512v51h51v-51h-51z" />
<glyph glyph-name="Ugrave" unicode="&#xd9;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461
v51h51v-51h-51zM153 563v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51
h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="Uacute" unicode="&#xda;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461
v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Ucircumflex" unicode="&#xdb;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52z
M204 563v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512
v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="Udieresis" unicode="&#xdc;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM306 563
v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Yacute" unicode="&#xdd;"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="germandbls" unicode="&#xdf;"
d="M50 -51v51h51v-51h-51zM50 51v51h51v-51h-51zM255 102v52h51v-52h-51zM153 102v52h51v-52h-51zM357 154v51h52v-51h-52zM50 154v51h51v-51h-51zM357 256v51h52v-51h-52zM50 256v51h51v-51h-51zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM357 358v52h52v-52h-52z
M50 358v52h51v-52h-51zM357 461v51h52v-51h-52zM50 461v51h51v-51h-51zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51z" />
<glyph glyph-name="agrave" unicode="&#xe0;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM50 0v50h50v-50h-50zM350 100v50h50v-50h-50zM50 100v50h50v-50h-50zM250 150v50h50v-50h-50zM150 150v50h50v-50h-50zM350 200v50h50v-50h-50zM50 200v50h50v-50h-50zM300 300v50h50v-50h-50zM100 300v50h50v-50h-50zM250 400v50h50v-50h-50z
M150 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50z" />
<glyph glyph-name="aacute" unicode="&#xe1;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="acircumflex" unicode="&#xe2;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="adieresis" unicode="&#xe4;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="ae" unicode="&#xe6;" horiz-adv-x="765"
d="M665 0v51h51v-51h-51zM562 0v51h51v-51h-51zM460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM562 205v51h51v-51h-51zM460 205v51h51v-51h-51zM357 205
v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM101 307v51h52v-51h-52zM665 410v51h51v-51h-51zM562 410v51h51v-51h-51zM460 410v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM665 0v51h51v-51h-51zM562 0v51h51
v-51h-51zM460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM562 205v51h51v-51h-51zM460 205v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51z
M357 307v51h52v-51h-52zM101 307v51h52v-51h-52zM665 410v51h51v-51h-51zM562 410v51h51v-51h-51zM460 410v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="ccedilla" unicode="&#xe7;"
d="M255 -154v52h51v-52h-51zM357 -102v51h52v-51h-52zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51z
M204 410v51h51v-51h-51zM101 410v51h52v-51h-52z" />
<glyph glyph-name="egrave" unicode="&#xe8;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400
v50h50v-50h-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50
h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100
h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50zM350 0h50v50
h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50z
M50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50
h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50z
M250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM250 500v50h50v-50h-50zM150 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0
v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50
v-50zM250 500h50v50h-50v-50zM150 550h50v50h-50v-50z" />
<glyph glyph-name="eacute" unicode="&#xe9;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="ecircumflex" unicode="&#xea;" horiz-adv-x="449"
d="M350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400
v50h50v-50h-50zM50 400v50h50v-50h-50zM300 500v50h50v-50h-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50
v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50z
M150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500
v50h50v-50h-50zM100 500h50v50h-50v-50zM200 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50
h-50zM350 400h50v50h-50v-50zM250 400h50v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50z
M50 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500v50h50v-50h-50zM100 500h50v50h-50v-50z
M200 550h50v50h-50v-50zM350 0h50v50h-50v-50zM250 0h50v50h-50v-50zM150 0h50v50h-50v-50zM50 0v50h50v-50h-50zM50 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200h50v50h-50v-50zM50 200v50h50v-50h-50zM50 300v50h50v-50h-50zM350 400h50v50h-50v-50zM250 400h50
v50h-50v-50zM150 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 500h50v50h-50v-50zM100 500v50h50v-50h-50zM200 550v50h50v-50h-50zM350 0v50h50v-50h-50zM250 0v50h50v-50h-50zM150 0v50h50v-50h-50zM50 0h50v50h-50v-50zM50 100h50v50h-50v-50zM250 200v50h50v-50h-50z
M150 200v50h50v-50h-50zM50 200h50v50h-50v-50zM50 300h50v50h-50v-50zM350 400v50h50v-50h-50zM250 400v50h50v-50h-50zM150 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 500v50h50v-50h-50zM100 500h50v50h-50v-50zM200 550h50v50h-50v-50z" />
<glyph glyph-name="edieresis" unicode="&#xeb;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="iacute" unicode="&#xed;" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="355"
d="M204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM153 102v52h51v-52h-51zM153 205v51h51v-51h-51zM153 307v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="idieresis" unicode="&#xef;" horiz-adv-x="253"
d="M153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 563v51h51v-51h-51zM50 563v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102
v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 563v51h51v-51h-51zM50 563v51h51v-51h-51z" />
<glyph glyph-name="eth" unicode="&#xf0;" horiz-adv-x="560"
d="M357 0v51h51v-51h-51zM255 0v51h51v-51h-51zM152 0v51h52v-51h-52zM460 102v52h51v-52h-51zM152 102v52h52v-52h-52zM460 205v51h51v-51h-51zM152 205v51h52v-51h-52zM460 307v51h51v-51h-51zM255 307v51h51v-51h-51zM152 307v51h52v-51h-52zM50 307v51h51v-51h-51z
M357 410v51h51v-51h-51zM255 410v51h51v-51h-51zM152 410v51h52v-51h-52zM357 0v51h51v-51h-51zM255 0v51h51v-51h-51zM152 0v51h52v-51h-52zM460 102v52h51v-52h-51zM152 102v52h52v-52h-52zM460 205v51h51v-51h-51zM152 205v51h52v-51h-52zM460 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM152 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h51v-51h-51zM255 410v51h51v-51h-51zM152 410v51h52v-51h-52z" />
<glyph glyph-name="ntilde" unicode="&#xf1;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM357 563v51h52v-51h-52zM153 563v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205
v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 512v51h51v-51h-51zM50 512v51h51v-51h-51zM357 563v51h52v-51h-52zM153 563v51
h51v-51h-51z" />
<glyph glyph-name="ograve" unicode="&#xf2;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM204 512v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="oacute" unicode="&#xf3;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51z" />
<glyph glyph-name="ocircumflex" unicode="&#xf4;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="odieresis" unicode="&#xf6;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="divide" unicode="&#xf7;"
d="M204 0v51h51v-51h-51zM357 154v51h52v-51h-52zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM50 154v51h51v-51h-51zM204 307v51h51v-51h-51z" />
<glyph glyph-name="oslash" unicode="&#xf8;"
d="M50 -102v51h52v-51h-52zM306 0v51h52v-51h-52zM204 0v51h51v-51h-51zM102 0v51h51v-51h-51zM358 102v52h51v-52h-51zM153 102v52h51v-52h-51zM50 102v52h52v-52h-52zM358 205v51h51v-51h-51zM204 205v51h51v-51h-51zM50 205v51h52v-51h-52zM358 307v51h51v-51h-51z
M255 307v51h51v-51h-51zM50 307v51h52v-51h-52zM306 410v51h52v-51h-52zM204 410v51h51v-51h-51zM102 410v51h51v-51h-51zM358 512v51h51v-51h-51z" />
<glyph glyph-name="ugrave" unicode="&#xf9;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461
v51h51v-51h-51zM153 563v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51
h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="uacute" unicode="&#xfa;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461
v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="ucircumflex" unicode="&#xfb;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512v51h52v-51h-52z
M204 563v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 512v51h51v-51h-51zM101 512
v51h52v-51h-52zM204 563v51h51v-51h-51z" />
<glyph glyph-name="udieresis" unicode="&#xfc;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM306 563
v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="yacute" unicode="&#xfd;"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="ydieresis" unicode="&#xff;" horiz-adv-x="449"
d="M200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM300 300v50h50v-50h-50zM100 300v50h50v-50h-50zM350 400v50h50v-50h-50zM50 400v50h50v-50h-50zM300 550v50h50v-50h-50zM100 550v50h50v-50h-50zM200 0v50h50v-50h-50z
M200 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200v50h50v-50h-50zM300 300h50v50h-50v-50zM100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50z
M250 200v50h50v-50h-50zM150 200h50v50h-50v-50zM300 300v50h50v-50h-50zM100 300h50v50h-50v-50zM350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50zM200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200h50v50h-50v-50z
M150 200v50h50v-50h-50zM300 300h50v50h-50v-50zM100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200h50v50h-50v-50z
M300 300v50h50v-50h-50zM100 300h50v50h-50v-50zM350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50zM200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200v50h50v-50h-50zM300 300h50v50h-50v-50z
M100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200h50v50h-50v-50zM300 300v50h50v-50h-50zM100 300h50v50h-50v-50z
M350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50z" />
<glyph glyph-name="Abreve" unicode="&#x102;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51zM357 563v51h52v-51h-52zM50 563v51h51v-51h-51z" />
<glyph glyph-name="abreve" unicode="&#x103;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM255 512v51h51v-51h-51zM153 512v51h51v-51h-51zM357 563v51h52v-51h-52zM50 563v51h51v-51h-51z" />
<glyph glyph-name="Aogonek" unicode="&#x104;"
d="M306 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52z
M255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="aogonek" unicode="&#x105;"
d="M306 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM255 154v51h51v-51h-51zM153 154v51h51v-51h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM306 307v51h51v-51h-51zM101 307v51h52v-51h-52z
M255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="Cacute" unicode="&#x106;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z
M204 512v51h51v-51h-51zM306 563v51h51v-51h-51z" />
<glyph glyph-name="cacute" unicode="&#x107;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z
M204 512v51h51v-51h-51zM306 563v51h51v-51h-51z" />
<glyph glyph-name="Ccaron" unicode="&#x10c;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z
M204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="ccaron" unicode="&#x10d;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51zM101 410v51h52v-51h-52z
M204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Dcaron" unicode="&#x10e;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410
v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51zM50 563v51h51v-51h-51z" />
<glyph glyph-name="dcaron" unicode="&#x10f;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410
v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51zM50 563v51h51v-51h-51z" />
<glyph glyph-name="Dcroat" unicode="&#x110;" horiz-adv-x="560"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM460 102v52h51v-52h-51zM153 102v52h51v-52h-51zM460 205v51h51v-51h-51zM153 205v51h51v-51h-51zM460 307v51h51v-51h-51zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="dcroat" unicode="&#x111;" horiz-adv-x="560"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM460 102v52h51v-52h-51zM153 102v52h51v-52h-51zM460 205v51h51v-51h-51zM153 205v51h51v-51h-51zM460 307v51h51v-51h-51zM255 307v51h51v-51h-51zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="Eogonek" unicode="&#x118;"
d="M153 410v51h51v-51h-51zM306 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410
v51h52v-51h-52zM255 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="eogonek" unicode="&#x119;"
d="M153 410v51h51v-51h-51zM306 -102v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410
v51h52v-51h-52zM255 410v51h51v-51h-51zM50 410v51h51v-51h-51z" />
<glyph glyph-name="Ecaron" unicode="&#x11a;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="ecaron" unicode="&#x11b;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410
v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Lacute" unicode="&#x139;" horiz-adv-x="509"
d="M409 0v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM101 410v51h52v-51h-52zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="lacute" unicode="&#x13a;" horiz-adv-x="509"
d="M409 0v51h51v-51h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM101 102v52h52v-52h-52zM101 205v51h52v-51h-52zM101 307v51h52v-51h-52zM101 410v51h52v-51h-52zM50 512v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="Lcaron" unicode="&#x13d;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM153 358v52h51v-52h-51zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51z" />
<glyph glyph-name="lcaron" unicode="&#x13e;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM50 102v52h51v-52h-51zM50 205v51h51v-51h-51zM50 307v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Lslash" unicode="&#x141;" horiz-adv-x="560"
d="M460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM153 102v52h51v-52h-51zM50 154v51h51v-51h-51zM153 205v51h51v-51h-51zM255 256v51h51v-51h-51zM153 307v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="lslash" unicode="&#x142;" horiz-adv-x="560"
d="M460 0v51h51v-51h-51zM357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM153 102v52h51v-52h-51zM50 154v51h51v-51h-51zM153 205v51h51v-51h-51zM255 256v51h51v-51h-51zM153 307v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="Nacute" unicode="&#x143;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="nacute" unicode="&#x144;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 461v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Ncaron" unicode="&#x147;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="ncaron" unicode="&#x148;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM357 102v52h52v-52h-52zM255 102v52h51v-52h-51zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM204 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM153 307v51h51v-51h-51zM50 307v51h51v-51h-51z
M357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Ohungarumlaut" unicode="&#x150;" horiz-adv-x="509"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 512v51h51v-51h-51zM50 512v51h51v-51h-51zM409 563v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="ohungarumlaut" unicode="&#x151;" horiz-adv-x="509"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM306 410v51h51v-51h-51zM204 410v51h51v-51h-51z
M101 410v51h52v-51h-52zM306 512v51h51v-51h-51zM50 512v51h51v-51h-51zM409 563v51h51v-51h-51zM153 563v51h51v-51h-51z" />
<glyph glyph-name="OE" unicode="&#x152;" horiz-adv-x="699"
d="M600 0v50h50v-50h-50zM500 0v50h50v-50h-50zM400 0v50h50v-50h-50zM300 0v50h50v-50h-50zM200 0v50h50v-50h-50zM100 0v50h50v-50h-50zM350 100v50h50v-50h-50zM50 100v50h50v-50h-50zM550 200v50h50v-50h-50zM450 200v50h50v-50h-50zM350 200v50h50v-50h-50zM50 200v50
h50v-50h-50zM350 300v50h50v-50h-50zM50 300v50h50v-50h-50zM600 400v50h50v-50h-50zM500 400v50h50v-50h-50zM400 400v50h50v-50h-50zM300 400v50h50v-50h-50zM200 400v50h50v-50h-50zM100 400v50h50v-50h-50z" />
<glyph glyph-name="oe" unicode="&#x153;" horiz-adv-x="699"
d="M600 0v50h50v-50h-50zM500 0v50h50v-50h-50zM400 0v50h50v-50h-50zM300 0v50h50v-50h-50zM200 0v50h50v-50h-50zM100 0v50h50v-50h-50zM350 100v50h50v-50h-50zM50 100v50h50v-50h-50zM550 200v50h50v-50h-50zM450 200v50h50v-50h-50zM350 200v50h50v-50h-50zM50 200v50
h50v-50h-50zM350 300v50h50v-50h-50zM50 300v50h50v-50h-50zM600 400v50h50v-50h-50zM500 400v50h50v-50h-50zM400 400v50h50v-50h-50zM300 400v50h50v-50h-50zM200 400v50h50v-50h-50zM100 400v50h50v-50h-50z" />
<glyph glyph-name="Racute" unicode="&#x154;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="racute" unicode="&#x155;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Rcaron" unicode="&#x158;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="rcaron" unicode="&#x159;"
d="M357 0v51h52v-51h-52zM50 0v51h51v-51h-51zM306 102v52h51v-52h-51zM50 102v52h51v-52h-51zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 205v51h51v-51h-51zM357 256v51h52v-51h-52zM50 307v51h51v-51h-51zM357 358v52h52v-52h-52zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Sacute" unicode="&#x15a;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="sacute" unicode="&#x15b;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM153 512v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Scedilla" unicode="&#x15e;"
d="M204 -154v52h51v-52h-51zM101 -154v52h52v-52h-52zM306 -102v51h51v-51h-51zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51z
M357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="scedilla" unicode="&#x15f;"
d="M204 -154v52h51v-52h-51zM101 -154v52h52v-52h-52zM306 -102v51h51v-51h-51zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51z
M357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51z" />
<glyph glyph-name="Scaron" unicode="&#x160;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="scaron" unicode="&#x161;"
d="M255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM357 51v51h52v-51h-52zM50 51v51h51v-51h-51zM357 154v51h52v-51h-52zM255 205v51h51v-51h-51zM153 205v51h51v-51h-51zM50 256v51h51v-51h-51zM357 358v52h52v-52h-52zM50 358v52h51v-52h-51zM255 410v51h51v-51h-51z
M153 410v51h51v-51h-51zM204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="Tcommaaccent" unicode="&#x162;"
d="M255 -154v52h51v-52h-51zM153 -154v52h51v-52h-51zM306 -51v51h51v-51h-51zM204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51
z" />
<glyph glyph-name="tcommaaccent" unicode="&#x163;"
d="M255 -154v52h51v-52h-51zM153 -154v52h51v-52h-51zM306 -51v51h51v-51h-51zM204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51
z" />
<glyph glyph-name="Tcaron" unicode="&#x164;"
d="M204 512v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52zM204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51z
" />
<glyph glyph-name="tcaron" unicode="&#x165;" horiz-adv-x="611"
d="M204 0v51h51v-51h-51zM204 102v52h51v-52h-51zM204 205v51h51v-51h-51zM204 307v51h51v-51h-51zM460 358v52h51v-52h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM511 461v51h51v-51h-51z" />
<glyph glyph-name="Uring" unicode="&#x16e;"
d="M255 459v53h51v-53h-51zM153 510v51h51v-51h-51zM306 561v51h51v-51h-51zM204 612v52h51v-52h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51z
M357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="uring" unicode="&#x16f;"
d="M255 459v53h51v-53h-51zM153 510v51h51v-51h-51zM306 561v51h51v-51h-51zM204 612v52h51v-52h-51zM306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51z
M357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51z" />
<glyph glyph-name="Uhungarumlaut" unicode="&#x170;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 461
v51h51v-51h-51zM153 461v51h51v-51h-51zM306 563v51h51v-51h-51zM204 563v51h51v-51h-51z" />
<glyph glyph-name="uhungarumlaut" unicode="&#x171;"
d="M306 0v51h51v-51h-51zM204 0v51h51v-51h-51zM101 0v51h52v-51h-52zM357 102v52h52v-52h-52zM50 102v52h51v-52h-51zM357 205v51h52v-51h-52zM50 205v51h51v-51h-51zM357 307v51h52v-51h-52zM50 307v51h51v-51h-51zM357 410v51h52v-51h-52zM50 410v51h51v-51h-51zM255 461
v51h51v-51h-51zM153 461v51h51v-51h-51zM306 563v51h51v-51h-51zM204 563v51h51v-51h-51z" />
<glyph glyph-name="Ydieresis" unicode="&#x178;" horiz-adv-x="449"
d="M200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200v50h50v-50h-50zM150 200v50h50v-50h-50zM300 300v50h50v-50h-50zM100 300v50h50v-50h-50zM350 400v50h50v-50h-50zM50 400v50h50v-50h-50zM300 550v50h50v-50h-50zM100 550v50h50v-50h-50zM200 0v50h50v-50h-50z
M200 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200v50h50v-50h-50zM300 300h50v50h-50v-50zM100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50z
M250 200v50h50v-50h-50zM150 200h50v50h-50v-50zM300 300v50h50v-50h-50zM100 300h50v50h-50v-50zM350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50zM200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200h50v50h-50v-50z
M150 200v50h50v-50h-50zM300 300h50v50h-50v-50zM100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200h50v50h-50v-50z
M300 300v50h50v-50h-50zM100 300h50v50h-50v-50zM350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50zM200 0v50h50v-50h-50zM200 100v50h50v-50h-50zM250 200h50v50h-50v-50zM150 200v50h50v-50h-50zM300 300h50v50h-50v-50z
M100 300v50h50v-50h-50zM350 400h50v50h-50v-50zM50 400v50h50v-50h-50zM300 550h50v50h-50v-50zM100 550v50h50v-50h-50zM200 0h50v50h-50v-50zM200 100h50v50h-50v-50zM250 200v50h50v-50h-50zM150 200h50v50h-50v-50zM300 300v50h50v-50h-50zM100 300h50v50h-50v-50z
M350 400v50h50v-50h-50zM50 400h50v50h-50v-50zM300 550v50h50v-50h-50zM100 550h50v50h-50v-50z" />
<glyph glyph-name="Zacute" unicode="&#x179;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512
v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="zacute" unicode="&#x17a;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM153 512
v51h51v-51h-51zM255 563v51h51v-51h-51z" />
<glyph glyph-name="Zdotaccent" unicode="&#x17b;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 563
v51h51v-51h-51z" />
<glyph glyph-name="zdotaccent" unicode="&#x17c;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 563
v51h51v-51h-51z" />
<glyph glyph-name="Zcaron" unicode="&#x17d;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512
v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="zcaron" unicode="&#x17e;"
d="M357 0v51h52v-51h-52zM255 0v51h51v-51h-51zM153 0v51h51v-51h-51zM50 0v51h51v-51h-51zM101 102v52h52v-52h-52zM204 205v51h51v-51h-51zM306 307v51h51v-51h-51zM357 410v51h52v-51h-52zM255 410v51h51v-51h-51zM153 410v51h51v-51h-51zM50 410v51h51v-51h-51zM204 512
v51h51v-51h-51zM306 563v51h51v-51h-51zM101 563v51h52v-51h-52z" />
<glyph glyph-name="caron" unicode="&#x2c7;" horiz-adv-x="355"
d="M153 358v52h51v-52h-51zM255 461v51h51v-51h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="breve" unicode="&#x2d8;" horiz-adv-x="355"
d="M204 358v52h51v-52h-51zM101 358v52h52v-52h-52zM255 461v51h51v-51h-51zM50 461v51h51v-51h-51z" />
<glyph glyph-name="dotaccent" unicode="&#x2d9;" horiz-adv-x="49"
/>
<glyph glyph-name="ogonek" unicode="&#x2db;" horiz-adv-x="304"
d="M204 -102v51h51v-51h-51zM101 -102v51h52v-51h-52zM50 0v51h51v-51h-51z" />
<glyph glyph-name="hungarumlaut" unicode="&#x2dd;" horiz-adv-x="355"
d="M204 358v52h51v-52h-51zM50 358v52h51v-52h-51zM255 461v51h51v-51h-51zM101 461v51h52v-51h-52z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
www/css/LED.eot Normal file

Binary file not shown.

519
www/css/LED.svg Normal file
View File

@ -0,0 +1,519 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg>
<metadata>
Created by FontForge 20110222 at Mon Mar 7 11:46:57 2011
By www-data
</metadata>
<defs>
<font id="LED" horiz-adv-x="463" >
<font-face
font-family="LED"
font-weight="500"
font-stretch="normal"
units-per-em="1000"
panose-1="0 0 0 0 0 0 0 0 0 0"
ascent="800"
descent="-200"
x-height="700"
cap-height="700"
bbox="-91 -200 898 863"
underline-thickness="20"
underline-position="-190"
unicode-range="U+0020-2122"
/>
<missing-glyph />
<glyph glyph-name="space" unicode=" " horiz-adv-x="282"
/>
<glyph glyph-name="exclam" unicode="!" horiz-adv-x="168"
d="M140 217l-112 70v301l112 112v-483zM140 0h-112v175h112v-175z" />
<glyph glyph-name="quotedbl" unicode="&#x22;" horiz-adv-x="325"
d="M303 413h-111v287h111v-287zM133 413h-112v287h112v-287z" />
<glyph glyph-name="numbersign" unicode="#" horiz-adv-x="569"
d="M523 482l-10 -48h-111l-36 -168h111l-9 -48h-112l-44 -204h-48l43 204h-101l-44 -204h-47l42 204h-111l10 48h112l35 168h-112l10 48h113l43 204h48l-43 -204h102l42 204h48l-43 -204h112zM353 434h-101l-36 -168h102z" />
<glyph glyph-name="dollar" unicode="$" horiz-adv-x="462"
d="M289 732l-56 -62l-58 59v54l59 58l55 -56v-53zM434 700l-124 -112h-156l-63 63l51 49h32l59 -55l55 55h146zM142 428l-113 -70v229l49 53l64 -65v-147zM405 350l-98 -49h-155l-96 50l97 62h155zM434 114l-48 -53l-63 65v163l111 54v-229zM371 48l-48 -48h-39l-52 52
l-60 -52h-144l126 112h156zM285 -89l-55 -54l-60 63v47l60 57l55 -60v-53z" />
<glyph glyph-name="percent" unicode="%" horiz-adv-x="810"
d="M286 649l-63 -61h-81l-62 61l48 51h111zM350 474l-52 -48l-61 61v86l61 64l52 -50v-113zM128 488l-64 -63l-50 48v114l52 50l62 -64v-85zM685 700l-225 -367l-61 112l154 255h132zM285 410l-48 -46h-111l-48 46l64 63h81zM732 288l-62 -61h-81l-63 61l49 50h110zM796 113
l-51 -49l-61 62v86l62 63l50 -49v-113zM576 126l-66 -63l-49 49v114l51 49l64 -64v-85zM448 313l-187 -313h-131l190 313h128zM731 49l-47 -49h-110l-49 49l64 63h81z" />
<glyph glyph-name="ampersand" unicode="&#x26;" horiz-adv-x="561"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM140 427l-112 -70v231l49 49l63 -63v-147zM433 430l-56 -61l-55 62v73l63 64l48 -64v-74zM575 351l-92 -50h-36l-55 56l55 56h37zM363 357l-54 -56h-86l-98 51l99 61h85zM432 113l-47 -48l-63 61v162l55 55l55 -56v-174zM140 126
l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="quotesingle" unicode="'" horiz-adv-x="155"
d="M133 413h-112v287h112v-287z" />
<glyph glyph-name="parenleft" unicode="(" horiz-adv-x="280"
d="M266 700l-70 -112h-41l-64 63l49 49h126zM140 427l-112 -70v231l49 49l63 -63v-147zM140 126l-63 -63l-49 49v231l112 -56v-161zM266 0h-126l-49 49l64 63h41z" />
<glyph glyph-name="parenright" unicode=")" horiz-adv-x="280"
d="M189 651l-64 -63h-41l-70 112h126zM252 357l-112 56v161l62 63l50 -49v-231zM252 112l-50 -49l-62 63v147l112 70v-231zM189 49l-49 -49h-126l70 112h41z" />
<glyph glyph-name="asterisk" unicode="*" horiz-adv-x="425"
d="M268 576l-58 -40l-55 43v135l57 40l56 -43v-135zM198 456l-119 -73l-65 28l6 69l129 75l49 -35v-64zM411 416l-54 -35l-135 74v67l50 31l130 -70z" />
<glyph glyph-name="plus" unicode="+" horiz-adv-x="465"
d="M286 422l-54 -58l-59 59v95l113 50v-146zM451 406l-47 -112h-104l-50 56l57 56h144zM217 351l-58 -57h-145l48 112h99zM286 182l-111 -49v147l57 56l54 -56v-98z" />
<glyph glyph-name="comma" unicode="," horiz-adv-x="155"
d="M133 -112h-112v287h112v-287z" />
<glyph glyph-name="hyphen" unicode="-" horiz-adv-x="335"
d="M322 408l-85 -110h-223l83 110h225z" />
<glyph glyph-name="period" unicode="." horiz-adv-x="200"
d="M163 0h-112v175h112v-175z" />
<glyph glyph-name="slash" unicode="/" horiz-adv-x="583"
d="M570 700l-226 -367l-62 112l155 255h133zM332 313l-187 -313h-131l189 313h129z" />
<glyph glyph-name="zero" unicode="0"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="one" unicode="1" horiz-adv-x="461"
d="M336 357l-111 70v147l111 112v-329zM336 14l-111 112v161l111 56v-329z" />
<glyph glyph-name="two" unicode="2"
d="M371 651l-63 -63h-273l105 112h182zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-63 -63l-49 49v231l112 -56v-161zM435 0h-295l-49 49l64 63h153z" />
<glyph glyph-name="three" unicode="3" horiz-adv-x="462"
d="M357 651l-63 -63h-154l-126 112h293zM420 357l-113 70v147l63 63l50 -49v-231zM392 350l-98 -49h-154l-98 49l98 63h154zM420 112l-50 -49l-63 63v161l113 56v-231zM357 49l-50 -49h-293l126 112h154z" />
<glyph glyph-name="four" unicode="4"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343z" />
<glyph glyph-name="five" unicode="5" horiz-adv-x="470"
d="M441 700l-126 -112h-154l-63 63l49 49h294zM147 427l-112 -70v231l50 49l62 -63v-147zM413 350l-98 -49h-154l-98 49l98 63h154zM441 112l-49 -49l-62 63v161l111 56v-231zM378 49l-48 -49h-302l133 112h154z" />
<glyph glyph-name="six" unicode="6"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="seven" unicode="7" horiz-adv-x="462"
d="M378 700l-112 -112h-154l-112 112h378zM392 357l-112 70v147l112 112v-329zM392 0l-112 112v175l112 56v-343z" />
<glyph glyph-name="eight" unicode="8"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49
l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="nine" unicode="9"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 56v161l63 63l50 -49v-231zM140 413l-112 -56v231l49 49l63 -63v-161zM406 350l-98 -63h-153l-99 63l99 49h153zM435 112l-50 -49l-63 63v147l113 70v-231zM371 49l-49 -49h-294l127 112h153z" />
<glyph glyph-name="colon" unicode=":" horiz-adv-x="168"
d="M140 315h-112v175h112v-175zM140 0h-112v175h112v-175z" />
<glyph glyph-name="semicolon" unicode=";" horiz-adv-x="168"
d="M140 315h-112v175h112v-175zM140 -112h-112v287h112v-287z" />
<glyph glyph-name="less" unicode="&#x3c;" horiz-adv-x="307"
d="M283 583l-132 -222h-127l196 339h63v-117zM283 0h-68l-194 339h130l132 -229v-110z" />
<glyph glyph-name="equal" unicode="="
d="M406 425l-98 -49h-153l-99 49l99 63h153zM406 260l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="greater" unicode="&#x3e;" horiz-adv-x="316"
d="M286 361h-127l-132 222v117h63zM289 339l-194 -339h-68v110l132 229h130z" />
<glyph glyph-name="question" unicode="?" horiz-adv-x="406"
d="M315 651l-63 -63h-154l-98 112h266zM378 357l-112 70v147l63 63l49 -49v-231zM350 350l-98 -49h-154v112h154zM210 0h-112v175h112v-175z" />
<glyph glyph-name="at" unicode="@" horiz-adv-x="756"
d="M700 351q-15 -90 -62 -148q-43 -53 -97 -61q-6 -1 -17 -1q-38 0 -60.5 23.5t-22.5 59.5q-6 -20 -19 -35q-35 -41 -80 -41q-22 0 -47 11q-75 34 -75 173l4 35q13 91 63 149t107 58t84 -52l5 -10l9 56l48 -1l-37 -209l-14 -85q-1 -8 -1.5 -17t-0.5 -13q0 -19 4 -31.5
t14 -20.5t25 -8q7 0 18 4t22.5 12.5t29.5 30.5t31.5 54.5t18.5 67t5 63.5q0 111 -68.5 181.5t-175.5 70.5q-22 0 -46 -3q-107 -15 -179 -104t-72 -211v-23q2 -11 2 -15l9 -54q27 -105 102 -166t172 -61q84 0 160 47q24 14 48 35q31 26 52 55l20 -35l-25 -28
q-112 -118 -258 -118q-150 0 -245 110t-95 278q0 52 20 105q17 47 49 91q46 61 107.5 98t132.5 45q11 2 23 2h19q130 0 214.5 -84.5t84.5 -215.5q0 -31 -6 -64zM462 471q-4 27 -22.5 43t-42.5 16q-15 0 -25 -5q-26 -11 -42 -33q-19 -26 -35 -75l-13 -48q-4 -24 -5 -44
t-1 -42q0 -18 3.5 -33t9.5 -27q16 -36 56 -36q25 0 44 19q14 14 23.5 31t22.5 53l17 73l2 9l7 41q1 10 1.5 20.5t0.5 15.5q0 15 -1 22z" />
<glyph glyph-name="A" unicode="A"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="B" unicode="B"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-112 -112v329l112 -56v-161zM371 49l-49 -49h-280
l113 112h153z" />
<glyph glyph-name="C" unicode="C" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-64 63l49 49h280zM140 413l-112 -56v231l49 49l63 -63v-161zM140 126l-63 -63l-49 49v231l112 -70v-147zM420 0h-280l-49 49l64 63h153z" />
<glyph glyph-name="D" unicode="D"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-112 -112v329l112 -56v-161zM371 49l-49 -49h-280l113 112h153z" />
<glyph glyph-name="E" unicode="E" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="F" unicode="F" horiz-adv-x="413"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="G" unicode="G"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-70l-98 49l98 63h70zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="H" unicode="H"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="I" unicode="I" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343z" />
<glyph glyph-name="J" unicode="J"
d="M435 357l-113 70v161l113 112v-343zM435 14l-113 112v161l113 56v-329zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="K" unicode="K"
d="M435 624l-114 -189l-129 -1l162 266h81v-76zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="L" unicode="L"
d="M140 427l-112 -70v343l112 -112v-161zM435 14l-113 112v161l113 56v-329zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="M" unicode="M" horiz-adv-x="756"
d="M715 700l-113 -112h-156l-68 70l-69 -70h-156l-111 112h673zM140 427l-112 -70v329l112 -112v-147zM728 357l-112 70v147l112 111v-328zM434 399l-56 -42l-56 42v175l55 57l57 -57v-175zM728 0l-112 112v175l112 56v-343zM435 112l-57 -112l-56 112v175l56 56l57 -56
v-175zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="N" unicode="N"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 0l-113 126v161l113 56v-343zM140 98l-112 -98v343l112 -56v-189z" />
<glyph glyph-name="O" unicode="O"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="P" unicode="P"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="Q" unicode="Q" horiz-adv-x="464"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 144l-50 -50l-63 63v130l113 56v-199zM140 123l-63 -60l-49 49v231l112 -56v-164zM265 0h-125l-51 51l67 60zM436 -143l-255 255h159
l96 -95v-160z" />
<glyph glyph-name="R" unicode="R"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175zM491 0l-164 111l-72 170h57l49 23z" />
<glyph glyph-name="S" unicode="S"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM371 49l-49 -49h-294l127 112h153z" />
<glyph glyph-name="T" unicode="T" horiz-adv-x="379"
d="M393 700l-78 -112h-55l-68 70l-72 -70h-57l-77 112h407zM246 419l-55 -62l-59 62v152l59 64l55 -63v-153zM246 62l-56 -62l-60 62v219l60 63l56 -63v-219z" />
<glyph glyph-name="U" unicode="U"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="V" unicode="V"
d="M435 357l-113 70v189l113 84v-343zM140 427l-112 -70v343l112 -84v-189zM435 344l-127 -344l-67 137l56 157zM227 135l-70 -135l-129 344l140 -51zM286 0h-110l57 111z" />
<glyph glyph-name="W" unicode="W" horiz-adv-x="757"
d="M728 357l-112 56v175l112 112v-343zM435 413l-57 -56l-56 56v175l56 112l57 -112v-175zM140 413l-112 -56v343l112 -112v-175zM435 126l-56 -57l-56 57v175l55 42l57 -42v-175zM729 113l-50 -48l-65 64v145l115 70v-231zM140 128l-64 -63l-48 48v232l112 -72v-145z
M666 52l-51 -52h-477l-49 51l63 61h158l68 -68l68 68h156z" />
<glyph glyph-name="X" unicode="X" horiz-adv-x="437"
d="M424 583l-132 -222h-63v111l132 228h63v-117zM209 361h-62l-133 228v111h68l127 -228v-111zM424 0h-68l-128 224v115h64l132 -229v-110zM209 224l-130 -224h-65v115l130 224h65v-115z" />
<glyph glyph-name="Y" unicode="Y"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM289 63l-58 -63l-57 63v217h115v-217z" />
<glyph glyph-name="Z" unicode="Z" horiz-adv-x="436"
d="M337 700l-66 -112h-145l-111 112h322zM422 585l-150 -256l-70 105l158 266h62v-115zM265 313l-162 -313h-89v106l126 216zM421 0h-299l59 112h127z" />
<glyph glyph-name="bracketleft" unicode="[" horiz-adv-x="287"
d="M280 700l-112 -112h-13l-113 112h238zM140 427l-112 -70v329l112 -112v-147zM140 126l-112 -112v329l112 -56v-161zM280 0h-238l113 112h13z" />
<glyph glyph-name="backslash" unicode="\" horiz-adv-x="583"
d="M298 445l-62 -112l-226 367h133zM566 0h-131l-187 313h129z" />
<glyph glyph-name="bracketright" unicode="]" horiz-adv-x="287"
d="M245 700l-112 -112h-14l-112 112h238zM259 357l-112 56v161l112 112v-329zM259 14l-112 112v147l112 70v-329zM245 0h-238l112 112h14z" />
<glyph glyph-name="asciicircum" unicode="^" horiz-adv-x="464"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147z" />
<glyph glyph-name="underscore" unicode="_" horiz-adv-x="354"
d="M354 -200h-354l-2 41h355z" />
<glyph glyph-name="grave" unicode="`"
d="M406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="a" unicode="a"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="b" unicode="b"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-112 -112v329l112 -56v-161zM371 49l-49 -49h-280
l113 112h153z" />
<glyph glyph-name="c" unicode="c" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-64 63l49 49h280zM140 413l-112 -56v231l49 49l63 -63v-161zM140 126l-63 -63l-49 49v231l112 -70v-147zM420 0h-280l-49 49l64 63h153z" />
<glyph glyph-name="d" unicode="d"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-112 -112v329l112 -56v-161zM371 49l-49 -49h-280l113 112h153z" />
<glyph glyph-name="e" unicode="e" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="f" unicode="f" horiz-adv-x="413"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="g" unicode="g"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-70l-98 49l98 63h70zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="h" unicode="h"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="i" unicode="i" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343z" />
<glyph glyph-name="j" unicode="j"
d="M435 357l-113 70v161l113 112v-343zM435 14l-113 112v161l113 56v-329zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="k" unicode="k"
d="M435 624l-114 -189l-129 -1l162 266h81v-76zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="l" unicode="l"
d="M140 427l-112 -70v343l112 -112v-161zM435 14l-113 112v161l113 56v-329zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153z" />
<glyph glyph-name="m" unicode="m" horiz-adv-x="756"
d="M715 700l-113 -112h-156l-68 70l-69 -70h-156l-111 112h673zM140 427l-112 -70v329l112 -112v-147zM728 357l-112 70v147l112 111v-328zM434 399l-56 -42l-56 42v175l55 57l57 -57v-175zM728 0l-112 112v175l112 56v-343zM435 112l-57 -112l-56 112v175l56 56l57 -56
v-175zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="n" unicode="n"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 0l-113 126v161l113 56v-343zM140 98l-112 -98v343l112 -56v-189z" />
<glyph glyph-name="o" unicode="o"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="p" unicode="p"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="q" unicode="q" horiz-adv-x="464"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 144l-50 -50l-63 63v130l113 56v-199zM140 123l-63 -60l-49 49v231l112 -56v-164zM265 0h-125l-51 51l67 60zM436 -143l-255 255h159
l96 -95v-160z" />
<glyph glyph-name="r" unicode="r"
d="M371 651l-63 -63h-153l-113 112h280zM140 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM406 350l-98 -49h-153l-99 49l99 63h153zM140 112l-112 -112v343l112 -56v-175zM491 0l-164 111l-72 170h57l49 23z" />
<glyph glyph-name="s" unicode="s"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM371 49l-49 -49h-294l127 112h153z" />
<glyph glyph-name="t" unicode="t" horiz-adv-x="379"
d="M393 700l-78 -112h-55l-68 70l-72 -70h-57l-77 112h407zM246 419l-55 -62l-59 62v152l59 64l55 -63v-153zM246 62l-56 -62l-60 62v219l60 63l56 -63v-219z" />
<glyph glyph-name="u" unicode="u"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="v" unicode="v"
d="M435 357l-113 70v189l113 84v-343zM140 427l-112 -70v343l112 -84v-189zM435 344l-127 -344l-67 137l56 157zM227 135l-70 -135l-129 344l140 -51zM286 0h-110l57 111z" />
<glyph glyph-name="w" unicode="w" horiz-adv-x="757"
d="M728 357l-112 56v175l112 112v-343zM435 413l-57 -56l-56 56v175l56 112l57 -112v-175zM140 413l-112 -56v343l112 -112v-175zM435 126l-56 -57l-56 57v175l55 42l57 -42v-175zM729 113l-50 -48l-65 64v145l115 70v-231zM140 128l-64 -63l-48 48v232l112 -72v-145z
M666 52l-51 -52h-477l-49 51l63 61h158l68 -68l68 68h156z" />
<glyph glyph-name="x" unicode="x" horiz-adv-x="437"
d="M424 583l-132 -222h-63v111l132 228h63v-117zM209 361h-62l-133 228v111h68l127 -228v-111zM424 0h-68l-128 224v115h64l132 -229v-110zM209 224l-130 -224h-65v115l130 224h65v-115z" />
<glyph glyph-name="y" unicode="y"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM289 63l-58 -63l-57 63v217h115v-217z" />
<glyph glyph-name="z" unicode="z" horiz-adv-x="436"
d="M337 700l-66 -112h-145l-111 112h322zM422 585l-150 -256l-70 105l158 266h62v-115zM265 313l-162 -313h-89v106l126 216zM421 0h-299l59 112h127z" />
<glyph glyph-name="braceleft" unicode="{" horiz-adv-x="280"
d="M266 700l-70 -112h-41l-64 63l49 49h126zM140 427l-112 -70v231l49 49l63 -63v-147zM140 126l-63 -63l-49 49v231l112 -56v-161zM266 0h-126l-49 49l64 63h41z" />
<glyph glyph-name="bar" unicode="|" horiz-adv-x="168"
d="M140 427l-112 -70v343l112 -112v-161zM140 112l-112 -112v343l112 -56v-175z" />
<glyph glyph-name="braceright" unicode="}" horiz-adv-x="280"
d="M189 651l-64 -63h-41l-70 112h126zM252 357l-112 56v161l62 63l50 -49v-231zM252 112l-50 -49l-62 63v147l112 70v-231zM189 49l-49 -49h-126l70 112h41z" />
<glyph glyph-name="asciitilde" unicode="~" horiz-adv-x="578"
d="M555 470h-288l113 112h63zM300 580l-112 -112h-58l-113 112h283z" />
<glyph glyph-name="Adieresis" unicode="&#xc4;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM401 800l-82 -49l-77 49l77 63z
M215 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="Aring" unicode="&#xc5;"
d="M352 800l-98 -49h-70l-98 49l98 63h70zM420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z
" />
<glyph glyph-name="Ccedilla" unicode="&#xc7;" horiz-adv-x="420"
d="M287 -145l-70 67l70 69v-136zM420 700l-112 -112h-153l-64 63l49 49h280zM140 413l-112 -56v231l49 49l63 -63v-161zM140 126l-63 -63l-49 49v231l112 -70v-147zM420 0h-280l-49 49l64 63h153z" />
<glyph glyph-name="Eacute" unicode="&#xc9;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM391 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Ntilde" unicode="&#xd1;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 0l-113 126v161l113 56v-343zM140 98l-112 -98v343l112 -56v-189zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Odieresis" unicode="&#xd6;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM401 800
l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="Udieresis" unicode="&#xdc;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM401 800l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63z
" />
<glyph glyph-name="aacute" unicode="&#xe1;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM403 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="agrave" unicode="&#xe0;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="acircumflex" unicode="&#xe2;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="adieresis" unicode="&#xe4;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM401 800l-82 -49l-77 49l77 63z
M215 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="atilde" unicode="&#xe3;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="aring" unicode="&#xe5;"
d="M352 800l-98 -49h-70l-98 49l98 63h70zM420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175z
" />
<glyph glyph-name="ccedilla" unicode="&#xe7;" horiz-adv-x="420"
d="M287 -145l-70 67l70 69v-136zM420 700l-112 -112h-153l-64 63l49 49h280zM140 413l-112 -56v231l49 49l63 -63v-161zM140 126l-63 -63l-49 49v231l112 -70v-147zM420 0h-280l-49 49l64 63h153z" />
<glyph glyph-name="eacute" unicode="&#xe9;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM391 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="egrave" unicode="&#xe8;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM388 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="ecircumflex" unicode="&#xea;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM396 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="edieresis" unicode="&#xeb;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM386 800l-82 -49l-77 49l77 63zM200 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="iacute" unicode="&#xed;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM265 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="igrave" unicode="&#xec;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM268 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="icircumflex" unicode="&#xee;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM271 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="idieresis" unicode="&#xef;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM254 800l-82 -49l-77 49l77 63zM68 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="ntilde" unicode="&#xf1;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 0l-113 126v161l113 56v-343zM140 98l-112 -98v343l112 -56v-189zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="oacute" unicode="&#xf3;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="ograve" unicode="&#xf2;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="ocircumflex" unicode="&#xf4;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="odieresis" unicode="&#xf6;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM401 800
l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="otilde" unicode="&#xf5;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="uacute" unicode="&#xfa;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="ugrave" unicode="&#xf9;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="ucircumflex" unicode="&#xfb;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM403 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="udieresis" unicode="&#xfc;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM401 800l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63z
" />
<glyph glyph-name="dagger" unicode="&#x2020;" horiz-adv-x="0"
/>
<glyph glyph-name="degree" unicode="&#xb0;" horiz-adv-x="416"
/>
<glyph glyph-name="cent" unicode="&#xa2;" horiz-adv-x="436"
d="M283 733l-60 -58l-54 56v55l56 54l58 -53v-54zM422 700l-115 -112h-152l-61 61l46 51h30l54 -55l56 55h142zM140 412l-111 -54v229l50 50l61 -64v-161zM140 123l-63 -62l-49 49v234l112 -71v-150zM421 0h-139l-56 54l-56 -54h-31l-46 46l63 66h153zM282 -87l-56 -58
l-57 60v54l57 55l56 -55v-56z" />
<glyph glyph-name="sterling" unicode="&#xa3;" horiz-adv-x="532"
d="M490 700l-93 -112h-157l-62 61l50 51h262zM227 430l-55 -61l-57 59v160l48 48l64 -63v-143zM367 351l-95 -50h-32l-54 56l54 56h33zM156 358l-53 -57h-35l-82 57l67 55h49zM227 124l-113 -124v286l56 58l57 -58v-162zM518 0h-378l102 112h181z" />
<glyph glyph-name="section" unicode="&#xa7;"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 471l-112 -70v187l49 49l63 -63v-103zM409 395l-102 -49h-153l-97 49l97 63h162zM140 171l-113 -56v274l113 -57v-161zM434 115l-112 56v161l112 55v-272zM406 108l-99 -63h-153l-98 63l98 49h153zM434 -70l-49 -49l-63 63
v87l112 70v-171zM371 -133l-49 -49h-295l127 112h153z" />
<glyph glyph-name="bullet" unicode="&#x2022;" horiz-adv-x="354"
d="M308 350q0 -55 -38 -93t-93 -38t-93 38t-38 93t38 93t93 38t93 -38t38 -93z" />
<glyph glyph-name="paragraph" unicode="&#xb6;" horiz-adv-x="0"
/>
<glyph glyph-name="germandbls" unicode="&#xdf;" horiz-adv-x="926"
d="M898 700l-127 -112h-153l-64 63l49 49h295zM435 700l-127 -112h-153l-64 63l49 49h295zM603 427l-112 -70v231l49 49l63 -63v-147zM140 427l-112 -70v231l49 49l63 -63v-147zM869 350l-98 -49h-153l-99 49l99 63h153zM406 350l-98 -49h-153l-99 49l99 63h153zM898 112
l-50 -49l-63 63v161l113 56v-231zM435 112l-50 -49l-63 63v161l113 56v-231zM834 49l-49 -49h-294l127 112h153zM371 49l-49 -49h-294l127 112h153z" />
<glyph glyph-name="registered" unicode="&#xae;" horiz-adv-x="768"
d="M719 316q0 -139 -98 -237t-237 -98q-140 0 -237.5 97.5t-97.5 237.5t97.5 237.5t237.5 97.5q139 0 237 -98t98 -237zM681 316q0 123 -87.5 209.5t-210.5 86.5q-122 0 -209 -87t-87 -210q0 -122 87 -209t210 -87q122 0 209.5 86.5t87.5 210.5zM580 147h-92l-129 172h-26
v-127q0 -15 14 -21.5t32 -6.5v-17h-155v17q21 0 34 6t13 20v280q0 21 -6 25t-37 4v17h163q27 0 42 -1.5t31 -7.5t28 -14q17 -12 23 -22.5t9 -23t3 -22.5q0 -22 -8.5 -40t-22 -30t-24.5 -17q-20 -10 -43 -10l116 -150q5 -7 12.5 -10.5t22.5 -3.5v-17zM461 430q-3 34 -24 50
q-15 12 -33 12l-51 4q-8 0 -14.5 -5.5t-6.5 -14.5l1 -135h16q17 0 28 1t26 7q59 20 59 69q0 8 -1 12z" />
<glyph glyph-name="copyright" unicode="&#xa9;" horiz-adv-x="768"
d="M720 321q0 -140 -97 -238t-238 -98q-139 0 -237 98t-98 238t97.5 237.5t237.5 97.5t237.5 -97.5t97.5 -237.5zM682 321q0 122 -87 209t-210 87q-122 0 -209.5 -86.5t-87.5 -209.5t87 -210t210 -87q122 0 209.5 86.5t87.5 210.5zM535 196q-32 -32 -51 -44q-42 -27 -93 -27
q-20 0 -30 1t-32 7q-60 16 -93 66.5t-33 113.5q0 49 18.5 89.5t57.5 68.5q26 18 54 26.5t52 8.5q30 0 63 -9q13 -4 27 -8q4 -1 12 -1q20 0 23 16h19l4 -125h-20q-4 24 -18 47q-19 31 -48 44q-10 4 -25 7t-25 3q-56 0 -92 -45q-19 -24 -29 -57t-10 -66q0 -62 30 -106t84 -44
q48 0 74 7.5t58 34.5l12 10z" />
<glyph glyph-name="trademark" unicode="&#x2122;" horiz-adv-x="865"
d="M831 285h-155v17q25 0 37 5.5t12 22.5v269l-130 -314h-18l-140 298v-253q0 -16 12.5 -22t37.5 -6v-17h-125v17q25 0 37 5.5t12 22.5v273q0 21 -9 27.5t-28 6.5h-14v17h111l128 -280l122 280h109v-17q-24 0 -35.5 -3.5t-11.5 -21.5v-282q0 -16 12 -22t36 -6v-17zM332 560
l-18 -1q0 35 -17 54t-58 19l-36 1v-303q0 -16 12.5 -22t36.5 -6v-17h-156v17q25 0 37.5 5.5t12.5 22.5v303l-36 -1q-40 0 -57.5 -19t-17.5 -54l-18 1l3 94h309z" />
<glyph glyph-name="acute" unicode="&#xb4;"
d="M406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="dieresis" unicode="&#xa8;" horiz-adv-x="0"
d="M401 800l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="AE" unicode="&#xc6;" horiz-adv-x="883"
d="M883 700l-112 -112h-153l-113 112h378zM420 700l-112 -112h-153l-113 112h378zM603 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM869 350l-98 -49h-153l-99 49l99 63h153zM406 350l-98 -49h-153l-99 49
l99 63h153zM603 126l-112 -112v329l112 -56v-161zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM883 0h-378l113 112h153z" />
<glyph glyph-name="Oslash" unicode="&#xd8;"
d="M309 498l-81 -135v211h81v-76zM234 126h-81v76l81 135v-211zM371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56
v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="plusminus" unicode="&#xb1;" horiz-adv-x="571"
/>
<glyph glyph-name="yen" unicode="&#xa5;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM289 234l-60 -45l-55 44v47h115v-46zM438 174l-78 -57h-51l-73 55l75 57h49zM223 172l-80 -55h-42l-75 55l75 57h47zM287 58l-57 -58l-57 58v56l57 42
l57 -43v-55z" />
<glyph glyph-name="mu" unicode="&#xb5;" horiz-adv-x="599"
/>
<glyph glyph-name="ordfeminine" unicode="&#xaa;" horiz-adv-x="324"
d="M294 700l-78 -78h-108l-79 78h265zM304 460l-79 49v103l79 78v-230zM98 509l-78 -49v230l78 -78v-103zM284 455l-68 -34h-108l-69 34l69 44h108zM304 210l-79 78v123l79 39v-240zM98 288l-78 -78v240l78 -39v-123z" />
<glyph glyph-name="ordmasculine" unicode="&#xba;" horiz-adv-x="324"
d="M260 666l-44 -44h-108l-44 44l34 34h127zM304 460l-79 49v103l44 44l35 -34v-162zM98 509l-78 -49v162l34 34l44 -44v-103zM304 288l-35 -34l-44 44v113l79 39v-162zM98 298l-44 -44l-34 34v162l78 -39v-113zM260 244l-35 -34h-127l-34 34l44 44h108z" />
<glyph glyph-name="ae" unicode="&#xe6;" horiz-adv-x="883"
d="M883 700l-112 -112h-153l-113 112h378zM420 700l-112 -112h-153l-113 112h378zM603 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM869 350l-98 -49h-153l-99 49l99 63h153zM406 350l-98 -49h-153l-99 49
l99 63h153zM603 126l-112 -112v329l112 -56v-161zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM883 0h-378l113 112h153z" />
<glyph glyph-name="oslash" unicode="&#xf8;"
d="M309 498l-81 -135v211h81v-76zM234 126h-81v76l81 135v-211zM371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56
v-161zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="questiondown" unicode="&#xbf;" horiz-adv-x="406"
d="M308 525h-112v175h112v-175zM308 287h-154l-98 63l98 49h154v-112zM140 126l-63 -63l-49 49v231l112 -70v-147zM406 0h-266l-49 49l63 63h154z" />
<glyph glyph-name="exclamdown" unicode="&#xa1;" horiz-adv-x="168"
d="M140 525h-112v175h112v-175zM140 112l-112 -112v483l112 -70v-301z" />
<glyph glyph-name="logicalnot" unicode="&#xac;" horiz-adv-x="742"
d="M707 0h-58v242h-633v58h691v-300z" />
<glyph glyph-name="florin" unicode="&#x192;" horiz-adv-x="520"
d="M515 644q0 -19 -15 -33t-37 -14q-16 0 -26 16q-12 15 -12 22l1 5l11 25l1 6q0 15 -21 15q-26 0 -48 -29t-28 -73l-20 -137l-2 -13h133l-10 -36h-127l-32 -258l-11 -48l-26 -116q-15 -66 -60 -126q-39 -50 -99 -50q-38 0 -62 20t-24 51q0 22 14 37t36 15q18 0 29.5 -10.5
t11.5 -26.5q0 -6 -8 -30l-4 -11l-1 -7v-5l6 -5h10q31 0 49 27.5t31 92.5l12 61l4 36l36 353h-122l14 36h116q16 130 68.5 204.5t130.5 74.5q33 0 57 -20t24 -49z" />
<glyph glyph-name="guillemotleft" unicode="&#xab;" horiz-adv-x="0"
/>
<glyph glyph-name="guillemotright" unicode="&#xbb;" horiz-adv-x="0"
/>
<glyph glyph-name="ellipsis" unicode="&#x2026;" horiz-adv-x="600"
d="M563 0h-112v175h112v-175zM363 0h-112v175h112v-175zM163 0h-112v175h112v-175z" />
<glyph glyph-name="nonbreakingspace" unicode="&#xa0;" horiz-adv-x="282"
/>
<glyph glyph-name="Agrave" unicode="&#xc0;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="Atilde" unicode="&#xc3;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="Otilde" unicode="&#xd5;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="OE" unicode="&#x152;" horiz-adv-x="883"
d="M883 700l-112 -112h-153l-113 112h378zM371 651l-63 -63h-153l-64 63l49 49h182zM603 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM869 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49
l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM603 126l-112 -112v329l112 -56v-161zM883 0h-378l113 112h153zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="oe" unicode="&#x153;" horiz-adv-x="883"
d="M883 700l-112 -112h-153l-113 112h378zM371 651l-63 -63h-153l-64 63l49 49h182zM603 427l-112 -70v329l112 -112v-147zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM869 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49
l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM603 126l-112 -112v329l112 -56v-161zM883 0h-378l113 112h153zM371 49l-49 -49h-182l-49 49l64 63h153z" />
<glyph glyph-name="endash" unicode="&#x2013;" horiz-adv-x="400"
d="M400 408l-46 -111h-354l47 111h353z" />
<glyph glyph-name="emdash" unicode="&#x2014;" horiz-adv-x="800"
d="M800 408l-46 -111h-754l47 111h753z" />
<glyph glyph-name="quotedblleft" unicode="&#x201c;" horiz-adv-x="325"
d="M303 413h-111v287h111v-287zM133 413h-112v287h112v-287z" />
<glyph glyph-name="quotedblright" unicode="&#x201d;" horiz-adv-x="325"
d="M303 413h-111v287h111v-287zM133 413h-112v287h112v-287z" />
<glyph glyph-name="quoteleft" unicode="&#x2018;" horiz-adv-x="155"
d="M133 413h-112v287h112v-287z" />
<glyph glyph-name="quoteright" unicode="&#x2019;" horiz-adv-x="155"
d="M133 413h-112v287h112v-287z" />
<glyph glyph-name="divide" unicode="&#xf7;" horiz-adv-x="571"
/>
<glyph glyph-name="ydieresis" unicode="&#xff;"
d="M401 800l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63zM435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM289 63l-58 -63l-57 63v217h115v-217z" />
<glyph glyph-name="Ydieresis" unicode="&#x178;"
d="M401 800l-82 -49l-77 49l77 63zM215 800l-82 -49l-77 49l77 63zM435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM406 350l-98 -49h-153l-99 49l99 63h153zM289 63l-58 -63l-57 63v217h115v-217z" />
<glyph glyph-name="currency" unicode="&#xa4;" horiz-adv-x="555"
d="M542 184l-70 -72l-87 84q-51 -30 -109 -30q-30 0 -57 7.5t-53 23.5l-81 -85l-71 72l81 85q-30 51 -30 109q0 30 7.5 57t23.5 53l-82 84l71 72l85 -85q49 30 106 30q60 0 110 -31l86 86l70 -72l-84 -85q30 -51 30 -109q0 -30 -7.5 -56.5t-22.5 -52.5zM389 378q0 47 -33 80
t-80 33t-80 -33t-33 -80t33 -80t80 -33t80 33t33 80z" />
<glyph glyph-name="guilsinglleft" unicode="&#x2039;" horiz-adv-x="0"
/>
<glyph glyph-name="guilsinglright" unicode="&#x203a;" horiz-adv-x="0"
/>
<glyph glyph-name="daggerdbl" unicode="&#x2021;" horiz-adv-x="0"
/>
<glyph glyph-name="periodcentered" unicode="&#xb7;" horiz-adv-x="200"
d="M163 266h-112v175h112v-175z" />
<glyph glyph-name="periodcentered" unicode="&#x2219;" horiz-adv-x="200"
d="M163 266h-112v175h112v-175z" />
<glyph glyph-name="quotesinglbase" unicode="&#x201a;" horiz-adv-x="0"
/>
<glyph glyph-name="quotedblbase" unicode="&#x201e;" horiz-adv-x="0"
/>
<glyph glyph-name="perthousand" unicode="&#x2030;" horiz-adv-x="0"
/>
<glyph glyph-name="Acircumflex" unicode="&#xc2;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM406 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="Ecircumflex" unicode="&#xca;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM396 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Aacute" unicode="&#xc1;"
d="M420 700l-112 -112h-153l-113 112h378zM435 357l-113 70v147l113 112v-329zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 0l-113 112v175l113 56v-343zM140 112l-112 -112v343l112 -56v-175zM403 800l-98 -49h-153l-99 49l99 63
h153z" />
<glyph glyph-name="Edieresis" unicode="&#xcb;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM386 800l-82 -49l-77 49l77 63zM200 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="Egrave" unicode="&#xc8;" horiz-adv-x="420"
d="M420 700l-112 -112h-153l-113 112h378zM140 427l-112 -70v329l112 -112v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM140 126l-112 -112v329l112 -56v-161zM420 0h-378l113 112h153zM388 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Iacute" unicode="&#xcd;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM265 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Icircumflex" unicode="&#xce;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM271 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Idieresis" unicode="&#xcf;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM254 800l-82 -49l-77 49l77 63zM68 800l-82 -49l-77 49l77 63z" />
<glyph glyph-name="Igrave" unicode="&#xcc;" horiz-adv-x="168"
d="M140 357l-112 70v161l112 112v-343zM140 0l-112 112v175l112 56v-343zM268 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Oacute" unicode="&#xd3;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Ocircumflex" unicode="&#xd4;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Ograve" unicode="&#xd2;"
d="M371 651l-63 -63h-153l-64 63l49 49h182zM435 357l-113 70v147l63 63l50 -49v-231zM140 427l-112 -70v231l49 49l63 -63v-147zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800
l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Uacute" unicode="&#xda;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Ucircumflex" unicode="&#xdb;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM403 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Ugrave" unicode="&#xd9;"
d="M435 357l-113 70v161l113 112v-343zM140 427l-112 -70v343l112 -112v-161zM435 112l-50 -49l-63 63v161l113 56v-231zM140 126l-63 -63l-49 49v231l112 -56v-161zM371 49l-49 -49h-182l-49 49l64 63h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="circumflex" unicode="&#x2c6;"
d="M406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="tilde" unicode="&#x2dc;"
d="M406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="macron" unicode="&#xaf;"
d="M406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="cedilla" unicode="&#xb8;" horiz-adv-x="420"
d="M287 -145l-70 67l70 69v-136z" />
<glyph glyph-name="Scaron" unicode="&#x160;"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM371 49l-49 -49h-294l127 112h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="scaron" unicode="&#x161;"
d="M435 700l-127 -112h-153l-64 63l49 49h295zM140 427l-112 -70v231l49 49l63 -63v-147zM406 350l-98 -49h-153l-99 49l99 63h153zM435 112l-50 -49l-63 63v161l113 56v-231zM371 49l-49 -49h-294l127 112h153zM406 800l-98 -49h-153l-99 49l99 63h153z" />
<glyph glyph-name="Zcaron" unicode="&#x17d;" horiz-adv-x="0"
/>
<glyph glyph-name="zcaron" unicode="&#x17e;" horiz-adv-x="0"
/>
<glyph glyph-name="brokenbar" unicode="&#xa6;" horiz-adv-x="0"
/>
<glyph glyph-name="Eth" unicode="&#xd0;" horiz-adv-x="0"
/>
<glyph glyph-name="eth" unicode="&#xf0;" horiz-adv-x="0"
/>
<glyph glyph-name="Yacute" unicode="&#xdd;" horiz-adv-x="0"
/>
<glyph glyph-name="yacute" unicode="&#xfd;" horiz-adv-x="0"
/>
<glyph glyph-name="Thorn" unicode="&#xde;" horiz-adv-x="0"
/>
<glyph glyph-name="thorn" unicode="&#xfe;" horiz-adv-x="0"
/>
<glyph glyph-name="minus" unicode="&#xad;" horiz-adv-x="0"
/>
<glyph glyph-name="multiply" unicode="&#xd7;" horiz-adv-x="0"
/>
<glyph glyph-name="onesuperior" unicode="&#xb9;" horiz-adv-x="0"
/>
<glyph glyph-name="twosuperior" unicode="&#xb2;" horiz-adv-x="0"
/>
<glyph glyph-name="threesuperior" unicode="&#xb3;" horiz-adv-x="0"
/>
<glyph glyph-name="onehalf" unicode="&#xbd;" horiz-adv-x="0"
/>
<glyph glyph-name="onequarter" unicode="&#xbc;" horiz-adv-x="0"
/>
<glyph glyph-name="threequarters" unicode="&#xbe;" horiz-adv-x="0"
/>
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 49 KiB

BIN
www/css/LED.ttf Normal file

Binary file not shown.

BIN
www/css/LED.woff Normal file

Binary file not shown.

377
www/css/common.css Normal file
View File

@ -0,0 +1,377 @@
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 0.8fr 47px 0.7fr 2.8fr;
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
}
.buttons-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr;
grid-template-areas: ". . ." ". . ." ". . ." ". . .";
}
.buttons-6container {
display: grid;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . .";
}
.buttons-7container {
display: grid;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . . .";
}
#title{
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
text-align: center;
color: #ddd;
font-size: 4ex;
}
#text{
font-family: "Lucida Grande", Verdana, Arial, sans-serif;
text-align: center;
color: #ccc;
font-size: 2ex;
}
.encoders{
margin: 0 auto;
}
.mainGrid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . . . .";
}
.buttonsGrid {
display: grid;
grid-template-columns: 70px 70px 70px 70px 70px 70px 70px 70px 70px 70px 70px 70px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . ." ". . . . ." ". . . . .";
}
.FxGrid {
display: grid;
grid-template-columns: 70px 70px 70px 70px 70px 70px 70px;
grid-template-rows: 1fr;
gap: 1px 1px;
align-items: stretch;
background-color: #020202;
grid-template-areas: ". . . . . . ,";
}
.RGYGrid {
display: grid;
grid-template-columns: 70px 70px 70px;
grid-template-rows: 1fr;
background-color: #020202;
gap: 1px 1px;
grid-template-areas: ". . .";
}
.RGBGrid {
display: grid;
grid-template-columns: 70px 70px 70px 70px 70px 70px;
grid-template-rows: 1fr;
gap: 1px 1px;
background-color: #020202;
grid-template-areas: ". . . . . .";
}
.TopRackGrid {
display: grid;
grid-template-columns: 120px 600px 30px 30px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . .";
}
.TextGrid {
display: grid;
grid-template-columns: 250px 370px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". .";
}
.Rackgrid {
display: grid;
grid-template-columns: 170px 70px 70px 70px 70px 70px 70px 70px 70px 70px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . . . . . .";
}
.Settingrid {
display: grid;
grid-template-columns: 110px 80px 80px 100px 80px 80px 140px 40px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . . . . .";
}
.ColorRackGrid {
display: grid;
grid-template-columns: 170px 70px 70px 70px 70px 70px 70px 70px 70px 70px;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . . . . . . . . .";
}
.grid3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr;
gap: 1px 1px;
grid-template-areas: ". . .";
}
.encodersGrid {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
gap: 1px 1px;
grid-template-areas: "." "." "." "." ".";
}
.webaudiobut{
border-radius: 4px;
border : #002020 1px solid;
-webkit-box-shadow: 2px 4px 8px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 2px 4px 8px -1px rgba(0,0,0,0.72);
box-shadow: 2px 4px 8px -1px rgba(0,0,0,0.72);
background-image: linear-gradient(174deg, #111,#030303);
}
.content{
width : 800px;
padding : 10px 20px;
margin : 5px auto;
border-radius: 10px;
border : #212121 2px solid;
background-color: #000000;
background-image: linear-gradient(174deg, #222,#111);
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
.Rackcontent{
width : 845px;
border-radius: 3px;
margin : 1px auto;
border : #111 2px solid;
background-color: #111;
-webkit-box-shadow: 4px 6px 44px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 44px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 44px -1px rgba(0,0,0,0.72);
}
.content-title {
color : #ddd;
border : none;
margin-top : 15px;
padding-bottom : 0;
margin-bottom : 0;
background-color : inherit;
}
.content-names {
padding : 10px;
padding-top : 0;
border : none;
box-shadow : none;
background-color : inherit;
}
.etherled {
color: #666;
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
font-size: 1.5ex;
text-align: middle;
margin-left: 20px
margin-top: 10px
}
a { text-decoration: none;
border: none;
outline: none;
}
a:hover, a:focus {
border: none;
outline: none;
color: #fff;
}
h2 {
position: relative;
line-height: 3ex;
color: #666;
text-align: middle;
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
font-size: 1.7ex;
margin-top : 1px;
text-shadow: 2px 3px #000;
}
@font-face {
font-family: "Bus Led Display Small";
src: url("7f37946c45abf5482c243bf326f82628.eot"); /* IE9*/
src: url("7f37946c45abf5482c243bf326f82628.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
url("7f37946c45abf5482c243bf326f82628.woff2") format("woff2"), /* chrome、firefox */
url("7f37946c45abf5482c243bf326f82628.woff") format("woff"), /* chrome、firefox */
url("7f37946c45abf5482c243bf326f82628.ttf") format("truetype"), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url("7f37946c45abf5482c243bf326f82628.svg#Bus Led Display Small") format("svg"); /* iOS 4.1- */
}
.busled{
color: #ccc;
background: #000;
font-family:"Bus Led Display Small" !important;
font-size:2em;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.7px;
-moz-osx-font-smoothing: grayscale;
}
.navled{
color: #ccc;
background: #000;
font-family:"Bus Led Display Small" !important;
font-size:1.5em;
margin-left: 2px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.7px;
-moz-osx-font-smoothing: grayscale;
}
.submit {
background: #000;
color: #f0f0f0;
width: 90px;
height: 20px;
text-align: center;
margin-left: 15px;
vertical-align: middle;
font-family:"Bus Led Display Small" !important;
font-size:1.1em;
font-style:normal;
border: 1px solid #445;
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
.submite {
background: #000;
color: #f0f0f0;
width: 120px;
height: 25px;
text-align: center;
margin-left: 15px;
vertical-align: middle;
font-family:"Bus Led Display Small" !important;
font-size:1.3em;
font-style:normal;
border: 1px solid #033;
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
.submitalign {
background: #000;
color: #f0f0f0;
width: 60px;
height: 30px;
text-align: center;
margin-left: 15px;
vertical-align: middle;
font-family:"Bus Led Display Small" !important;
font-size:1.4em;
font-style:normal;
border: 1px solid #001515;
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
.submites {
background: #000;
color: #f0f0f0;
width: 30px;
height: 35px;
text-align: center;
margin-left: 15px;
vertical-align: middle;
font-family:"Bus Led Display Small" !important;
font-size:1.3em;
font-style:normal;
border: 1px solid #445;
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
.submitext{
font-family: 'Bus Led Display Small';
color: #fff;
background: #000;
font-size: 5ex;"
border-radius: 5px;
border : #222222 2px solid;
-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);
}
#overlay {
position: absolute;
top: 200px;
left: 650px;
-o-transform : scaleX(-1);
-webkit-transform : scaleX(-1);
transform : scaleX(-1);
-ms-filter : fliph; /*IE*/
filter : fliph; /*IE*/
}
#videoel {
-o-transform : scaleX(-1);
-webkit-transform : scaleX(-1);
transform : scaleX(-1);
-ms-filter : fliph; /*IE*/
filter : fliph; /*IE*/
}
.lasertext {
font-size: small;
font-family: Helvetica, Verdana, Arial, sans-serif;
color: #bbb;
}
.mgalign {
color: #aaa;
text-align: middle;
font-family: 'Lucida Grande', Verdana, Arial, sans-serif;
display: grid;
height: 400px;
grid-template-columns: 200px 200px 200px 200px;
grid-template-rows: 1Fr;
border-width: 1px solid #445;
gap: 1px 1px;
grid-template-areas: ". . . . . . . . . .";
}
.lasergrid {
display: grid;
height: 323px;
width: 200px;
grid-template-columns: 62px 62px 62px;
grid-template-rows: 30px 19px 10px 55px 19px 20px 16px 55px 19px 25px 16px 55px 19px;
line-height: 1;
justify-items: center;
align-items: center;
color:#88c;
}
.lissabox {
display: grid;
height: 353px;
width: 126px;
grid-template-columns: 124px;
grid-template-rows: 15px 338px;
background-color: #000000;
background-image: linear-gradient(147deg, #000000 0%, #434343 374%);
line-height: 1;
padding: 6px;
justify-items: center;
align-items: center;
border-color: #334;
border-style: groove;
border-width: 1px;
}

5
www/css/font-awesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

4522
www/css/fontawesome.css vendored Normal file

File diff suppressed because it is too large Load Diff

1
www/css/style.css Normal file
View File

@ -0,0 +1 @@
* { box-sizing: border-box; } body {margin: 0;}.row{display:table;padding:10px;width:100%;}.cell{width:8%;display:table-cell;height:75px;}body, html{background-color:#1d1c25;margin:0;padding:0;}.range{-webkit-appearance:none;-moz-appearance:none;position:absolute;left:50%;top:50%;width:200px;margin-top:10px;transform:translate(-50%, -50%);}input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]:focus{outline:none;}input[type=range]::-moz-range-track{-moz-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-webkit-grab;-webkit-transition:border 1000ms ease;transition:border 1000ms ease;}input[type=range]::-moz-range-thumb{-moz-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-moz-grab;-moz-transition:border 1000ms ease;transition:border 1000ms ease;}.range.blue::-webkit-slider-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-webkit-slider-thumb{border-color:rgb(87,111,230);}.range.purple::-webkit-slider-thumb{border-color:rgb(152,68,183);}.range.pink::-webkit-slider-thumb{border-color:rgb(255,53,127);}.range.blue::-moz-range-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-moz-range-thumb{border-color:rgb(87,111,230);}.range.purple::-moz-range-thumb{border-color:rgb(152,68,183);}.range.pink::-moz-range-thumb{border-color:rgb(255,53,127);}input[type=range]::-webkit-slider-thumb:active{cursor:-webkit-grabbing;}input[type=range]::-moz-range-thumb:active{cursor:-moz-grabbing;}.range.blue{position:relative;background-color:rgba(31,27,27,0.09);width:75%;top:37.5px;}*{box-sizing:border-box;}body{margin:0;}#iy3nk{height:68px;}#il4ah{text-align:center;color:#ef4079;}#i7ox{font-family:Verdana, Geneva, sans-serif;color:#40acef;text-align:left;}#io5q{color:#40acef;}#iy2eh{text-align:center;}#i8a73{text-align:center;color:#4540ef;}#i3fzo{text-align:center;color:#7740ef;}#ikbjg{text-align:center;color:#b140ef;}@media (max-width: 768px){.cell{width:100%;display:block;}}

1
www/css/style2.css Normal file
View File

@ -0,0 +1 @@
input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]:focus{outline:none;}input[type=range]::-moz-range-track{-moz-appearance:none;background:linear-gradient(45deg, rgba(59,173,227,1) 0%, rgba(87,111,230,1) 25%, rgba(152,68,183,1) 51%, rgba(255,53,127,1) 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3bade3 ', endColorstr='#ff357f ', GradientType=1 );height:2px;}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-webkit-grab;-webkit-transition:border 1000ms ease;transition:border 1000ms ease;}input[type=range]::-moz-range-thumb{-moz-appearance:none;border:2px solid;border-radius:50%;height:25px;width:25px;max-width:80px;position:relative;bottom:11px;background-color:#1d1c25;cursor:-moz-grab;-moz-transition:border 1000ms ease;transition:border 1000ms ease;}.range.blue::-webkit-slider-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-webkit-slider-thumb{border-color:rgb(87,111,230);}.range.purple::-webkit-slider-thumb{border-color:rgb(152,68,183);}.range.pink::-webkit-slider-thumb{border-color:rgb(255,53,127);}.range.blue::-moz-range-thumb{border-color:rgb(59,173,227);}.range.ltpurple::-moz-range-thumb{border-color:rgb(87,111,230);}.range.purple::-moz-range-thumb{border-color:rgb(152,68,183);}.range.pink::-moz-range-thumb{border-color:rgb(255,53,127);}input[type=range]::-webkit-slider-thumb:active{cursor:-webkit-grabbing;}input[type=range]::-moz-range-thumb:active{cursor:-moz-grabbing;}.range.blue{position:relative;background-color:rgba(31,27,27,0.09);width:75%;top:37.5px;}body{margin:0;}#iy3nk{font-family: "Lucida Grande", Verdana, Arial, sans-serif; font-size: 10ex; height:68px;}#il4ah{text-align:center;color:#ef4079;}#i7ox{font-family:Verdana, Geneva, sans-serif;color:#40acef;text-align:left;}#io5q{color:#40acef;}#iy2eh{text-align:center;}#i8a73{text-align:center;color:#4540ef;}#i3fzo{text-align:center;color:#7740ef;}#ikbjg{text-align:center;color:#b140ef;}@media (max-width: 768px){}

BIN
www/favicon-16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
www/favicon-32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
www/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
www/knobs/.DS_Store vendored Normal file

Binary file not shown.

BIN
www/knobs/big0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
www/knobs/big1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
www/knobs/big2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
www/knobs/big3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
www/knobs/blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
www/knobs/blue.psd Normal file

Binary file not shown.

BIN
www/knobs/blue2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

37
www/knobs/close.svg Normal file
View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 31.112 31.112" style="enable-background:new 0 0 31.112 31.112;" xml:space="preserve">
<polygon points="31.112,1.414 29.698,0 15.556,14.142 1.414,0 0,1.414 14.142,15.556 0,29.698 1.414,31.112 15.556,16.97
29.698,31.112 31.112,29.698 16.97,15.556 "/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 672 B

BIN
www/knobs/cyan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
www/knobs/green.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
www/knobs/indexs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
www/knobs/lasergrid0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
www/knobs/lasergrid1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
www/knobs/lasergrid2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
www/knobs/lasergrid3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
www/knobs/leds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
www/knobs/pink.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
www/knobs/power.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
www/knobs/red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
www/knobs/simplegray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

BIN
www/knobs/white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
www/knobs/yellow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
www/launch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
www/repository/calig.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
www/repository/display.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
www/repository/layer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
www/repository/lj2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
www/repository/scenes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Some files were not shown because too many files have changed in this diff Show More