changeset 1555:14508266a00a

more work on solver api updates. Ignore-this: 52382237d531582cbf64e7a95acf4547
author Drew Perttula <drewp@bigasterisk.com>
date Mon, 22 May 2017 06:42:37 +0000
parents 7f1852dc8304
children 61f3f378cc62
files light9/effect/settings.py light9/effect/settings_test.py light9/paint/solve.py light9/paint/solve_test.py
diffstat 4 files changed, 70 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/light9/effect/settings.py	Mon May 22 06:42:20 2017 +0000
+++ b/light9/effect/settings.py	Mon May 22 06:42:37 2017 +0000
@@ -1,14 +1,23 @@
+from __future__ import division
 """
 Data structure and convertors for a table of (device,attr,value)
 rows. These might be effect attrs ('strength'), device attrs ('rx'),
 or output attrs (dmx channel).
 """
 import decimal
+import numpy
 from rdflib import URIRef, Literal
 from light9.namespaces import RDF, L9, DEV
 from light9.rdfdb.patch import Patch
 
 
+def parseHex(h):
+    if h[0] != '#': raise ValueError(h)
+    return [int(h[i:i+2], 16) for i in 1, 3, 5]
+
+def toHex(rgbFloat):
+    return '#%02x%02x%02x' % tuple(int(v * 255) for v in rgbFloat)
+
 def getVal(graph, subj):
     lit = graph.value(subj, L9['value']) or graph.value(subj, L9['scaledValue'])
     ret = lit.toPython()
@@ -18,12 +27,12 @@
 
 class _Settings(object):
     """
-    default values are 0. Internal rep must not store zeros or some
+    default values are 0 or '#000000'. Internal rep must not store zeros or some
     comparisons will break.
     """
     def __init__(self, graph, settingsList):
         self.graph = graph # for looking up all possible attrs
-        self._compiled = {} # dev: { attr: val }
+        self._compiled = {} # dev: { attr: val }; val is number or colorhex
         for row in settingsList:
             self._compiled.setdefault(row[0], {})[row[1]] = row[2]
         # self._compiled may not be final yet- see _fromCompiled
@@ -50,14 +59,26 @@
     @classmethod
     def fromVector(cls, graph, vector):
         compiled = {}
-        for (d, a), v in zip(cls(graph, [])._vectorKeys(), vector):
+        i = 0
+        for (d, a) in cls(graph, [])._vectorKeys():
+            if a == L9['color']:
+                v = toHex(vector[i:i+3])
+                i += 3
+            else:
+                v = vector[i]
+                i += 1
             compiled.setdefault(d, {})[a] = v
         return cls._fromCompiled(graph, compiled)
 
+    def _zeroForAttr(self, attr):
+        if attr == L9['color']:
+            return '#000000'
+        return 0
+
     def _delZeros(self):
         for dev, av in self._compiled.items():
             for attr, val in av.items():
-                if val == 0:
+                if val == self._zeroForAttr(attr):
                     del av[attr]
             if not av:
                 del self._compiled[dev]
@@ -81,7 +102,7 @@
         def accum():
             for dev, av in self._compiled.iteritems():
                 for attr, val in av.iteritems():
-                    words.append('%s.%s=%g' % (dev.rsplit('/')[-1],
+                    words.append('%s.%s=%s' % (dev.rsplit('/')[-1],
                                                attr.rsplit('/')[-1],
                                                val))
                     if len(words) > 5:
@@ -91,7 +112,7 @@
         return '<%s %s>' % (self.__class__.__name__, ' '.join(words))
         
     def getValue(self, dev, attr):
-        return self._compiled.get(dev, {}).get(attr, 0)
+        return self._compiled.get(dev, {}).get(attr, self._zeroForAttr(attr))
 
     def _vectorKeys(self):
         """stable order of all the dev,attr pairs for this type of settings"""
@@ -111,7 +132,12 @@
     def toVector(self):
         out = []
         for dev, attr in self._vectorKeys():
-            out.append(self._compiled.get(dev, {}).get(attr, 0))
+            # color components may need to get spread out
+            v = self.getValue(dev, attr)
+            if attr == L9['color']:
+                out.extend([x / 255 for x in parseHex(v)])
+            else:
+                out.append(v)
         return out
 
     def byDevice(self):
@@ -123,14 +149,9 @@
                                             {dev: self._compiled.get(dev, {})})
         
     def distanceTo(self, other):
-        raise NotImplementedError
-        dist = 0
-        for key in set(attrs1).union(set(attrs2)):
-            if key not in attrs1 or key not in attrs2:
-                dist += 999
-            else:
-                dist += abs(attrs1[key] - attrs2[key])
-        return dist
+        diff = numpy.array(self.toVector()) - other.toVector()
+        d = numpy.linalg.norm(diff, ord=None)
+        return d
 
     def statements(self, subj, ctx, settingRoot, settingsSubgraphCache):
         """
--- a/light9/effect/settings_test.py	Mon May 22 06:42:20 2017 +0000
+++ b/light9/effect/settings_test.py	Mon May 22 06:42:37 2017 +0000
@@ -12,7 +12,7 @@
 
     def testToVectorZero(self):
         ds = DeviceSettings(self.graph, [])
-        self.assertEqual([0] * 20, ds.toVector())
+        self.assertEqual([0] * 30, ds.toVector())
 
     def testEq(self):
         s1 = DeviceSettings(self.graph, [
@@ -53,17 +53,20 @@
     def testToVector(self):
         v = DeviceSettings(self.graph, [
             (DEV['aura1'], L9['rx'], 0.5),
+            (DEV['aura1'], L9['color'], '#00ff00'),
         ]).toVector()
         self.assertEqual(
-            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], v)
+            [0, 1, 0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+            v)
         
     def testFromVector(self):
         s = DeviceSettings.fromVector(
             self.graph,
-            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+            [0, 1, 0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
         
         self.assertEqual(DeviceSettings(self.graph, [
             (DEV['aura1'], L9['rx'], 0.5),
+            (DEV['aura1'], L9['color'], '#00ff00'),
         ]), s)                            
 
     def testAsList(self):
@@ -75,22 +78,33 @@
 
     def testDevices(self):
         s = DeviceSettings(self.graph, [
-            (L9['aura1'], L9['rx'], 0),
-            (L9['aura2'], L9['rx'], 0.1),
+            (DEV['aura1'], L9['rx'], 0),
+            (DEV['aura2'], L9['rx'], 0.1),
             ])
         # aura1 is all defaults (zeros), so it doesn't get listed
-        self.assertItemsEqual([L9['aura2']], s.devices())
+        self.assertItemsEqual([DEV['aura2']], s.devices())
 
     def testAddStatements(self):
         s = DeviceSettings(self.graph, [
-            (L9['aura2'], L9['rx'], 0.1),
+            (DEV['aura2'], L9['rx'], 0.1),
             ])
         stmts = s.statements(L9['foo'], L9['ctx1'], L9['s_'], set())
         self.maxDiff=None
         self.assertItemsEqual([
-            (L9['foo'], L9['setting'], L9['s_set8011962'], L9['ctx1']),
-            (L9['s_set8011962'], L9['device'], L9['aura2'], L9['ctx1']),
-            (L9['s_set8011962'], L9['deviceAttr'], L9['rx'], L9['ctx1']),
-            (L9['s_set8011962'], L9['value'], Literal(0.1), L9['ctx1']),
+            (L9['foo'], L9['setting'], L9['s_set4350023'], L9['ctx1']),
+            (L9['s_set4350023'], L9['device'], DEV['aura2'], L9['ctx1']),
+            (L9['s_set4350023'], L9['deviceAttr'], L9['rx'], L9['ctx1']),
+            (L9['s_set4350023'], L9['value'], Literal(0.1), L9['ctx1']),
         ], stmts)
         
+    def testDistanceTo(self):
+        s1 = DeviceSettings(self.graph, [
+            (DEV['aura1'], L9['rx'], 0.1),
+            (DEV['aura1'], L9['ry'], 0.6),
+        ])
+        s2 = DeviceSettings(self.graph, [
+            (DEV['aura1'], L9['rx'], 0.3),
+            (DEV['aura1'], L9['ry'], 0.3),
+        ])
+        self.assertEqual(0.36055512754639896, s1.distanceTo(s2))
+        
--- a/light9/paint/solve.py	Mon May 22 06:42:20 2017 +0000
+++ b/light9/paint/solve.py	Mon May 22 06:42:37 2017 +0000
@@ -5,7 +5,7 @@
 import scipy.misc, scipy.ndimage, scipy.optimize
 import cairo
 
-from light9.effect.settings import DeviceSettings
+from light9.effect.settings import DeviceSettings, parseHex, toHex
 
 # numpy images in this file are (x, y, c) layout.
 
@@ -27,13 +27,6 @@
 def saveNumpy(path, img):
     scipy.misc.imsave(path, img.transpose((1, 0, 2)))
 
-def parseHex(h):
-    if h[0] != '#': raise ValueError(h)
-    return [int(h[i:i+2], 16) for i in 1, 3, 5]
-
-def toHex(rgbFloat):
-    return '#%02x%02x%02x' % tuple(int(v * 255) for v in rgbFloat)
-
 def scaledHex(h, scale):
     rgb = parseHex(h)
     rgb8 = (rgb * scale).astype(numpy.uint8)
@@ -66,7 +59,7 @@
                 self.samples[samp] = self.fromPath[base] = loadNumpy(path)
                 self.blurredSamples[samp] = self._blur(self.samples[samp])
                 
-                key = (samp, g.value(samp, L9['path']).toPython())
+                key = (samp, g.value(samp, L9['path']).toPython().encode('utf8'))
                 self.sampleSettings[key] = DeviceSettings.fromResource(self.graph, samp)
 
     def _blur(self, img):
@@ -117,7 +110,7 @@
         brightestSample = brightest(self.samples[sample])
         
         if max(brightest0) < 1 / 255:
-            return []
+            return DeviceSettings(self.graph, [])
 
         scale = brightest0 / brightestSample
 
@@ -138,19 +131,6 @@
                                          slice(0, 1 + colorStep, colorStep),
                                          slice(0, 1 + colorStep, colorStep)]),
         ]
-
-        def settingsFromVector(x):
-            settings = []
-
-            xLeft = x.tolist()
-            for dev, attr, _ in dims:
-                if attr == L9['color']:
-                    rgb = (xLeft.pop(), xLeft.pop(), xLeft.pop())
-                    settings.append((dev, attr, toHex(rgb)))
-                else:
-                    settings.append((dev, attr, xLeft.pop()))
-            return DeviceSettings(self.graph, settings)
-
         
         def drawError(x):
             settings = DeviceSettings.fromVector(self.graph, x)
--- a/light9/paint/solve_test.py	Mon May 22 06:42:20 2017 +0000
+++ b/light9/paint/solve_test.py	Mon May 22 06:42:37 2017 +0000
@@ -7,7 +7,8 @@
 
 class TestSolve(unittest.TestCase):
     def setUp(self):
-        self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/bg.n3'])
+        self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
+                                             'show/dance2017/cam/test/bg.n3'])
         self.solver = solve.Solver(self.graph)
         self.solver.loadSamples()
         self.solveMethod = self.solver.solve
@@ -33,7 +34,8 @@
         
 class TestSimulationLayers(unittest.TestCase):
     def setUp(self):
-        self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/bg.n3'])
+        self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
+                                             'show/dance2017/cam/test/bg.n3'])
         self.solver = solve.Solver(self.graph)
         self.solver.loadSamples()
         
@@ -72,7 +74,8 @@
 
 class TestCombineImages(unittest.TestCase):
     def setUp(self):
-        graph = LocalSyncedGraph(files=['show/dance2017/cam/test/bg.n3'])
+        graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
+                                        'show/dance2017/cam/test/bg.n3'])
         self.solver = solve.Solver(graph)
         self.solver.loadSamples()
     def test(self):