diff --git a/LJ.conf b/LJ.conf index 2e30401..c87de98 100644 --- a/LJ.conf +++ b/LJ.conf @@ -1,5 +1,5 @@ [General] -lasernumber = 2 +lasernumber = 4 debug = 0 ljayserverip = 127.0.0.1 nozoscip = 127.0.0.1 @@ -58,9 +58,9 @@ 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 @@ -77,9 +77,9 @@ 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 = { @@ -88,8 +88,9 @@ plugins = { "planet": {"OSC": 8005, "command": "python3 plugins/planetarium/main.py"}, "words": {"OSC": 8006, "command": "python3 plugins/livewords.py"}, "cycl": {"OSC": 8007, "command": "python3 plugins/textcycl.py"}, - "simu": {"OSC": 8008, "command": "python plugins/simu.py"}, + "simu": {"OSC": 8008, "command": "python plugins/pysimu.py"}, "bank0": {"OSC": 8010, "command": "python3 plugins/VJing/bank0.py"}, + "pose": {"OSC": 8011, "command": "python3 plugins/VJing/idiotia.py"}, "ljpong": {"OSC": 8020, "command": "python plugins/games/ljpong/main.py"}, "ljwars": {"OSC": 8021, "command": "python plugins/games/ljsw/main.py"} } diff --git a/LJ.py b/LJ.py new file mode 120000 index 0000000..11a5d8e --- /dev/null +++ b/LJ.py @@ -0,0 +1 @@ +main.py \ No newline at end of file diff --git a/README.md b/README.md index ecaf613..1a23d4d 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,28 @@ LICENCE : CC BY ![LJ](http://www.teamlaser.fr/thsf/images/fulls/THSF9-33.jpg) -A software laser server with GUI for up to 4 lasers live actions. Think creative like Laser "battles", planetarium,... +A software laser server with GUI for up to 4 lasers live actions. Think creative like Laser "battles", planetarium, sharing available lasers in demoparties for competition, ... -LJ has 3 main components : +LJ has 5 main components : -- A "tracer" per etherdream/laser that take its point list, correct geometry, recompute in etherdreams coordinates, send it to its controller,... and report etherdream status to the manager. +- 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 the webui functions, OSC commands,... -- To share the lasers between people, LJ accept up to 4 virtual "clients" that can simultaneously send one point list per laser. You select in WebUI wich client is actually sent to lasers. +- To share the lasers between people/computers, LJ accept up to 4 virtual "clients" that can simultaneously send one point list per laser. You select in WebUI wich "client" should be used by tracers. +- A web GUI in html, css, and vanilla js. No html server or js framework here : it's complex enough. This GUI has a (currently slow) simulator, but one can used a builtin python simulator (pysimu) or an etherdream/laser emulator (from nannou) to work without physical lasers !! +- A network available database (redis). "Clients" send directly their pointlists to redis and each "tracer" is instructed to get a given pointlist in redis. + + +4 clients can send 4 pointlists so up to 16 pointlists can be accessed at anytime from anywhere in the network. The server/network/webUI idea allows to share cpu intensive tasks and especially give tracers enough cpu to draw smoothly. Of course all this can happen in one computer. There is no real limits : 4 everything is tested and works smoothly if *you have enough cpu/computers/network ressources*. + +It's obviously overkill for one laser in a garage, but for several laserS game events, laserS art, laserS competition, laserS planetarium,... LJ will handle the complexity. Content providers like artists, demomakers,... just need to send points. Needs at least : an etherdream DAC connected to an ILDA laser, RJ 45 IP network (gigabits only !! no wifi, 100 mpbs doesn't work well with several lasers) -LJ supports Linux and OS X. Windows is unkown but welcome, if someone want to jump in and care about it. +LJ is tested with Firefox, supports Linux and OS X. Windows is unkown but welcome, if someone want to jump in and care about it. + +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. @@ -29,6 +38,7 @@ LJ supports Linux and OS X. Windows is unkown but welcome, if someone want to ju (Doc in progress) +- Laser alignment like in laser mapping. - OSC and websocket commands. Very cool : LJ can script or be scripted. - Web ui : In your browser open webui/index.html. Javascript is needed. By default it connect to localhost. If you want to control a remote server, you need to change the uri line in LJ.js. - Status update every 0.5 seconds : every etherdream DAC state, number of buffer points sent,... @@ -58,11 +68,11 @@ For Linux and OS X : You probably want redis bound to all network interfaces : comment the bind line in /etc/redis/redis.conf and restart it. -In webui/index.html change the ws ip adress to the server IP if needed. +In webui/index.html change the websocket (ws) ip adress to the server IP if needed. Using the same idea check all ip address in LJ.conf. -There is a nice ws debug tool websocat. +There is a nice websocket debug tool : websocat. @@ -94,8 +104,7 @@ python main.py -L 1 Run your client -3/ to monitor redis server, there is app for that or : - +3/ to monitor redis server, there is an app for that (redis-manager/medis/...) or : redis-cli monitor @@ -114,7 +123,7 @@ python main.py -r 192.168.1.13 -L 1 node nodeclient.js -4/ to monitor redis server : +4/ to monitor redis server use redis-manager/medis/... or : redis-cli -h redisserverIP monitor @@ -123,12 +132,13 @@ redis-cli -h redisserverIP monitor # Plugins # -LJ comes with different plugins in python 3 : +A "plugin" is a software that send any number of pointlist(s). LJ comes with different plugins in python 3 : - LiveWords : Fill the input form and it's displayed. One word / laser. - Textcycl : Cycle some words with adjustable length on one laser. - Anaglyph : A green/red rotating cube. Try it with green/red 3D glasses ! - Planetarium : A 4 lasers planetarium. +- pySimu : A full speed laser simulator (pygame, python 2.7) - LaserPong : Our laser Pong is back ! (python 2.7) @@ -137,18 +147,21 @@ LJ comes with different plugins in python 3 : # -The server approach is based on redis, so you write and run your laser client software in any redis capable programming langage (50+ : https://redis.io/clients). +The server approach is based on redis, so you write and run your laser client software in any redis capable programming langage (50+ : https://redis.io/clients). If you want some interaction with GUI, like in text status area, you also need OSC. + +- Read all this readme ;-) +- There is a client and plugin folders with examples in different languages. If you want to do game especially with pygame, see ljpong in plugins/games directory. +- Generate at least one point list array (say a square) with *enough points*, one point is likely to fail for buffering reason. +- Feed your point list array in string format to redis server. i.e use "/pl/0/1" redis key to feed client 0, pointlist 1. +- Tell LJ.conf your plugin configuration : OSC port and command line to start it. +- lj3.py (python 3) and lj.py (python 2.7) have many very useful functions to not reinvent the wheel. Maybe it's better to symlink them in your directory than having a separated copy, to get future enhancements transparently. -- Read the Introduction part in this readme. -- There is a client and plugin folders with examples in different languages. -- Generate at least one point list array (say a square). -- Feed your point list array in string format to redis server. -- Tell LJ.conf your plugin configuration : OSC port and command line to start it () # # Nannou etherdeam simulator # +(Doc in Progress) # @@ -160,6 +173,7 @@ The server approach is based on redis, so you write and run your laser client so - kpps live modification for glitch art. - A grid style warp correction process in webUI. - IP change should not need a LJ relaunch +- Way faster WebUI simulator. Use pysimu plugin for full speed. # @@ -171,9 +185,11 @@ LJ is network based and this is *critical and flickering reason #1* if not manag Our "always working solution", as we regularly move our gear for different venues : -We use static network configuration. Our Etherdreams controllers have static IPs defined in their SDcard from 192.168.1.1 to 192.168.1.9. Because wifi will always finally sucks for many reasons, our computers (laser server and clients) are *gigabits wired connected* with 192.168.1.10 and after. Don't trust end user gear marketing on wifi, we have a big gigabits switch for laser only stuff. We provide Internet through wifi on different network like 192.168.2.x +- We use static network configuration. Our Etherdreams controllers have static IPs defined in their SDcard from 192.168.1.1 to 192.168.1.9. -Even if etherdreams are 100 Mbits, we use gigabits gear. +- Because wifi will always finally sucks for many reasons, our computers (laser server and clients) are *gigabits wired* with 192.168.1.10 and after. Don't trust end user gear marketing on wifi. We have a big gigabits switch for the *laser only lan*. We provide Internet through wifi on a different network like 192.168.2.x if really needed. + +- Again, even if etherdreams are 100 Mbits, we use *gigabits* gear. By default LJ uses on 127.0.0.1 (localhost) : diff --git a/commands.py b/commands.py index 82eb113..b423e7b 100644 --- a/commands.py +++ b/commands.py @@ -87,7 +87,7 @@ import plugins r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0) -GenericCommands = ["start","ljclient","clientnumber","noteon","pong","mouse","emergency","simu","status","run","nozoid","planet","live","words","ai","bank0","lj"] +GenericCommands = ["start","ljclient","clientnumber","noteon","ljpong","ljwars","mouse","emergency","simu","status","run","nozoid","planet","live","words","ai","bank0","pose","lj","cycl","glyph"] @@ -177,7 +177,6 @@ def handler(oscpath, args): # 2 incoming cases : generic or specific for a given lasernumber : # Generic : Commands without a laser number - #if oscpath[1] == "client" or oscpath[1]=="clientnumber" or oscpath[1] =="noteon" or oscpath[1]=="pong" or oscpath[1]=="mouse" or oscpath[1]=="emergency" or oscpath[1]=="simu" or oscpath[1]=="status" or oscpath[1]=="run" or oscpath[1]=="nozoid" or oscpath[1]=="planet" or oscpath[1]=="live" or oscpath[1]=="planet" : if oscpath[1] in GenericCommands: diff --git a/main.py b/main.py index f6d0b7a..446ac9b 100755 --- a/main.py +++ b/main.py @@ -385,6 +385,7 @@ def message_received(client, wserver, message): sendWSall("/"+ plugin + "/start 0") if gstt.debug >0: print "plugin", plugin, "didn't answered." + plugins.sendWSall("/status Running...") ''' if plugins.Send("planet",oscpath): diff --git a/plugins/VJing/bank0.py b/plugins/VJing/bank0.py index d2f96e5..08f729c 100644 --- a/plugins/VJing/bank0.py +++ b/plugins/VJing/bank0.py @@ -7,10 +7,11 @@ VJing Bank 0 was Franken for compo laser at coockie 2018 demoparty -0 : many Starfields +0 : Starfields 1 : generic pose animations 2 : Faces 3 : Dancers +4 : IdiotIA LICENCE : CC Sam Neurohack, Loloster, @@ -28,6 +29,7 @@ from random import randrange import redis import lj3 import sys,time +import os from osc4py3.as_eventloop import * from osc4py3 import oscbuildparse @@ -76,11 +78,6 @@ else: lj3.Config(redisIP,ljclient) -def OSCljclient(value): - # Will receive message address, and message data flattened in s, x, y - print("I got /bank0/ljclient with value", value) - ljclient = value - lj3.LjClient(ljclient) def hex2rgb(hexcode): return tuple(map(ord,hexcode[1:].decode('hex'))) @@ -440,7 +437,8 @@ CurrentPose = 1 def prepareFACES(): - # anim format (name, xpos,ypos, resize, currentframe, totalframe, count, speed) + # anim format (name, xpos, ypos, resize, currentframe, totalframe, count, frame repeat) + # 0 1 2 3 4 5 6 7 # total frame is fetched from directory file count anims[0] = [['detroit1', 300,300, 100,0,0,0,1]] @@ -470,32 +468,40 @@ def prepareFACES(): #by this one #thanks to https://stackoverflow.com/questions/19184335/is-there-a-need-for-rangelena + for laseranims in anims: - if debug > 1: - print("anims:",laseranims) - for anim in laseranims: - anim[5]=lengthPOSE(anim[0]) - if debug > 1: - print(anim[5]) + + for anim in laseranims: + anim[5] = lengthPOSE(anim[0]) + + if debug > 0: + print("anim :", anim) + print("length :", anim[5]) # display the face animation describe in PoseDir def Faces(): - + for laseranims in range(3): for anim in anims[laseranims]: PL = laseranims #print PL, anim + dots = [] - #print anim, anim[5] - # repeat anim[7] time the same frame + + # increase counter [6] + # compare to repeat [7] time the same frame anim[6] +=1 if anim[6] == anim[7]: + # reset repeat anim[6] = 0 - # increase current frame and compare to total frame + + # increase current frame [4] anim[4] += 1 + + # compare to total frame [5] if anim[4] == anim[5]: anim[4] = 0 @@ -509,7 +515,7 @@ def Faces(): for people in range(len(pose_json['people'])): - #lj3.PolyLineOneColor(face(pose), c=color, PL = 0, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + #lj3.rPolyLineOneColor(face(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) lj3.rPolyLineOneColor(browL(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) lj3.rPolyLineOneColor(browR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) lj3.rPolyLineOneColor(eyeR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) @@ -594,12 +600,107 @@ def Dancers(): PL[PL] = fwork.LinesPL(PL) ''' + +# Curve 4 IdiotIA +import json +CurrentPose = 1 + +def prepareIdiotIA(): + + + # anim format (name, xpos,ypos, resize, currentframe, totalframe, count, speed) + # total frame is fetched from directory file count + + anims[0] = [['detroit1', 300,300, 100,0,0,0,1]] + anims[1] = [['detroit1', 400,200, 200,0,0,0,1]] + anims[2] = [['detroit1', 500,200, 300,0,0,0,1]] + + ''' + # read anims number of frames from disk. + for anim in range(len(anims0)): + anims0[anim][5]= lengthPOSE(anims0[anim][0]) + for anim in range(len(anims1)): + anims1[anim][5]= lengthPOSE(anims1[anim][0]) + for anim in range(len(anims2)): + anims2[anim][5]= lengthPOSE(anims2[anim][0]) + ''' + + #replace code below + ''' + for laseranims in range(3): + if debug > 0: + print "anims:",anims[laseranims], + for anim in range(len(anims[laseranims])): + anims[laseranims][anim][5]= lengthPOSE(anims[laseranims][anim][0]) + if debug > 1: + print anims[laseranims][anim][5] + ''' + #by this one + #thanks to https://stackoverflow.com/questions/19184335/is-there-a-need-for-rangelena + + for laseranims in anims: + if debug > 1: + print("anims:",laseranims) + for anim in laseranims: + anim[5]=lengthPOSE(anim[0]) + if debug > 1: + print(anim[5]) + + + +# display the face animation describe in PoseDir +def Faces(): + + for laseranims in range(3): + for anim in anims[laseranims]: + PL = laseranims + #print PL, anim + dots = [] + #print anim, anim[5] + # repeat anim[7] time the same frame + anim[6] +=1 + if anim[6] == anim[7]: + + anim[6] = 0 + # increase current frame and compare to total frame + anim[4] += 1 + if anim[4] == anim[5]: + anim[4] = 0 + + + posename = 'poses/' + anim[0] + '/' + anim[0] +'-'+str("%05d"%anim[4])+'.json' + posefile = open(posename , 'r') + posedatas = posefile.read() + pose_json = json.loads(posedatas) + + # Face + + for people in range(len(pose_json['people'])): + + #lj3.rPolyLineOneColor(face(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(browL(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(browR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(eyeR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(eyeL(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(nose(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(mouth(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + + lj3.DrawPL(PL) + time.sleep(0.02) + + def OSCljclient(value): # Will receive message address, and message data flattened in s, x, y - print("I got /bank0/ljclient with value", value) + print("Bank0 got /bank0/ljclient with value", value) ljclient = value lj3.LjClient(ljclient) +def OSCpl(value): + + print("Bank0 got /bank0/pl with value", value) + lj3.WebStatus("Bank0 to pl "+ str(value)) + lj3.LjPl(value) + # Dancers, Starfield, Pose, Face def OSCrun(value): @@ -619,10 +720,6 @@ def WebStatus(message): lj3.SendLJ("/status",message) -#doit = Starfield -doit = Pose -#doit = Faces -#doit = Dancers print('Loading Bank0...') @@ -636,15 +733,16 @@ osc_udp_server("127.0.0.1", OSCinPort, "InPort") osc_method("/bank0/run*", OSCrun) osc_method("/bank0/ping*", lj3.OSCping) osc_method("/bank0/ljclient", OSCljclient) +osc_method("/bank0/ljpl", OSCpl) osc_method("/quit", OSCquit) - +''' import pygame pygame.init() Nbpads = pygame.joystick.get_count() print ("Joypads : ", str(Nbpads)) -''' + if Nbpads != 2: print ('') @@ -652,7 +750,6 @@ if Nbpads != 2: print ("THIS VERSION NEEDS 2 PADS. PLEASE CONNECT THEM.") print ('') sys.exit() -''' if Nbpads > 1: @@ -678,16 +775,22 @@ if Nbpads > 0: #print ("Buttons Pad 1 :" , str(numButtons)) - +''' anims =[[],[],[],[]] color = lj3.rgb2int(255,255,255) -prepareSTARFIELD() -preparePOSE() -prepareDANCERS() +#prepareSTARFIELD() +#preparePOSE() +#prepareDANCERS() prepareFACES() + +#doit = Starfield +#doit = Pose +doit = Faces +#doit = Dancers + WebStatus("Bank0 ready.") print("Bank0 ready") diff --git a/plugins/VJing/idiotia.py b/plugins/VJing/idiotia.py new file mode 100644 index 0000000..8a38eec --- /dev/null +++ b/plugins/VJing/idiotia.py @@ -0,0 +1,439 @@ +#!/usr/bin/python2.7 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' +LJ v0.8.1 + +IdiotIA for THSF 10 + +Include IdiotIA and Starfields + + +LICENCE : CC +Sam Neurohack, Loloster, + +''' + + +import math + +import numpy as np +import pdb +from datetime import datetime +from random import randrange +import redis +import lj3 +import sys,time +import os + +from osc4py3.as_eventloop import * +from osc4py3 import oscbuildparse +#from osc4py3 import oscmethod as osm +from osc4py3.oscmethod import * +import argparse + + + +screen_size = [700,700] +xy_center = [screen_size[0]/2,screen_size[1]/2] + +message = "LO" +OSCinPort = 8011 + +redisIP = '127.0.0.1' +ljclient = 0 + +print ("") +print ("Arguments parsing if needed...") +argsparser = argparse.ArgumentParser(description="Pose bank for LJ") +argsparser.add_argument("-r","--redisIP",help="IP of the Redis server used by LJ (127.0.0.1 by default) ",type=str) +argsparser.add_argument("-c","--client",help="LJ client number (0 by default)",type=int) +argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int) + +args = argsparser.parse_args() + + +if args.verbose: + debug = args.verbose +else: + debug = 0 + +if args.client: + ljclient = args.client +else: + ljclient = 0 + +# Redis Computer IP +if args.redisIP != None: + redisIP = args.redisIP +else: + redisIP = '127.0.0.1' + + +lj3.Config(redisIP,ljclient) + + +def hex2rgb(hexcode): + return tuple(map(ord,hexcode[1:].decode('hex'))) + + +def rgb2hex(rgb): + return int('0x%02x%02x%02x' % tuple(rgb),0) + + +# IdiotIA +import json +CurrentPose = 1 + +# Get frame number for pose path describe in PoseDir +def lengthPOSE(pose_dir): + + #if debug > 0: + # print("Check directory",'poses/' + pose_dir) + if os.path.exists('poses/' + pose_dir): + numfiles = sum(1 for f in os.listdir('poses/' + pose_dir) if os.path.isfile(os.path.join('poses/' + pose_dir + '/', f)) and f[0] != '.') + return numfiles + else: + if debug > 0: + print("but it doesn't even exist!") + return 0 + + +# get absolute face position points +def getFACE(pose_json,pose_points, people): + + dots = [] + for dot in pose_points: + + if len(pose_json['people'][people]['face_keypoints_2d']) != 0: + #print "people 0" + if pose_json['people'][people]['face_keypoints_2d'][dot * 3] != -1 and pose_json['people'][people]['face_keypoints_2d'][(dot * 3)+1] != -1: + dots.append((pose_json['people'][people]['face_keypoints_2d'][dot * 3], pose_json['people'][people]['face_keypoints_2d'][(dot * 3)+1])) + + return dots + + +# Face keypoints +def face(pose_json, people): + pose_points = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] + return getFACE(pose_json,pose_points, people) + +def browL(pose_json, people): + pose_points = [26,25,24,23,22] + return getFACE(pose_json,pose_points, people) + +def browR(pose_json, people): + pose_points = [21,20,19,18,17] + return getFACE(pose_json,pose_points, people) + +def eyeR(pose_json, people): + pose_points = [36,37,38,39,40,41,36] + return getFACE(pose_json,pose_points, people) + +def eyeL(pose_json, people): + pose_points = [42,43,44,45,46,47,42] + return getFACE(pose_json,pose_points, people) + +def nose(pose_json, people): + pose_points = [27,28,29,30] + return getFACE(pose_json,pose_points, people) + +def mouth(pose_json, people): + pose_points = [48,59,58,57,56,55,54,53,52,51,50,49,48,60,67,66,65,64,63,62,61,60] + return getFACE(pose_json,pose_points, people) + + + +def prepareIdiotIA(): + + WebStatus("Init IdiotIA...") + + # anim format (name, xpos, ypos, resize, currentframe, totalframe, count, speed) + # 0 1 2 3 4 5 6 7 + # total frames is fetched from directory by lengthPOSE() + + anims[0] = [['detroit1', xy_center[0], xy_center[1], 300,0,0,0,5]] + anims[1] = [['detroit1', xy_center[0], xy_center[1] + 50, 400,0,0,0,15]] + anims[2] = [['detroit1', xy_center[0], xy_center[1] + 50, 500,0,0,0,25]] + anims[3] = [['detroit1', xy_center[0], xy_center[1], 500,0,0,0,25]] + + for laseranims in anims: + + for anim in laseranims: + anim[5] = lengthPOSE(anim[0]) + + if debug > 0: + print("anim :", 'poses/' + anim[0], "length :", anim[5], "frames") + + +# display the face animation describe in PoseDir +def IdiotIA(): + + for laseranims in range(3): + for anim in anims[laseranims]: + PL = laseranims + #print PL, anim + + dots = [] + + # increase current frame [4] of speed [7] frames + anim[4] += 1 + + # compare to total frame [5] + if anim[4] >= anim[5]: + anim[4] = 0 + + posename = 'poses/' + anim[0] + '/' + anim[0] +'-'+str("%05d"%anim[4])+'.json' + posefile = open(posename , 'r') + posedatas = posefile.read() + pose_json = json.loads(posedatas) + + # Face + + for people in range(len(pose_json['people'])): + + #lj3.rPolyLineOneColor(face(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(browL(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(browR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(eyeR(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(eyeL(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(nose(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + lj3.rPolyLineOneColor(mouth(pose_json, people), c=color, PL = laseranims, closed = False, xpos = anim[1], ypos = anim[2], resize = anim[3]) + + lj3.DrawPL(PL) + time.sleep(0.02) + + +# many starfields +def prepareSTARFIELD(): + global star, stars0, stars1, stars2, starfieldcount, starspeed, displayedstars, displayedstars, num_stars, max_depth + + WebStatus("Init starfields...") + stars0=[] + stars1=[] + stars2=[] + #stars3=[] + num_stars = 50 + max_depth = 20 + stars = [] + starfieldcount = 0 + displayedstars = 5 + starspeed = 0.05 + + for i in range(num_stars): + # A star is represented as a list with this format: [X,Y,Z] + star = [randrange(-25,25), randrange(-25,25), randrange(1, max_depth)] + stars0.append(star) + star = [randrange(-25,25), randrange(-25,25), randrange(1, max_depth)] + stars1.append(star) + star = [randrange(-25,25), randrange(-25,25), randrange(1, max_depth)] + stars2.append(star) + +def Starfield(hori=0,verti=0): + global star, stars0, stars1, stars2, starfieldcount, starspeed, displayedstars, displayedstars, num_stars, max_depth + + starfieldcount += 1 + #print starfieldcount + starpoints = [] + #print displayedstars, 'stars displayed' + + # Increase number of + if displayedstars < num_stars and starfieldcount % 15 == 0: + displayedstars += 1 + + if displayedstars == num_stars and starfieldcount % 10 == 0: + starspeed += 0.005 + + #print starspeed + + for starnumber in range(0,displayedstars): + + # The Z component is decreased on each frame. + stars0[starnumber][2] -= starspeed * 3 + stars1[starnumber][2] -= starspeed * 3 + stars2[starnumber][2] -= starspeed * 3 + + # If the star has past the screen (I mean Z<=0) then we + # reposition it far away from the screen (Z=max_depth) + # with random X and Y coordinates. + if stars0[starnumber][2] <= 0: + stars0[starnumber][0] = randrange(-25,25) + stars0[starnumber][1] = randrange(-25,25) + stars0[starnumber][2] = max_depth + + if stars1[starnumber][2] <= 0: + stars1[starnumber][0] = randrange(-25,25) + stars1[starnumber][1] = randrange(-25,25) + stars1[starnumber][2] = max_depth + + if stars2[starnumber][2] <= 0: + stars2[starnumber][0] = randrange(-25,25) + stars2[starnumber][1] = randrange(-25,25) + stars2[starnumber][2] = max_depth + + + # Convert the 3D coordinates to 2D using perspective projection. + k0 = 128.0 / stars0[starnumber][2] + k1 = 128.0 / stars1[starnumber][2] + k2 = 128.0 / stars2[starnumber][2] + + # Move Starfield origin. + # if stars xpos/ypos is same sign (i.e left stars xpos is <0) than (joystick or code) acceleration (hori and verti moves the star field origin) + if np.sign(stars0[starnumber][0]) == np.sign(hori): + x0 = int(stars0[starnumber][0] * k0 + xy_center[0] + (hori*600)) + else: + x0 = int(stars0[starnumber][0] * k0 + xy_center[0] + (hori*500)) + + if np.sign(stars0[starnumber][1]) == np.sign(verti): + y0 = int(stars0[starnumber][1] * k0 + xy_center[1] + (verti*600)) + else: + y0 = int(stars0[starnumber][1] * k0 + xy_center[1] + (verti*500)) + + + if np.sign(stars1[starnumber][0]) == np.sign(hori): + x1 = int(stars1[starnumber][0] * k1 + xy_center[0] + (hori*600)) + else: + x1 = int(stars1[starnumber][0] * k1 + xy_center[0] + (hori*300)) + + if np.sign(stars1[starnumber][1]) == np.sign(verti): + y1 = int(stars1[starnumber][1] * k1 + xy_center[1] + (verti*600)) + else: + y1 = int(stars1[starnumber][1] * k1 + xy_center[1] + (verti*300)) + + + if np.sign(stars2[starnumber][0]) == np.sign(hori): + x2 = int(stars2[starnumber][0] * k2 + xy_center[0] + (hori*600)) + else: + x2 = int(stars2[starnumber][0] * k2 + xy_center[0] + (hori*300)) + + if np.sign(stars2[starnumber][1]) == np.sign(verti): + y2 = int(stars2[starnumber][1] * k2 + xy_center[1] + (verti*600)) + else: + y2 = int(stars2[starnumber][1] * k2 + xy_center[1] + (verti*300)) + + + # Add star to pointlist PL 0 + if 0 <= x0 < screen_size[0] - 2 and 0 <= y0 < screen_size[1] - 2: + lj3.PolyLineOneColor([(x0,y0),((x0+1),(y0+1))], c = white, PL = 0, closed = False) + + # Add star to pointlist PL 1 + if 0 <= x1 < screen_size[0] - 2 and 0 <= y1 < screen_size[1] - 2: + lj3.PolyLineOneColor([(x1,y1),((x1+1),(y1+1))], c = white, PL = 1, closed = False) + + # Add star to pointlist PL 2 + if 0 <= x2 < screen_size[0] - 2 and 0 <= y2 < screen_size[1] - 2: + lj3.PolyLineOneColor([(x2,y2),((x2+1),(y2+1))], c= white, PL = 2, closed = False) + + ''' + if starfieldcount < 200: + + if 0 <= x3 < screen_size[0] - 2 and 0 <= y3 < screen_size[1] - 2: + fwork.PolyLineOneColor([(x3,y3),((x3+2),(y3+2))], c=colorify.rgb2hex([255,255,255]), PL = 3, closed = False) + ''' + + + lj3.Text(message, white, PL = 3, xpos = 300, ypos = 300, resize = 1, rotx =0, roty =0 , rotz=0) + lj3.DrawPL(0) + lj3.DrawPL(1) + lj3.DrawPL(2) + lj3.DrawPL(3) + + +def OSCidiotia(address, value): + print("Pose bank idiotia got", address, "with value", value) + + +def OSCfield(address, value): + print("Pose bank field got", address, "with value", value) + + +def OSCljclient(value): + print("Pose bank got /pose/ljclient with value", value) + ljclient = value + lj3.LjClient(ljclient) + + +def OSCpl(value): + + print("Pose bank got /pose/pl with value", value) + lj3.WebStatus("Pose bank to pl "+ str(value)) + lj3.LjPl(value) + + +# Starfield, idiotia +def OSCrun(value): + # Will receive message address, and message data flattened in s, x, y + print("Pose bank got /run with value", value) + doit = value + +# /quit +def OSCquit(): + + WebStatus("Pose bank stopping") + print("Stopping OSC...") + lj3.OSCstop() + sys.exit() + +def WebStatus(message): + lj3.SendLJ("/status",message) + + + +print('Loading Pose bank...') + +WebStatus("Loading Pose bank...") + +# OSC Server callbacks +print("Starting OSC at 127.0.0.1 port",OSCinPort,"...") +osc_startup() +osc_udp_server("127.0.0.1", OSCinPort, "InPort") + +osc_method("/pose/run*", OSCrun) +osc_method("/pose/ping*", lj3.OSCping) +osc_method("/pose/ljclient", OSCljclient) +osc_method("/pose/ljpl", OSCpl) +osc_method("/quit", OSCquit) +osc_method("/pose/idiotia*", OSCidiotia, argscheme=OSCARG_ADDRESS + OSCARG_DATA) +osc_method("/pose/field*", OSCfield, argscheme=OSCARG_ADDRESS + OSCARG_DATA) + +anims =[[],[],[],[]] +color = lj3.rgb2int(255,255,255) + + +prepareIdiotIA() +#prepareSTARFIELD() + + +#doit = Starfield +doit = IdiotIA + +white = lj3.rgb2int(255,255,255) +red = lj3.rgb2int(255,0,0) +blue = lj3.rgb2int(0,0,255) +green = lj3.rgb2int(0,255,0) + +WebStatus("Pose bank running.") +print("Pose bank running") + +def Run(): + + try: + while 1: + #Starfield(hori=0,verti=0) + doit() + + except KeyboardInterrupt: + pass + + # Gently stop on CTRL C + + finally: + + WebStatus("Pose bank Exit") + print("Stopping OSC...") + lj3.OSCstop() + + print ("Pose bank Stopped.") + +Run() diff --git a/plugins/VJing/lj3.py b/plugins/VJing/lj3.py index 2ddd8d3..3060693 100644 --- a/plugins/VJing/lj3.py +++ b/plugins/VJing/lj3.py @@ -1,7 +1,9 @@ # coding=UTF-8 ''' -LJ v0.8.1 in python3 + +lj3 v0.8.1 + Some LJ functions useful for python clients (was framy.py) OSC functions commented, waiting working on OSC in python3 @@ -11,24 +13,28 @@ PolyLineOneColor rPolyLineOneColor Text(word, color, PL, xpos, ypos, resize, rotx, roty, rotz) : Display a word -Send(adress,message) : remote control. See commands.py -WebStatus(message) : display message on webui +SendLJ(adress,message) : LJ remote control. See commands.py +WebStatus(message) : display message on webui DrawPL(point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers. rgb2int(r,g,b) +LjClient(client): Change Client number in redis keys +LjPl(pl): Change pl number in redis keys = laser target. -OSCstart(): Start the OSC system. -OSCframe(): -OSCstop(): Properly close the OSC system -OSCping(value): Answer to LJ pings + +OSCstart(): Start the OSC system. +OSCframe(): Handle incoming OSC message. Calling the right callback +OSCstop(): Properly close the OSC system +OSCping(value): Answer to LJ pings by sending /pong value +OSCquit(name): Exit calling script using name in terminal setup_controls(joystick) -XboxController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger -Ps3Controller : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger, getUp, getDown, getLeft, getRight, getFire1, getFire2(self): +XboxController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger +Ps3Controller : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger, getUp, getDown, getLeft, getRight, getFire1, getFire2(self): MySaitekController : getLeftHori,getLeftVert, getRightHori,getRightVert, getLeftTrigger,getRightTrigger MyThrustController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger -CSLController : getLeftHori,getLeftVert,getRightHori, getRightVert,getLeftTrigger,getRightTrigger,getFire1,getFire2 -my USB Joystick : getUp,getDown,getLeft,getRight,etLeftTrigger, getRightTrigger,getFire1, getFire2 +CSLController : getLeftHori,getLeftVert,getRightHori, getRightVert,getLeftTrigger,getRightTrigger,getFire1,getFire2 +my USB Joystick : getUp,getDown,getLeft,getRight,etLeftTrigger, getRightTrigger,getFire1, getFire2 LICENCE : CC @@ -42,6 +48,8 @@ import redis # Import needed modules from osc4py3 from osc4py3.as_eventloop import * from osc4py3 import oscbuildparse +#from osc4py3 import oscmethod as osm +from osc4py3.oscmethod import * redisIP = '127.0.0.1' @@ -69,6 +77,7 @@ def OSCframe(): def OSCstop(): osc_terminate() + def SendLJ(oscaddress,oscargs=''): try: @@ -80,13 +89,24 @@ def SendLJ(oscaddress,oscargs=''): print ('Connection to LJ refused : died ?') pass +def WebStatus(message): + SendLJ("/status", message) + + # Answer to LJ pings def OSCping(value): # Will receive message address, and message data flattened in s, x, y - print("I got /ping with value", value) + print("Got /ping with value", value) SendLJ("/pong",value) +# /quit +def OSCquit(name): + WebStatus(name + " quit.") + print("Stopping OSC...") + + OSCstop() + sys.exit() ''' def handlerfunction(s, x, y): @@ -216,7 +236,11 @@ def LjClient(client): global ClientNumber ClientNumber = client - + +def LjPl(pl): + global PL + + PL = pl def LineTo(xy, c, PL): @@ -320,7 +344,6 @@ def ResetPL(self, PL): pl[PL] = [] - def DigitsDots(number,color): dots =[] for dot in ASCII_GRAPHICS[number]: @@ -329,6 +352,7 @@ def DigitsDots(number,color): #self.point_list.append((xy + (c,))) return dots + def CharDots(char,color): dots =[] @@ -336,6 +360,7 @@ def CharDots(char,color): dots.append((dot[0],dot[1],color)) return dots + def Text(message,c, PL, xpos, ypos, resize, rotx, roty, rotz): dots =[] diff --git a/plugins/games/ljpong/entities.py b/plugins/games/ljpong/entities.py index 4d787a3..b640faf 100644 --- a/plugins/games/ljpong/entities.py +++ b/plugins/games/ljpong/entities.py @@ -112,7 +112,7 @@ LOGO = [ LOGO_OFFSET_X = 460 LOGO_OFFSET_Y = 250 -def LogoDraw(): +def LogoDraw(plnumber): ''' Dessine le logo ''' @@ -122,8 +122,7 @@ def LogoDraw(): for xy in pl_color[0]: xy_list.append((LOGO_OFFSET_X + xy[0], LOGO_OFFSET_Y + xy[1])) #print xy_list - lj.PolyLineOneColor(xy_list, c,0, False) - + lj.PolyLineOneColor(xy_list, c, plnumber, False) @@ -190,23 +189,23 @@ def FlipsMoveJoy(left_key,right_key,up_key,down_key,lvertax): FlipsLy = screen_size[1] - PADDLE_height return FlipsLy, FlipsRy -def FlipsDraw(): +def FlipsDraw(plnumber): - lj.PolyLineOneColor([(FlipsLx,FlipsLy),(FlipsLx,FlipsLy + PADDLE_height),(FlipsLx + PADDLE_width , FlipsLy + PADDLE_height),(FlipsLx + PADDLE_width,FlipsLy)], white,0,True) - lj.PolyLineOneColor([(FlipsRx,FlipsRy),(FlipsRx,FlipsRy + PADDLE_height),(FlipsRx + PADDLE_width , FlipsRy + PADDLE_height),(FlipsRx + PADDLE_width,FlipsRy)], white,0,True) + lj.PolyLineOneColor([(FlipsLx,FlipsLy),(FlipsLx,FlipsLy + PADDLE_height),(FlipsLx + PADDLE_width , FlipsLy + PADDLE_height),(FlipsLx + PADDLE_width,FlipsLy)], white, plnumber, True) + lj.PolyLineOneColor([(FlipsRx,FlipsRy),(FlipsRx,FlipsRy + PADDLE_height),(FlipsRx + PADDLE_width , FlipsRy + PADDLE_height),(FlipsRx + PADDLE_width,FlipsRy)], white, plnumber, True) -def FiletDraw(): - lj.PolyLineOneColor([(screen_size[0]/2,screen_size[1]),(screen_size[0]/2,0)], white, 0,True) +def FiletDraw(plnumber): + lj.PolyLineOneColor([(screen_size[0]/2,screen_size[1]),(screen_size[0]/2,0)], white, plnumber,True) -def Score1Draw(score): +def Score1Draw(score, plnumber): #print "score1",score - lj.Text(str(score),white, 0, 350, 50, 1, 0, 0, 0) + lj.Text(str(score),white, plnumber, 350, 50, 1, 0, 0, 0) -def Score2Draw(score): +def Score2Draw(score, plnumber): #print "score2",score - lj.Text(str(score),white, 0, 500, 50, 1, 0, 0, 0) + lj.Text(str(score),white, plnumber, 500, 50, 1, 0, 0, 0) @@ -235,7 +234,7 @@ def BallMove(xcoord,ycoord): elif BallY >= screen_size[1]: BallY = screen_size[1] -def BallDraw(): +def BallDraw(plnumber): global BallX,BallY xmin = 0 @@ -255,5 +254,5 @@ def BallDraw(): #print "ball position",xmin,xmax,ymin,ymax - lj.PolyLineOneColor([(xmin,ymin),(xmin,ymax),(xmax,ymax),(xmax,ymin)], white,0,True) + lj.PolyLineOneColor([(xmin,ymin),(xmin,ymax),(xmax,ymax),(xmax,ymin)], white, plnumber, True) diff --git a/plugins/games/ljpong/lj.py b/plugins/games/ljpong/lj.py index da6fe60..5244032 100644 --- a/plugins/games/ljpong/lj.py +++ b/plugins/games/ljpong/lj.py @@ -1,15 +1,21 @@ # coding=UTF-8 ''' -LJ v0.8.0 -Some LJ functions useful for python clients (was framy.py) +LJ v0.8.1 +Some LJ functions useful for python 2.7 clients (was framy.py) +Functions and documentation here is low priority as python 2 support will stop soon. +Better code your plugin with python 3 and lj3.py. + Config PolyLineOneColor rPolyLineOneColor Text -sendlj : remote control +SendLJ : remote control +LjClient : +LjPl : DrawPL +WebStatus LICENCE : CC Sam Neurohack @@ -29,21 +35,14 @@ point_list = [] pl = [[],[],[],[]] -''' -LJIP = "127.0.0.1" -osclientlj = OSCClient() -oscmsg = OSCMessage() -osclientlj.connect((redisIP, 8002)) -''' - -def sendlj(oscaddress,oscargs=''): +def SendLJ(oscaddress,oscargs=''): oscmsg = OSCMessage() oscmsg.setAddress(oscaddress) oscmsg.append(oscargs) - #print ("sending to bhorosc : ",oscmsg) + print ("sending OSC message : ",oscmsg) try: osclientlj.sendto(oscmsg, (redisIP, 8002)) oscmsg.clearData() @@ -52,7 +51,8 @@ def sendlj(oscaddress,oscargs=''): pass #time.sleep(0.001 - +def WebStatus(message): + SendLJ("/status", message) ASCII_GRAPHICS = [ @@ -158,6 +158,17 @@ def Config(redisIP,client): ClientNumber = client #print "client configured",ClientNumber + +def LjClient(client): + global ClientNumber + + ClientNumber = client + +def LjPl(pl): + global PL + + PL = pl + def LineTo(xy, c, PL): @@ -248,6 +259,7 @@ def LinesPL(PL): print "Stupido !! your code is to old : use DrawPL() instead of LinesPL()" DrawPL(PL) + def DrawPL(PL): #print '/pl/0/'+str(PL), str(pl[PL]) if r.set('/pl/'+str(ClientNumber)+'/'+str(PL), str(pl[PL])) == True: diff --git a/plugins/games/ljpong/main.py b/plugins/games/ljpong/main.py index 5e7c7f3..9a76036 100755 --- a/plugins/games/ljpong/main.py +++ b/plugins/games/ljpong/main.py @@ -13,6 +13,7 @@ import math import itertools import sys import os +import types ''' is_py2 = sys.version[0] == '2' @@ -30,6 +31,9 @@ import entities from controller import setup_controls import argparse +from OSC import OSCServer, OSCClient, OSCMessage +OSCIP = "127.0.0.1" +OSCPort = 8020 score = None @@ -87,6 +91,9 @@ red = rgb2int(255,0,0) blue = rgb2int(0,0,255) green = rgb2int(0,255,0) +# +# Arguments handling +# print ("") print ("Arguments parsing if needed...") @@ -129,11 +136,17 @@ def StartPlaying(first_time = False): app_path = os.path.dirname(os.path.realpath(__file__)) +# +# Pads via pygame +# + +print "Pygame init..." pygame.init() #sounds.InitSounds() clock = pygame.time.Clock() + Nbpads = pygame.joystick.get_count() print ("Joypads : ", str(Nbpads)) @@ -184,6 +197,66 @@ y = ball_origin[1] keystates = pygame.key.get_pressed() +# +# OSC +# + +oscserver = OSCServer( (OSCIP, OSCPort) ) +oscserver.timeout = 0 +OSCRunning = True + + +def OSCljclient(path, tags, args, source): + + + print("LJ Pong got /ljpong/ljclient with value", args[0]) + lj.WebStatus("LJPong to virtual "+ str(args[0])) + ljclient = args[0] + lj.LjClient(ljclient) + +def OSCpl(path, tags, args, source): + + print("LJ Pong got /ljpong/pl with value", args[0]) + lj.WebStatus("LJPong to pl "+ str(args[0])) + plnumber = args[0] + +# /ping +def OSCping(path, tags, args, source): + + print("LJ Pong got /ping") + lj.SendLJ("/pong","ljpong") + lj.SendLJ("/ljpong/start",1) + + +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() + + +def handle_timeout(self): + self.timed_out = True + +print "" +print "Launching OSC server..." +print "at", OSCIP, "port",str(OSCPort) + +oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver) + +# OSC callbacks + +oscserver.addMsgHandler( "/ljpong/ljclient", OSCljclient ) +oscserver.addMsgHandler("/ljpong/pl", OSCpl) +oscserver.addMsgHandler("/ping", OSCping) + +print "Running..." + +# +# Game main loop +# + while fs != GAME_FS_QUIT: @@ -192,6 +265,8 @@ while fs != GAME_FS_QUIT: if event.type == pygame.QUIT: fs = GAME_FS_QUIT + OSC_frame() + keystates_prev = keystates[:] keystates = pygame.key.get_pressed()[:] @@ -346,21 +421,19 @@ while fs != GAME_FS_QUIT: if fs == GAME_FS_PLAY or fs == GAME_FS_GAMEOVER or fs == GAME_FS_LAUNCH: - entities.Score1Draw(lscore) - entities.Score2Draw(rscore) - entities.FlipsDraw() - entities.BallDraw() - entities.FiletDraw() - lj.DrawPL(0) + entities.Score1Draw(lscore, plnumber) + entities.Score2Draw(rscore, plnumber) + entities.FlipsDraw(plnumber) + entities.BallDraw(plnumber) + entities.FiletDraw(plnumber) + lj.DrawPL(plnumber) if fs == GAME_FS_MENU: - entities.LogoDraw() - lj.DrawPL(0) + entities.LogoDraw(plnumber) + lj.DrawPL(plnumber) - # TODO : rendre indépendante la fréquence de rafraîchissement de l'écran par - # rapport à celle de l'animation du jeu clock.tick(100) pygame.quit() diff --git a/clients/laserglyph.py b/plugins/laserglyph.py similarity index 95% rename from clients/laserglyph.py rename to plugins/laserglyph.py index 7dd419c..5a50844 100644 --- a/clients/laserglyph.py +++ b/plugins/laserglyph.py @@ -110,11 +110,18 @@ def rgb2int(r,g,b): def OSCljclient(value): - # Will receive message address, and message data flattened in s, x, y - print("I got /glyph/ljclient with value", value) + + print("Glyph got /glyph/ljclient with value", value) + lj3.WebStatus("Glyph to virtual "+ str(value)) ljclient = value lj3.LjClient(ljclient) +def OSCpl(value): + + print("Glyph got /glyph/pl with value", value) + lj3.WebStatus("Glyph to pl "+ str(value)) + lj3.LjPl(value) + def Proj(x,y,z,angleX,angleY,angleZ): diff --git a/plugins/livewords.py b/plugins/livewords.py index 5c5990d..d0d48c1 100644 --- a/plugins/livewords.py +++ b/plugins/livewords.py @@ -94,10 +94,12 @@ def OSCword3(value): def OSCljclient(value): # Will receive message address, and message data flattened in s, x, y print("Words got /words/ljclient with value", value) + lj3.WebStatus("Words to virtual "+ str(value)) ljclient = value lj3.LjClient(ljclient) + # /ping def OSCping(): @@ -108,7 +110,7 @@ def OSCping(): # /quit def OSCquit(): - lj3.OSCquit("Words") + lj3.OSCquit("words") def Run(): @@ -122,7 +124,7 @@ def Run(): osc_method("/words/text/2*", OSCword2) osc_method("/words/text/3*", OSCword3) osc_method("/ping*", OSCping) - osc_method("/words/ljclient", OSCljclient) + osc_method("/words/ljclient*", OSCljclient) osc_method("/quit", OSCquit) color = lj3.rgb2int(255,255,255) diff --git a/plugins/lj3.py b/plugins/lj3.py index 140f2df..3060693 100644 --- a/plugins/lj3.py +++ b/plugins/lj3.py @@ -17,6 +17,9 @@ SendLJ(adress,message) : LJ remote control. See commands.py WebStatus(message) : display message on webui DrawPL(point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers. rgb2int(r,g,b) +LjClient(client): Change Client number in redis keys +LjPl(pl): Change pl number in redis keys = laser target. + OSCstart(): Start the OSC system. OSCframe(): Handle incoming OSC message. Calling the right callback @@ -233,7 +236,11 @@ def LjClient(client): global ClientNumber ClientNumber = client - + +def LjPl(pl): + global PL + + PL = pl def LineTo(xy, c, PL): diff --git a/plugins/planetarium/lj3.py b/plugins/planetarium/lj3.py index 59f02e8..115f2db 100644 --- a/plugins/planetarium/lj3.py +++ b/plugins/planetarium/lj3.py @@ -67,7 +67,7 @@ def SendLJ(oscaddress,oscargs=''): # Answer to LJ pings def OSCping(value): - # Will receive message address, and message data flattened in s, x, y + print("I got /ping with value", value) SendLJ("/pong",value) @@ -190,6 +190,9 @@ def Config(redisIP,client): ClientNumber = client osc_udp_client(redisIP, 8002, "LJ 8002") +# If you want to use rgb for color : +def rgb2int(r,g,b): + return int('0x%02x%02x%02x' % (r,g,b),0) def LineTo(xy, c, PL): @@ -308,13 +311,13 @@ def CharDots(char,color): dots.append((dot[0],dot[1],color)) return dots -def Text(message,c, PL, xpos, ypos, resize, rotx, roty, rotz): +def Text(message, c, PL, xpos, ypos, resize, rotx, roty, rotz): dots =[] l = len(message) i= 0 - #print message + #print (message) for ch in message: @@ -333,10 +336,10 @@ def Text(message,c, PL, xpos, ypos, resize, rotx, roty, rotz): for xy in char_pl_list: char_draw.append((xy[0] + x_offset,xy[1],c)) - i +=1 + i += 1 #print ch,char_pl_list,char_draw rPolyLineOneColor(char_draw, c, PL , False, xpos, ypos, resize, rotx, roty, rotz) - #print ("laser",PL,"message",message) + # print ("laser",PL,"message",message) #dots.append(char_draw) diff --git a/plugins/planetarium/main.py b/plugins/planetarium/main.py index 1cd8f81..0a813be 100755 --- a/plugins/planetarium/main.py +++ b/plugins/planetarium/main.py @@ -17,7 +17,7 @@ Todo: - use debug mode and check altaz calculated values against online sites. - Validate aa2radec() with online calculator. Rewrite it to remove need for Astropy. - Findout how to use OSC in python 3. -- +- North sky is hardcoded. - Code WebUI page. - UpdateStars() in each laser sky. Get magnitude. See UpdateSolar for example. - All Draw operations should also check visibility in the given laser altitude range. @@ -33,10 +33,12 @@ import lj3 import numpy as np import math,time +print("Importing astropy...") from astropy.coordinates import SkyCoord, EarthLocation, AltAz from astropy import units as u from astropy.time import Time +print("Importing skyfield...") from skyfield.api import Star, load, Topos,Angle from skyfield.data import hipparcos @@ -45,10 +47,6 @@ from osc4py3 import oscbuildparse #from osc4py3 import oscmethod as osm from osc4py3.oscmethod import * - - - - import json ''' @@ -65,14 +63,12 @@ else: import argparse - - print ("") print ("Arguments parsing if needed...") argsparser = argparse.ArgumentParser(description="Planetarium for LJ") argsparser.add_argument("-r","--redisIP",help="IP of the Redis server used by LJ (127.0.0.1 by default) ",type=str) argsparser.add_argument("-c","--client",help="LJ client number (0 by default)",type=int) -argsparser.add_argument("-L","--Lasers",help="Number of lasers connected (1 by default).",type=int) +argsparser.add_argument("-L","--Lasers",help="Number of lasers connected (4 by default).",type=int) argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int) argsparser.add_argument("-i","--input",help="inputs OSC Port (8005 by default)",type=int) #argsparser.add_argument("-n","--name",help="City Name of the observer",type=str) @@ -89,7 +85,7 @@ else: if args.Lasers: lasernumber = args.Lasers else: - lasernumber = 1 + lasernumber = 4 if args.verbose: debug = args.verbose @@ -178,10 +174,17 @@ def Proj(x,y,z,angleX,angleY,angleZ): def aa2radec(azimuth, altitude, t): #AstrObserver = EarthLocation(lat=lati * u.deg, lon=longi *u.deg, height= elevation*u.m,) - ObjectCoord = SkyCoord(alt = altitude * u.deg, az = azimuth *u.deg, obstime = t, frame = 'altaz', location = AstrObserver) + #print("Oserver",AstrObserver ) + print("time",t) + ObjectCoord = SkyCoord(alt = altitude * u.deg, az = azimuth * u.deg, obstime = t, frame = 'altaz', location = AstrObserver) #print("icrs",ObjectCoord.icrs) + print("Altitude", altitude) + print("Altitude", altitude * u.deg) + print("Azimuth", azimuth) + print("Azimuth", azimuth * u.deg) #print("ICRS Right Ascension", ObjectCoord.icrs.ra) #print("ICRS Declination", ObjectCoord.icrs.dec) + #print() return ObjectCoord.icrs.ra.degree, ObjectCoord.icrs.dec.degree @@ -227,16 +230,20 @@ def RadecSkies(LaserSkies, AstroSkyTime): print() print("Converting", lasernumber, "LaserSkies limits in Right Ascension & Declination (radec) coordinates ") for laser in range(lasernumber): + if debug > 0: + print ("") print("Laser",laser) # Left top point LaserSkies[laser][4],LaserSkies[laser][6] = aa2radec(azimuth = LaserSkies[laser][0], altitude =LaserSkies[laser][2], t =AstroSkyTime) - if debug > 0: - print(LaserSkies[laser][4],LaserSkies[laser][6]) + # Right Bottom point LaserSkies[laser][5],LaserSkies[laser][7] = aa2radec(azimuth = LaserSkies[laser][1], altitude =LaserSkies[laser][3], t =AstroSkyTime) if debug > 0: - print(LaserSkies[laser][5],LaserSkies[laser][7]) + print("Top left Altaz :",LaserSkies[laser][2],LaserSkies[laser][0]) + print("Top left radec :",LaserSkies[laser][4],LaserSkies[laser][6]) + print("Bottom right Altaz :",LaserSkies[laser][3],LaserSkies[laser][1]) + print("Bottom right radec :",LaserSkies[laser][5],LaserSkies[laser][7]) if debug > 0: print(LaserSkies) print ("Done.") @@ -248,7 +255,10 @@ def azimuth2scrX(leftAzi,rightAzi,s): b1, b2 = 0, width #if debug > 0: # print(leftAzi, rightAzi, s, b1 + ((s - a1) * (b2 - b1) / (a2 - a1))) - return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) + if s != 0: + return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) + else: + return width/2 @@ -297,7 +307,7 @@ def LoadSolar(): def UpdateSolar(): global SolarObjects - # Compute Alt Az coordinates for all solar objects for obsehttps://www.startpage.com/do/searchrver. + # Compute Alt Az coordinates for all solar objects for Observer. for number,object in enumerate(SolarObjects): #print(object[0],number) @@ -305,13 +315,11 @@ def UpdateSolar(): if debug > 0: PrintSolar() - def PrintSolar(): for number,object in enumerate(SolarObjects): print (SolarObjects[number][0],"is at (alt,az)",SolarObjects[number][1],SolarObjects[number][2]) - # Draw the SolarShapeObject for any Solar object is in the laser Sky def DrawSolar(laser): @@ -363,12 +371,11 @@ def StarSelect(): for index in range(len(Starnames)-1): StarsObjects.append([Starnames[index+1],0,0]) - def UpdateStars(ts): global StarsObjects hipparcos_epoch = ts.tt(1991.25) - # Compute Alt Az coordinates for all solar objects for obsehttps://www.startpage.com/do/searchrver. + # Compute Alt Az coordinates for all stars objects for Observer. for number,object in enumerate(StarsObjects): #print(object[0],number) @@ -405,7 +412,7 @@ def DrawStars(laser): # -# Anything system. Say you want +# Template for Anything you want system. # AnythingObjectShape = [(-50,30), (-30,-30), (30,-30), (10,30), (-50,30)] @@ -433,13 +440,11 @@ def UpdateAnything(): if debug > 0: PrintAnything() - def PrintAnything(): for number,object in enumerate(AnythingObjects): print (AnythingObjects[number][0],"is at (alt,az)",AnythingObjects[number][1],AnythingObjects[number][2]) - # Draw the AnythingShapeObject for any Anything object is in the laser Sky def DrawAnything(laser): @@ -453,12 +458,11 @@ def DrawAnything(laser): - - # # On Earth Gps positions # from https://github.com/lutangar/cities.json # + def LoadCities(): global world @@ -493,8 +497,7 @@ def DrawOrientation(laser): # North direction is in given laser sky azimuth range? if LaserSkies[laser][1] < LaserSkies[laser][0]: #print ("N az",azimuth2scrX(LaserSkies[laser][0],LaserSkies[laser][1],0)) - lj3.Text("NORTH",white,laser,azimuth2scrX(LaserSkies[laser][0],LaserSkies[laser][1],359), 770, resize = 0.5, rotx =0, roty =0 , rotz=0) - + lj3.Text("NORTH",white,laser,azimuth2scrX(LaserSkies[laser][0],LaserSkies[laser][1],0), 770, resize = 0.5, rotx =0, roty =0 , rotz=0) # East direction is in given laser sky azimuth range ? if LaserSkies[laser][0] <= 90 < LaserSkies[laser][1]: @@ -546,7 +549,9 @@ def InitObserver(SkyCity, SkyCountryCode, time,ts): # to later select their visible objects in radec catalogs like hipparcos. # LaserSky definition for one laser (in decimal degrees) : [RightAzi, LeftAzi, TopAlt, BotAlt, LeftRa, RightRa, TopDec, BottomDec] # With 4 lasers with each one a quarter of the 360 ° real sky, there is 4 LaserSky : - LaserSkies = [[45,135.0,90.0,0.0,0.0,0.0,0.0,0.0],[135,225,90,0,0,0,0,0],[225,315,90,0,0,0,0,0],[305,0,90,0,0,0,0,0]] + print() + print("LaserSkies Radec conversion") + LaserSkies = [[45,135.0,90.0,0.0,0.0,0.0,0.0,0.0],[135,225,90,0,0,0,0,0],[225,315,90.0,0,0,0,0,0],[315,45,90.0,0,0,0,0,0]] RadecSkies(LaserSkies, AstroSkyTime) @@ -612,7 +617,7 @@ try: osc_method("/ping*", lj3.OSCping) osc_method("/quit", OSCquit) - WebStatus("Load Cities.") + WebStatus("Loading Cities...") ts = load.timescale() LoadCities() @@ -620,17 +625,17 @@ try: SkyCountryCode = 'FR' WebStatus(SkyCity) - WebStatus("Solar System..") + WebStatus("Loading Solar System...") LoadSolar() - WebStatus("Observer..") + WebStatus("Finding observer position..") InitObserver(SkyCity, SkyCountryCode, Time.now(),ts) - WebStatus("Load Stars..") + WebStatus("Loading Stars...") LoadHipparcos(ts) StarSelect() - WebStatus("Updating...") + WebStatus("Computing observer skies") print("Updating solar system (de421) objects position for observer at", Skylat, Skylong, "time", SkyfieldTime.utc_iso()) UpdateSolar() diff --git a/plugins/simu.py b/plugins/pysimu.py similarity index 100% rename from plugins/simu.py rename to plugins/pysimu.py diff --git a/plugins/textcycl.py b/plugins/textcycl.py index cb140d6..4ac34e6 100644 --- a/plugins/textcycl.py +++ b/plugins/textcycl.py @@ -74,14 +74,22 @@ def rgb2int(r,g,b): def WebStatus(message): + lj3.SendLJ("/status",message) def OSCljclient(value): - # Will receive message address, and message data flattened in s, x, y - print("I got /cycl/ljclient with value", value) + + print("Cycl got /cycl/ljclient with value", value) + lj3.WebStatus("Cycl to virtual "+ str(value)) ljclient = value lj3.LjClient(ljclient) +def OSCpl(value): + + print("Cycl got /cycl/pl with value", value) + lj3.WebStatus("Cycl to pl "+ str(value)) + lj3.LjPl(value) + # /ping def OSCping(): @@ -91,15 +99,15 @@ def OSCping(): # /quit def OSCquit(): - lj3.OSCquit("Cycl") + lj3.OSCquit("cycl") osc_startup() osc_udp_server("127.0.0.1", OSCinPort, "InPort") osc_method("/ping*", OSCping) osc_method("/quit", OSCquit) -osc_method("/cycl/ljclient", OSCljclient) - +osc_method("/cycl/ljclient*", OSCljclient) +osc_method("/cycl/pl*", OSCpl) WebStatus("Textcycl Ready") lj3.SendLJ("/cycl/start 1") diff --git a/webui/LJ.js b/webui/LJ.js index 226e361..5b3de27 100644 --- a/webui/LJ.js +++ b/webui/LJ.js @@ -80,6 +80,8 @@ x.style.display = "none"; var x = document.getElementById("vjUI"); x.style.display = "none"; + var x = document.getElementById("poseUI"); + x.style.display = "none"; var x = document.getElementById("wordsUI"); x.style.display = "none"; var x = document.getElementById("pluginsUI"); @@ -119,6 +121,15 @@ _WS.send("/bank0/ping"); } + + function showposeUI() { + nosimuUI(); + var x = document.getElementById("poseUI"); + x.style.display = "grid"; + _WS.send("/pose/ping"); + } + + function showwordsUI() { nosimuUI(); var x = document.getElementById("wordsUI"); @@ -154,9 +165,12 @@ if (clicked_id === "lissa/lissaUI") { showlissaUI(); } - if (clicked_id === "vj/vjUI") { + if (clicked_id === "bank0/vjUI") { showvjUI(); } + if (clicked_id === "pose/poseUI") { + showposeUI(); + } if (clicked_id === "words/wordsUI") { showwordsUI(); } @@ -238,6 +252,7 @@ //console.log("plpoint"); break; default: + //console.log(e); document.getElementById(res[0].slice(1)).value = res[1]; _WS.showin(e.data); } diff --git a/webui/LJgrid.css b/webui/LJgrid.css index ed820fb..cb93f9c 100644 --- a/webui/LJgrid.css +++ b/webui/LJgrid.css @@ -69,6 +69,22 @@ transition: all .3s ease; background-color: #151515; } +.posebuttons { + display: none; + height: 400px; + width: 400px; + grid-template-columns: 66px 66px 66px 66px 66px 66px; + grid-template-rows: 30px 67px 67px 67px 30px; + background-color: #000; + justify-items: center; + align-items: center; + border-color: #445; + border-style: groove; + border-width: 1px; + grid-gap: 1px; + transition: all .3s ease; + background-color: #151515; +} .mglive { display: none; height: 400px; diff --git a/webui/index.html b/webui/index.html index 1827498..94a42ee 100644 --- a/webui/index.html +++ b/webui/index.html @@ -1,10 +1,10 @@ + - @@ -26,8 +26,6 @@ - - @@ -55,9 +53,7 @@
- - - +
@@ -65,29 +61,32 @@
S
C
+
0
+
1
+
2
- +
3
- +
Emergy Black
@@ -142,20 +141,18 @@
+ -
-
-
- -
+
+ Virtual + PL - - - - -
-
+ +
+
+ + + + + +
+
+
@@ -203,6 +207,7 @@
+
@@ -221,38 +226,46 @@
+
- -
+ +
kPPS
-
Points
-
-
+
Points
+
+
+ +
Offset X
-
Offset Y
+
Offset Y
-
-
+
+
+ +
Scale X
Scale Y
-
-
-
+
+
+ + +
Angle
Intens.
+
@@ -275,30 +288,39 @@
+
-
-
+ + +
+
kPPS
-
Points
-
-
+
Points
+
+
+ +
Offset X
Offset Y
-
-
+
+
+ +
Scale X
Scale Y
-
-
+
+
+ +
Angle
@@ -306,6 +328,7 @@
+
@@ -330,31 +353,36 @@
-
+ + +
kPPS
Points
+
+
-
-
+
Offset X
Offset Y
-
+ +
Scale X
Scale Y
- -
+
+ +
Angle
@@ -362,6 +390,7 @@
+
@@ -386,31 +415,36 @@
+ +
kPPS
Points
-
-
+
+ +
Offset X
Offset Y
- -
-
+
+
+ +
Scale X
Scale Y
- -
-
+
+
+ +
Angle
@@ -421,7 +455,6 @@
-
@@ -436,30 +469,34 @@
With AI
+ +
Velocity
Express.
-
+ +
Sens.
Beauty
-
+ +
CC 1
-
CC 2
+
CC 2
@@ -470,22 +507,27 @@
+
Select X
-
Select Y
-
-
+
Select Y
+
+
+ +
-
FOV
-
Dist
-
-
+
FOV
+
Dist
+
+
+ +
@@ -521,43 +563,49 @@ -->
- + +
- -
+ - +
+ -
+ + Country (FR) City
+ +
Date/Time (2012-7-12 23:00:00)
+ +
Laser 0 : Alt @@ -566,6 +614,8 @@ Angle
+ +
Laser 1 : Alt @@ -574,6 +624,8 @@ Angle
+ +
Laser 2 : Alt @@ -582,6 +634,8 @@ Angle
+ +
Laser 3 : Alt @@ -592,42 +646,48 @@
- + +