diff --git a/generators/dummy.py b/generators/dummy.py index 497dd26..fd12ed8 100755 --- a/generators/dummy.py +++ b/generators/dummy.py @@ -21,6 +21,10 @@ from os import path, getcwd abspath, filename = path.split(path.realpath(__file__ )) sys.path.insert(0, path.join(abspath,"../lib")) from clitools import Clitools + +name="generator::dummy" + + import argparse argsparser = argparse.ArgumentParser(description="dummy generator") @@ -31,10 +35,11 @@ args = argsparser.parse_args() fps=args.fps verbose=args.verbose +optimal_looptime = 1 / fps cli = Clitools({ "verbose" : verbose, - "looptime" : 1 / fps, - "name" : "generator::dummy" + "looptime" : looptime, + "name" : name }) diff --git a/generators/fromRedis.py b/generators/fromRedis.py index 46111b5..3e77c9a 100755 --- a/generators/fromRedis.py +++ b/generators/fromRedis.py @@ -24,10 +24,7 @@ import json import redis import sys import time -from os import path, getcwd -abspath, filename = path.split(path.realpath(__file__ )) -sys.path.insert(0, path.join(abspath,"../lib")) -from clitools import Clitools +name="generator::fromRedis" def debug(*args, **kwargs): @@ -36,31 +33,28 @@ def debug(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) -argsparser = argparse.ArgumentParser(description="Dummy generator") +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() +args = argsparser.parse_args() fps = args.fps verbose = args.verbose key = args.key ip = args.ip port = args.port -cli = Clitools({ - "verbose" : verbose, - "looptime" : 1 / fps, - "name" : "generator::fromRedis" - }) +optimal_looptime = 1 / fps +debug(name+" optimal looptime "+str(optimal_looptime)) - -r = redis.Redis( host=ip, port=port ) +r = redis.Redis( + host=ip, + port=port) while True: - - cli.startFrame() + start = time.time() # Read from Redis line = r.get(key) # Decode as list of tuples @@ -69,6 +63,10 @@ while True: pointsList = [list(elem) for elem in pointsList] # Convert to JSON string line = json.dumps( pointsList ) - cli.debug(name,"Key:{} line:{}".format(key,line)) + debug(name,"Key:{} line:{}".format(key,line)) print(line, flush=True); - cli.endFrame() + looptime = time.time() - start + if( looptime < optimal_looptime ): + time.sleep( optimal_looptime - looptime) + debug(name+" micro sleep:"+str( optimal_looptime - looptime)) + diff --git a/generators/osc2redis.py b/generators/osc2redis.py new file mode 100644 index 0000000..2e0e5ba --- /dev/null +++ b/generators/osc2redis.py @@ -0,0 +1,136 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' + +Forward pointlist to redis key + +END POINT Format : (x,y,color) + +/pl/0/0 "[(150.0, 230.0, 255), (170.0, 170.0, 255), (230.0, 170.0, 255), (210.0, 230.0, 255), (150.0, 230.0, 255)]" + +v0.1.0 + +Licensed under GNU GPLv3 + +by Cocoa, Sam Neurohack + +''' + +import sys +from os import path, getcwd +abspath, filename = path.split(path.realpath(__file__ )) +sys.path.insert(0, path.join(abspath,"../lib")) +from clitools import Clitools + +from OSC3 import OSCServer, OSCClient, OSCMessage +from time import sleep +import ast +import redis + +import argparse +argsparser = argparse.ArgumentParser(description="osc2redis generator") +argsparser.add_argument("-i","--ip",help="IP to bind to (0.0.0.0 by default)",default="0.0.0.0",type=str) +argsparser.add_argument("-p","--port",help="OSC port to bind to (9002 by default)",default=9002,type=str) + +argsparser.add_argument("-r","--rip",help="Redis server IP (127.0.0.1 by default)",default="127.0.0.1",type=str) +argsparser.add_argument("-o","--rout",help="Redis port (6379 by default)",default=6379,type=str) +argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") +args = argsparser.parse_args() + +verbose = args.verbose +ip = args.ip +port = int(args.port) +rip = args.rip +rport = int(args.rout) + + +r = redis.StrictRedis(host=rip, port=rport, db=0) + + +def debug(msg): + if( verbose == False ): + return + print(msg) + + +oscserver = OSCServer( (ip, port) ) +oscserver.timeout = 0 +run = True + +# this method of reporting timeouts only works by convention +# that before calling handle_request() field .timed_out is +# set to False +def handle_timeout(self): + self.timed_out = True + +# funny python's way to add a method to an instance of a class +import types +oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver) + + +def validate(pointlist): + + state = True + + if len(pointlist)<9: + state = False + + try: + pl = bytes(pointlist, 'ascii') + check = ast.literal_eval(pl.decode('ascii')) + + except: + state = False + + return state + + +# RAW OSC Frame available ? +def OSC_frame(): + # clear timed_out flag + oscserver.timed_out = False + # handle all pending requests then return + while not oscserver.timed_out: + oscserver.handle_request() + + +# default handler +def OSChandler(oscpath, tags, args, source): + + oscaddress = ''.join(oscpath.split("/")) + print("fromOSC Default OSC Handler got oscpath :", oscpath, "from :" + str(source[0]), "args :", args) + print(oscpath.find("/pl/"), len(oscpath)) + + if oscpath.find("/pl/") ==0 and len(args)==1: + + print("correct OSC type :'/pl/") + + if validate(args[0]) == True and len(oscpath) == 7: + print("new pl for key ", oscpath, ":", args[0]) + + if r.set(oscpath,args[0])==True: + debug("exports::redis set("+str(oscpath)+") to "+args[0]) + + else: + print("Bad pointlist -> msg trapped.") + + + else: + print("BAD OSC Message :", oscpath) + +oscserver.addMsgHandler( "default", OSChandler ) + + + + +# simulate a "game engine" +while run: + # do the game stuff: + sleep(0.01) + # call user script + OSC_frame() + +oscserver.close() + diff --git a/generators/particles_example.py b/generators/particles_example.py deleted file mode 100755 index c98564c..0000000 --- a/generators/particles_example.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -# -*- mode: Python -*- - - -''' - -v0.1.0 - -An example particle system reproducing some kind of flock simulation - -Licensed under GNU GPLv3 - -by cocoa - -''' -from __future__ import print_function -import math -import random -import sys -import os -import time -import redis -import ast -import argparse - -from os import path, getcwd -abspath, filename = path.split(path.realpath(__file__ )) -sys.path.insert(0, path.join(abspath,"../lib")) -from clitools import Clitools - - -MAX_WIDTH = 800 -MIN_WIDTH = 0 -MAX_HEIGHT = 800 -MIN_HEIGHT = 0 -MAX_SPEED = 10 -WHITE = 16777215 - - -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") -argsparser.add_argument("-n","--number",help="Particule Number",default=50,type=int) - - -args = argsparser.parse_args() -verbose = args.verbose -fps = args.fps -patricle_number = args.number -scene_width = MAX_WIDTH - MIN_WIDTH -scene_height = MAX_HEIGHT - MIN_HEIGHT -center_x = MIN_WIDTH + scene_width / 2 -center_y = MIN_HEIGHT + scene_height / 2 - -cli = Clitools({ - "verbose" : verbose, - "looptime" : 1 / fps, - "name" : "generator::particles_example" - }) - - -class Particle(object): - - def __init__(self, x = 0, y = 0, color = 0, dx = 0, dy = 0): - self.x = x - self.y = y - self.color = color - # Store speed in 2 dimensions - self.dx = dx - self.dy = dy - - def interact(self, bodies): - """ - Computes particles interactions - This is a good place to calculate distance between particles for example - """ - for other in bodies: - if other is self: - continue - - # For example, we average our speed with the other particle - dist_x = other.x - self.x - dist_y = other.y - self.y - dist = math.sqrt(dist_x*dist_x + dist_y*dist_y) - if dist == 0 : - dist = 1 - if dist > 50 : - continue - if dist > 5 : - factor = math.sqrt(50-dist) -1 - if factor > 1: - factor = 1 - self.dx = 0.5*self.dx + 0.5*((1 -factor)*self.dx + factor*other.dx) + (random.random() - 0.5) * 2 - self.dy = 0.5*self.dy + 0.5*((1 -factor)*self.dy + factor*other.dy) + (random.random() - 0.5) * 2 - else: - # Too close: avoid - self.dx += random.random() - self.dy += random.random() - - def move(self): - """ - Computes how particle move - The basis is to add speed to current position - You can also add friction or compute distance to border in order to slow down - - """ - self.x += self.dx - self.y += self.dy - - - if self.dx > MAX_SPEED: - self.dx = MAX_SPEED - if self.dx < -MAX_SPEED: - self.dx = -MAX_SPEED - if self.dy > MAX_SPEED: - self.dy = MAX_SPEED - if self.dy < -MAX_SPEED: - self.dy = -MAX_SPEED - - - if self.x > MAX_WIDTH: - self.x = MIN_WIDTH - if self.x < MIN_WIDTH: - self.x = MAX_WIDTH - if self.y > MAX_HEIGHT: - self.y = MIN_HEIGHT - if self.y < MIN_HEIGHT: - self.y = MAX_HEIGHT - - - - - -class Simulator(object): - def __init__(self, particles): - """ - Defines the initial particles - """ - - # In this example, we will randomly dispatch a flock - for i in range(0,patricle_number): - x = random.randint(MIN_WIDTH,MAX_WIDTH) - y = random.randint(MIN_HEIGHT,MAX_HEIGHT) - dx = random.randint(-MAX_SPEED, MAX_SPEED) - dy = random.randint(-MAX_SPEED, MAX_SPEED) - color = WHITE - p = Particle(x,y,color,dx=dx,dy=dy) - cli.debug( "init: i:{} dx:{} dy:{} p:{}".format(i,dx,dy,vars(p))) - particles.append( p ) - self.particles = particles - - def redraw(self): - """ - Sends to standard output - A good place to use certain particles properties and draw more / less things - """ - pl = [] - for p in self.particles: - color = p.color - pl.append([p.x+1,p.y+1,0]) - pl.append([p.x+1,p.y+1,color]) - pl.append([p.x,p.y,color]) - - print(pl,flush = True) - - - def tick(self): - for p in self.particles: - p.interact(self.particles) - p.move() - self.redraw() - - -if __name__ == "__main__": - particles = [] - simulator = Simulator(particles) - try: - while True: - cli.startFrame() - simulator.tick() - cli.endFrame() - - except KeyboardInterrupt: - pass diff --git a/generators/text.py b/generators/text.py index 9a55559..2afc3c5 100644 --- a/generators/text.py +++ b/generators/text.py @@ -4,7 +4,9 @@ ''' -Static text writer +Experimental Laserized Turtle graphics library + +See turtle1.py for example pip3 install Hershey-Fonts @@ -27,38 +29,48 @@ import time import argparse import sys from HersheyFonts import HersheyFonts -from os import path, getcwd -abspath, filename = path.split(path.realpath(__file__ )) -sys.path.insert(0, path.join(abspath,"../lib")) -from clitools import Clitools + +name="generator::text" -argsparser = argparse.ArgumentParser(description="Text generator") +def debug(*args, **kwargs): + if( verbose == False ): + return + print(*args, file=sys.stderr, **kwargs) + + +argsparser = argparse.ArgumentParser(description="Text generator") argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int) argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") argsparser.add_argument("-t","--text",help="Text to display",default="hello",type=str) -argsparser.add_argument("-p","--police",help="Herschey font to use. One of ({})".format(", ".join(HersheyFonts().default_font_names)),default="futural",type=str) -args = argsparser.parse_args() -fps = args.fps -verbose = args.verbose -text = args.text -fontname = args.police +argsparser.add_argument("-p","--police",help="Herschey font to use",default="futural",type=str) +args = argsparser.parse_args() -cli = Clitools({ - "verbose" : verbose, - "looptime" : 1 / fps, - "name" : "generator::text" - }) +fps=args.fps +verbose=args.verbose +text = args.text +fontname = args.police + +optimal_looptime = 1 / fps +debug(name+" optimal looptime "+str(optimal_looptime)) + +def rgb2int(rgb): + return int('0x%02x%02x%02x' % tuple(rgb),0) # Useful variables init. -white = cli.rgb2int((255,255,255)) -red = cli.rgb2int((255,0,0)) -blue = cli.rgb2int((0,0,255)) -green = cli.rgb2int((0,255,0)) -color = 65280 -shape = [] -thefont = HersheyFonts() +white = rgb2int((255,255,255)) +red = rgb2int((255,0,0)) +blue = rgb2int((0,0,255)) +green = rgb2int((0,255,0)) + +color = 65280 + +shape =[] + +Allfonts = ['futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram', 'gothgbt', 'gothgrt', 'gothiceng', 'gothicger', 'gothicita', 'gothitt', 'greek', 'greekc', 'greeks', 'japanese', 'markers', 'mathlow', 'mathupp', 'meteorology', 'music', 'rowmand', 'rowmans', 'rowmant', 'scriptc', 'scripts', 'symbolic', 'timesg', 'timesi', 'timesib', 'timesr', 'timesrb'] + +thefont = HersheyFonts() #thefont.load_default_font() thefont.load_default_font(fontname) thefont.normalize_rendering(120) @@ -69,6 +81,14 @@ for (x1, y1), (x2, y2) in thefont.lines_for_text(text): while True: - cli.startFrame() - print(shape, flush = True); - cli.endFrame() + start = time.time() + print(shape, flush=True); + + looptime = time.time() - start + if( looptime < optimal_looptime ): + time.sleep( optimal_looptime - looptime) + debug(name+" micro sleep:"+str( optimal_looptime - looptime)) + + +#[[14.285714285714286, 100.0, 14.285714285714286, 25.0, 65280], [64.28571428571429, 100.0, 64.28571428571429, 25.0, 65280], [14.285714285714286, 64.28571428571429, 64.28571428571429, 64.28571428571429, 65280], [89.28571428571428, 53.57142857142858, 132.14285714285714, 53.57142857142858, 65280], [132.14285714285714, 53.57142857142858, 132.14285714285714, 60.714285714285715, 65280], [132.14285714285714, 60.714285714285715, 128.57142857142856, 67.85714285714286, 65280], [128.57142857142856, 67.85714285714286, 125.0, 71.42857142857143, 65280], [125.0, 71.42857142857143, 117.85714285714286, 75.0, 65280], [117.85714285714286, 75.0, 107.14285714285714, 75.0, 65280], [107.14285714285714, 75.0, 100.0, 71.42857142857143, 65280], [100.0, 71.42857142857143, 92.85714285714286, 64.28571428571429, 65280], [92.85714285714286, 64.28571428571429, 89.28571428571428, 53.57142857142858, 65280], [89.28571428571428, 53.57142857142858, 89.28571428571428, 46.42857142857143, 65280], [89.28571428571428, 46.42857142857143, 92.85714285714286, 35.714285714285715, 65280], [92.85714285714286, 35.714285714285715, 100.0, 28.571428571428573, 65280], [100.0, 28.571428571428573, 107.14285714285714, 25.0, 65280], [107.14285714285714, 25.0, 117.85714285714286, 25.0, 65280], [117.85714285714286, 25.0, 125.0, 28.571428571428573, 65280], [125.0, 28.571428571428573, 132.14285714285714, 35.714285714285715, 65280]] + diff --git a/lib/clitools.py b/lib/clitools.py index cb25553..ed36bd1 100644 --- a/lib/clitools.py +++ b/lib/clitools.py @@ -29,10 +29,4 @@ class Clitools: if( elapsed < self.looptime ): delta = self.looptime - elapsed time.sleep( delta ) - - def rgb2int(rgb): - return int('0x%02x%02x%02x' % tuple(rgb),0) - - def msNow(): - return time.time() - + self.debug(self.name + " micro sleep:" + str( delta ))