From 371003fce24a8c05a258dd4e87e067615e38c6a2 Mon Sep 17 00:00:00 2001 From: alban Date: Sat, 3 Oct 2020 17:50:28 +0200 Subject: [PATCH 1/2] [fix] clitools many tweaks and changes batch --- clitools/filters/anaglyph.py | 201 +++++++++++++++++++++++++++++++ clitools/filters/kaleidoscope.py | 16 +-- clitools/filters/redilysis.py | 165 ++++++++++++++++++++----- clitools/generators/tunnel.py | 42 +++++-- 4 files changed, 380 insertions(+), 44 deletions(-) create mode 100755 clitools/filters/anaglyph.py diff --git a/clitools/filters/anaglyph.py b/clitools/filters/anaglyph.py new file mode 100755 index 0000000..b67dc3e --- /dev/null +++ b/clitools/filters/anaglyph.py @@ -0,0 +1,201 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + + +''' + +anaglyph +v0.1.0 + +Attempts to create a valid 3D-glasses structure + +LICENCE : CC + +by cocoa + + +''' +from __future__ import print_function +import argparse +import ast +import math +import os +import random +import sys +import time +name = "filters::cycle" + +maxDist = 300 + +argsparser = argparse.ArgumentParser(description="Redis exporter LJ") +argsparser.add_argument("-x","--centerX",help="geometrical center X position",default=400,type=int) +argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int) +argsparser.add_argument("-m","--min",help="Minimal displacement (default:2) ",default=1,type=int) +argsparser.add_argument("-M","--max",help="Maximal displacement (default:20) ",default=5,type=int) +argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int) +argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose") + +args = argsparser.parse_args() +fps = args.fps +minVal = args.min +maxVal = args.max +centerX = args.centerX +centerY = args.centerY +verbose = args.verbose + +optimal_looptime = 1 / fps +name = "filters::anaglyph" + +def debug(*args, **kwargs): + if( verbose == False ): + return + print(*args, file=sys.stderr, **kwargs) + +def rgb2int(rgb): + #debug(name,"::rgb2int rbg:{}".format(rgb)) + return int('0x%02x%02x%02x' % tuple(rgb),0) + +def isValidColor( color, intensityColThreshold ): + if color[0] + color[1] + color[2] > intensityColThreshold: + return True + return False + +# These are paper colors +red = (41,24,24) +white = (95,95,95) +blue = (0,41,64) + +red = (86,0,0) +blue = (0,55,86) +white = (125,125,125) +def anaglyph( pl ): + + debug(name,'--------------- new loop ------------------') + # We will send one list after the other to optimize color change + blueList = list() + redList = list() + whiteList = list() + out = [] + out1 = [] + out2 = [] + out3 = [] + + # The anaglyphic effect will be optained by : + # * having close objects appear as white + # * having distant objects appear as blue + red + # * having in between objects appear as distanceDecreased(white) + blue + red + for i, point in enumerate(pl): + ref_x = point[0]-centerX + ref_y = point[1]-centerY + ref_color = point[2] + angle = math.atan2( ref_x , ref_y ) + dist = ref_y / math.cos(angle) + white_rvb = (0,0,0) + blue_rvb = (0,0,0) + red_rvb = (0,0,0) + + # Calculate the point's spread factor (0.0 to 1.0) + # The spread is high if the point is close to center + """ + dist = 0 : spread = 1.0 + dist = maxDist spread = 0.0 + """ + if dist == 0: + spread = 1.0 + else : + spread =( maxDist - dist ) / maxDist + if spread < 0.0: + spread = 0.0 + + #debug(name,"dist:{} spread:{}".format(dist,spread)) + + # White color is high if spread is low, i.e. point away from center + """ + spread = 1.0 : white_c = 0.0 + spread = 0.0 : whice_c = 1.0 + """ + if point[2] == 0: + white_color = 0 + else: + white_factor = 1.0 - math.pow(spread,0.5) + white_rvb = tuple(map( lambda a: int(white_factor* a), white)) + white_color = rgb2int( white_rvb) + #debug(name,"spread:{}\t white_rvb:{}\t white_color:{}".format(spread, white_rvb, white_color)) + + # Blue and Red colors are high if spread is high, i.e. close to center + """ + spread = 1.0 : red_c = 1.0 + spread = 0.0 : red_c = 0.0 + """ + color_factor = math.pow(spread,1) + if point[2] == 0: + blue_color = 0 + red_color = 0 + else: + blue_rvb = tuple(map( lambda a: int(color_factor * a), blue)) + blue_color = rgb2int( blue_rvb) + red_rvb = tuple(map( lambda a: int(color_factor * a), red)) + red_color = rgb2int( red_rvb) + + #debug(name,"color_factor:{}\t\t blue_color:{}\t\t red_color:{}".format(color_factor,blue_color,red_color)) + + # Blue-to-Red spatial spread is high when spread is high, i.e. point close to center + """ + spread = 1.0 : spatial_spread = maxVal + spread = 0.0 : spatial_spread = minVal + """ + spatial_spread = minVal + spread * (maxVal - minVal) + #debug(name,"spatial_spread:{}".format(spatial_spread)) + red_x = int(point[0] + spatial_spread) + blue_x = int(point[0] - spatial_spread ) + red_y = int(point[1] ) + blue_y = int(point[1]) + + white_point = [point[0], point[1], white_color] + blue_point = [blue_x,blue_y,blue_color] + red_point = [red_x,red_y,red_color] + + #debug(name,"white[x,y,c]:{}".format(white_point)) + #debug(name,"blue[x,y,c]:{}".format(blue_point)) + #debug(name,"red[x,y,c]:{}".format(red_point)) + # Do not append "black lines" i.e. a color where each composent is below X +# if isValidColor(white_rvb, 150): +# out1.append(white_point) +# if isValidColor(blue_rvb, 50): +# out2.append(blue_point) +# if isValidColor(red_rvb, 30): +# out3.append(red_point) + out1.append(white_point) + out2.append(blue_point) + out3.append(red_point) + + #debug(name,"source pl:{}".format(pl)) + debug(name,"whiteList:{}".format(out1)) + debug(name,"blueList:{}".format(out2)) + debug(name,"redList:{}".format(out3)) + return out3 + out2 + return out1 + out3 + out2 + #return out1 + out2 + out3 + + + +try: + while True: + start = time.time() + line = sys.stdin.readline() + if line == "": + time.sleep(0.01) + line = line.rstrip('\n') + pointsList = ast.literal_eval(line) + # Do the filter + result = anaglyph( pointsList ) + print( result, flush=True ) + looptime = time.time() - start + # debug(name+" looptime:"+str(looptime)) + if( looptime < optimal_looptime ): + time.sleep( optimal_looptime - looptime) + # debug(name+" micro sleep:"+str( optimal_looptime - looptime)) +except EOFError: + debug(name+" break")# no more information + diff --git a/clitools/filters/kaleidoscope.py b/clitools/filters/kaleidoscope.py index ed5547c..0495f23 100755 --- a/clitools/filters/kaleidoscope.py +++ b/clitools/filters/kaleidoscope.py @@ -53,7 +53,6 @@ def kaleidoscope( pl ): quad1 = [] # Iterate trough the segments for i in range( 0, len(pl) ): - #debug(name+" point #", i) currentpoint = cp = pl[i] @@ -74,8 +73,8 @@ def kaleidoscope( pl ): #debug(name+" rect: ", rect,"curr",currentpoint,"next",nextpoint ) # Enumerate the points in rectangle to see - # how many right / wrong there are to add or skip early - # + # how many right / wrong there are + # either to add or skip early for iterator, p in enumerate(rect): if p[0] >= centerX and p[1] >= centerY: right += 1 @@ -118,7 +117,7 @@ def kaleidoscope( pl ): #print("on x axis, v=",str(v)," and absnewY=",str(absnewY)) crossY = [( absnewY*v[0] + cy ),( absnewY*v[1]+cy ), nc] # Inject in order - # If current is valid, Add + # If current point is the quadrant, add it if cx >= centerX and cy >= centerY : quad1.append( currentpoint ) # If absnewX smaller, it is closest to currentPoint @@ -128,6 +127,9 @@ def kaleidoscope( pl ): else : if None != crossY : quad1.append( crossY ) if None != crossX : quad1.append( crossX ) + # Add a black point at the end + #lastQuad1Point = quad1[-1] + #quad1.append( [lastQuad1Point[0],lastQuad1Point[1],0] ) ## Stage 2 : Mirror points # @@ -144,10 +146,10 @@ def kaleidoscope( pl ): point = quad3[iterator] quad4.append([ 2*centerX - point[0], point[1], point[2] ]) - debug(name+" quad1:",quad1) + #debug(name+" quad1:",quad1) #debug(name+" quad2:", quad2 ) - debug(name+" quad3:", quad3 ) - debug(name+" quad4:", quad4 ) + #debug(name+" quad3:", quad3 ) + #debug(name+" quad4:", quad4 ) return quad3+quad4 try: diff --git a/clitools/filters/redilysis.py b/clitools/filters/redilysis.py index d6d4d3b..00cd61a 100755 --- a/clitools/filters/redilysis.py +++ b/clitools/filters/redilysis.py @@ -34,16 +34,18 @@ def debug(*args, **kwargs): if( verbose == False ): return print(*args, file=sys.stderr, **kwargs) -def now(): - return time.time() * 1000 +def msNow(): + return time.time() -# The list of available modes and the redis keys they need +# The list of available modes => redis keys each requires to run oModeList = { "rms_noise": ["rms"], "rms_size": ["rms"], - "bpm_size": ["bpm"] + "bpm_size": ["bpm"], + "bpm_detect_size": ["bpm","bpm_delay","bpm_sample_interval","beats"] } CHAOS = 1 +REDISLATENCY = 30 REDIS_FREQ = 300 # General Args @@ -58,17 +60,19 @@ argsparser.add_argument("-x","--centerX",help="geometrical center X position",de argsparser.add_argument("-y","--centerY",help="geometrical center Y position",default=400,type=int) argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int) # Modes And Common Modes Parameters +argsparser.add_argument("-l","--redisLatency",help="Latency in ms to substract. Default:{}".format(REDISLATENCY),default=REDISLATENCY,type=float) argsparser.add_argument("-m","--modelist",required=True,help="Comma separated list of modes to use from: {}".format("i, ".join(oModeList.keys())),type=str) argsparser.add_argument("--chaos",help="How much disorder to bring. High value = More chaos. Default {}".format(CHAOS), default=CHAOS, type=str) args = argsparser.parse_args() ip = args.ip port = args.port -redisFreq = args.redis_freq +redisFreq = args.redis_freq / 1000 verbose = args.verbose fps = args.fps centerX = args.centerX centerY = args.centerY +redisLatency = args.redisLatency chaos = float(args.chaos) optimal_looptime = 1 / fps @@ -82,33 +86,127 @@ for mode in modeList: redisKeys = list(set(redisKeys)) debug(name,"Redis Keys:{}".format(redisKeys)) redisData = {} -redisLastHit = now() - redisFreq +redisLastHit = msNow() - 99999 r = redis.Redis( host=ip, port=port) # Records the last bpm -last_bpm = time.time() +tsLastBeat = time.time() def gauss(x, mu, sigma): return( math.exp(-math.pow((x-mu),2)/(2*math.pow(sigma,2))/math.sqrt(2*math.pi*math.pow(sigma,2)))) +previousPTTL = 0 +tsNextBeatsList = [] +def bpmDetect( ): + """ + An helper to compute the next beat time in milliseconds + Returns True if the cache was updated + """ + global tsNextBeatsList + global previousPTTL + global redisLastHit + global redisLatency + + # Get the redis PTTL value for bpm + PTTL = redisData["bpm_pttl"] -def bpm_size( pl ): - global last_bpm + # Skip early if PTTL < 0 + if PTTL < 0 : + debug(name,"bpmDetect skip detection : PTTL expired for 'bpm' key") + return False + + # Skip early if the record hasn't been rewritten + if PTTL <= previousPTTL : + previousPTTL = PTTL + #debug(name,"bpmDetect skip detection : {} <= {}".format(PTTL, previousPTTL)) + return False + debug(name,"bpmDetect running detection : {} > {}".format(PTTL, previousPTTL)) + previousPTTL = PTTL + + # Skip early if beat list is empty + beatsList = ast.literal_eval(redisData["beats"]) + tsNextBeatsList = [] + if( len(beatsList) == 0 ): + return True + + # Read from redis bpm = float(redisData["bpm"]) - # Milliseconds ber beat - milliSecondsPerBeat = int(60 / bpm * 1000) + msBpmDelay = float(redisData["bpm_delay"]) + samplingInterval = float(redisData["bpm_sample_interval"]) + + # Calculate some interpolations + lastBeatTiming = float(beatsList[len(beatsList) - 1]) + msPTTLDelta = 2 * samplingInterval - float(PTTL) + sPerBeat = 60 / bpm + lastBeatDelay = msBpmDelay - lastBeatTiming*1000 + msPTTLDelta + countBeatsPast = math.floor( (lastBeatDelay / 1000) / sPerBeat) + #debug(name,"bpmDetect lastBeatTiming:{}\tmsPTTLDelta:{}\tsPerBeat:{}".format(lastBeatTiming,msPTTLDelta,sPerBeat)) + #debug(name,"lastBeatDelay:{}\t countBeatsPast:{}".format(lastBeatDelay, countBeatsPast)) + for i in range( countBeatsPast, 1000): + beatTime = i * sPerBeat - lastBeatTiming + if beatTime < 0: + continue + if beatTime * 1000 > 2 * samplingInterval : + break + #debug(name, "bpmDetect beat add beatTime:{} redisLastHit:{}".format(beatTime, redisLastHit)) + tsNextBeatsList.append( redisLastHit + beatTime - redisLatency/1000) + debug(name, "bpmDetect new tsNextBeatsList:{}".format(tsNextBeatsList)) + + return True + +def bpm_detect_size( pl ): + bpmDetect() + + # Find the next beat in the list + tsNextBeat = 0 + + now = time.time() + msNearestBeat = None + msRelativeNextBTList = list(map( lambda a: abs(now - a) * 1000, tsNextBeatsList)) + msToBeat = min( msRelativeNextBTList) + + #debug(name,"bpm_detect_size msRelativeNextBTList:{} msToBeat:{}".format(msRelativeNextBTList,msToBeat)) # Calculate the intensity based on bpm coming/leaving # The curb is a gaussian - mu = math.sqrt(milliSecondsPerBeat) - milliTimeToLastBeat = (time.time() - last_bpm) * 1000 - milliTimeToNextBeat = (milliSecondsPerBeat - milliTimeToLastBeat) - intensity = gauss( milliTimeToNextBeat, 0 , mu) - debug(name,"bpm_size","milliSecondsPerBeat:{}\tmu:{}".format(milliSecondsPerBeat, mu)) - debug(name,"bpm_size","milliTimeToLastBeat:{}\tmilliTimeToNextBeat:{}\tintensity:{}".format(milliTimeToLastBeat, milliTimeToNextBeat, intensity)) - if milliTimeToNextBeat <= 0 : - last_bpm = time.time() + mu = 15 + intensity = gauss( msToBeat, 0 , mu) + #debug(name,"bpm_size","mu:{}\t msToBeat:{}\tintensity:{}".format(mu, msToBeat, intensity)) + if msToBeat < 20: + debug(name,"bpm_detect_size kick:{}".format(msToBeat)) + pass + for i, point in enumerate(pl): + ref_x = point[0]-centerX + ref_y = point[1]-centerY + #debug(name,"In new ref x:{} y:{}".format(point[0]-centerX,point[1]-centerY)) + angle=math.atan2( point[0] - centerX , point[1] - centerY ) + l = ref_y / math.cos(angle) + new_l = l * intensity + #debug(name,"bpm_size","angle:{} l:{} new_l:{}".format(angle,l,new_l)) + new_x = math.sin(angle) * new_l + centerX + new_y = math.cos(angle) * new_l + centerY + #debug(name,"x,y:({},{}) x',y':({},{})".format(point[0],point[1],new_x,new_y)) + pl[i][0] = new_x + pl[i][1] = new_y + #debug( name,"bpm_detect_size output:{}".format(pl)) + return( pl ); + +def bpm_size( pl ): + global tsLastBeat + bpm = float(redisData["bpm"]) + # msseconds ber beat + msPerBeat = int(60 / bpm * 1000) + # Calculate the intensity based on bpm coming/leaving + # The curb is a gaussian + mu = math.sqrt(msPerBeat) + msTimeToLastBeat = (time.time() - tsLastBeat) * 1000 + msTimeToNextBeat = (msPerBeat - msTimeToLastBeat) + intensity = gauss( msTimeToNextBeat, 0 , mu) + debug(name,"bpm_size","msPerBeat:{}\tmu:{}".format(msPerBeat, mu)) + debug(name,"bpm_size","msTimeToLastBeat:{}\tmsTimeToNextBeat:{}\tintensity:{}".format(msTimeToLastBeat, msTimeToNextBeat, intensity)) + if msTimeToNextBeat <= 0 : + tsLastBeat = time.time() for i, point in enumerate(pl): ref_x = point[0]-centerX ref_y = point[1]-centerY @@ -158,21 +256,30 @@ def rms_noise( pl ): return pl -def updateRedis(): +def refreshRedis(): global redisLastHit global redisData - for key in redisKeys: - redisData[key] = r.get(key).decode('ascii') - debug("name","updateRedis key:{} value:{}".format(key,redisData[key])) - if key == 'bpm': - redisData['bpm_ttl'] = r.pttl(key) - debug(name,"redisData:{}".format(redisData)) + # Skip if cache is sufficent + diff = msNow() - redisLastHit + if diff < redisFreq : + #debug(name, "refreshRedis not updating redis, {} < {}".format(diff, redisFreq)) + pass + else: + #debug(name, "refreshRedis updating redis, {} > {}".format(diff, redisFreq)) + redisLastHit = msNow() + for key in redisKeys: + redisData[key] = r.get(key).decode('ascii') + #debug(name,"refreshRedis key:{} value:{}".format(key,redisData[key])) + # Only update the TTLs + if 'bpm' in redisKeys: + redisData['bpm_pttl'] = r.pttl('bpm') + #debug(name,"refreshRedis key:bpm_ttl value:{}".format(redisData["bpm_pttl"])) + #debug(name,"redisData:{}".format(redisData)) + return True try: while True: - # it is time to query redis - if now() - redisLastHit > redisFreq: - updateRedis() + refreshRedis() start = time.time() line = sys.stdin.readline() if line == "": diff --git a/clitools/generators/tunnel.py b/clitools/generators/tunnel.py index 00b220d..f324475 100644 --- a/clitools/generators/tunnel.py +++ b/clitools/generators/tunnel.py @@ -34,7 +34,7 @@ argsparser = argparse.ArgumentParser(description="tunnel generator") argsparser.add_argument("-c","--color",help="Color",default=65280,type=int) argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int) argsparser.add_argument("-i","--interval",help="point per shape interval",default=30,type=int) -argsparser.add_argument("-m","--max-size",help="maximum size for objects",default=500,type=int) +argsparser.add_argument("-m","--max-size",help="maximum size for objects",default=400,type=int) argsparser.add_argument("-r","--randomize",help="center randomization",default=5,type=int) argsparser.add_argument("-s","--speed",help="point per frame progress",default=3,type=int) argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output") @@ -77,14 +77,19 @@ class polylineGenerator( object ): self.polylineList = [[0,[currentCenter[0],currentCenter[1]]]] self.buf = [] + def init(self): + finished = False + while not finished: + finished = self.increment() + debug(name,"init done:{}".format(self.polylineList)) def draw( self ): self.buf = [] for it_pl, infoList in enumerate(self.polylineList): size = infoList[0] center = infoList[1] for it_sqr, point in enumerate(shape): - x = center[0] + point[0]*size - y = center[1] + point[1]*size + x = int( center[0] + point[0]*size ) + y = int( center[1] + point[1]*size ) # Add an invisible point in first location if 0 == it_sqr: self.buf.append([x,y,0]) @@ -114,22 +119,43 @@ class polylineGenerator( object ): speed = origSpeed elif speed > (origSpeed + randomize / 2) : speed = origSpeed + randomize / 2 - debug(name, "currentCenter:{} speed:{}".format(currentCenter,speed)) + #debug(name, "currentCenter:{} speed:{}".format(currentCenter,speed)) for i, shapeInfo in enumerate(self.polylineList): size = shapeInfo[0] - size += speed + # Augment speed with size + """ + size = 0 : += sqrt(speed) + size = half max size : +=speed + + """ + if size < max_size / 4: + size += math.pow(speed, 0.1) + elif size < max_size / 3: + size += math.pow(speed, 0.25) + elif size < max_size / 2: + size += math.pow(speed, 0.5) + else: + size += math.pow(speed, 1.25) if size < min_size : min_size = size if size > max_size : delList.append(i) self.polylineList[i][0] = size for i in delList: del self.polylineList[i] - if min_size >= interval: self.polylineList.append([0,[currentCenter[0],currentCenter[1]]]) #debug(name, "polyline:",self.polylineList) + if min_size >= interval: + debug(name, "new shape") + self.polylineList.append([0,[currentCenter[0],currentCenter[1]]]) + + # Return True if we delete a shape + + if len(delList): + return True + return False pgen = polylineGenerator() - +pgen.init() while True: start = time.time() @@ -140,7 +166,7 @@ while True: # send pl = pgen.draw() print(pl, flush=True) - debug(name,"output:{}".format(pl)) + #debug(name,"output:{}".format(pl)) looptime = time.time() - start if( looptime < optimal_looptime ): From 5e26faa798a20e049dae207f86ab305079dcc775 Mon Sep 17 00:00:00 2001 From: alban Date: Sat, 3 Oct 2020 17:51:26 +0200 Subject: [PATCH 2/2] [fix] www: rework the simulator page --- www/simu.html | 201 +++++++++++++++++++++----------------------------- 1 file changed, 85 insertions(+), 116 deletions(-) diff --git a/www/simu.html b/www/simu.html index 95ae133..39f5fb3 100644 --- a/www/simu.html +++ b/www/simu.html @@ -10,8 +10,8 @@ - - + +