diff --git a/LJ.conf b/LJ.conf index ee32cb2..69d6b6f 100644 --- a/LJ.conf +++ b/LJ.conf @@ -1,5 +1,5 @@ [General] -lasernumber = 1 +lasernumber = 4 debug = 0 ljayserverip = 0.0.0.0 wwwip = 192.168.2.43 @@ -13,13 +13,13 @@ type = DS1000 ip = 192.168.2.4 kpps = 25000 centerx = 0 -centery = 0 +centery = 765 zoomx = 45.0 zoomy = 45.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.], @@ -32,20 +32,20 @@ color = -1 type = LOCAL ip = 192.168.2.43 kpps = 25000 -centerx = 0 -centery = 0 -zoomx = 45.0 -zoomy = 45.0 +centerx = -11970 +centery = -6510 +zoomx = 30.0 +zoomy = 30.0 sizex = 32000 sizey = 32000 -finangle = -30.0 +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.]] + [ 1500., 1500.], + [ 1500.,-1500.], + [-1500.,-1500.]] [laser2] color = -1 @@ -63,14 +63,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.1.5 +ip = 192.168.2.3 kpps = 25000 centerx = 0 centery = 0 @@ -83,19 +83,13 @@ 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 = { - "trckr": {"OSC": 8017, "command": "python3 plugins/VJing/trckr.py", "display": True}, "aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True}, - "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}, "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}, @@ -103,6 +97,11 @@ plugins = { "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}, diff --git a/LJ_template..conf b/LJ_template..conf index 7e839ef..fa413f6 100644 --- a/LJ_template..conf +++ b/LJ_template..conf @@ -34,11 +34,11 @@ ip = 192.168.2.43 kpps = 25000 centerx = 0 centery = 0 -zoomx = 45.0 -zoomy = 45.0 +zoomx = 30.0 +zoomy = 30.0 sizex = 32000 sizey = 32000 -finangle = -30.0 +finangle = 0 swapx = -1 swapy = -1 lsteps = [ (1.0, 2),(0.25, 1), (0.75, 1), (1.0, 5)] @@ -89,13 +89,7 @@ warpdest = [[-1500., 1500.], [plugins] plugins = { - "trckr": {"OSC": 8017, "command": "python3 plugins/VJing/trckr.py", "display": True}, "aurora": {"OSC": 8090, "command": "python3 plugins/aurora/aurora.py", "display": True}, - "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}, "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}, @@ -103,6 +97,11 @@ plugins = { "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}, diff --git a/README.md b/README.md index 95eca6f..ee8e4ff 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Important : for best performance LJ is meant to run in a dedicated computer espe - Some Lasermapping ('alignment') like in videomapping. - OSC and websocket commands. Very cool : LJ can script or be scripted. - Python3 -- Web User Interface in your browser : open www/index.html. Javascript is needed. By default it connect to localhost. If you want to control remotely, you need to change the uri line in LJ.js. +- Web User Interface in your browser : open www/index.html. Javascript is needed. By default it connect to localhost. If you want to control remotely, you need edit webui choice : python3 configure.py - Live WebUI extras : change debug level, restart plugin, rescan DACs,... - Status update every 0.5 seconds : every etherdream DAC state, number of buffer points sent,... - "Optimisation" points automatically added, can be changed live for glitch art. Search "resampler" commands. @@ -260,10 +260,14 @@ DrawDests() will take care of all your declared drawn elements/"objects" and Des # Nannou etherdeam simulator aka visualiser # -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. +Nannou visualiser kind of emulate an etherdream and display in a window what a real laser draws. + +2 compiled nannou visualisers are included, one for Linux, one for macOS. It's pretty old versions but much more compatible with "old" processors/computer, as you may want to repurpose an old computer to run LJ. 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. +Nannou visualiser emulation is better and better but one can find a few known non-working situations. See it's github repository (https://github.com/nannou-org/ether-dream/tree/master/dac-emulator) for more recent versions. + # # Todo # @@ -349,9 +353,9 @@ About hardware setup, especially if you have several lasers : ILDA cables are in # Ethertools directory # -2 useful and always working tools from j4cdac github repository : sitter and talk -- Sitter will display all real etherdreams available on the network and their state (playing, idle,...). python sitter.py or use the compiled version (for macOS). -- Talk : will draw a 4 colors square. python3 talk3.py +2 useful and *always working tools* from j4cdac github repository : sitter and talk. +- Sitter will display all etherdreams available on the network and their state (playing, idle,...). python sitter.py or use the compiled version (for macOS). May need tkinter : pip3 install tk +- Talk : will draw a 4 colors square. Try : python3 talk3.py -h # # Links @@ -367,6 +371,8 @@ Generic : ![Laser Faq](https://www.repairfaq.org/sam/lasersam.htm) +![Etherdream protocol](https://ether-dream.com/protocol.html) + There is a nice websocket debug tool : websocat. # diff --git a/clitools/README.md b/clitools/README.md index 407050a..b69e4a5 100644 --- a/clitools/README.md +++ b/clitools/README.md @@ -9,7 +9,7 @@ BOOM | WIIIIIZ :: PHHHHHRACKRACKRACK ~~ WOOP ~~###~~ WIIT ## The basic loop ``` -python3 generators/dummy.py -f 2 | filters/kaleidoscope.py | exports/toRedis.py -v +python3 generators/dummy.py -f 2 | python3 filters/kaleidoscope.py | python3 exports/toRedis.py -v ------------------------------ --------------------- ------------------- \/ \/ \/ Generator Filter Export diff --git a/ethertools/dac3.py b/ethertools/dac3.py index 325672b..3c50adc 100755 --- a/ethertools/dac3.py +++ b/ethertools/dac3.py @@ -165,7 +165,7 @@ class DAC(object): return self.readresp("d") def prepare(self): - self.conn.sendall('p') + self.conn.sendall(b'p') return self.readresp('p') def stop(self): diff --git a/ethertools/talk3.py b/ethertools/talk3.py index 183e613..b841124 100755 --- a/ethertools/talk3.py +++ b/ethertools/talk3.py @@ -1,4 +1,17 @@ #!/usr/bin/env python + +''' + +Improved dac.py and talk.py (by Jacob Potter). Pimped by Sam Neurohack. + +- python3 +- works with nannou visualisers +- can talk to a given etherdream knowing it's IP. + +v0.1.0 + +''' + # # j4cDAC test code # @@ -17,6 +30,19 @@ # along with this program. If not, see . import dac3 as dac +import argparse +import sys + +argsparser = argparse.ArgumentParser(description="Draw a square on a laser via Etherdream DAC") + +argsparser.add_argument("-i","--ip",help="Etherdream IP (default : first etherdream broadcast received",default="True",type=str) + + + +args = argsparser.parse_args() +etherIP = args.ip + + class SquarePointStream(object): ''' @@ -88,8 +114,14 @@ class NullPointStream(object): return [(0, 0, 0, 0, 0)] * n #dac.find_dac() +if etherIP == "True": + print("Waiting for the first DAC broadcast...") + d = dac.DAC(dac.find_first_dac()) -d = dac.DAC(dac.find_first_dac()) -#d = dac.DAC("192.168.1.43") +else: + print("Using Etherdream :", etherIP) + d = dac.DAC(etherIP) + +print("Sending points...") d.play_stream(SquarePointStream()) diff --git a/plugins/custom1.py b/plugins/custom1.py index 75506c3..3ee5e6f 100644 --- a/plugins/custom1.py +++ b/plugins/custom1.py @@ -25,15 +25,15 @@ ljpath = r'%s' % os.getcwd().replace('\\','/') # import from shell -sys.path.append(ljpath +'/../libs/') +sys.path.append(ljpath +'/../libs3/') #import from LJ -sys.path.append(ljpath +'/libs/') -print(ljpath+'/../libs/') +sys.path.append(ljpath +'/libs3/') +print(ljpath+'/../libs3/') import lj23layers as lj -sys.path.append('../libs') +sys.path.append('../libs3') from OSC3 import OSCServer, OSCClient, OSCMessage import redis import math diff --git a/plugins/square.py b/plugins/square.py index a9f6c34..9b48d3f 100644 --- a/plugins/square.py +++ b/plugins/square.py @@ -25,15 +25,15 @@ ljpath = r'%s' % os.getcwd().replace('\\','/') # import from shell -sys.path.append(ljpath +'/../libs/') +sys.path.append(ljpath +'/../libs3/') #import from LJ -sys.path.append(ljpath +'/libs/') -print(ljpath+'/../libs/') +sys.path.append(ljpath +'/libs3/') +print(ljpath+'/../libs3/') import lj23layers as lj -sys.path.append('../libs') +sys.path.append('../libs3') from OSC3 import OSCServer, OSCClient, OSCMessage import redis import math diff --git a/plugins/trckr.py b/plugins/trckr.py new file mode 100644 index 0000000..fa449cc --- /dev/null +++ b/plugins/trckr.py @@ -0,0 +1,647 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + + +''' + +trckr +Work with clmtrackr-dev and www/trckr/trckr.html +v0.1.0 + + +LICENCE : CC +by Sam Neurohack + +To test the examples locally, you need to run a local server. +One easy way to do this is to install http-server, a small node.js utility: +npm install -g http-server. + +Then run http-server in the root of clmtrackr and go to + +- read a video : +http://localhost:8080/examples/trckr.html +- webcam + + +clmtrackr : +https://github.com/auduno/clmtrackr/tree/dev/examples + + +''' +import sys +import os +print() +ljpath = r'%s' % os.getcwd().replace('\\','/') + +# import from shell +sys.path.append(ljpath +'/../libs3/') +#import from LJ +import log + +print ("") + +log.infog("Trckr Plugin v0.1") + + +sys.path.append('../libs3') +sys.path.append(ljpath +'/../../libs3') + +from OSC3 import OSCServer, OSCClient, OSCMessage +import lj23layers as lj +import redis +import math +import time +import argparse + +OSCinPort = 8017 + +print ("") +print ("Arguments parsing if needed...") +argsparser = argparse.ArgumentParser(description="trckr example 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("-s","--scene",help="LJ scene number (0 by default)",type=int) +#argsparser.add_argument("-l","--laser",help="Laser number to be displayed (0 by default)",type=int) +argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int) +argsparser.add_argument("-m","--myIP",help="Local IP (127.0.0.1 by default) ",type=str) + +args = argsparser.parse_args() + +if args.scene: + ljscene = args.scene +else: + ljscene = 0 +''' +if args.laser: + plnumber = args.laser +else: + plnumber = 0 +''' + +# Redis Computer IP +if args.redisIP != None: + redisIP = args.redisIP +else: + redisIP = '127.0.0.1' + +print("redisIP",redisIP) + +# myIP +if args.myIP != None: + myIP = args.myIP +else: + myIP = '127.0.0.1' + +print("myIP",myIP) + +if args.verbose: + debug = args.verbose +else: + debug = 0 + +# Useful variables init. +white = lj.rgb2int(255,255,255) +red = lj.rgb2int(255,0,0) +blue = lj.rgb2int(0,0,255) +green = lj.rgb2int(0,255,0) + +# 3D to 2D projection parameters +fov = 256 +viewer_distance = 2.2 + +width = 800 +height = 600 +centerX = width / 2 +centerY = height / 2 + +TrckrPts = [[0,0]] * 70 + + + +# +# LJ inits +# + +layer = 0 + +# Setup LJ library mandatory properties for this layerugin +lj.Config(redisIP, ljscene, "trckr") + +# Define properties for each drawn "element" : name, intensity, active, xy, color, red, green, blue, layer , closed +FaceForm = lj.FixedObject('Face', True, 255, [], red, 255, 0, 0, layer , False) + +# 'Destination' for given layer : name, number, active, layer , scene, laser +Dest0 = lj.DestObject('0', 0, True, 0 , 0, 0) # Dest0 will send layer 0 points to scene 0, laser 0 + + + +# +# OSC +# + +oscserver = OSCServer( (myIP, OSCinPort) ) +oscserver.timeout = 0 + + +# 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) + + +# OSC callbacks + +# /trckr/ljscene +def OSCljscene(path, tags, args, source): + + print("Got /trckr/ljscene with value", args[0]) + lj.WebStatus("trckr to virtual "+ str(args[0])) + ljscene = args[0] + lj.Ljscene(ljscene) + +# /trckr/layer +def OSClayer(path, tags, args, source): + + print() + print() + print("Got /trckr/layer with value", args[0]) + lj.WebStatus("trckr to layer "+ str(args[0])) + FaceForm.layer = int(args[0]) + Dest0.layer = int(args[0]) + Dest0.laser = int(args[0]) + +# /trckr/frame +def OSCtrckr(path, tags, args, source): + global TrckrPts + + #print("trckr got frame", args[0]) + if debug != 0: + print("trckr plugin got frame", args[0]) + print(len(args),"args", args) + counter =0 + TrckrPts = [] + + for dot in range(1,len(args)-1,2): + + TrckrPts.append([float(args[dot]), float(args[dot+1])]) + print(TrckrPts) + +# /trckr/frame layernumber framenumber points +def OSCTrckrframe(path, tags, args, source): + global TrckrPts + + if debug != 0: + print("trckr plugin got frame", args[1], "for layer", args[0], "with path", path) + print(len(args),"args", args) + + TrckrPts[args[0]] = [] + + for dot in range(2,len(args)-1,2): + TrckrPts[args[0]].append([float(args[dot]), float(args[dot+1])]) + + + + +#/trckr/color/0 red ? +def OSColor(): + global FaceForm + + if debug != 0: + print("trckr color got ", args[1], "for layer", args[0], "with path", path) + print(len(args),"args", args) + FaceForm.color = args[1] + + +# default handler +def OSChandler(path, tags, args, source): + + oscaddress = ''.join(path.split("/")) + print() + print("trckr default OSC Handler :", path, "from Client :" + str(source[0])) + + +# +# Face Tracking +# + +# get absolute face position points +def getPART(pose_points): + + dots = [] + for dot in pose_points: + + dots.append((TrckrPts[dot][0], TrckrPts[dot][1],0)) + + return dots + + +# Face keypoints +def face(): + pose_points = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] + return getPART(pose_points) + +def browL(): + pose_points = [15,16,17,18] + return getPART(pose_points) + +def browR(): + pose_points = [22,21,20,19] + return getPART(pose_points) + +def eyeR(): + pose_points = [25,64,24,63,23,66,26,65,25] + return getPART(pose_points) + +def eyeL(): + pose_points = [28,67,29,68,30,69,31,28] + return getPART(pose_points) + +def pupR(): + pose_points = [27] + return getPART(pose_points) + +def pupL(): + pose_points = [32] + return getPART(pose_points) + + +def nose1(): + pose_points = [62,41,33] + return getPART(pose_points) + +def nose2(): + pose_points = [40,39,38,43,37,42,36,35,34] + return getPART(pose_points) + +def mouth(): + pose_points = [50,49,48,47,46,45,44,55,54,53,52,51,50] + return getPART(pose_points) + +def mouthfull(): + pose_points = [50,49,48,47,46,45,44,55,54,53,52,51,50,59,60,61,44,56,57,58,50] + return getPART(pose_points) + + + + +def Proj(x,y,z,angleX,angleY,angleZ): + + rad = angleX * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + y2 = y + y = y2 * cosa - z * sina + z = y2 * sina + z * cosa + + rad = angleY * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + z2 = z + z = z2 * cosa - x * sina + x = z2 * sina + x * cosa + + rad = angleZ * math.pi / 180 + cosa = math.cos(rad) + sina = math.sin(rad) + x2 = x + x = x2 * cosa - y * sina + y = x2 * sina + y * cosa + + + """ Transforms this 3D point to 2D using a perspective projection. """ + factor = fov / (viewer_distance + z) + x = x * factor + centerX + y = - y * factor + centerY + return (x,y) + + +# +# Main +# + +def Run(): + + + Facepts = [] + counter =0 + lj.WebStatus("trckr") + lj.SendLJ("/trckr/start 1") + + # OSC Server callbacks + print("Starting OSC server at",myIP," port",OSCinPort,"...") + oscserver.addMsgHandler( "/trckr/ljscene", OSCljscene) + oscserver.addMsgHandler( "/trckr/layer", OSClayer) + oscserver.addMsgHandler( "/trckr/frame", OSCtrckr) + oscserver.addMsgHandler( "/trckr/color", OSColor) + + + # Add OSC generic layerugins commands : 'default", /ping, /quit, /pluginame/obj, /pluginame/var, /pluginame/adddest, /pluginame/deldest + lj.addOSCdefaults(oscserver) + oscserver.addMsgHandler( "default", OSChandler ) + try: + + while lj.oscrun: + + lj.OSCframe() + + #print("browL", browL(), "browR", browR(), "nose1", nose1(), "mouth", mouth()) + lj.rPolyLineOneColor(browL(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(eyeL(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(browR(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(eyeR(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(pupL(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(pupR(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(nose1(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(nose2(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + lj.rPolyLineOneColor(mouthfull(), c = FaceForm.color, layer = FaceForm.layer, closed = FaceForm.closed, xpos = -300, ypos = -300, resize = 3.6, rotx = 0, roty = 0, rotz = 0) + + lj.DrawDests() + + time.sleep(0.005) + + counter += 1 + if counter > 360: + counter = 0 + + except KeyboardInterrupt: + pass + + # Gently stop on CTRL C + + finally: + + lj.ClosePlugin() + + +Run() +''' +// +// a template for receiving face tracking osc messages from +// Kyle McDonald's trckr https://github.com/kylemcdonald/ofxFaceTracker +// +// 2012 Dan Wilcox danomatika.com +// for the IACD Spring 2012 class at the CMU School of Art +// +// adapted from from Greg Borenstein's 2011 example +// http://www.gregborenstein.com/ +// https://gist.github.com/1603230 +// + +//Xavier Apostol +//Generative Faces: Plotter Project Concept + +import oscP5.*; +OscP5 oscP5; + +// num faces found +int found; + +// pose +float poseScale; +PVector posePosition = new PVector(); +PVector poseOrientation = new PVector(); + +// gesture +float mouthHeight; +float mouthWidth; +float eyeLeft; +float eyeRight; +float eyebrowLeft; +float eyebrowRight; +float jaw; +float nostrils; + +float sz = 1; +float spacing = 100; +float genSz = spacing/4; +float fcOff = genSz/2; + +//Initialization of Colors +float R = random(255); +float G = random(255); +float B = random(255); + +//Initialization of Head +float rotInt = 15; +float hdX = cos(sz) + random(genSz, 3*genSz); +float hdY = sin(sz) + random(genSz, 3*genSz); +float rotAngle = random(-rotInt,rotInt); + +//Initialization of Eyes +float lEyeX1 = sin(sz*0) + random(genSz); +float lEyeY1 = cos(sz*0) + random(genSz); +float rEyeX1 = sin(sz*0) + random(genSz); +float rEyeY1 = cos(sz*0) + random(genSz); +float lEyeX2 = sin(sz*1) + random(genSz); +float lEyeY2 = cos(sz*1) + random(genSz); +float rEyeX2 = sin(sz*1) + random(genSz); +float rEyeY2 = cos(sz*1) + random(genSz); +float ranREye = random(7, 9); +float ranLEye = random(7, 9); + +//Initialization of Mouth +float mthX = cos(sz) + random(genSz); +float mthY = sin(sz) + random(genSz); +float ranM = random(-0.1, 1.5); + +//Initialization of Spine +float hdOffset = hdY/1.5; +float spineSz = random(genSz/2); +float spXOff1 = random(-8, 8); +float spYOff1 = hdOffset + random(genSz/3); +float spXOff2 = random(-8, 8)+spXOff1; +float spYOff2 = random(genSz/3)+spYOff1; +float spXOff3 = random(-8, 8)+spXOff2; +float spYOff3 = random(genSz/3)+spYOff2; +float spXOff4 = random(-8, 8)+spXOff3; +float spYOff4 = random(genSz/3)+spYOff3; +float spXOff5 = random(-8, 8)+spXOff4; +float spYOff5 = random(genSz/3)+spYOff4; + +void setup() { + size(800, 600, OPENGL); + frameRate(30); + + oscP5 = new OscP5(this, 8338); + oscP5.plug(this, "found", "/found"); + oscP5.plug(this, "poseScale", "/pose/scale"); + oscP5.plug(this, "posePosition", "/pose/position"); + oscP5.plug(this, "poseOrientation", "/pose/orientation"); + oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width"); + oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height"); + oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left"); + oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right"); + oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left"); + oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right"); + oscP5.plug(this, "jawReceived", "/gesture/jaw"); + oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils"); +} + +void keyPressed() { + if (key == CODED) { + if (keyCode == UP) { + //Create an entirely new character. + + //For Eyes + lEyeX1 = sin(sz*0) + random(genSz); + lEyeY1 = cos(sz*0) + random(genSz); + rEyeX1 = sin(sz*0) + random(genSz); + rEyeY1 = cos(sz*0) + random(genSz); + lEyeX2 = sin(sz*1) + random(genSz); + lEyeY2 = cos(sz*1) + random(genSz); + rEyeX2 = sin(sz*1) + random(genSz); + rEyeY2 = cos(sz*1) + random(genSz); + ranREye = random(7, 9); + ranLEye = random(7, 9); + + //For Mouth + mthX = cos(sz) + random(genSz); + mthY = sin(sz) + random(genSz); + ranM = random(-0.1, 1.5); + + //For Spine + spineSz = random(genSz/2); + spXOff1 = random(-8, 8); + spYOff1 = hdOffset + random(genSz/3); + spXOff2 = random(-8, 8) + spXOff1; + spYOff2 = random(genSz/3) + spYOff1; + spXOff3 = random(-8, 8) + spXOff2; + spYOff3 = random(genSz/3) + spYOff2; + spXOff4 = random(-8, 8) + spXOff3; + spYOff4 = random(genSz/3) + spYOff3; + spXOff5 = random(-8, 8) + spXOff4; + spYOff5 = random(genSz/3) + spYOff4; + + //For Head + hdX = cos(sz) + random(genSz, 3*genSz); + hdY = sin(sz) + random(genSz, 3*genSz); + rotAngle = random(-rotInt,rotInt); + + //For Colors + R = random(255); + G = random(255); + B = random(255); + draw(); + } + } +} + +void draw() { + background(0); + strokeWeight(1); + noFill(); + + if(found != 0) { + pushMatrix(); + translate(posePosition.x, posePosition.y); + //Scales head and allows for rotations + scale(poseScale*2); + rotateY(0 - poseOrientation.y); + rotateX(0 - poseOrientation.x); + rotateZ(poseOrientation.z); + rotate(radians(rotAngle)); + ellipse(0,0, hdX,hdY); + popMatrix(); + + //FACE + translate(posePosition.x, posePosition.y); + scale(poseScale); + noFill(); + + //Eyes + float eyeFac = 1; + float eyeBL = eyebrowLeft * 2; + float eyeBR = eyebrowRight * 2; + ellipse(-20,eyeLeft * -ranLEye, lEyeX1*eyeFac + eyeBL,lEyeY1*eyeFac + eyeBL); + ellipse(20,eyeRight * -ranREye, rEyeX1*eyeFac + eyeBR,rEyeY1*eyeFac + eyeBR); + ellipse(-20,eyeLeft * -ranLEye, lEyeX2*eyeFac + eyeBL,lEyeY2*eyeFac + eyeBL); + ellipse(20,eyeRight * -ranREye, rEyeX2*eyeFac + eyeBR,rEyeY2*eyeFac + eyeBR); + + //Mouth + ellipse(0, 20*ranM, mouthWidth* mthX/3, mouthHeight * mthY); + + //BODY/BUBBLES + stroke(R,G,B); + ellipse(spXOff1,spYOff1, spineSz,spineSz); + ellipse(spXOff2,spYOff2, spineSz,spineSz); + ellipse(spXOff3,spYOff3, spineSz,spineSz); + ellipse(spXOff4,spYOff4, spineSz,spineSz); + ellipse(spXOff5,spYOff5, spineSz,spineSz); + + } +} + +// OSC CALLBACK FUNCTIONS + +public void found(int i) { + println("found: " + i); + found = i; +} + +public void poseScale(float s) { + println("scale: " + s); + poseScale = s; +} + +public void posePosition(float x, float y) { + println("pose position\tX: " + x + " Y: " + y ); + posePosition.set(x, y, 0); +} + +public void poseOrientation(float x, float y, float z) { + println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z); + poseOrientation.set(x, y, z); +} + +public void mouthWidthReceived(float w) { + println("mouth Width: " + w); + mouthWidth = w; +} + +public void mouthHeightReceived(float h) { + println("mouth height: " + h); + mouthHeight = h; +} + +public void eyeLeftReceived(float f) { + println("eye left: " + f); + eyeLeft = f; +} + +public void eyeRightReceived(float f) { + println("eye right: " + f); + eyeRight = f; +} + +public void eyebrowLeftReceived(float f) { + println("eyebrow left: " + f); + eyebrowLeft = f; +} + +public void eyebrowRightReceived(float f) { + println("eyebrow right: " + f); + eyebrowRight = f; +} + +public void jawReceived(float f) { + println("jaw: " + f); + jaw = f; +} + +public void nostrilsReceived(float f) { + println("nostrils: " + f); + nostrils = f; +} + +// all other OSC messages end up here +void oscEvent(OscMessage m) { + if(m.isPlugged() == false) { + println("UNPLUGGED: " + m); + } +} +''' diff --git a/server/install.sh b/server/install.sh index 76e317b..3f2e34b 100755 --- a/server/install.sh +++ b/server/install.sh @@ -25,6 +25,7 @@ sudo apt install libjack-dev pip3 install python-rtmidi pip3 install mido git clone https://github.com/ptone/pyosc --depth 1 /tmp/pyosc && cd /tmp/pyosc && sudo ./setup.py install +pip3 install tk cd ../ python3 configure.py # todo : ask for computer ip and run updateUI.py diff --git a/server/install.sh.darwin b/server/install.sh.darwin index e0e3165..63edb84 100644 --- a/server/install.sh.darwin +++ b/server/install.sh.darwin @@ -21,7 +21,7 @@ brew install libasound2-dev brew install libjack-dev pip3 install python-rtmidi pip3 install mido - +#pip3 install tk cd ../ python3 configure.py #sudo cp syncthing.conf to /etc/supervisor/conf.d/ diff --git a/updateUI.py b/updateUI.py index e2f2a90..ee985dd 100644 --- a/updateUI.py +++ b/updateUI.py @@ -60,6 +60,7 @@ def www(wwwip): print("Updating www files to use", wwwIP) Updatepage(ljpath+"/www/LJ.js") Updatepage(ljpath+"/www/trckr/trckrcam1.html") + Updatepage(ljpath+"/www/trckr/trckr.html") Updatepage(ljpath+"/www/simu.html") Updatepage(ljpath+"/www/align.html") Updatepage(ljpath+"/www/auralls.html") diff --git a/www/README.md b/www/README.md index f3ed726..22d19eb 100644 --- a/www/README.md +++ b/www/README.md @@ -1,5 +1,21 @@ -clmtrackr -====== += Web Interface + +You can load index.html file from your browser or have a webserver of your choice pointing to this directory. + +Webserver is mandatory if you want : + +- to remotely control LJ : imagine LJ can be installed in a dedicated computer/container with no easy access. +- to use the face tracking, say from a smartphone. That's Lasercam (a clmtrackr plugin). + +== Simu + +A laser simulator. Choose lasernumber and it will display redis points for current scene/lasernumber + + + + +== clmtrackr + [![npm version](https://img.shields.io/npm/v/clmtrackr.svg)](https://www.npmjs.com/package/clmtrackr) diff --git a/www/index.html b/www/index.html index 3101372..3196027 100644 --- a/www/index.html +++ b/www/index.html @@ -300,16 +300,6 @@ 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: diff --git a/www/simu.html b/www/simu.html index e2d0c5c..95ae133 100644 --- a/www/simu.html +++ b/www/simu.html @@ -287,17 +287,6 @@ case "/plpoi": //console.log("plpoint"); 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]; diff --git a/www/trckr.png b/www/trckr.png deleted file mode 100644 index a0a1443..0000000 Binary files a/www/trckr.png and /dev/null differ diff --git a/www/trckr/trckr.html b/www/trckr/trckr.html new file mode 100644 index 0000000..e630dcc --- /dev/null +++ b/www/trckr/trckr.html @@ -0,0 +1,679 @@ + + + + LASERCam 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ + + + +
+
+
+

+ /TL RGY 1 +   +

+ + +
+
+
+ LASERcam 1 : Allow to use your webcam + start +
+
+ /team/laser +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+

+ Colors + +

+
+ +
+
+
+
+
+
+
+
+
+ +
+
+ + + +
+ + + +
+ + + +
+
+
+

To try it out: +

    +
  1. Allow the page to use your webcamera
  2. +
  3. Make sure that your face is clearly visible in the video, and click start
  4. +
  5. See the model fitted to your face
  6. +
      +

      +
+
+
+
+
+
+ + + + + + + + + + + + + + + +