From 48770457fcd30149e5fd369ebeb23c81ac12cd65 Mon Sep 17 00:00:00 2001 From: alban Date: Sat, 28 Nov 2020 21:44:10 +0100 Subject: [PATCH 1/2] [fix] Reworks some generators to use the library object --- generators/dummy.py | 3 +- generators/fromRedis.py | 32 +++++----- generators/osc2redis.py | 136 ---------------------------------------- generators/text.py | 74 ++++++++-------------- lib/clitools.py | 3 + 5 files changed, 48 insertions(+), 200 deletions(-) delete mode 100644 generators/osc2redis.py diff --git a/generators/dummy.py b/generators/dummy.py index fd12ed8..725e9bd 100755 --- a/generators/dummy.py +++ b/generators/dummy.py @@ -35,10 +35,9 @@ args = argsparser.parse_args() fps=args.fps verbose=args.verbose -optimal_looptime = 1 / fps cli = Clitools({ "verbose" : verbose, - "looptime" : looptime, + "looptime" : 1 / fps, "name" : name }) diff --git a/generators/fromRedis.py b/generators/fromRedis.py index 3e77c9a..46111b5 100755 --- a/generators/fromRedis.py +++ b/generators/fromRedis.py @@ -24,7 +24,10 @@ import json import redis import sys import time -name="generator::fromRedis" +from os import path, getcwd +abspath, filename = path.split(path.realpath(__file__ )) +sys.path.insert(0, path.join(abspath,"../lib")) +from clitools import Clitools def debug(*args, **kwargs): @@ -33,28 +36,31 @@ 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 -optimal_looptime = 1 / fps -debug(name+" optimal looptime "+str(optimal_looptime)) +cli = Clitools({ + "verbose" : verbose, + "looptime" : 1 / fps, + "name" : "generator::fromRedis" + }) -r = redis.Redis( - host=ip, - port=port) + +r = redis.Redis( host=ip, port=port ) while True: - start = time.time() + + cli.startFrame() # Read from Redis line = r.get(key) # Decode as list of tuples @@ -63,10 +69,6 @@ while True: pointsList = [list(elem) for elem in pointsList] # Convert to JSON string line = json.dumps( pointsList ) - debug(name,"Key:{} line:{}".format(key,line)) + cli.debug(name,"Key:{} line:{}".format(key,line)) print(line, flush=True); - looptime = time.time() - start - if( looptime < optimal_looptime ): - time.sleep( optimal_looptime - looptime) - debug(name+" micro sleep:"+str( optimal_looptime - looptime)) - + cli.endFrame() diff --git a/generators/osc2redis.py b/generators/osc2redis.py deleted file mode 100644 index 2e0e5ba..0000000 --- a/generators/osc2redis.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/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/text.py b/generators/text.py index 2afc3c5..9a55559 100644 --- a/generators/text.py +++ b/generators/text.py @@ -4,9 +4,7 @@ ''' -Experimental Laserized Turtle graphics library - -See turtle1.py for example +Static text writer pip3 install Hershey-Fonts @@ -29,48 +27,38 @@ import time import argparse import sys from HersheyFonts import HersheyFonts - -name="generator::text" +from os import path, getcwd +abspath, filename = path.split(path.realpath(__file__ )) +sys.path.insert(0, path.join(abspath,"../lib")) +from clitools import Clitools -def debug(*args, **kwargs): - if( verbose == False ): - return - print(*args, file=sys.stderr, **kwargs) - - -argsparser = argparse.ArgumentParser(description="Text generator") +argsparser = argparse.ArgumentParser(description="Text generator") argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int) argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") argsparser.add_argument("-t","--text",help="Text to display",default="hello",type=str) -argsparser.add_argument("-p","--police",help="Herschey font to use",default="futural",type=str) -args = argsparser.parse_args() +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 -fps=args.fps -verbose=args.verbose +cli = Clitools({ + "verbose" : verbose, + "looptime" : 1 / fps, + "name" : "generator::text" + }) -text = args.text -fontname = args.police - -optimal_looptime = 1 / fps -debug(name+" optimal looptime "+str(optimal_looptime)) - -def rgb2int(rgb): - return int('0x%02x%02x%02x' % tuple(rgb),0) # Useful variables init. -white = rgb2int((255,255,255)) -red = rgb2int((255,0,0)) -blue = rgb2int((0,0,255)) -green = rgb2int((0,255,0)) - -color = 65280 - -shape =[] - -Allfonts = ['futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram', 'gothgbt', 'gothgrt', 'gothiceng', 'gothicger', 'gothicita', 'gothitt', 'greek', 'greekc', 'greeks', 'japanese', 'markers', 'mathlow', 'mathupp', 'meteorology', 'music', 'rowmand', 'rowmans', 'rowmant', 'scriptc', 'scripts', 'symbolic', 'timesg', 'timesi', 'timesib', 'timesr', 'timesrb'] - -thefont = HersheyFonts() +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() #thefont.load_default_font() thefont.load_default_font(fontname) thefont.normalize_rendering(120) @@ -81,14 +69,6 @@ for (x1, y1), (x2, y2) in thefont.lines_for_text(text): while True: - start = time.time() - print(shape, flush=True); - - looptime = time.time() - start - if( looptime < optimal_looptime ): - time.sleep( optimal_looptime - looptime) - debug(name+" micro sleep:"+str( optimal_looptime - looptime)) - - -#[[14.285714285714286, 100.0, 14.285714285714286, 25.0, 65280], [64.28571428571429, 100.0, 64.28571428571429, 25.0, 65280], [14.285714285714286, 64.28571428571429, 64.28571428571429, 64.28571428571429, 65280], [89.28571428571428, 53.57142857142858, 132.14285714285714, 53.57142857142858, 65280], [132.14285714285714, 53.57142857142858, 132.14285714285714, 60.714285714285715, 65280], [132.14285714285714, 60.714285714285715, 128.57142857142856, 67.85714285714286, 65280], [128.57142857142856, 67.85714285714286, 125.0, 71.42857142857143, 65280], [125.0, 71.42857142857143, 117.85714285714286, 75.0, 65280], [117.85714285714286, 75.0, 107.14285714285714, 75.0, 65280], [107.14285714285714, 75.0, 100.0, 71.42857142857143, 65280], [100.0, 71.42857142857143, 92.85714285714286, 64.28571428571429, 65280], [92.85714285714286, 64.28571428571429, 89.28571428571428, 53.57142857142858, 65280], [89.28571428571428, 53.57142857142858, 89.28571428571428, 46.42857142857143, 65280], [89.28571428571428, 46.42857142857143, 92.85714285714286, 35.714285714285715, 65280], [92.85714285714286, 35.714285714285715, 100.0, 28.571428571428573, 65280], [100.0, 28.571428571428573, 107.14285714285714, 25.0, 65280], [107.14285714285714, 25.0, 117.85714285714286, 25.0, 65280], [117.85714285714286, 25.0, 125.0, 28.571428571428573, 65280], [125.0, 28.571428571428573, 132.14285714285714, 35.714285714285715, 65280]] - + cli.startFrame() + print(shape, flush = True); + cli.endFrame() diff --git a/lib/clitools.py b/lib/clitools.py index ed36bd1..2c64b54 100644 --- a/lib/clitools.py +++ b/lib/clitools.py @@ -30,3 +30,6 @@ class Clitools: delta = self.looptime - elapsed time.sleep( delta ) self.debug(self.name + " micro sleep:" + str( delta )) + + def rgb2int(rgb): + return int('0x%02x%02x%02x' % tuple(rgb),0) From 8a6de3f4ecf41051475b1f64ca1ec05897014490 Mon Sep 17 00:00:00 2001 From: alban Date: Sun, 29 Nov 2020 07:59:39 +0100 Subject: [PATCH 2/2] [fix] cleaning up --- generators/dummy.py | 6 +- generators/particles_example.py | 185 ++++++++++++++++++++++++++++++++ lib/clitools.py | 5 +- 3 files changed, 190 insertions(+), 6 deletions(-) create mode 100755 generators/particles_example.py diff --git a/generators/dummy.py b/generators/dummy.py index 725e9bd..497dd26 100755 --- a/generators/dummy.py +++ b/generators/dummy.py @@ -21,10 +21,6 @@ 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") @@ -38,7 +34,7 @@ verbose=args.verbose cli = Clitools({ "verbose" : verbose, "looptime" : 1 / fps, - "name" : name + "name" : "generator::dummy" }) diff --git a/generators/particles_example.py b/generators/particles_example.py new file mode 100755 index 0000000..c98564c --- /dev/null +++ b/generators/particles_example.py @@ -0,0 +1,185 @@ +#!/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/lib/clitools.py b/lib/clitools.py index 2c64b54..cb25553 100644 --- a/lib/clitools.py +++ b/lib/clitools.py @@ -29,7 +29,10 @@ class Clitools: if( elapsed < self.looptime ): delta = self.looptime - elapsed time.sleep( delta ) - self.debug(self.name + " micro sleep:" + str( delta )) def rgb2int(rgb): return int('0x%02x%02x%02x' % tuple(rgb),0) + + def msNow(): + return time.time() +