Initial snapshot
This commit is contained in:
commit
659d9dccbe
3 changed files with 370 additions and 0 deletions
326
LeitLightLightLeitLeidLite/__init__.py
Normal file
326
LeitLightLightLeitLeidLite/__init__.py
Normal file
|
@ -0,0 +1,326 @@
|
|||
import json
|
||||
import math
|
||||
|
||||
# This is the source information:
|
||||
# - x/y is in LED distances
|
||||
# - x is distance from right wall
|
||||
# - y is distance from ceiling near door
|
||||
# - Indices increase along rows
|
||||
# - Cells are [right, ..., left]
|
||||
# - Index increases from right-to-left if rev else left-to-right
|
||||
# - Negative numbers in cells (also fractions) denote a gap
|
||||
rows = [
|
||||
|
||||
# Controller: 0-218
|
||||
|
||||
# 139, # unter decke (rechts)
|
||||
{ 'y': 0/2, 'x': 0, 'cells': [58, 60, 21], 'rev': False, 'name': 'unter_decke_rechts' },
|
||||
|
||||
# 80, # unter decke (links)
|
||||
{ 'y': -5/2, 'x': 139, 'cells': [19, 39, 22], 'rev': False, 'name': 'unter_decke_links' },
|
||||
|
||||
# Controller: 219-702
|
||||
|
||||
#236, # ueber ampel
|
||||
{ 'y': 40/2, 'x': 0, 'cells': [57, 59, 39, 37, 44], 'rev': False, 'name': 'ueber_ampel' },
|
||||
|
||||
#-248, # unter ampel
|
||||
{ 'y': 70/2, 'x': 0, 'cells': [57, 59, 40, 37, 55], 'rev': True, 'name': 'unter_ampel' },
|
||||
|
||||
# Controller: 703-1295
|
||||
|
||||
#153, # ueber garderobe
|
||||
{ 'y': 100/2, 'x': 0, 'cells': [55, 58, 40], 'rev': False, 'name': 'ueber_garderobe' },
|
||||
|
||||
#35, # gewuerze (oben)
|
||||
{ 'y': 115/2, 'x': 153, 'cells': [35], 'rev': False, 'name': 'gewuerze_oben' },
|
||||
|
||||
#47, # unter schrank
|
||||
{ 'y': 110/2, 'x': 153+35, 'cells': [47], 'rev': False, 'name': 'unter_schrank' },
|
||||
|
||||
#-36, # gewuerze (unten)
|
||||
{ 'y': 130/2, 'x': 153, 'cells': [36], 'rev': True, 'name': 'gewuerze_unten' },
|
||||
|
||||
#-89, # zwei von unten #FIXME: Bad source?
|
||||
{ 'y': 130/2, 'x': 55, 'cells': [49+5, 40], 'rev': True, 'name': 'zwei_von_unten' },
|
||||
|
||||
# 100, # eins von unten #FIXME: Bad source?
|
||||
{ 'y': 160/2, 'x': 55, 'cells': [61-5, 39], 'rev': False, 'name': 'eins_von_unten' },
|
||||
|
||||
#FIXME: Missing one LED?
|
||||
# -132 # unten
|
||||
{ 'y': 190/2, 'x': 55, 'cells': [57, 39, 36], 'rev': True, 'name': 'unten' },
|
||||
]
|
||||
|
||||
|
||||
|
||||
# 3D vector math
|
||||
|
||||
def scaleV(v, s):
|
||||
return (v[0]*s,v[1]*s,v[2]*s)
|
||||
|
||||
def addV(a, b):
|
||||
return (a[0]+b[0], a[1]+b[1], a[2]+b[2])
|
||||
|
||||
def subV(a, b):
|
||||
return addV(a, scaleV(b, -1.0))
|
||||
|
||||
def mulV(a, b):
|
||||
return (a[0]*b[0], a[1]*b[1], a[2]*b[2])
|
||||
|
||||
def dotV(a, b):
|
||||
t = mulV(a, b)
|
||||
return t[0]+t[1]+t[2]
|
||||
|
||||
def normalizeV(v):
|
||||
l = math.sqrt(dotV(v, v))
|
||||
return scaleV(v, 1.0 / l)
|
||||
|
||||
def lerp(a, b, p):
|
||||
return round(a + (b - a) * p)
|
||||
|
||||
def lerpV(a, b, p):
|
||||
return [lerp(a[0],b[0],p), lerp(a[1],b[1],p), lerp(a[2],b[2],p)]
|
||||
|
||||
|
||||
|
||||
# Strip functions
|
||||
|
||||
# Turn strips into 3D line segments
|
||||
def getSourceStrips(rows):
|
||||
sourceStrips = []
|
||||
index = 0
|
||||
for row in rows:
|
||||
|
||||
rowSourceStrips = []
|
||||
|
||||
# Extract cells (right to left)
|
||||
step = (1, 0, 0)
|
||||
start = (row['x'], row['y'], 0)
|
||||
for length in row['cells']:
|
||||
end = addV(start, scaleV(step, abs(length)))
|
||||
if length > 0:
|
||||
rowSourceStrip = {
|
||||
'start': start,
|
||||
'end': end,
|
||||
'count': length,
|
||||
'index': len(rowSourceStrips),
|
||||
'name': row['name']
|
||||
}
|
||||
rowSourceStrips += [rowSourceStrip]
|
||||
start = end
|
||||
|
||||
if row['rev']:
|
||||
rowSourceStrips = rowSourceStrips[::-1]
|
||||
for rowSourceStrip in rowSourceStrips:
|
||||
rowSourceStrip['start'], rowSourceStrip['end'] = rowSourceStrip['end'], rowSourceStrip['start']
|
||||
|
||||
sourceStrips += rowSourceStrips
|
||||
|
||||
print(sourceStrips)
|
||||
return sourceStrips
|
||||
|
||||
# Adds index information to each strip
|
||||
def bakeStrips(sourceStrips):
|
||||
strips = []
|
||||
first = 0
|
||||
for sourceStrip in sourceStrips:
|
||||
strip = dict(sourceStrip)
|
||||
|
||||
# Fix stupidity in sourceStrip
|
||||
strip['direction'] = scaleV(subV(sourceStrip['end'], sourceStrip['start']), 1.0 / sourceStrip['count'])
|
||||
del strip['end']
|
||||
|
||||
# Bake the index
|
||||
strip['first'] = first
|
||||
strips += [strip]
|
||||
|
||||
# Prepare for next strip
|
||||
first += sourceStrip['count']
|
||||
|
||||
return strips
|
||||
|
||||
# Only keeps strip in range start to end (in range 0.0 - 1.0)
|
||||
def extractStrip(strip, start, startInclusive, end, endInclusive):
|
||||
strip = dict(strip)
|
||||
count = strip['count']
|
||||
halfStep = 1.0 / count
|
||||
startRounder = math.floor if startInclusive else math.ceil
|
||||
endRounder = math.ceil if endInclusive else math.floor
|
||||
startIndex = max(0, min(startRounder(start * (count - 1) + halfStep), count - 1))
|
||||
endIndex = max(0, min(endRounder(end * (count - 1) - halfStep), count - 1))
|
||||
strip['first'] += startIndex
|
||||
strip['start'] = addV(strip['start'], scaleV(strip['direction'], startIndex))
|
||||
strip['count'] = (endIndex - startIndex) + 1
|
||||
assert(strip['count'] >= 0)
|
||||
return strip
|
||||
|
||||
# Only keep portions of the clip above the plane surface
|
||||
def clipStripAgainstPlane(strip, origin, direction):
|
||||
strip = dict(strip)
|
||||
start = strip['start']
|
||||
stripDirection = strip['direction']
|
||||
count = strip['count']
|
||||
|
||||
# If we don't have any LEDs left, then there's nothing to clip against
|
||||
if count == 0:
|
||||
return strip
|
||||
|
||||
assert(count > 0)
|
||||
|
||||
startToEnd = scaleV(stripDirection, count - 1)
|
||||
startToOrigin = subV(origin, start)
|
||||
towardsPlane = dotV(startToEnd, direction) # 0 if in plane, positive if along plane direction
|
||||
abovePlane = dotV(startToOrigin, direction) # negative if start above
|
||||
|
||||
isStartAbove = abovePlane < 0.0
|
||||
isTowardsPlane = (towardsPlane < 0.0) == isStartAbove
|
||||
|
||||
# Strip parallel to plane
|
||||
if (abs(towardsPlane) <= 1.0e-6):
|
||||
|
||||
# Force no intersection
|
||||
t = -1.0
|
||||
|
||||
# Any other case
|
||||
else:
|
||||
t = abovePlane / towardsPlane
|
||||
|
||||
print("above" if isStartAbove else "behind", "towards" if isTowardsPlane else "away", t)
|
||||
|
||||
# No intersection
|
||||
if t <= 0 or t >= 1:
|
||||
# Remove everything if we are below the plane
|
||||
if not isStartAbove:
|
||||
strip['count'] = 0
|
||||
return strip
|
||||
|
||||
# Handle intersection
|
||||
if isStartAbove:
|
||||
if isTowardsPlane:
|
||||
# Remove end of strip
|
||||
strip = extractStrip(strip, 0.0, True, t, True)
|
||||
else:
|
||||
# Keep entire strip
|
||||
pass
|
||||
else:
|
||||
if isTowardsPlane:
|
||||
# Remove start of strip
|
||||
strip = extractStrip(strip, t, True, 1.0, True)
|
||||
else:
|
||||
# Remove entire strip
|
||||
strip['count'] = 0
|
||||
return strip
|
||||
|
||||
def clipStripsAgainstPlane(strips, origin, direction):
|
||||
clippedStrips = []
|
||||
for strip in strips:
|
||||
clippedStrip = clipStripAgainstPlane(strip, origin, direction)
|
||||
clippedStrips += [clippedStrip]
|
||||
return clippedStrips
|
||||
|
||||
def clipStripsAgainstAabb(strips, xyz0, xyz1):
|
||||
strips = clipStripsAgainstPlane(strips, xyz0, ( 1, 0, 0))
|
||||
strips = clipStripsAgainstPlane(strips, xyz1, (-1, 0, 0))
|
||||
strips = clipStripsAgainstPlane(strips, xyz0, ( 0, 1, 0))
|
||||
strips = clipStripsAgainstPlane(strips, xyz1, ( 0, -1, 0))
|
||||
strips = clipStripsAgainstPlane(strips, xyz0, ( 0, 0, 1))
|
||||
strips = clipStripsAgainstPlane(strips, xyz1, ( 0, 0, -1))
|
||||
return strips
|
||||
|
||||
# Debug helpers
|
||||
|
||||
def generateObj(strips, onlyBounds=False):
|
||||
s = []
|
||||
vs = []
|
||||
ls = []
|
||||
for strip in strips:
|
||||
count = strip['count']
|
||||
assert(count >= 0)
|
||||
l = []
|
||||
if onlyBounds:
|
||||
for i in range(2):
|
||||
v = addV(strip['start'], scaleV(strip['direction'], i * (count - 1)))
|
||||
vs += [v]
|
||||
l += [len(vs)]
|
||||
else:
|
||||
for i in range(count):
|
||||
v = addV(strip['start'], scaleV(strip['direction'], i))
|
||||
vs += [v]
|
||||
l += [len(vs)]
|
||||
ls += [(l, strip['name'], strip['index'])]
|
||||
for v in vs:
|
||||
s += ["v %f %f %f" % (v[0], v[1], v[2])]
|
||||
for l in ls:
|
||||
s += ["# %s [%d]" % (l[1], l[2])]
|
||||
if len(l[0]) >= 2:
|
||||
s += ["l " + " ".join(["%d" % i for i in l[0]])]
|
||||
return "\n".join(s)
|
||||
|
||||
def testStrip(a, b, count=10):
|
||||
return {
|
||||
'name': 'test',
|
||||
'index': 0,
|
||||
'start': a,
|
||||
'direction': scaleV(subV(b, a), 1.0 / (count - 1)),
|
||||
'first': 0,
|
||||
'count': count
|
||||
}
|
||||
|
||||
def debugStrip(strip):
|
||||
strip = dict(strip)
|
||||
strip['end'] = addV(strip['start'], scaleV(strip['direction'], strip['count'] - 1))
|
||||
#del strip['direction']
|
||||
return strip
|
||||
|
||||
|
||||
def shadeStrips(strips, shader):
|
||||
stripDatas = []
|
||||
|
||||
for strip in strips:
|
||||
data = []
|
||||
first = strip['first']
|
||||
|
||||
def emitData():
|
||||
stripData = {
|
||||
'first': strip['first'],
|
||||
'data': bytes(buffer)
|
||||
}
|
||||
|
||||
for i in range(strip['count']):
|
||||
color = shader(position)
|
||||
data += [color]
|
||||
|
||||
emitData()
|
||||
|
||||
stripDatas += [stripData]
|
||||
return stripData
|
||||
|
||||
|
||||
def sendSequence(sock, start, data, d = 1):
|
||||
|
||||
# https://kno.wled.ge/interfaces/udp-realtime/
|
||||
#FIXME: Somehow 489 does not work
|
||||
maxChunk = 489-10
|
||||
while len(data) > 0:
|
||||
|
||||
buf = bytearray()
|
||||
|
||||
buf += bytes([4, d]) # DNRGB, Number of seconds before normal mode
|
||||
|
||||
buf += bytes([(start >> 8) & 0xFF])
|
||||
buf += bytes([(start >> 0) & 0xFF])
|
||||
|
||||
buf += data[0:maxChunk*3]
|
||||
data = data[maxChunk*3:]
|
||||
start += maxChunk
|
||||
|
||||
sock.send(buf)
|
||||
|
||||
def fade(a, b, count):
|
||||
buf = []
|
||||
for i in range(count):
|
||||
p = i / (count - 1)
|
||||
buf += lerp(a, b, p)
|
||||
return buf
|
||||
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## LeitLightLightLeitLeidLite
|
||||
|
||||
LeitLightLightLeitLeidLite macht dein Leid mit den Leitstellen Lights Lite'r indem es Light Leitet.
|
41
test.py
Executable file
41
test.py
Executable file
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import LeitLightLightLeitLeidLite as l6
|
||||
|
||||
rows = l6.rows
|
||||
|
||||
# Export 3D model of physical arrangement
|
||||
sourceStrips = l6.getSourceStrips(rows)
|
||||
print(sourceStrips)
|
||||
strips = l6.bakeStrips(sourceStrips)
|
||||
obj = l6.generateObj(strips)
|
||||
open("/tmp/strips.obj", "w").write(obj)
|
||||
obj = l6.generateObj(strips, True)
|
||||
open("/tmp/strips-bounds.obj", "w").write(obj)
|
||||
|
||||
# From behind of plane to above plane
|
||||
print(l6.debugStrip(l6.clipStripAgainstPlane(l6.testStrip((-10, 0, 0), (10, 0, 0), 21), (0, 0, 0), (1, 0, 0))))
|
||||
|
||||
# From above of plane to behind plane
|
||||
print(l6.debugStrip(l6.clipStripAgainstPlane(l6.testStrip((10, 0, 0), (-10, 0, 0), 21), (0, 0, 0), (1, 0, 0))))
|
||||
|
||||
# Parallel above of plane
|
||||
print(l6.debugStrip(l6.clipStripAgainstPlane(l6.testStrip((10, -10, 0), (10, 10, 0), 21), (0, 0, 0), (1, 0, 0))))
|
||||
|
||||
# Parallel behind of plane
|
||||
print(l6.debugStrip(l6.clipStripAgainstPlane(l6.testStrip((-10, -10, 0), (-10, 10, 0), 21), (0, 0, 0), (1, 0, 0))))
|
||||
|
||||
|
||||
|
||||
stripsXp = l6.clipStripsAgainstPlane(strips, (100, 0, 0), (1, 0, 0))
|
||||
obj = l6.generateObj(stripsXp)
|
||||
open("/tmp/strips-xp.obj", "w").write(obj)
|
||||
|
||||
stripsYp = l6.clipStripsAgainstPlane(strips, (0, 50, 0), (0, 1, 0))
|
||||
obj = l6.generateObj(stripsYp)
|
||||
open("/tmp/strips-yp.obj", "w").write(obj)
|
||||
|
||||
stripsAabb = l6.clipStripsAgainstAabb(strips, (80, 60/2, -100), (120, 140/2, +100))
|
||||
obj = l6.generateObj(stripsAabb)
|
||||
open("/tmp/strips-aabb.obj", "w").write(obj)
|
||||
|
Loading…
Add table
Reference in a new issue