Post sonoptik commit
This commit is contained in:
commit
ab868905b3
109 changed files with 22885 additions and 0 deletions
70
clitools/README.md
Normal file
70
clitools/README.md
Normal 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
|
||||
BIN
clitools/__pycache__/runner_lib.cpython-38.pyc
Normal file
BIN
clitools/__pycache__/runner_lib.cpython-38.pyc
Normal file
Binary file not shown.
11
clitools/_run.sh
Executable file
11
clitools/_run.sh
Executable 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
BIN
clitools/exports/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
clitools/exports/__pycache__/websocket_server.cpython-38.pyc
Normal file
BIN
clitools/exports/__pycache__/websocket_server.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
46
clitools/exports/toNull.py
Executable file
46
clitools/exports/toNull.py
Executable 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
|
||||
|
||||
63
clitools/exports/toRedis.py
Normal file
63
clitools/exports/toRedis.py
Normal 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
84
clitools/exports/toUDP.py
Normal 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
115
clitools/exports/toWS.py
Normal 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
157
clitools/exports/tonano.py
Normal 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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
388
clitools/exports/websocket_server.py
Normal file
388
clitools/exports/websocket_server.py
Normal 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
BIN
clitools/filters/.DS_Store
vendored
Normal file
Binary file not shown.
200
clitools/filters/anaglyph.py
Executable file
200
clitools/filters/anaglyph.py
Executable 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
108
clitools/filters/colorcycle.py
Executable 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
174
clitools/filters/kaleidoscope.py
Executable 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
300
clitools/filters/redilysis.py
Executable 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
|
||||
|
||||
186
clitools/filters/redilysis_colors.py
Normal file
186
clitools/filters/redilysis_colors.py
Normal 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
BIN
clitools/generators/.DS_Store
vendored
Normal file
Binary file not shown.
2407
clitools/generators/159.gml
Executable file
2407
clitools/generators/159.gml
Executable file
File diff suppressed because it is too large
Load diff
2791
clitools/generators/160.gml
Executable file
2791
clitools/generators/160.gml
Executable file
File diff suppressed because it is too large
Load diff
2873
clitools/generators/OSC3.py
Normal file
2873
clitools/generators/OSC3.py
Normal file
File diff suppressed because it is too large
Load diff
BIN
clitools/generators/__pycache__/OSC3.cpython-38.pyc
Normal file
BIN
clitools/generators/__pycache__/OSC3.cpython-38.pyc
Normal file
Binary file not shown.
BIN
clitools/generators/__pycache__/turtle.cpython-38.pyc
Normal file
BIN
clitools/generators/__pycache__/turtle.cpython-38.pyc
Normal file
Binary file not shown.
53
clitools/generators/blank.py
Executable file
53
clitools/generators/blank.py
Executable 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
BIN
clitools/generators/book2.ild
Executable file
Binary file not shown.
54
clitools/generators/brmlab1.svg
Executable file
54
clitools/generators/brmlab1.svg
Executable 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
53
clitools/generators/brmlab2.svg
Executable 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
85
clitools/generators/dummy.py
Executable 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
182
clitools/generators/example.py
Executable 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()
|
||||
406
clitools/generators/fromGML.py
Normal file
406
clitools/generators/fromGML.py
Normal 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>
|
||||
'''
|
||||
126
clitools/generators/fromOSC.py
Normal file
126
clitools/generators/fromOSC.py
Normal 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()
|
||||
|
||||
72
clitools/generators/fromRedis.py
Executable file
72
clitools/generators/fromRedis.py
Executable 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))
|
||||
|
||||
84
clitools/generators/fromUDP.py
Normal file
84
clitools/generators/fromUDP.py
Normal 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()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
319
clitools/generators/fromilda.py
Normal file
319
clitools/generators/fromilda.py
Normal 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")
|
||||
131
clitools/generators/osc2redis.py
Normal file
131
clitools/generators/osc2redis.py
Normal 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()
|
||||
|
||||
174
clitools/generators/redilysis_lines.py
Executable file
174
clitools/generators/redilysis_lines.py
Executable 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
|
||||
|
||||
288
clitools/generators/redilysis_particles.py
Executable file
288
clitools/generators/redilysis_particles.py
Executable 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
|
||||
93
clitools/generators/text.py
Normal file
93
clitools/generators/text.py
Normal 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]]
|
||||
|
||||
175
clitools/generators/trckr.py
Normal file
175
clitools/generators/trckr.py
Normal 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
194
clitools/generators/tunnel.py
Executable 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
126
clitools/runner.py
Executable 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
379
clitools/runner_lib.py
Normal 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
87
clitools/runner_midi.py
Executable 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue