Compare commits

...

24 Commits

Author SHA1 Message Date
Lapin f3314441d3 [feat] drawing_optimisation: in progess
the feature implement a paper: https://art-science.org/journal/v7n4/v7n4pp155/artsci-v7n4pp155.pdf

there is some generator to test the optimisation in: ./clitools/generators/drawingTests/
Now, all the optimisation will be in ./libs3/plotOptimizer.py
in ./libs3/tracer3.py the adding of point is avoid an will be replace by the optimisation from the paper
2020-12-17 20:21:04 +01:00
Lapin 8164320694 [fix] to avoid temporary vim's file 2020-12-17 20:18:32 +01:00
alban 5f7c61f616 [enh] Bring back the clitools fromRedis generator 2020-10-13 22:06:02 +02:00
alban 31e4e66408 [enh] there should be a clitools blank generator 2020-10-13 22:06:02 +02:00
alban 50b3806825 [fix] clitools midi runner should work with novation pad 2020-10-13 22:06:02 +02:00
alban 2f54d37856 [fix] Many fixes to the clitools runner lib 2020-10-13 22:06:02 +02:00
alban 1e43a5fc43 [fix] clitools tunnel generator should use circles 2020-10-13 22:06:02 +02:00
alban 8e9cd509ab [fix] clitools dummy generator should send white points 2020-10-13 22:06:02 +02:00
alban df068b8e30 [fix] exceptions for redilisys colors filter 2020-10-13 22:06:02 +02:00
alban 0b7ad2d75b [fix] clitools filters kaleidoscop should not send empty points lists 2020-10-13 22:06:02 +02:00
alban b2cc1b1ff5 [fix] clitools toRedis should not accept empty input 2020-10-13 22:05:11 +02:00
alban 18aaa1b625 Update 'clitools/exports/toRedis.py' 2020-10-13 19:54:06 +00:00
alban e3b1b255a3 Merge branch 'master' of lapin/LJ into master 2020-10-13 19:52:05 +00:00
Sam 8fbaaa7880 Add watchLJ to identify all LJ in network 2020-10-13 01:56:26 +02:00
Sam 3b0d44deb1 Easyer doc for newcomers. 2020-10-11 15:29:54 +02:00
Sam bfae3baa37 Better readme for newcomer 2020-10-11 15:01:26 +02:00
Sam b7851f518d Missing files 2020-10-11 11:48:53 +02:00
Sam a89cf3d414 Merge branch 'master' of https://git.interhacker.space/teamlaser/LJ 2020-10-11 11:26:10 +02:00
alban be60f25b16 [fix] clitools anaglyph should not color black points 2020-10-10 20:11:54 +02:00
alban eadc8ca19d [fix] clitools anaglyph should not color black points 2020-10-10 20:09:40 +02:00
Sam aef92762b5 Merge branch 'master' of https://git.interhacker.space/teamlaser/LJ 2020-10-10 19:29:54 +02:00
Sam 93cbcfefd5 fireandforget version 2020-10-10 19:29:07 +02:00
alban a06e3ba07e [enh] clitools : adds a color redilysis filter 2020-10-10 16:52:42 +02:00
alban b0c28e1510 [enh] clitools: adds a cool audio based particle generater 2020-10-10 13:23:01 +02:00
42 changed files with 4797 additions and 1960 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
.*swp*
.*sw*
*__pycache__
www/config.js

55
LJ.conf
View File

@ -1,8 +1,8 @@
[General]
lasernumber = 4
debug = 0
lasernumber = 1
debug = 1
ljayserverip = 0.0.0.0
wwwip = 192.168.2.44
wwwip = 127.0.0.1
nozoscip = 127.0.0.1
bhoroscip = 127.0.0.1
autostart = artnet
@ -12,7 +12,7 @@ wsport = 9001
[laser0]
color = -1
type = DS1000
ip = 192.168.2.3
ip = 127.0.0.1
kpps = 25000
centerx = 0
centery = 0
@ -21,7 +21,7 @@ zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = -1
swapx = 1
swapy = -1
lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
@ -36,8 +36,8 @@ ip = 192.168.2.5
kpps = 25000
centerx = 0
centery = 0
zoomx = 80.0
zoomy = 80.0
zoomx = 50.0
zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
@ -45,9 +45,9 @@ swapx = -1
swapy = -1
lsteps = [ (1.0, 2),(0.25, 1), (0.75, 1), (1.0, 5)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[laser2]
color = -1
@ -65,14 +65,14 @@ swapx = -1
swapy = -1
lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[laser3]
color = -1
type = LUKE400
ip = 192.168.2.4
ip = 192.168.1.5
kpps = 25000
centerx = 0
centery = 0
@ -81,34 +81,19 @@ zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = 1
swapy = 1
swapx = -1
swapy = -1
lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[plugins]
plugins = {
"aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True},
"nozoid": {"OSC": 8003, "command": "python3 plugins/audio/nozoids3.py", "display": True},
"glyph": {"OSC": 8004, "command": "python3 plugins/laserglyph.py", "display": True},
"planet": {"OSC": 8005, "command": "python3 plugins/planetarium/main.py", "display": True},
"words": {"OSC": 8006, "command": "python3 plugins/livewords3.py", "display": True},
"cycl": {"OSC": 8007, "command": "python3 plugins/textcycl.py", "display": True},
"simu": {"OSC": 8008, "command": "python plugins/pysimu.py", "display": False},
"artnet": {"OSC": 8009, "command": "python3 libs3/artnet.py", "display": False},
"trckr": {"OSC": 8017, "command": "python3 plugins/trckr.py", "display": False},
"maxw": {"OSC": 8012, "command": "python3 plugins/maxwell.py", "display": True},
"square": {"OSC": 8013, "command": "python3 plugins/square.py", "display": True},
"custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True},
"mitraille": {"OSC": 8015, "command": "python3 plugins/audio/mitraille.py", "display": True},
"livecode": {"OSC": 8016, "command": "python3 plugins/livecoding.py", "display": True},
"ljpong": {"OSC": 8020, "command": "python plugins/games/ljpong/main.py", "display": True},
"ljwars": {"OSC": 8021, "command": "python plugins/games/ljsw/main.py", "display": True},
"audiogen": {"OSC": 8030, "command": "python3 plugins/audio/audiogen.py", "display": False},
"midigen": {"OSC": 8031, "command": "python3 plugins/audio/midigen.py", "display": False},
"viewgen": {"OSC": 8032, "command": "python3 plugins/audio/viewgen.py", "display": True}
"custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True}
}

View File

@ -1,114 +0,0 @@
[General]
lasernumber = 1
debug = 0
ljayserverip = 0.0.0.0
wwwip = 192.168.2.43
nozoscip = 127.0.0.1
bhoroscip = 127.0.0.1
autostart = artnet
wstype = ws
wsport = 9001
[laser0]
color = -1
type = DS1000
ip = 192.168.2.3
kpps = 25000
centerx = 0
centery = 0
zoomx = 50.0
zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = 1
swapy = -1
lsteps = [ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[laser1]
color = -1
type = LOCAL
ip = 192.168.2.5
kpps = 25000
centerx = 0
centery = 0
zoomx = 50.0
zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = -1
swapy = -1
lsteps = [ (1.0, 2),(0.25, 1), (0.75, 1), (1.0, 5)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[laser2]
color = -1
type = LUKE400
ip = 192.168.2.6
kpps = 25000
centerx = 0
centery = 0
zoomx = 50.0
zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = -1
swapy = -1
lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[laser3]
color = -1
type = LUKE400
ip = 192.168.1.5
kpps = 25000
centerx = 0
centery = 0
zoomx = 50.0
zoomy = 50.0
sizex = 32000
sizey = 32000
finangle = 0.0
swapx = -1
swapy = -1
lsteps = [(1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]
warpdest = [[-1500., 1500.],
[ 1500., 1500.],
[ 1500.,-1500.],
[-1500.,-1500.]]
[plugins]
plugins = {
"aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True},
"nozoid": {"OSC": 8003, "command": "python3 plugins/audio/nozoids3.py", "display": True},
"glyph": {"OSC": 8004, "command": "python3 plugins/laserglyph.py", "display": True},
"planet": {"OSC": 8005, "command": "python3 plugins/planetarium/main.py", "display": True},
"words": {"OSC": 8006, "command": "python3 plugins/livewords3.py", "display": True},
"cycl": {"OSC": 8007, "command": "python3 plugins/textcycl.py", "display": True},
"simu": {"OSC": 8008, "command": "python plugins/pysimu.py", "display": False},
"artnet": {"OSC": 8009, "command": "python3 libs3/artnet.py", "display": False},
"trckr": {"OSC": 8017, "command": "python3 plugins/trckr.py", "display": False},
"maxw": {"OSC": 8012, "command": "python3 plugins/maxwell.py", "display": True},
"square": {"OSC": 8013, "command": "python3 plugins/square.py", "display": True},
"custom1": {"OSC": 8014, "command": "python3 plugins/custom1.py", "display": True},
"mitraille": {"OSC": 8015, "command": "python3 plugins/audio/mitraille.py", "display": True},
"livecode": {"OSC": 8016, "command": "python3 plugins/livecoding.py", "display": True},
"ljpong": {"OSC": 8020, "command": "python plugins/games/ljpong/main.py", "display": True},
"ljwars": {"OSC": 8021, "command": "python plugins/games/ljsw/main.py", "display": True},
"audiogen": {"OSC": 8030, "command": "python3 plugins/audio/audiogen.py", "display": False},
"midigen": {"OSC": 8031, "command": "python3 plugins/audio/midigen.py", "display": False},
"viewgen": {"OSC": 8032, "command": "python3 plugins/audio/viewgen.py", "display": True}
}

BIN
Pd/.DS_Store vendored Normal file

Binary file not shown.

23
Pd/LJsender.pd Normal file
View File

@ -0,0 +1,23 @@
#N canvas 468 143 709 527 10;
#X msg 60 237 disconnect;
#X floatatom 27 294 0 0 0 0 - - -;
#X text 22 315 Outlet is nonzero if connection is open \, zero otherwise.
;
#X msg 37 60 send mytext trololo;
#X obj 28 267 netsend 1;
#X msg 51 125 send /pl/0/0 (150 2300 65280) (170 170 65280) (230 170
65280) (210 230 65280) (150 230 65280);
#X text 258 29 LJ Sender;
#X msg 46 84 send /pl/0/0 150 2300 65280 170 170 65280 230 170 65280
210 230 65280 150 230 65280;
#X msg 26 39 connect 127.0.0.1 8083;
#X msg 58 165 send /pl/0/0 [(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)];
#X text 444 174 <- le mieux;
#X connect 0 0 4 0;
#X connect 3 0 4 0;
#X connect 4 0 1 0;
#X connect 5 0 4 0;
#X connect 7 0 4 0;
#X connect 8 0 4 0;
#X connect 9 0 4 0;

106
Pd/ljpd.py Normal file
View File

@ -0,0 +1,106 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
LJPD
Udp server to redis
v0.1b
'''
import traceback, time
import argparse
import socket
import _thread
import redis
print()
print ("LJPD")
print ("Arguments parsing if needed...")
argsparser = argparse.ArgumentParser(description="dumpUDP v0.1b help mode")
argsparser.add_argument("-i","--IP",help="IP to bind to (0.0.0.0 by default)", type=str)
argsparser.add_argument("-p","--port",help="UDP port to bind to (9000 by default)", type=str)
argsparser.add_argument("-l","--lj",help="LJ IP address (127.0.0.1 by default)", type=str)
args = argsparser.parse_args()
# LJ server IP name
if args.IP:
ljIP = lj.IP
else:
ljIP = "127.0.0.1"
# Server
if args.IP:
serverIP = args.IP
else:
serverIP = "0.0.0.0"
# ORCA destination device
if args.port:
UDPORT = int(args.port)
else:
UDPORT = 8083
print("Connecting to Redis...")
r = redis.StrictRedis(host= ljIP, port=6379, db=0)
def GetTime():
return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
def udp_thread():
while True:
payload, client_address = sock.recvfrom(1024)
udpath = payload.decode('utf_8')
print(GetTime(),"From", str(client_address),"got", udpath )
#r.set('/pl/0/0', "/pl/"+str(clientnumber)+"/")
#print(udpath[0:1], " ",udpath[1:2], " ",udpath[2:3], " ",udpath[3:4], " " )
# Reply to client
bytesToSend = str.encode("ACK :"+str(payload))
serverAddressPort = (client_address, UDPORT)
bufferSize = 1024
#sock.sendto(bytesToSend, serverAddressPort)
sock.sendto(bytesToSend, client_address)
time.sleep(0.005)
def Start(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, ())
# Launch server in another thread.
print("Launching UDP Server", serverIP,':', UDPORT)
Start(serverIP, UDPORT)
# Do something else
try:
while True:
time.sleep(0.005)
except Exception:
traceback.print_exc()
finally:
print("")
print("ljpd stopped.")

147
README.md
View File

@ -1,4 +1,4 @@
LJ v0.8.2
LJ v0.8.2 'fireandforget'
By Sam Neurohack, Loloster, Cocoa
@ -7,19 +7,104 @@ LICENCE : CC BY
![LJ](https://www.teamlaser.fr/lj/images/lj2.png)
A software laser framework with GUI, for up to 4 lasers live actions with ethedreams DACs. Think creative like Laser "battles", planetarium, sharing available lasers in demoparties for competition, ...
LJ is like a video projector where you fire images and forget. Lasers are dangerous : you can really fire real world objects. That's why you really shouldn't forget your lasers and go drink a beer.
As content creator, it's damn easy to send frames : LJ do the heavy lifting for up to 4 lasers live actions. Think creative like games, Laser "battles", planetarium, sharing available lasers in demoparties for competition,...
LJ has 5 main components :
- "Plugins" are points generators (to one or more lasers). Lot examples comes with LJ : planetarium, 3D anaglyph animations,... See plugins directory.
- Frame *generators* to one or more lasers. Generators can be program "plugins" managed by LJ or you can feed the "connectors" directly. Remember with laser : *one frame = one polyline*, like in LOGO.
- A "tracer" per etherdream/laser that take its given point list, correct geometry, recompute in laser controller coordinates, send it to its controller and report its status to the "manager".
- A "manager" that talk to all tracers (which client number point lists to draw, new geometry correction,...), handle IOs (webui functions, OSC commands,...) and plugins.
- A web GUI in html, css, and vanilla js. This UI can be used in a tablet, computer, whatever. LJ does not come with an html server but absolutely can . This GUI has a (currently slow) simulator, but one can also use an etherdream/laser emulator (see nannou simulator below) to work without physical lasers !!
- A network available database (redis). "Plugins" can send directly their pointlists to redis. Each "tracer" is instructed to get one of the avalaible pointlist in redis.
- A "manager" that talk to all tracers (which point lists to draw, new geometry correction,...), handle IOs (webui functions, OSC commands,...) and plugins.
- A web GUI in html, css, and vanilla js. *Yes, there is a builtin simulator* so you can create without actual lasers. This UI can be used in a tablet, computer, whatever. You can open www directory and load html files. To not mess with user computer, it's "bring your own webserver".
- Frame *connectors* for pointlists, if you don't want to talk directly to the network available database (redis). Each "tracer" is instructed to get one of the avalaible pointlist in redis.
"Frames connectors" are :
- OSC (port 8002)
- Websocket (port 9001)
- Redis keys (one key per laser)
All derivatives you can think of like :
- Clitools is very powerfull : tunnel -> kaleidoscop -> color cycler -> redis connector.
- PureData (wip via UDP)
- Netlogo (text outputs are piped to clitools)
- ...
#
# Scenes and pointlists.
#
![Scenes](https://www.teamlaser.fr/lj/images/scenes.png)
LJ accept up to 4 groups = virtual "scenes" of 4 "pointlists" each (= one pointlist per laser), so up to 16 pointlists can be sent to redis at anytime from anywhere in the network. The idea behind this is to easily share actual lasers. Imagine in demo party :
Erica needs 4 lasers, that's the 4 pointlists of "scene" 0.
Paula and Jennifer use only 2 lasers each, so they can share "scene" 1.
And so on..
To change current scene used by lasers/tracers use the command : /scene/scenenumber/start 1
#
# How talk to frames connectors
#
One "frame" must be formated like /pl/scenenumber/lasernumber pointlist.
Example :
/pl/0/0 "[(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)]"
Use the same syntax if you send your pointlist directly in redis : "/pl/0/0" is the key and keyvalue is "[(150.0,..."
Every point must be : (x,y,color). Color is the hex color #FFFFFF in decimal.
You can't specify an output framerate. It depends on your pointlist and laser mirrors scanners :
- if you draw a simple + single object like a oneline you'll get thousands fps.
- Longer pointlists are longer to draw by laser mirrors.
Inside "clitools" every point is [x,y,color]
See command reference for extensive documentation, but /pl is the only one you need to draw something. The webUI is here to masquerade all these commands.
#
# Requirements
#
Computer :
- 4 threads+ processor
- RAM 4 Go
- Gigabit *ethernet*
Laser(s) :
- ILDA capable
- DAC : Etherdream
Web User Interface :
- any computer, phone, tablet,...
RJ 45 IP network : gigabits only !! wifi and wired 100 mpbs doesn't work well with several lasers. Seriously : if you experience frame drops you need to upgrade your network and use a dedicated computer to run seperately main program from plugins, youtube,...
Important : for best performance LJ is meant to run in a dedicated computer especially with multiple lasers and highly multitasked load : if you watch video, use live webcam face recognition, webui simulator,... and run LJ on the same computer, well you need a bunch of cores. If you don't, spread the load : you can use webui on a tablet, the livecam on a phone, run pointlists generators on another computer,...
The server/network/webUI idea allows to spread cpu intensive tasks on different cpu cores and especially give tracers enough cpu to feed etherdreams DACs smoothly. Of course all this can happen in one computer if *you have enough cpu/computers/network ressources*.
It's obviously overkill for one laser in a garage, but for several laserS games events, laserS art, laserS competition, laserS planetarium,... LJ will handle the complexity. Content providers like artists, demomakers,... just need create plugin in whatever langage, send the points to redis.
LJ is tested with Firefox, supports Linux and OS X. Windows is unkown but welcome, if someone want to jump in.
#
# Features among many others.
#
@ -41,33 +126,6 @@ Important : for best performance LJ is meant to run in a dedicated computer espe
- Plugins list auto start, see line in LJ.conf : autostart = artnet
- user.py plugin code example
#
# Scenes and pointlists.
#
![Scenes](https://www.teamlaser.fr/lj/images/scenes.png)
LJ accept up to 4 groups = virtual "scenes" of 4 "pointlists" each (= one pointlist per laser), so up to 16 pointlists can be sent to redis at anytime from anywhere in the network. The idea behind this is to easily share actual lasers. Imagine in demo party :
Erica needs 4 lasers, that's the 4 pointlists of "scene" 0.
Paula and Jennifer use only 2 lasers each, so they can share "scene" 1.
And so on..
The server/network/webUI idea allows to spread cpu intensive tasks on different cpu cores and especially give tracers enough cpu to feed etherdreams DACs smoothly. Of course all this can happen in one computer if *you have enough cpu/computers/network ressources*.
It's obviously overkill for one laser in a garage, but for several laserS games events, laserS art, laserS competition, laserS planetarium,... LJ will handle the complexity. Content providers like artists, demomakers,... just need create plugin in whatever langage, send the points to redis.
To change current scene used by lasers/tracers use the command : /scene/scenenumber/start 1
Registering the plugin in LJ.conf is absolutely not mandatory.
Needs at least : an etherdream DAC connected to an ILDA laser, RJ 45 IP network (gigabits only !! wifi and wired 100 mpbs doesn't work well with several lasers). Seriously : if you experience frame drops you need to upgrade your network and use a dedicated computer to run seperately main program from plugins, youtube,...
LJ is tested with Firefox, supports Linux and OS X. Windows is unkown but welcome, if someone want to jump in.
LJ is in dev : versions in this repository will always be core functionnal : accept and draw pointlists. New features can be not fully implemented, wait for the next commit. Any feedback is welcome at any time.
#
@ -93,7 +151,8 @@ type all install.sh commands beginning line 4. An OS X install script soon !!
- KVM :
an ISO is available here : https://www.tmplab.org/wp-content/lazer-iso.zip
- Postinstall for all :
## Postinstall for all :
You probably want redis bound to all network interfaces : comment the bind line in /etc/redis/redis.conf and restart it.
@ -161,6 +220,8 @@ node nodeclient.js
redis-cli -h redisserverIP monitor
#
# Plugin
#
@ -262,7 +323,7 @@ DrawDests() will take care of all your declared drawn elements/"objects" and Des
2 compiled nannou visualisers are included, one for Linux, one for macOS. It's pretty old version but much more compatible with "old" processors/computer.
To use this visualiser as one of LJ's lasers, in LJ.conf edit one of line ip = someipaddress with the IP of the computer running the visualiser. Relaunch LJ. One visualiser per computer.
To use this visualiser as one of LJ's lasers, use configure.py to set one laser to the IP of the computer running the visualiser. Relaunch LJ. One visualiser per computer.
#
# Todo
@ -280,6 +341,8 @@ To use this visualiser as one of LJ's lasers, in LJ.conf edit one of line ip = s
#
You need to update LJ to your network/etherdreams IPs. Be sure to check command arguments : python3 configure.py
LJ is network based and this is *critical and flickering reason #1* if not managed properly, especially if you have several lasers.
Our "always working solution", as we regularly move our gear for different venues :
@ -299,8 +362,6 @@ By default LJ uses on 127.0.0.1 (localhost) :
- Some OSC clients defined in LJ.conf to forward commands to defined plugins.
You need to update LJ.conf to your network/etherdreams IPs and be sure to check command arguments : python3 main.py --help
The need for a dedicated computer to act as "laser server" usually depends on how many lasers you want to control and your main computer load. If you seen flickering with small point lists, try the dedicated computer idea and/or stop process interfering like redis monitoring,...
#
@ -320,7 +381,7 @@ For glitching experience you can change resampling strategy live with "resampler
# Colors
#
LJ is compatible with TLL and analog modulation. Each point color is an int value, wich is simply the hex color in decimal. Example : white = #fffff = 65535.
LJ is compatible with TLL and analog modulation. Each point color is an int value, wich is simply the hex color in decimal. Example : white = #fffff = 16777215
#
# Ether dream DAC
@ -384,7 +445,9 @@ python3 talk3.py -i etherdreamIP
- Switch to simu page. If you don't see anything : check redis server or your points in redis doesn't respect pointlist formatting (see command reference).
- If talk3 works but you don't see your points : click on the Grid icon in Align page. This will override your pointlist and display squares. If Grid works : recomputed points by tracers are "bad" with given values in LJ.conf.
- Check Leds : for each IRL lasers the two sets of must be green at startup and laser should display their number or the pointlist you want.
- If talk3 works but you don't see your points : click on the Grid icon in Align page. This will override your pointlist and display squares. If Grid works : recomputed points by tracers are "bad" with given values in LJ.conf.
"Bad points" ?
@ -394,7 +457,7 @@ python3 talk3.py -i etherdreamIP
*Don't kill your scanners : becareful with kpps setting.*
The reset button copy the LJ_template.conf to LJ.conf.
#
# LJ commands reference
@ -444,8 +507,6 @@ lsteps is a string like "[ (1.0, 8),(0.25, 3), (0.75, 3), (1.0, 10)]"
/scene/scenenumber/start 0 or 1 : tell all tracers to use given scene
/regen : regen webui index html page.
/scim : change webui simulated laser.

8
clitools/exports/toRedis.py Executable file → Normal file
View File

@ -8,11 +8,11 @@
redis exporter
v0.1.0
A basic exporter
A basic exporter
LICENCE : CC
by cocoa
by cocoa
'''
@ -21,7 +21,7 @@ import sys
import os
import argparse
import redis
import time
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)
@ -47,12 +47,14 @@ try:
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)

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

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

View File

@ -66,9 +66,9 @@ red = (41,24,24)
white = (95,95,95)
blue = (0,41,64)
red = (255,0,0)
blue = (0,255,255)
white = (255,255,255)
red = (127,0,0)
blue = (0,128,128)
white = (128,128,128)
def anaglyph( pl ):
debug(name,'--------------- new loop ------------------')
@ -174,7 +174,6 @@ def anaglyph( pl ):
debug(name,"whiteList:{}".format(out1))
debug(name,"blueList:{}".format(out2))
debug(name,"redList:{}".format(out3))
return out3 + out2
return out1 + out3 + out2
#return out1 + out2 + out3

View File

@ -72,7 +72,8 @@ def cycleColor( pl ):
debug(name,"currentColor:{}".format(currentColor))
for i in range( 0, len(pl)):
pl[i][2] = rgb2int( currentColor)
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 :

View File

@ -162,7 +162,7 @@ try:
pointsList = ast.literal_eval(line)
# Do the filter
result = kaleidoscope( pointsList )
print( result, flush=True )
if len(result) : print( result, flush=True )
looptime = time.time() - start
# debug(name+" looptime:"+str(looptime))
if( looptime < optimal_looptime ):

View File

@ -0,0 +1,188 @@
#!/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 * 7)
try:
power = spect[key] / spect10Correct[key] * chaos
except:
pass
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

2873
clitools/generators/OSC3.py Normal file

File diff suppressed because it is too large Load Diff

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

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

View File

@ -0,0 +1,98 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This generator print different angle form 0 to 180 degres
v0.1.0
LICENCE : CC
by lapin (aka nipal)
'''
from __future__ import print_function
import time
import argparse
import sys
import math
name="generator::endingPoint"
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))
width = 800
height = 800
white = 0xFFFFFF
blank = 0
radius = 100
offset_circles = 10
beg_angle = 0
end_angle = 90
offset_angle = 10
angles_lines = []
shape = []
def set_angles_lines():
margin = radius + offset_circles
spacing_betwen = 2 * radius + offset_circles
circles_per_line = math.floor((width - margin) / spacing_betwen)
for ang in range(beg_angle, end_angle + offset_angle, offset_angle):
nb = int(ang / offset_angle)
cx = margin + (nb % circles_per_line) * spacing_betwen
cy = margin + int(nb / circles_per_line) * spacing_betwen
px = radius * math.cos(math.radians(ang))
py = radius * math.sin(math.radians(ang))
# line up
angles_lines.append([-px + cx, py + cy, blank])
angles_lines.append([-px + cx, py + cy, white])
angles_lines.append([ cx, 2 + cy, white])
angles_lines.append([ px + cx, py + cy, white])
#angles_lines.append([ px + cx, py + cy, blank])
# line down
angles_lines.append([-px + cx, -py + cy, blank])
angles_lines.append([-px + cx, -py + cy, white])
angles_lines.append([ cx, -2 + cy, white])
angles_lines.append([ px + cx, -py + cy, white])
#angles_lines.append([ px + cx, -py + cy, blank])
set_angles_lines()
shape = angles_lines
# print(angles_lines)
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))

View File

@ -0,0 +1,117 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This generator print a shape with 3 discinected component, 2 non eulerian and one eulerian.
v0.1.0
LICENCE : CC
by lapin (aka nipal)
'''
from __future__ import print_function
import time
import argparse
import sys
import math
name="generator::endingPoint"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def debug2(*args, **kwargs):
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))
width = 800
height = 800
white = 0xFFFFFF
blank = 0
def shape_scale(shape, scale_factor):
new_shape = []
for p in shape:
new_shape.append([p[0] * scale_factor, p[1] * scale_factor, p[2]])
return new_shape
def shape_incr(shape, x, y):
new_shape = []
for p in shape:
new_shape.append([p[0] + x, p[1] + y, p[2]])
return new_shape
comp_a = []
comp_b = []
comp_c = []
comp_b.append([ 0, 3, blank])
comp_b.append([ 0, 4, white])
comp_b.append([ 0, 0, white])
comp_b.append([ 3, 0, white])
comp_b.append([ 3, 6, white])
comp_b.append([ 3, 6, white])
comp_b.append([ 3, 0, white])
comp_b.append([ 3, 0, blank])
comp_b.append([ 3, 0, white])
comp_b.append([ 5, 4, white])
comp_b.append([ 5, 4, blank])
comp_a.append([ 5, 17, blank])
comp_a.append([ 5, 17, white])
comp_a.append([ 0, 5, white])
comp_a.append([12, 0, white])
comp_a.append([17, 12, white])
comp_a.append([ 5, 17, white])
comp_a.append([ 5, 17, blank])
comp_c.append([-3, 5, blank])
comp_c.append([-3, 5, white])
comp_c.append([ 0, 4, white])
comp_c.append([ 0, 0, white])
comp_c.append([ 4, 0, white])
comp_c.append([ 4, 4, white])
comp_c.append([ 7, 5, white])
comp_c.append([ 7, 5, blank])
comp_a = shape_scale(comp_a, 11)
comp_a = shape_incr(comp_a, 300, 75)
comp_b = shape_scale(comp_b, 45)
comp_b = shape_incr(comp_b, 0, 300)
comp_c = shape_scale(comp_c, 30)
comp_c = shape_incr(comp_c, 600, 300)
shape = comp_a + comp_b + comp_c
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))

View File

@ -0,0 +1,77 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This generator print 3 static vertical line.
The aim is to show The aim is to show the laser beam ignition time.
beam when ther is no optimisation
v0.1.0
LICENCE : CC
by lapin (aka nipal)
'''
from __future__ import print_function
import time
import argparse
import sys
name="generator::endingPoint"
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))
white = 0xFFFFFF
blank = 0
offset_y = 100
offset_x = 50
begin_x = 200
begin_y = 200
shape_factor = [
[0, 0, white],
[0, 1, blank],
[1, 1, white],
[1, 0, blank],
[2, 0, white],
[2, 1, blank],
[2, 1, blank],
]
shape = []
for point in shape_factor:
shape.append([begin_x + offset_x * point[0],
begin_y + offset_y * point[1],
point[2]])
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))

View File

@ -0,0 +1,84 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
This generator print a shape with best angle representation when the path is redraw
v0.1.0
LICENCE : CC
by lapin (aka nipal)
'''
from __future__ import print_function
import time
import argparse
import sys
import math
name="generator::endingPoint"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
def debug2(*args, **kwargs):
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))
width = 800
height = 800
white = 0xFFFFFF
blank = 0
point_offset = 250
point_width = 4
point_list = [
[8,7,6,10,7,3,6,2,7,11,6,9],
[5,6,1,],
[4,7,12,],
]
shape = []
# on ajoute des lilste de point
for l in point_list:
x = point_offset * ((l[0] - 1) % (point_width))
y = point_offset * int((l[0] - 1) / (point_width))
shape.append([x, y, blank])
debug2("=====")
debug2(f"id: {l[0]}\tx: {x}\ty: {y}\t\tpoint_width: {point_width}\t\n")
for p in l:
x = point_offset * ((p - 1) % (point_width))
y = point_offset * int((p - 1) / (point_width))
shape.append([x, y, white])
debug2(f"id: {p}\tx: {x}\ty: {y}\t\tpoint_width: {point_width}\t\n")
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))

View File

@ -12,7 +12,7 @@ Use it to test your filters and outputs
LICENCE : CC
by cocoa
by cocoa
'''
@ -28,17 +28,17 @@ def debug(*args, **kwargs):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="Dummy generator")
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()
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
color = 65280
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)):
@ -71,7 +71,7 @@ mire = [
[400,450,color],
]
shape = mire
shape = mire
while True:
@ -81,5 +81,5 @@ while True:
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ Use it to test your filters and outputs
LICENCE : CC
by cocoa
by cocoa
'''
@ -52,7 +52,7 @@ randomize = args.randomize
speed = args.speed
verbose = args.verbose
origSpeed = speed
origSpeed = speed
optimal_looptime = 1 / fps
square = [
[-1,1],
@ -62,14 +62,32 @@ square = [
[-1,1]
]
shape = square
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
centerRand = int(math.sqrt(randomize) / 4 ) + 1
debug( name, "centerRand:{}".format(centerRand ) )
class polylineGenerator( object ):
@ -103,10 +121,10 @@ class polylineGenerator( object ):
min_size = 9999
delList = []
if randomize :
# Change the vector
# Change the vector
centerVector[0] += random.randrange( -centerRand,centerRand )
centerVector[1] += random.randrange( -centerRand,centerRand )
# Modify the vector if it is over the limit
# 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:
@ -115,19 +133,19 @@ class polylineGenerator( object ):
currentCenter[1] += centerVector[1]
# Change speed
speed += int( random.randrange( int(-origSpeed),origSpeed ) )
if speed < origSpeed :
if speed < origSpeed :
speed = origSpeed
elif speed > (origSpeed + randomize / 2) :
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
# Augment speed with size
"""
size = 0 : += sqrt(speed)
size = half max size : +=speed
"""
if size < max_size / 4:
size += math.pow(speed, 0.1)
@ -143,7 +161,7 @@ class polylineGenerator( object ):
for i in delList:
del self.polylineList[i]
#debug(name, "polyline:",self.polylineList)
if min_size >= interval:
if min_size >= interval:
debug(name, "new shape")
self.polylineList.append([0,[currentCenter[0],currentCenter[1]]])
@ -172,5 +190,5 @@ while True:
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
#debug(name+" micro sleep:"+str( optimal_looptime - looptime))

View File

@ -10,6 +10,16 @@ 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'
@ -37,12 +47,12 @@ def intkey():
except ValueError:
print("Error.")
current_id=0
current_cmd=""
process = None
current_filename = ""
currentPlayList = []
playlistsDir = Path("./playlists")
current_id = 0
current_cmd = ""
process = None
current_filename = ""
currentPlayList = []
playlistsDir = Path("./playlists")
if not playlistsDir.is_dir() : playlistsDir.mkdir()
def ask(q):
@ -105,10 +115,12 @@ def action_changeCommand( inc ):
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():
@ -155,7 +167,7 @@ def action_deleteCommand():
action_listAll()
key = int(input())
# Exit early
if "x" == k:
if "x" == key:
return(False)
del currentPlayList[key]
return True
@ -163,7 +175,7 @@ def action_deleteCommand():
def action_listAll():
global currentPlayList, current_cmd
global currentPlayList, current_cmd, current_id
print("\n--------------------------------------")
for i,seq in enumerate(currentPlayList):
pre=""
@ -232,7 +244,7 @@ def action_loadPlaylist():
print( bcolors.HL + "This key '{}' is not valid".format(k) + bcolors.ENDC )
return False
# Load file
# @todo replace with _loadPlaylist
playlistFile = Path("./playlists/"+file_list[k].name)
currentPlayList = json.loads(playlistFile.read_text())
current_playlist_name = file_list[k].name
@ -242,7 +254,18 @@ def action_loadPlaylist():
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))
@ -353,13 +376,4 @@ def action_quit():
quit = inkey()
if quit != "n":
_kill(process)
sys.exit(1)
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"
}
sys.exit(1)

91
clitools/runner_midi.py Normal file → Executable file
View File

@ -1,12 +1,87 @@
#!/usr/bin/python3
import sys
import os
import signal
import subprocess
import time
import tty,termios
import argparse
import re
import json
from pathlib import Path
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)

22
ethertools/watchLJ.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
# or tcpdump -i eth1 port 54545 -XX
import socket
def find_LJ():
"""Listen for broadcast packets."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 54545))
while True:
data, addr = s.recvfrom(1024)
print(" %s " % (data, ))
print("Packet from %s: " % (addr, ))
find_LJ()

View File

@ -70,12 +70,9 @@ def OSCframe():
oscserver.handle_request()
# OSC server Thread : handler, dacs reports and simulator points sender to UI.
def osc_thread():
#print("osc Thread launched")
try:
while True:
@ -92,8 +89,6 @@ def osc_thread():
print("\n")
# Properly close the system. Todo
def Stop():
oscserver.close()

View File

@ -398,6 +398,16 @@ def handler(oscpath, args):
print()
DAChecks()
print("Done.")
if oscpath[2] == "reset":
import shutil
print()
shutil.copyfile(gstt.ljpath+'/templates/LJ_template.conf', gstt.ljpath+'/LJ.conf')
print("templates/LJ_template.conf copied to LJ.conf.")
print("** RESTART LJ **")
#LJautokill()
if oscpath[2] == "restart":
print()
@ -597,7 +607,7 @@ def UpdateAllwww():
Updatepage(gstt.ljpath+"/www/LJ.js")
Updatepage(gstt.ljpath+"/www/trckr/trckrcam1.html")
Updatepage(gstt.ljpath+"/www/simu.html")
Updatepage(gstt.ljpath+"/www/align.html")
Updatepage(gstt.ljpath+"/www/settings.html")
Updatepage(gstt.ljpath+"/www/auralls.html")
Updatepage(gstt.ljpath+"/www/index.html")

View File

@ -1012,8 +1012,4 @@ def TextRGB(message, zpos, c, layer, xpos, ypos, resize, rotx, roty, rotz):
Text(message, zpos, int('0x%02x%02x%02x' % (red,green,blue),0), layer, xpos, ypos, resize, rotx, roty, rotz)

93
libs3/plotOptimizer.py Normal file
View File

@ -0,0 +1,93 @@
class Node:
def __init__(self, sid, color):
self.sid = sid
self.connected = []
self.used = False
self.color = color
self.is_free = None # may be an other value to initialise
def add_nodes(self, neighbord):
not_the_same = neighbord != self.sid
not_allrady_inside = neighbord not in self.connected
if neighbord != self.sid and neighbord not in self.connected:
self.connected.append(neighbord)
# print the content of the objet to debug with print()
def __repr__(self):
is_free = " \t###" if self.is_free else " \t___"
return is_free + str(self.connected) + "\n"
#class Graph:
# nodes = {} # dict of all nodes
#
# def __init__(selt):
# pass
def list_to_nodes(pl):
all_nodes = {} # it will contain all nodes
sid_prev = None
for p in pl:
sid = str([int(p[0]), int(p[1])])
is_colored = p[2] != 0
if is_colored:
if sid not in all_nodes:
all_nodes[sid] = Node(sid, p[2])
if sid_prev != None:
all_nodes[sid].add_nodes(sid_prev)
all_nodes[sid_prev].add_nodes(sid)
sid_prev = sid if is_colored else None
return all_nodes
# recursiv function witch get all connected node for one component and tag them as used
def get_one_comp(id_elem, nodes):
comp = []
comp.append(id_elem)
nodes[id_elem].used = True
for id_near in nodes[id_elem].connected:
if nodes[id_near].used == False:
comp += get_one_comp(id_near, nodes)
return comp
def get_comps(nodes):
comps = [] #all component
iter_nodes = iter(nodes)
nb_elem = len(nodes)
for id_nodes in iter_nodes:
if nodes[id_nodes].used == False:
comps.append(get_one_comp(id_nodes, nodes))
return comps
# if ther is a class for the component it would be a good idea to set en atribute about eulerian graph or non eulerian graph
def set_free_vertices(components, nodes):
for comp in components:
all_even_neighbord = True
for id_node in comp:
if len(nodes[id_node].connected) % 2 == 0: # test if even neighbord
nodes[id_node].is_free = False
else:
nodes[id_node].is_free = True
all_even_neighbord = False
if all_even_neighbord:
for id_node in comp:
nodes[id_node].is_free = True
def optimizer(pl):
all_nodes = {} # it will contain all nodes
components = [] # list of connected node as a graph
# construct dict of connected all_nodes
all_nodes = list_to_nodes(pl)
components = get_comps(all_nodes)
set_free_vertices(components, all_nodes)
print("\n\nall_nodes:\n", all_nodes)
print("\n\nconnected_components:\n", components)
return pl
if __name__ == '__main__':
pl = [(355, 262, 0), (355, 262, 16777215), (300, 130, 16777215), (432, 75, 16777215), (487, 207, 16777215), (355, 262, 16777215), (355, 262, 0), (0, 435, 0), (0, 480, 16777215), (0, 300, 16777215), (135, 300, 16777215), (135, 570, 16777215), (135, 570, 16777215), (135, 300, 16777215), (135, 300, 0), (135, 300, 16777215), (225, 480, 16777215), (225, 480, 0), (510, 450, 0), (510, 450, 16777215), (600, 420, 16777215), (600, 300, 16777215), (720, 300, 16777215), (720, 420, 16777215), (810, 450, 16777215), (810, 450, 0)]
optimizer(pl)

View File

@ -15,7 +15,7 @@ from libs3 import gstt
import os
import subprocess
import sys
from socket import *
def Init(wserver):
global WSserver
@ -177,6 +177,14 @@ def OSCsend(name, oscaddress, oscargs =''):
#PluginStart(name)
return False
def sendbroadcast():
if gstt.debug > 0:
print("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 0.8".encode(), ("255.255.255.255", 54545))
# for each plugin will automatically add /pluginame before oscpath to send like /aurora/scim 1, if oscpath = "/scim 1"

View File

@ -23,44 +23,44 @@ This tracer include an enhanced version (support for several lasers) of the ethe
- Drawing things :
/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber
/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber
/resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules.
the first tuple (1.0,8) is for short line < 4000 in etherdream space
(0.25,3),(0.75,3),(1.0,10) for long line > 4000
i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream
/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis
/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis
/EDH/lasernumber
- Tracer control :
/order 0-8 Set redis key with new value then issue the order number
/order 0-8 Set redis key with new value then issue the order number
0 : Draw Normal point list
1 : Get the new EDH = reread redis key /EDH/lasernumber
1 : Get the new EDH = reread redis key /EDH/lasernumber
2 : Draw BLACK point list
3 : Draw GRID point list
4 : Resampler Change (longs and shorts lsteps)
5 : Client Key Change = reread redis key /clientkey
6 : Max Intensity Change = reread redis key /intensity
7 : kpps change = reread redis key /kpps
8 : color balance change = reread redis keys /red /green /blue
5 : Client Key Change = reread redis key /clientkey
6 : Max Intensity Change = reread redis key /intensity
7 : kpps change = reread redis key /kpps
8 : color balance change = reread redis keys /red /green /blue
- Managing Etherdream DACs :
Discrete drawing values
/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle
/intensity 0-255 Laser output power, then order 6 (for alignement,...)
/red 0-100 % of full red, then order 8
/green 0-100 % of full green, then order 8
/blue 0-100 % of full blue, then order 8
/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle
/intensity 0-255 Laser output power, then order 6 (for alignement,...)
/red 0-100 % of full red, then order 8
/green 0-100 % of full green, then order 8
/blue 0-100 % of full blue, then order 8
DAC status report
/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799)
/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799)
/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
Geometric corrections
@ -81,7 +81,8 @@ import pdb
import ast
import redis
from libs3 import homographyp
from libs3 import homographyp, plotOptimizer as po
import numpy as np
import binascii
@ -223,43 +224,39 @@ class DAC(object):
while True:
#pdb.set_trace()
self.pl = po.optimizer(self.pl)
for indexpoint,currentpoint in enumerate(self.pl):
#print indexpoint, currentpoint
# transformations des point au format adapter au etherdream
xyc = [currentpoint[0],currentpoint[1],currentpoint[2]]
self.xyrgb = self.EtherPoint(xyc)
#print(self.xyrgb[2:])
rgb = (round(self.xyrgb[2:][0] *self.intred/100), round(self.xyrgb[2:][1] *self.intgreen/100), round(self.xyrgb[2:][2] *self.intblue/100))
#print("rgb :", rgb)
#round(*self.intred/100)
#round(*self.intgreen/100)
#round(*self.intblue/100)
delta_x, delta_y = self.xyrgb[0] - self.xyrgb_prev[0], self.xyrgb[1] - self.xyrgb_prev[1]
#test adaptation selon longueur ligne
if math.hypot(delta_x, delta_y) < 4000:
yield self.xyrgb
##**//# ajout de point pour un tracer adapter
##**//delta_x, delta_y = self.xyrgb[0] - self.xyrgb_prev[0], self.xyrgb[1] - self.xyrgb_prev[1]
##**//#test adaptation selon longueur ligne
##**//if math.hypot(delta_x, delta_y) < 4000:
# For glitch art : decrease lsteps
#l_steps = [ (1.0, 8)]
l_steps = gstt.stepshortline
##**// # For glitch art : decrease lsteps
##**// #l_steps = [ (1.0, 8)]
##**// l_steps = gstt.stepshortline
else:
# For glitch art : decrease lsteps
#l_steps = [ (0.25, 3), (0.75, 3), (1.0, 10)]
l_steps = gstt.stepslongline
##**//else:
##**// # For glitch art : decrease lsteps
##**// #l_steps = [ (0.25, 3), (0.75, 3), (1.0, 10)]
##**// l_steps = gstt.stepslongline
for e in l_steps:
step = e[0]
##**//for e in l_steps:
##**// step = e[0]
for i in range(0,e[1]):
##**// for i in range(0,e[1]):
self.xyrgb_step = (self.xyrgb_prev[0] + step*delta_x, self.xyrgb_prev[1] + step*delta_y) + rgb # + self.xyrgb_prev[2:]# + rgb
#print(self.xyrgb_step)
yield self.xyrgb_step
##**// self.xyrgb_step = (self.xyrgb_prev[0] + step*delta_x, self.xyrgb_prev[1] + step*delta_y) + rgb # + self.xyrgb_prev[2:]# + rgb
##**// #print(self.xyrgb_step)
##**// yield self.xyrgb_step
self.xyrgb_prev = self.xyrgb
##**//self.xyrgb_prev = self.xyrgb
def GetPoints(self, n):
@ -335,7 +332,7 @@ class DAC(object):
# ipconn state is -1 at startup (see gstt) and modified here
r.set('/lack/'+str(self.mylaser), self.connstatus)
gstt.lstt_ipconn[self.mylaser] = self.connstatus
gstt.lstt_ipconn[self.mylaser] = self.connstatus
self.buf = b''
# Upper case PL is the Point List number
@ -349,7 +346,7 @@ class DAC(object):
if r.get('/EDH/'+str(self.mylaser)) == None:
#print("Laser",self.mylaser,"NO EDH !! Computing one...")
homographyp.newEDH(self.mylaser)
else:
else:
gstt.EDH[self.mylaser] = np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser)).decode('ascii')))
#print("Laser",self.mylaser,"found its EDH in redis")
@ -447,7 +444,23 @@ class DAC(object):
if order == 0:
# USER point list
self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
#self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
#print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
# si la clef est vide utiliser les points noirs ? -> syntax error -> black points.
try:
#newpl = ""
#newpl = r.get(self.clientkey+str(self.mylaser))
#self.pl = ast.literal_eval(newpl.decode('ascii'))
self.pl = ast.literal_eval(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
except SyntaxError:
print("BAD POINTLIST on Tracer : laser", self.mylaser, " order 0 : pl : ", self.pl)
self.pl = black_points
#print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
else:
@ -524,13 +537,13 @@ class DAC(object):
time.sleep(0.001)
cap += 150
# print("Writing %d points" % (cap, ))
#print("Writing %d points" % (cap, ))
#t0 = time.time()
#if self.mylaser == 2:
# print(points)
# print(points)
self.write(points)
#t1 = time.time()
# print("Took %f" % (t1 - t0, )
#print("Took %f" % (t1 - t0, )
if not started:
print("Tracer", self.mylaser, "starting with", gstt.kpps[self.mylaser],"kpps")
@ -571,7 +584,7 @@ def find_dac():
order = r.get('/order')
neworder = order & ~(1<< self.mylaser*2)
neworder = neworder & ~(1<< 1+ self.mylaser*2)
r.set('/order', str(neworder))
r.set('/order', str(neworder))
else:
# Laser bit 0 = 1

29
main.py
View File

@ -36,35 +36,32 @@ print("")
import redis
import os
ljpath = r'%s' % os.getcwd().replace('\\','/')
import sys
#sys.path.append('libs3/')
from libs3 import gstt, settings
gstt.ljpath= ljpath
gstt.ljpath = ljpath
log.info("Reading " + gstt.ConfigName + " setup file...")
settings.Read()
# Arguments may alter .conf file so import settings first then cli
from libs3 import cli
settings.Write()
from multiprocessing import Process, set_start_method
import random, ast
from libs3 import plugins
#from libs3 import lasytracer as tracer
from libs3 import tracer3 as tracer
from libs3 import homographyp, commands, font1
#import subprocess
import os
#import midi
from libs3 import OSC3
from websocket_server import WebsocketServer
#import socket
@ -144,15 +141,17 @@ oscserverIPout = oscserverIP
oscserverPORTout = 8001
'''
# Nozoid OSC Client : to send OSC message to Nozoid inport 8003
NozoscIPout = nozoscIP
NozoscPORTout = plugins.Port("nozoid")
'''
''''
# Planetarium OSC Client : to send OSC message to planetarium inport 8005
planetIPout = nozoscIP
planetPORTout = plugins.Port("planet")
'''
import socket
@ -265,13 +264,15 @@ def osc_thread():
#print("Sending simu frame from",'/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser))
#print(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)))
sendWSall("/simul" +" "+ str(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)).decode('ascii')))
if random.randint(0,100)>95:
plugins.sendbroadcast()
except Exception as e:
import sys, traceback
print('\n---------------------')
print('Exception: %s' % e)
print('- - - - - - - - - - -')
print('\n--------------------------')
print('OSC Thread Exception: %s' % e)
print('- - - - - - - - - - - - - - ')
traceback.print_tb(sys.exc_info()[2])
print("\n")

View File

@ -181,7 +181,7 @@ log.infog("Aurora v0.1b")
OSCinPort = 8090
ljscene = 0
StartFXs = ["anim.Maxwell","anim.Starfield","anim.Starfield", "anim.Word"]
StartFXs = ["anim.Starfield","anim.Starfield","anim.Starfield", "anim.Word"]
# Useful variables init.
white = lj.rgb2int(255,255,255)
@ -1125,9 +1125,9 @@ def AllFX():
dots = eval(LAY['FX']+"(LAY)")
if LAY['FX'] != "Zero" or lent(dots) != 0:
#print(dots, LAY['color'])
for cc in range(15):
ccmidi[cc] = int(lj.fromKey("/midi/cc/1/"+str(cc)))
#print(ccmidi)
#for cc in range(15):
# ccmidi[cc] = int(lj.fromKey("/midi/cc/1/"+str(cc)))
# #print(ccmidi)
lj.rPolyLineOneColor(dots, c = LAY['color'], layer = l, closed = LAY['closed'], xpos = LAY['Xcoord'] + LAY['stepvals'][LAY['step']] - (LAY['lineSize']/2), ypos = Layer[l]['Ycoord'], resize = LAY['scale'] * audioR, rotx = LAY['Xrotdirec'], roty = LAY['Yrotdirec'], rotz = LAY['Zrotdirec'])
#lj.rPolyLineOneColor(dots, c = LAY['color'], layer = l, closed = LAY['closed'], xpos = LAY['Xcoord'] - (ccmidi[0]-64)*4 - (LAY['lineSize']/2), ypos = Layer[l]['Ycoord'] + (ccmidi[1]-64)*4, resize = LAY['scale'] * audioR * (ccmidi[2])/50, rotx = LAY['Xrotdirec']+(ccmidi[3]*3), roty = LAY['Yrotdirec']+(ccmidi[3]*3), rotz = LAY['Zrotdirec']+(ccmidi[3]*3))
else:

View File

@ -1,937 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Align Rack</title>
<meta charset="utf-8" />
<meta name="apple-mobile-web-app-title" content="Align">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-startup-image" href="/launch.png">
<link rel="apple-touch-icon" href="touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="touch-icon-ipad.png">
<link rel="apple-touch-icon" sizes="180x180" href="touch-icon-iphone-retina.png">
<link rel="apple-touch-icon" sizes="167x167" href="touch-icon-ipad-retina.png">
<!-- Page specific styles -->
<style>
</style>
<!-- Web audio buttons defaults -->
<script type="application/javascript" src="webcomponents-lite.js"></script>
<script type="application/javascript" src="config.js"></script>
<script>
WebAudioControlsOptions={
useMidi:1,
knobSrc:"knobs/simplegray.png",
knobSprites:100,
switchSrc:"knobs/switch_toggle.png",
sliderSrc:"knobs/vsliderbody.png",
sliderKnobsrc:"knobs/vsliderknob.png",
}
</script>
<script src="webaudio-controls.js"></script>
<!-- link rel="stylesheet" href="LJgrid.css" / -->
<link rel="stylesheet" type="text/css" href="selector.min.css">
<script type="application/javascript" src="selector.min.js"></script>
<link rel="stylesheet" href="css/common.css" />
</head>
<body style="background-color:#222;">
<div class="Rackcontent">
<!--
Navigation Rack
-->
<div class="content">
<div class="buttons-7container">
<a href="index.html">
<div class="webaudiobut">
<div align="center" class="navled">
Index
</div>
</div>
</a>
<a href="align.html">
<div class="webaudiobut">
<div align="center" class="navled">
Align
</div>
</div>
</a>
<a href="auralls.html">
<div class="webaudiobut">
<div align="center" class="navled">
Aurora
</div>
</div>
</a>
<a href="trckr/trckrcam1.html">
<div class="webaudiobut">
<div align="center" class="navled">
Lasercam
</div>
</div>
</a>
<a href="simu.html">
<div class="webaudiobut">
<div align="center" class="navled">
Simu
</div>
</div>
</a>
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart lj" >
restrt LJ
</button>
</div>
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart aurora" >
restrt AU
</button>
</div>
</div>
</div>
<!--
Display Rack
-->
<div class="content">
<div class="TopRackGrid">
<div>
<h2>
/TL Align
<span class="shade">&nbsp;</span>
</h2>
<webaudio-switch id="on" value="1" tooltip="Switch-B" height="35" width="85" src="knobs/switch1.png">
</webaudio-switch>
</div>
<div class="webaudiobut">
<div align="center" id="line1" class="busled">
Align Rack
</div>
<div align="center" id="status" class="busled">
/team/laser
</div>
</div>
<div class="etherled" style="margin-left: 30px;margin-top: 1px;">
Stt
<webaudio-knob id="lstt/0" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lstt/1" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lstt/2" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lstt/3" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
</div>
<div class="etherled" style="margin-left: 30px;margin-top: 1px;">
Ack
<webaudio-knob id="lack/0" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lack/1" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lack/2" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
<webaudio-knob id="lack/3" src="knobs/leds.png" height="17" width="17" diameter="17" min="0" max="6" value="0" sprites="6"></webaudio-knob>
</div>
</div>
</div>
<!--
Scene choice Rack
-->
<div class="content">
<div class="Settingrid">
<div>
<h2>Scene</h2>
</div>
<div class="webaudiobut" align="center" style="width: 83px;">
<button type="button" class="navled" style="border: 1px solid #002020; text-align: middle;" onclick="onSubmit(this.id)" id="scene/0/start 1" >
0
</button>
</div>
<div>
</div>
<div class="webaudiobut" align="center" style="width: 83px;">
<button type="button" class="navled" style="border: 1px solid #002020; text-align: middle;" onclick="onSubmit(this.id)" id="scene/1/start 1" >
1
</button>
</div>
<div>
</div>
<div class="webaudiobut" align="center" style="width: 83px;">
<button type="button" class="navled" style="border: 1px solid #002020; text-align: middle;" onclick="onSubmit(this.id)" id="scene/2/start 1" >
2
</button>
</div>
<div>
</div>
<div class="webaudiobut" align="center" style="width: 83px;">
<button type="button" class="navled" style="border: 1px solid #002020; text-align: middle;" onclick="onSubmit(this.id)" id="scene/3/start 1" >
3
</button>
</div>
<div></div>
<div></div>
<div></div>
<div> </div>
<div></div>
</div>
</div>
<!--
Settings Rack
<div class="content">
<div class="Settingrid">
<div>
<h2>Settings</h2>
</div>
<div class="webaudiobut">
<div align="center" class="navled" style="border: 1px solid #222;">
Lasers
</div>
</div>
<div>
<form onsubmit="onSubmit(); return false;">
<select name="settings/lasers" class = "submitalign" onchange = "onSubmit(this.id)" type="text" id="settings/lasers">
<option value="1" selected>1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</form>
</div>
<div class="webaudiobut" align="center" style="width: 83px;">
<button type="button" class="navled" style="border: 1px solid #002020; text-align: middle;" onclick="onSubmit(this.id)" id="settings/rescan" >
Rescan
</button>
</div>
<div>
<form onsubmit="onSubmit(); return false;">
<select name="settings/debug" class = "submitalign" onchange = "onSubmit(this.id)" type="text" id="settings/debug">
<option value="0" selected>0</option>
<option value="1">1</option>
<option value="2">2</option>
</select>
</form>
</div>
<div class="webaudiobut">
<div align="center" class="navled" style="border: 1px solid #222;">
Debug
</div>
</div>
<div>
<form onsubmit="onSubmit(); return false;">
<input class = "submite" onchange = "onSubmit(this.id)" type="text" id="settings/IP">
</form>
</div>
<div class="webaudiobut" align="center" style="width: 73px;">
<button type="button" class="navled" style="border: 1px solid #002020;" onclick="onSubmit(this.id)" id="settings/regen" >
Regen
</button>
</div>
<div></div>
<div></div>
<div></div>
<div> </div>
<div></div>
</div>
</div>
-->
<!--
Align Rack
-->
<div class="content" style="height: 500px">
<div class="TextGrid">
<div class="mgalign" style="margin-left:0px;">
<!--
Laser 0
-->
<div class="laserbox" align="center">
<!-- IP laser 0 -->
<div>
<form onsubmit="onSubmit(); return false;">
<input class = "submite" onchange = "onSubmit(this.id)" type="text" id="ip/0">
</form>
</div>
<!-- Laser type
<div class="spacer" style="height: 10px;"></div>
<div >
<form onsubmit="onSubmit(); return false;">
<select name="lasertype" class = "submite" onchange = "onSubmit(this.id)" type="text" id="lasertype/0">
<option value="DS1000" selected>DS1000</option>
<option value="Luke400">Luke400</option>
<option value="Spectra">Spectra</option>
<option value="Fire500">Fire500</option>
</select>
</form>
</div>
-->
<div class="spacer" style="height: 8px;"></div>
<div>
<!-- Align Icons -->
<webaudio-switch id="grid/0" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/grid.png"></webaudio-switch>
<webaudio-switch id="mouse/0" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/mouse.png"></webaudio-switch>
<!-- Blackout icon -->
<webaudio-switch id="black/0" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/blackout.png"></webaudio-switch>
<!-- Swap Icons -->
<webaudio-switch id="swap/X/0" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapx.png"></webaudio-switch>
<webaudio-switch id="swap/Y/0" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapy.png"></webaudio-switch>
</div>
<div class="spacer" style="height: 6px;"></div>
<!-- kpps knob -->
<div><webaudio-knob id="kpps/0" diameter="60" min="100" max="40000" value="25000"></webaudio-knob></div>
<!-- Lasergrid 0 -->
<div class="lasergrid" style="background-image: url(knobs/lasergrid0.png);">
<!-- kPPS & Points-->
<div></div>
<div><webaudio-param id="kpps/0" link="kpps/0" ></webaudio-param></div>
<div><webaudio-param id="points/0" link="points/0"></webaudio-param></div>
<div></div>
<div class="lasertext">kPPS</div>
<div class="lasertext">Buffer</div>
<div></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<!-- Angle, Offset X, Offset Y -->
<div><webaudio-knob id="angle/0" diameter="60" min="0" max="360" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/X/0" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/Y/0" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div class="lasertext">Angle</div>
<div class="lasertext">Offset X</div>
<div class="lasertext">Offset Y</div>
<div><webaudio-param link="angle/0" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/X/0" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/Y/0" value="0"></webaudio-param></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<!-- Max global intensity, Scale X, Scale Y -->
<div><webaudio-knob id="intens/0" diameter="60" min="0" max="255" value="255"></webaudio-knob></div>
<div><webaudio-knob id="scale/X/0" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div><webaudio-knob id="scale/Y/0" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div class="lasertext"style="color: #ddd;">Intens</div>
<div class="lasertext">Scale X</div>
<div class="lasertext">Scale X</div>
<div><webaudio-param link="intens/0" value="255"></webaudio-param></div>
<div><webaudio-param link="scale/X/0" value="0"></webaudio-param></div>
<div><webaudio-param link="scale/Y/0" value="0"></webaudio-param></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Red green blue % Intensity for color balancing -->
<div><webaudio-knob id="red/0" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="green/0" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="blue/0" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div class="lasertext" style="color: #cbb;">Red</div>
<div class="lasertext" style="color: #bcb;">Green</div>
<div class="lasertext" style="color: #bbc;">Blue</div>
</div>
</div>
<!--
Laser 1
-->
<div class="laserbox" align="center">
<!-- IP laser 1 -->
<div>
<form onsubmit="onSubmit(); return false;">
<input class = "submite" onchange = "onSubmit(this.id)" type="text" id="ip/1">
</form>
</div>
<div class="spacer" style="height: 8px;"></div>
<div >
<!-- Align Icons -->
<webaudio-switch id="grid/1" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/grid.png"></webaudio-switch>
<webaudio-switch id="mouse/1" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/mouse.png"></webaudio-switch>
<!-- Blackout icon -->
<webaudio-switch id="black/1" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/blackout.png"></webaudio-switch>
<!-- Swap Icons -->
<webaudio-switch id="swap/X/1" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapx.png"></webaudio-switch>
<webaudio-switch id="swap/Y/1" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapy.png"></webaudio-switch>
</div>
<div class="spacer" style="height: 6px;"></div>
<!-- kpps knob -->
<div><webaudio-knob id="kpps/1" diameter="60" min="100" max="40000" value="25000"></webaudio-knob></div>
<!-- Lasergrid 1 -->
<div class="lasergrid" style="background-image: url(knobs/lasergrid1.png);">
<!-- kPPS & Points-->
<div></div>
<div><webaudio-param id="kpps/1" link="kpps/1"></webaudio-param></div>
<div><webaudio-param id="points/1" link="points/1"></webaudio-param></div>
<div></div>
<div class="lasertext">kPPS</div>
<div class="lasertext">Buffer</div>
<div></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Angle, Offset X, Offset Y -->
<div><webaudio-knob id="angle/1" diameter="60" min="0" max="360"value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/X/1" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/Y/1" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div class="lasertext">Angle</div>
<div class="lasertext">Offset X</div>
<div class="lasertext">Offset Y</div>
<div><webaudio-param link="angle/1" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/X/1" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/Y/1" value="0"></webaudio-param></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<!-- Max global intensity, Scale X, Scale Y -->
<div><webaudio-knob id="intens/1" diameter="60" min="0" max="255" value="255"></webaudio-knob></div>
<div><webaudio-knob id="scale/X/1" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div><webaudio-knob id="scale/Y/1" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div class="lasertext"style="color: #ddd;">Intens</div>
<div class="lasertext">Scale X</div>
<div class="lasertext">Scale X</div>
<div><webaudio-param link="intens/1" value="255"></webaudio-param></div>
<div><webaudio-param link="scale/X/1" value="0"></webaudio-param></div>
<div><webaudio-param link="scale/Y/1" value="0"></webaudio-param></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Red green blue % Intensity for color balancing -->
<div><webaudio-knob id="red/1" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="green/1" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="blue/1" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div class="lasertext" style="color: #cbb;">Red</div>
<div class="lasertext" style="color: #bcb;">Green</div>
<div class="lasertext" style="color: #bbc;">Blue</div>
</div>
</div>
<!--
Laser 2
-->
<div class="laserbox" align="center">
<!-- IP laser 2 -->
<div>
<form onsubmit="onSubmit(); return false;">
<input class = "submite" onchange = "onSubmit(this.id)" type="text" id="ip/2">
</form>
</div>
<div class="spacer" style="height: 8px;"></div>
<div>
<!-- Align Icons -->
<webaudio-switch id="grid/2" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/grid.png"></webaudio-switch>
<webaudio-switch id="mouse/2" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/mouse.png"></webaudio-switch>
<!-- Blackout icon -->
<webaudio-switch id="black/2" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/blackout.png"></webaudio-switch>
<!-- Swap Icons -->
<webaudio-switch id="swap/X/2" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapx.png"></webaudio-switch>
<webaudio-switch id="swap/Y/2" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapy.png"></webaudio-switch>
</div>
<div class="spacer" style="height: 6px;"></div>
<!-- kpps knob -->
<div><webaudio-knob id="kpps/2" diameter="60" min="100" max="40000" value="25000"></webaudio-knob></div>
<!-- Laser 2 grid -->
<div class="lasergrid" style="background-image: url(knobs/lasergrid2.png)">
<!-- kPPS & Points-->
<div></div>
<div><webaudio-param id="kpps/2" link="kpps/2"></webaudio-param></div>
<div><webaudio-param id="points/2" link="points/2"></webaudio-param></div>
<div></div>
<div class="lasertext">kPPS</div>
<div class="lasertext">Buffer</div>
<div></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Angle, Offset X, Offset Y -->
<div><webaudio-knob id="angle/2" diameter="60" min="0" max="360" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/X/2" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/Y/2" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div class="lasertext">Angle</div>
<div class="lasertext">Offset X</div>
<div class="lasertext">Offset Y</div>
<div><webaudio-param link="angle/2" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/X/2" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/Y/2" value="0"></webaudio-param></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<!-- Max global intensity, Scale X, Scale Y -->
<div><webaudio-knob id="intens/2" diameter="60" min="0" max="255" value="255"></webaudio-knob></div>
<div><webaudio-knob id="scale/X/2" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div><webaudio-knob id="scale/Y/2" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div class="lasertext"style="color: #ddd;">Intens</div>
<div class="lasertext">Scale X</div>
<div class="lasertext">Scale X</div>
<div><webaudio-param link="intens/2" value="255"></webaudio-param></div>
<div><webaudio-param link="scale/X/2" value="0"></webaudio-param></div>
<div><webaudio-param link="scale/Y/2" value="0"></webaudio-param></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Red green blue % Intensity for color balancing -->
<div><webaudio-knob id="red/2" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="green/2" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="blue/2" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div class="lasertext" style="color: #cbb;">Red</div>
<div class="lasertext" style="color: #bcb;">Green</div>
<div class="lasertext" style="color: #bbc;">Blue</div>
</div>
</div>
<!--
Laser 3
-->
<div class="laserbox" align="center">
<!-- IP laser 3 -->
<div>
<form onsubmit="onSubmit(); return false;">
<input class = "submite" onchange = "onSubmit(this.id)" type="text" id="ip/3">
</form>
</div>
<div class="spacer" style="height: 8px;"></div>
<div>
<!-- Align Icons -->
<webaudio-switch id="grid/3" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/grid.png"></webaudio-switch>
<webaudio-switch id="mouse/3" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/mouse.png"></webaudio-switch>
<!-- Blackout icon -->
<webaudio-switch id="black/3" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/blackout.png"></webaudio-switch>
<!-- Swap Icons -->
<webaudio-switch id="swap/X/3" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapx.png"></webaudio-switch>
<webaudio-switch id="swap/Y/3" value="0" height="25" width="25" tooltip="Switch-B" src="knobs/swapy.png"></webaudio-switch>
</div>
<div class="spacer" style="height: 6px;"></div>
<!-- kpps knob -->
<div><webaudio-knob id="kpps/3" diameter="60" min="100" max="40000" value="25000"></webaudio-knob></div>
<!-- Laser 3 grid -->
<div class="lasergrid" style="background-image: url(knobs/lasergrid3.png)">
<!-- kPPS & Points-->
<div></div>
<div><webaudio-param id="kpps/3" link="kpps/3" ></webaudio-param></div>
<div><webaudio-param id="points/3" link="points/3"></webaudio-param></div>
<div></div>
<div class="lasertext">kPPS</div>
<div class="lasertext">Buffer</div>
<div></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Angle, Offset X, Offset Y -->
<div><webaudio-knob id="angle/3" diameter="60" min="0" max="360" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/X/3" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div><webaudio-knob id="loffset/Y/3" diameter="60" min="-27000" max="27000" value="0"></webaudio-knob></div>
<div class="lasertext">Angle</div>
<div class="lasertext">Offset X</div>
<div class="lasertext">Offset Y</div>
<div><webaudio-param link="angle/3" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/X/3" value="0"></webaudio-param></div>
<div><webaudio-param link="loffset/Y/3" value="0"></webaudio-param></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<div class="spacer" style="height: 5px;"></div>
<!-- Max global intensity, Scale X, Scale Y -->
<div><webaudio-knob id="intens/3" diameter="60" min="0" max="255" value="255"></webaudio-knob></div>
<div><webaudio-knob id="scale/X/3" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div><webaudio-knob id="scale/Y/3" diameter="60" min="0" max="200" value="50"></webaudio-knob></div>
<div class="lasertext"style="color: #ddd;">Intens</div>
<div class="lasertext">Scale X</div>
<div class="lasertext">Scale X</div>
<div><webaudio-param link="intens/3" value="255"></webaudio-param></div>
<div><webaudio-param link="scale/X/3" value="0"></webaudio-param></div>
<div><webaudio-param link="scale/Y/3" value="0"></webaudio-param></div>
<div class="spacer"></div>
<div class="spacer"></div>
<div class="spacer"></div>
<!-- Red green blue % Intensity for color balancing -->
<div><webaudio-knob id="red/3" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="green/3" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div><webaudio-knob id="blue/3" diameter="60" min="0" max="100" value="100"></webaudio-knob></div>
<div class="lasertext" style="color: #cbb;">Red</div>
<div class="lasertext" style="color: #bcb;">Green</div>
<div class="lasertext" style="color: #bbc;">Blue</div>
</div>
</div>
</div>
</div>
</div>
<!--
Encoders Line
# /aurora/radius layernumber radius [0-1]
# /aurora/rotdirec layer axe direc
# /aurora/linesize layer value
# /aurora/rotdirec layer axe direc
# /aurora/rotspeed layernumber axe speed
# /aurora/transpeed layernumber axe transpeed
# /aurora/transamt layernumber axe maxposition
-->
<!--
JS
-->
<!-- LJ style WS : A nettoyer ! -->
<script type="text/javascript">
var LJ = websocket_uri
var _WS = {
uri: LJ,
ws: null,
init : function (e) {
_WS.s = new WebSocket(_WS.uri);
_WS.s.onopen = function (e) { _WS.onOpen(e); };
_WS.s.onclose = function (e) { _WS.onClose(e); };
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
_WS.s.onerror = function (e) { _WS.onError(e); };
},
onOpen: function () {
_WS.showstatus("Connected to "+LJ);
document.getElementById("on").value = 1;
},
onClose: function () {
_WS.showline1('<span style="color: red;">LJ DISCONNECTED</span> ');
document.getElementById("on").value = 0;
document.getElementById("lstt/0").value = 0;
document.getElementById("lstt/1").value = 0;
document.getElementById("lstt/2").value = 0;
document.getElementById("lstt/3").value = 0;
document.getElementById("lack/0").value = 0;
document.getElementById("lack/1").value = 0;
document.getElementById("lack/2").value = 0;
document.getElementById("lack/3").value = 0;
},
onMessage: function (e) {
var res = e.data.split(" ");
//console.log(e.data)
//console.log(res[0].substring(0,6))
//console.log(res)
//console.log(res[0].slice(1))
var divtext = document.getElementById('status');
var divtextp = document.getElementById('players');
switch (res[0].substring(0,6)) {
case "/statu":
_WS.showline1("connected to "+LJ);
if (res[2]==="Disconnected"){
_WS.showstatus(res[1]+" "+'<span style="color: red;">'+ res[2]+'</span> ');
}
else{
_WS.showstatus(res[1]+" "+res[2]);
}
break;
case "/redst":
//console.log("red"+res[1]+" "+res[2]);
_WS.showstatus('<span style="color: red;">'+res[1]+" "+ res[2]+'</span> ');
break;
case "/line1":
//divtext.innerHTML="connected to "+LJ;
divtext1.innerHTML=res[1]+" "+res[2];
break;
case "/redline1":
//divtext.innerHTML="connected to "+LJ;
divtext1.innerHTML='<span style="color: red;">'+ res[1]+" "+res[2]+'</span>';
break;
case "/lack/":
console.log("/lack "+res[1])
document.getElementById(res[0].slice(1)).value = res[1];
break;
case "/lstt/":
console.log("/lstt "+res[1])
document.getElementById(res[0].slice(1)).value = res[1];
break;
default:
//console.log("test "+res[0].slice(1)+" "+res[1]);
document.getElementById(res[0].slice(1)).value = res[1];
//_WS.showstatus(e.data);
break
}
},
onError: function (e) {
_WS.showstatus('<span style="color: red;">ERROR:</span> ' + e.data);
},
showin: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showout: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showstatus: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showline1: function (message) {
var divtext = document.getElementById('line1');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
send: function (message) {
if (!message.length) {
alert('Empty message not allowed !');
} else {
_WS.showstatus(message);
_WS.s.send(message);
}
},
close: function () {
_WS.showstatus('GOODBYE !');
_WS.s.close();
}
};
window.addEventListener('load', _WS.init, false);
//
// Forms submits
//
function onSubmit(clicked_id) {
var input = document.getElementById(clicked_id);
console.log("/" + clicked_id + " " + input.value);
_WS.send("/" + clicked_id + " " + input.value);
}
</script>
<!--
web audio encoders scripts
-->
<script type="text/javascript">
var message="";
var log=[];
var knobs = document.getElementsByTagName('webaudio-knob');
for(var i = 0; i < knobs.length; i++){
knobs[i].addEventListener("input",Dump,false);
knobs[i].addEventListener("change",Dump,false);
}
var sliders = document.getElementsByTagName('webaudio-slider');
for(var i = 0; i < sliders.length; i++){
sliders[i].addEventListener("input",Dump,false);
sliders[i].addEventListener("change",Dump,false);
}
var switches = document.getElementsByTagName('webaudio-switch');
for(var i = 0; i < switches.length; i++) {
switches[i].addEventListener("change",Dump,false);
}
function newlaser(id) {
console.log("newlaser")
nolaser();
nofx();
laserid = "noteon "+(24-id);
console.log("laserid "+laserid);
var x = document.getElementById(laserid);
x.value = 1 ;
console.log("laser "+id);
}
function nofx0() {
console.log("nofx0")
var x = document.getElementById("aurora/fx/0 ScanH");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 ScanV");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 Wave");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 Circle");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 Starfield");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 Word");
x.value = 0 ;
var x = document.getElementById("aurora/fx/0 Trckr");
x.value = 0 ;
}
function nocolor0() {
console.log("nocolor0")
var x = document.getElementById("aurora/color/0 red");
x.value = 0 ;
var x = document.getElementById("aurora/color/0 yellow");
x.value = 0 ;
var x = document.getElementById("aurora/color/0 green");
x.value = 0 ;
var x = document.getElementById("aurora/color/0 blue");
x.value = 0 ;
var x = document.getElementById("aurora/color/0 cyan");
x.value = 0 ;
var x = document.getElementById("aurora/color/0 white");
x.value = 0 ;
}
function Dump(e) {
var str="";
str=e.type + " : " + e.target.id + " : " + e.target.value + " ";
console.log(str);
log.unshift(str);
log.length=1;
str="";
for(var i=19;i>=0;--i) {
if(log[i])
str+=log[i]+"<br/>";
}
_WS.send("/" + e.target.id + " " + e.target.value);
var res = e.target.id.split(" ");
// on off
if (e.target.id === "on" && e.type === "change") {
window.location.reload();
}
// Go to index
if (e.target.id === "index" && e.type === "change") {
window.location.assign("index.html");
}
// Fx
if (res[0].substring(7,9) === "fx" && e.type === "change") {
var layer = res[0].substring(10,12);
nofx0();
var x = document.getElementById(e.target.id);
x.value = 1 ;
_WS.showstatus(e.target.id);
}
// Colors
if (res[0].substring(7,9) === "co" && e.type === "change") {
var layer = res[0].substring(13,14);
console.log(layer)
nocolor0();
var x = document.getElementById(e.target.id);
x.value = 1 ;
_WS.showstatus(e.target.id);
}
}
</script>
</body>
</html>

View File

@ -59,10 +59,10 @@
</div>
</a>
<a href="align.html">
<a href="settings.html">
<div class="webaudiobut">
<div align="center" class="navled">
Align
Settings
</div>
</div>
</a>
@ -93,8 +93,8 @@
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart lj" >
rstrt LJ
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/reset" >
Reset
</button>
</div>

View File

@ -101,15 +101,15 @@
<!--
Align Rack
Settings Rack
-->
<a href="align.html">
<a href="settings.html">
<div class="content">
<div class="TopRackGrid">
<div>
<h2>
Align
Settings
<span class="shade">&nbsp;</span>
</h2>
<webaudio-switch id="on" value="1" tooltip="Switch-B" height="35" width="85" src="knobs/switch1.png">
@ -117,7 +117,7 @@
</div>
<div class="webaudiobut">
<div align="center" class="busled">
Align
Settings
</div>
<div align="center" class="busled">
&nbsp;

View File

@ -56,10 +56,10 @@
</div>
</a>
<a href="align.html">
<a href="settings.html">
<div class="webaudiobut">
<div align="center" class="navled">
Align
Settings
</div>
</div>
</a>
@ -90,8 +90,8 @@
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart lj" >
rstrt LJ
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/reset" >
reset
</button>
</div>

View File

@ -1,680 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<title>LASERCam 1</title>
<meta charset="utf-8">
<link href="./styles/bootstrap.min.css" rel="stylesheet" type="text/css">
<meta name="apple-mobile-web-app-title" content="Tracker">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="apple-touch-startup-image" href="../launch.png">
<link rel="apple-touch-icon" href="../touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="../touch-icon-ipad.png">
<link rel="apple-touch-icon" sizes="180x180" href="../touch-icon-iphone-retina.png">
<link rel="apple-touch-icon" sizes="167x167" href="../touch-icon-ipad-retina.png">
<script type="application/javascript" src="../config.js"></script>
<style>
</style>
<script>
// getUserMedia only works over https in Chrome 47+, so we redirect to https. Also notify user if running from file.
if (window.location.protocol == "file:") {
alert("You seem to be running this example directly from a file. Note that these examples only work when served from a server or localhost due to canvas cross-domain restrictions.");
} else if (window.location.hostname !== "localhost" && window.location.protocol !== "https:"){
window.location.protocol = "http";
//window.location.protocol = "https";
}
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-32642923-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
-->
<!-- Web audio buttons defaults -->
<script type="application/javascript" src="../webcomponents-lite.js"></script>
<script>
WebAudioControlsOptions={
useMidi:1,
knobSrc:"knobs/simplegray.png",
knobSprites:100,
switchSrc:"knobs/switch_toggle.png",
sliderSrc:"knobs/vsliderbody.png",
sliderKnobsrc:"knobs/vsliderknob.png",
}
</script>
<script src="../webaudio-controls.js"></script>
<!-- link rel="stylesheet" href="LJgrid.css" / -->
<link rel="stylesheet" type="text/css" href="../selector.min.css">
<script type="application/javascript" src="../selector.min.js"></script>
<link rel="stylesheet" href="../css/common.css" />
</head>
<body style="background-color:#222;">
<script src="./js/libs/utils.js"></script>
<script src="../build/clmtrackr.js"></script>
<script src="./js/libs/Stats.js"></script>
<!--
Top Rack
<div align="center">
<a href="index.html"><img height="25" width="21" src="../knobs/indexs.png"></a>
</div>
-->
<div class="Rackcontent">
<!--
Navigation Rack
-->
<div class="content">
<div class="buttons-7container">
<a href="../index.html">
<div class="webaudiobut">
<div align="center" class="navled">
Index
</div>
</div>
</a>
<a href="../align.html">
<div class="webaudiobut">
<div align="center" class="navled">
Align
</div>
</div>
</a>
<a href="../auralls.html">
<div class="webaudiobut">
<div align="center" class="navled">
Aurora
</div>
</div>
</a>
<a href="trckrcam1.html">
<div class="webaudiobut">
<div align="center" class="navled">
Lasercam
</div>
</div>
</a>
<a href="../simu.html">
<div class="webaudiobut">
<div align="center" class="navled">
Simu
</div>
</div>
</a>
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart lj" >
rstrt LJ
</button>
</div>
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart aurora" >
rstrt AU
</button>
</div>
</div>
</div>
<!--
Lasers & colors Rack
-->
<div class="content">
<div class="TopRackGrid">
<div>
<h2>
/TL RGY 1
<span class="shade">&nbsp;</span>
</h2>
<webaudio-switch id="on" value="1" tooltip="Switch-B" height="35" width="85" src="../knobs/switch1.png">
</webaudio-switch>
</div>
<div style="border : #242424 1px solid;background: #000;">
<div align="center" id="line1" class="busled">
LASERcam 1 : Allow to use your webcam + start
</div>
<div align="center" id="status" class="busled">
/team/laser
</div>
</div>
<div></div>
<div>
<div style="margin-top : 30px;">
</div>
<div>
</div>
</div>
</div>
</div>
<!--
Colors Rack
-->
<div class="content">
<div class="Rackgrid">
<div>
<h2>
Colors
</h2>
</div>
<div class="webaudiobut"><webaudio-switch id="noteon 24" value="1" tooltip="Switch-B" height="64" width="64" src="../knobs/big0.png"></webaudio-switch></div>
<div class="webaudiobut"><webaudio-switch id="noteon 25" value="0" tooltip="Switch-B" height="64" width="64" src=" ../knobs/big1.png"></webaudio-switch></div>
<div class="webaudiobut"><webaudio-switch id="noteon 26" value="0" tooltip="Switch-B" height="64" width="64" src="../knobs/big2.png"></webaudio-switch></div>
<div class="webaudiobut"><webaudio-switch id="noteon 27" value="0" tooltip="Switch-B" height="64" width="64" src="../knobs/big3.png"></webaudio-switch></div>
<div></div>
<div class="webaudiobut"><webaudio-switch id="trckr/color/0 red" value="1" tooltip="Switch-B" height="64" width="64" src="knobs/red.png"></webaudio-switch></div>
<div class="webaudiobut"><webaudio-switch id="trckr/color/0 yellow" value="0" tooltip="Switch-B" height="64" width="64" src="knobs/yellow.png"></webaudio-switch></div>
<div class="webaudiobut"><webaudio-switch id="trckr/color/0 green" value="0" tooltip="Switch-B" height="64" width="64" src="knobs/green.png"></webaudio-switch></div>
<div></div>
</div>
</div>
<!--
Webcam Rack
-->
<div class="content" style="background-image: linear-gradient(174deg, #111,#030303);">
<video id="videoel" class="webaudiobut" width="400" height="300" preload="auto" loop playsinline autoplay>
</video>
<canvas id="overlay" width="400" height="300"></canvas>
<input class="btn" type="button" value="wait" style="margin-left: 150px;" disabled="disabled" onclick="startVideo()" id="startbutton"></input>
</div>
<!--
Big Display Rack
-->
<div class="content">
<div id="text" class="busled" style="font-size:1.5em;border : #242424 1px solid;background: #000;-webkit-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);-moz-box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);box-shadow: 4px 6px 10px -1px rgba(0,0,0,0.72);">
<div id="gum" class="gum">
<p>To try it out:
<ol>
<li>Allow the page to use your webcamera</li>
<li>Make sure that your face is clearly visible in the video, and click start</li>
<li>See the model fitted to your face</li>
<ol>
</p>
</div>
<div id="nogum" class="nogum">
</div>
</div>
</div>
</div>
<!--
JS
-->
<script>
var vid = document.getElementById('videoel');
var vid_width = vid.width;
var vid_height = vid.height;
var overlay = document.getElementById('overlay');
var overlayCC = overlay.getContext('2d');
/*********** Setup of video/webcam and checking for webGL support *********/
function enablestart() {
var startbutton = document.getElementById('startbutton');
startbutton.value = "start";
startbutton.disabled = null;
}
var insertAltVideo = function(video) {
// insert alternate video if getUserMedia not available
if (supports_video()) {
if (supports_webm_video()) {
video.src = "./media/cap12_edit.webm";
} else if (supports_h264_baseline_video()) {
video.src = "./media/cap12_edit.mp4";
} else {
return false;
}
return true;
} else return false;
}
function adjustVideoProportions() {
// resize overlay and video if proportions of video are not 4:3
// keep same height, just change width
var proportion = vid.videoWidth/vid.videoHeight;
vid_width = Math.round(vid_height * proportion);
vid.width = vid_width;
overlay.width = vid_width;
}
function gumSuccess( stream ) {
// add camera stream if getUserMedia succeeded
if ("srcObject" in vid) {
vid.srcObject = stream;
} else {
vid.src = (window.URL && window.URL.createObjectURL(stream));
}
vid.onloadedmetadata = function() {
adjustVideoProportions();
vid.play();
}
vid.onresize = function() {
adjustVideoProportions();
if (trackingStarted) {
ctrack.stop();
ctrack.reset();
ctrack.start(vid);
}
}
}
function gumFail() {
// fall back to video if getUserMedia failed
insertAltVideo(vid);
document.getElementById('gum').className = "hide";
document.getElementById('nogum').className = "nohide";
}
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
window.URL = window.URL || window.webkitURL || window.msURL || window.mozURL;
// set up video
if (navigator.mediaDevices) {
navigator.mediaDevices.getUserMedia({video : true}).then(gumSuccess).catch(gumFail);
} else if (navigator.getUserMedia) {
navigator.getUserMedia({video : true}, gumSuccess, gumFail);
} else {
insertAltVideo(vid);
document.getElementById('gum').className = "hide";
document.getElementById('nogum').className = "nohide";
alert("Your browser does not seem to support getUserMedia, using a fallback video instead.");
}
vid.addEventListener('canplay', enablestart, false);
/*********** Code for face tracking *********/
var ctrack = new clm.tracker();
ctrack.init();
var trackingStarted = false;
var counter = 0;
var layer = 0;
function startVideo() {
// start video
vid.play();
// start tracking
ctrack.start(vid);
trackingStarted = true;
// start loop to draw face
drawLoop();
}
function drawLoop() {
requestAnimFrame(drawLoop);
overlayCC.clearRect(0, 0, vid_width, vid_height);
//psrElement.innerHTML = "score :" + ctrack.getScore().toFixed(4);
var positions = ctrack.getCurrentPosition();
// do something with the positions ...
// print the positions
var positionString = "";
var positionFace = 'trckr/frame '+layer+" "+counter+" ";
if (positions) {
ctrack.draw(overlay);
for (var p = 0;p < 71;p++) {
positionString += "featurepoint "+p+" : ["+positions[p][0].toFixed(2)+","+positions[p][1].toFixed(2)+"]<br/>";
positionFace += positions[p][0].toFixed(2)+" "+positions[p][1].toFixed(2)+" ";
}
//document.getElementById('positions').innerHTML = positionString;
_WS.s.send(positionFace);
counter +=1;
}
}
/*********** Code for stats **********/
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
document.getElementById('container').appendChild( stats.domElement );
// update stats on every iteration
document.addEventListener('clmtrackrIteration', function(event) {
stats.update();
}, false);
</script>
<!-- LJ style WS : A nettoyer ! -->
<script type="text/javascript">
var LJ = 'ws://192.168.2.43:9001/'
var _WS = {
uri: LJ,
ws: null,
init : function (e) {
_WS.s = new WebSocket(_WS.uri);
_WS.s.onopen = function (e) { _WS.onOpen(e); };
_WS.s.onclose = function (e) { _WS.onClose(e); };
_WS.s.onmessage = function (e) { _WS.onMessage(e); };
_WS.s.onerror = function (e) { _WS.onError(e); };
},
onOpen: function () {
_WS.showstatus("Connected to "+LJ);
document.getElementById("on").value = 1;
},
onClose: function () {
_WS.showline1('<span style="color: red;">LJ DISCONNECTED</span> ');
},
onMessage: function (e) {
var res = e.data.split(" ");
//console.log(e.data)
//console.log(res[0].substring(0,6))
//console.log(res)
//console.log(res[0].slice(1))
var divtext = document.getElementById('status');
var divtextp = document.getElementById('players');
switch (res[0].substring(0,6)) {
case "/statu":
if (res[2]==="Disconnected"){
_WS.showstatus(res[1]+" "+'<span style="color: red;">'+ res[2]+'</span> ');
}
else{
_WS.showstatus(res[1]+" "+res[2]);
}
break;
case "/redst":
//console.log("red"+res[1]+" "+res[2]);
_WS.showstatus('<span style="color: red;">'+res[1]+" "+ res[2]+'</span> ');
break;
case "/line1":
//divtext.innerHTML="connected to "+LJ;
divtext1.innerHTML=res[1]+" "+res[2];
break;
case "/redline1":
//divtext.innerHTML="connected to "+LJ;
divtext1.innerHTML='<span style="color: red;">'+ res[1]+" "+res[2]+'</span>';
break;
case "/laser":
console.log("/laser "+res[1])
newlaser(res[1])
break;
case "/lack/":
console.log("/lack "+res[1])
document.getElementById(res[0].slice(1)).value = res[1];
break;
case "/lstt/":
console.log("/lstt "+res[1])
document.getElementById(res[0].slice(1)).value = res[1];
break;
default:
//console.log("test "+res[0].slice(1)+" "+res[1]);
document.getElementById(res[0].slice(1)).value = res[1];
//_WS.showstatus(e.data);
}
},
showline1: function (message) {
var divtext = document.getElementById('line1');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
onError: function (e) {
_WS.showstatus('<span style="color: red;">ERROR:</span> ' + e.data);
},
showin: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showout: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
showstatus: function (message) {
var divtext = document.getElementById('status');
divtext.innerHTML="";
divtext.innerHTML= message.toString();
},
send: function (message) {
if (!message.length) {
alert('Empty message not allowed !');
} else {
_WS.showstatus(message);
_WS.s.send(message);
}
},
close: function () {
_WS.showstatus('GOODBYE !');
_WS.s.close();
}
};
window.addEventListener('load', _WS.init, false);
//
// Forms submits
//
function onSubmit(clicked_id) {
var input = document.getElementById(clicked_id);
console.log("/" + clicked_id + " " + input.value);
_WS.send("/" + clicked_id + " " + input.value);
}
</script>
<!--
web audio encoders scripts
-->
<script type="text/javascript">
var message="";
var log=[];
var knobs = document.getElementsByTagName('webaudio-knob');
for(var i = 0; i < knobs.length; i++){
knobs[i].addEventListener("input",Dump,false);
knobs[i].addEventListener("change",Dump,false);
}
var sliders = document.getElementsByTagName('webaudio-slider');
for(var i = 0; i < sliders.length; i++){
sliders[i].addEventListener("input",Dump,false);
sliders[i].addEventListener("change",Dump,false);
}
var switches = document.getElementsByTagName('webaudio-switch');
for(var i = 0; i < switches.length; i++) {
switches[i].addEventListener("change",Dump,false);
}
function newlaser(id) {
console.log("newlaser " +id)
var laserid = 24+ parseInt(id,10);
laser = "noteon "+laserid;
console.log("laser "+laser);
nolaser();
nofx();
var x = document.getElementById(laser);
x.value = 1 ;
}
function nolaser() {
//console.log("nolaser")
var x = document.getElementById("noteon 24");
x.value = 0 ;
var x = document.getElementById("noteon 25");
x.value = 0 ;
var x = document.getElementById("noteon 26");
x.value = 0 ;
var x = document.getElementById("noteon 27");
x.value = 0 ;
}
// RGB Colors
function nocolor() {
//console.log("nocolor0")
var x = document.getElementById("trckr/color/ red");
x.value = 0 ;
var x = document.getElementById("trckr/color/ yellow");
x.value = 0 ;
var x = document.getElementById("trckr/color/ green");
x.value = 0 ;
var x = document.getElementById("trckr/color/ blue");
x.value = 0 ;
var x = document.getElementById("trckr/color/ cyan");
x.value = 0 ;
var x = document.getElementById("trckr/color/ white");
x.value = 0 ;
}
// RGY Colors
//function nocolor() {
//var x = document.getElementById("trckr/color/ red");
// x.value = 0 ;
//var x = document.getElementById("trckr/color/ yellow");
// x.value = 0 ;
//var x = document.getElementById("trckr/color/ green");
// x.value = 0 ;
//}
function Dump(e) {
var str="";
str=e.type + " : " + e.target.id + " : " + e.target.value + " ";
console.log(str);
log.unshift(str);
log.length=1;
str="";
for(var i=19;i>=0;--i) {
if(log[i])
str+=log[i]+"<br/>";
}
_WS.send("/" + e.target.id + " " + e.target.value);
var res = e.target.id.split(" ");
// on off
if (e.target.id === "on" && e.type === "change") {
window.location.reload();
}
// Go to index
if (e.target.id === "index" && e.type === "change") {
window.location.assign("../index.html");
}
// Fx
if (res[0].substring(7,9) === "fx" && e.type === "change") {
var layer = res[0].substring(10,12);
nofx0();
var x = document.getElementById(e.target.id);
x.value = 1 ;
_WS.showstatus(e.target.id);
}
// Colors
if (res[0].substring(7,9) === "co" && e.type === "change") {
nocolor();
_WS.send("/"+res[0]+laser+ " "+ res[1]+ " " + e.target.value);
var x = document.getElementById(e.target.id);
x.value = 1 ;
//_WS.showstatus("Laser "+laser+ " "+ res[1]);
console.log("colors sending "+res[0]+laser+ " "+ res[1]+ " " + e.target.value);
}
// Lasers
if (res[0] === "noteon" && e.type === "change") {
//console.log(e.target.id);
//console.log(res);
//console.log(res[1] - 24)
newlaser(res[1] - 24);
_WS.send("/" + e.target.id + " " + e.target.value);
//nolaser();
//nofx();
//_WS.send("/" + e.target.id + " " + e.target.value);
//var x = document.getElementById(e.target.id);
//x.value = 1 ;
//laser = res[1] - 24;
//console.log("laser "+laser);
}
</script>
</body>
</html>

View File

@ -93,10 +93,10 @@
</div>
</a>
<a href="../align.html">
<a href="../settings.html">
<div class="webaudiobut">
<div align="center" class="navled">
Align
Settings
</div>
</div>
</a>
@ -127,8 +127,8 @@
<div class="webaudiobut">
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/restart lj" >
rstrt LJ
<button type="button" class="navled" style="border : #222222 1px;" onclick="onSubmit(this.id)" id="settings/reset" >
Reset
</button>
</div>