comparison lib/devices_shared/devices_shared.py @ 1336:7993c7bcd04a

make devices_shared into lib Ignore-this: 9c4c0c05413eb2660312862ecc5a743b darcs-hash:b6eb09e457a7e8c49a9399a463a3ef64e1bae123
author drewp <drewp@bigasterisk.com>
date Thu, 25 Apr 2019 17:22:39 -0700
parents
children be9b456717bd
comparison
equal deleted inserted replaced
1335:013af6808aca 1336:7993c7bcd04a
1 from __future__ import division
2 import time
3 import numpy
4 import logging
5 import imageio
6 from rdflib import Namespace, RDF, URIRef, Literal
7
8 ROOM = Namespace('http://projects.bigasterisk.com/room/')
9 log = logging.getLogger()
10
11 def _rgbFromHex(h):
12 rrggbb = h.lstrip('#')
13 return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]]
14
15 class PixelColumnsFromImages(object):
16 # could use this instead:
17 # https://github.com/OpenImageIO/oiio/blob/master/src/python/py_imagecache.cpp
18 def __init__(self):
19 self.lastImg = None, None
20
21 def get(self, path, x, y, h):
22 if self.lastImg[0] != path:
23 fp = 'config/' + path
24 self.lastImg = path, imageio.imread(fp) # or etcd or http
25 log.debug('read image from %r', fp)
26 img = self.lastImg[1]
27
28 y = numpy.clip(y, 0, img.shape[0] - 1)
29 h = numpy.clip(h, 1, img.shape[0] - y)
30 x = numpy.clip(x, 0, img.shape[1] - 1)
31 log.info('getcol y=%s h=%s img=%r ret=%r', y, h, img.shape, img[y:y + h,x,:].shape)
32 return img[y:y + h,x,:]
33
34 getPixelColumn = PixelColumnsFromImages().get
35
36
37 class AnimChannel(object):
38 def __init__(self, x):
39 self.x = self.x2 = x
40 self.start = self.end = 0
41
42 def animTo(self, x2, rate):
43 self.get() # bring self.x to current time
44 log.info('animTo %s -> %s', self.x, x2)
45 if x2 == self.x:
46 return
47 self.start = time.time()
48 self.end = self.start + abs(x2 - self.x) / rate
49 self.x0 = self.x
50 self.x2 = x2
51
52 def get(self):
53 now = time.time()
54 if now > self.end:
55 self.x = self.x2
56 else:
57 dur = self.end - self.start
58 self.x = (self.end - now) / dur * self.x0 + (now - self.start) / dur * self.x2
59 return self.x
60
61 class ScanGroup(object):
62
63 def __init__(self, uri, numLeds):
64 self.uri = uri
65 self.current = numpy.zeros((numLeds, 3), dtype=numpy.uint8)
66
67 self.x = AnimChannel(0)
68 self.y = AnimChannel(0)
69 self.height = AnimChannel(numLeds)
70 self.src = ""
71
72 def animateTo(self, x, y, height, src, rate=30, interpolate=ROOM['slide']):
73 log.info('anim to %s x=%s y=%s h=%s', src, x, y, height)
74 self.x.animTo(x, rate)
75 self.y.animTo(y, rate) # need separate y rate?
76 self.height.animTo(height, rate)
77
78 self.src = src
79
80 def updateCurrent(self):
81 try:
82 self.current = getPixelColumn(self.src,
83 int(self.x.get()),
84 int(self.y.get()),
85 int(self.height.get()))
86 except IOError as e:
87 log.warn('getPixelColumn %r', e)
88 log.debug('current = %r', self.current)
89
90 def currentStatements(self):
91 return [
92 (self.uri, RDF.type, ROOM['ScanGroup']),
93 (self.uri, ROOM['xValue'], Literal(self.x.get())),
94 (self.uri, ROOM['yValue'], Literal(self.y.get())),
95 (self.uri, ROOM['heightValue'], Literal(self.height.get())),
96 (self.uri, ROOM['src'], Literal(self.src)),
97 ]
98
99 def colorForIndex(self, i):
100 return list(self.current[i,:])
101
102 args = {ROOM['src']: ('src', str),
103 ROOM['x']: ('x', int),
104 ROOM['y']: ('y', int),
105 ROOM['height']: ('height', int),
106 ROOM['interpolate']: ('interpolate', lambda x: x),
107 ROOM['rate']: ('rate', float),
108 }
109
110
111 class RgbPixelsAnimation(object):
112
113 def __init__(self, graph, uri, updateOutput):
114 """we call updateOutput after any changes"""
115 self.graph = graph
116 self.uri = uri
117 self.updateOutput = updateOutput
118 self.setupGroups()
119
120 def setupGroups(self):
121 self.groups = {}
122 self.groupWithIndex = {}
123 attrStatements = set()
124 for grp in self.graph.objects(self.uri, ROOM['pixelGroup']):
125 s = int(self.graph.value(grp, ROOM['startIndex']))
126 e = int(self.graph.value(grp, ROOM['endIndex']))
127 log.info('ScanGroup %s from %s to %s', grp, s, e)
128 sg = ScanGroup(grp, e - s + 1)
129 self.groups[grp] = [s, e, sg]
130 for i in range(s, e + 1):
131 self.groupWithIndex[i] = sg, i - s
132 attrStatements.update(self.graph.triples((grp, None, None)))
133 self.onStatements(attrStatements, _groups=False)
134
135 def maxIndex(self):
136 return max(v[1] for v in self.groups.itervalues())
137
138 def hostStatements(self):
139 return (
140 [(self.uri, ROOM['pixelGroup'], grp) for grp in self.groups.keys()] +
141 sum([v[2].currentStatements()
142 for v in self.groups.itervalues()], []) +
143 [] # current
144 )
145
146 def getColorOrder(self, graph, uri):
147 colorOrder = graph.value(uri, ROOM['colorOrder'],
148 default=ROOM['ledColorOrder/RGB'])
149 head, tail = str(colorOrder).rsplit('/', 1)
150 if head != str(ROOM['ledColorOrder']):
151 raise NotImplementedError('%r colorOrder %r' % (uri, colorOrder))
152 stripType = None
153 return colorOrder, stripType
154
155 def step(self):
156 # if animating...
157 self.updateOutput()
158
159 def onStatements(self, statements, _groups=True):
160 needSetup = False
161 animateCalls = {} # group uri : kw for animateTo
162 for s, p, o in statements:
163 if s not in self.groups:
164 # missing the case that you just added a new group
165 continue
166 if p in args:
167 k, conv = args[p]
168 animateCalls.setdefault(s, {})[k] = conv(o.toPython())
169 else:
170 needSetup = True
171
172 if needSetup and _groups:
173 self.setupGroups()
174 for grp, kw in animateCalls.items():
175 for pred, (k, conv) in args.items():
176 if k not in kw:
177 v = self.graph.value(grp, pred)
178 if v is not None:
179 kw[k] = conv(v.toPython())
180
181 self.groups[grp][2].animateTo(**kw)
182
183 def outputPatterns(self):
184 pats = []
185 for grp in self.groups:
186 for attr in args:
187 pats.append((grp, attr, None))
188 return pats
189
190 def outputWidgets(self):
191 return []
192
193 def currentColors(self):
194 for _, _, sg in self.groups.values():
195 sg.updateCurrent()
196 for idx in range(self.maxIndex() + 1):
197 sg, offset = self.groupWithIndex[idx]
198 r, g, b = sg.colorForIndex(offset)
199 yield idx, (r, g, b)