From 9f5f278d14af6029c8d4d53c9d93b0f9c409bf8a Mon Sep 17 00:00:00 2001
From: Ben Connors <benconnors@outlook.com>
Date: Sun, 1 Dec 2019 13:44:13 -0500
Subject: Finish implementing joins; fix chaserstep duration

---
 blc2/functions/chaser.py     | 22 ++++++++++++++++++++--
 blc2/functions/chaserstep.py | 12 +++++++-----
 blc2/functions/join.py       |  6 +++---
 blc2/workspace.py            |  3 +++
 4 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/blc2/functions/chaser.py b/blc2/functions/chaser.py
index 2f91dcb..cbf4636 100644
--- a/blc2/functions/chaser.py
+++ b/blc2/functions/chaser.py
@@ -266,13 +266,31 @@ class Chaser(Function):
         ## Render
         lc = {c: 0 for c in self._scope}
         ac = []
+
+        done = []
+        fading = []
         for n, d in enumerate(data.steps):
             s = d.step
-            slc, sac, data.steps[n] = s.render(t, data=d)
+            slc, sac, data.steps[n], mul = s.render(t, data=d)
+            if slc:
+                if mul == 1:
+                    done.append(slc)
+                else:
+                    fading.append((abs(mul), slc))
+            ac.extend(sac)
+
+        ## Handle the lighting demuxing
+
+        ## First do the fade-finished ones
+        for slc in done:
             for c, v in slc:
                 if lc[c] < v:
                     lc[c] = v
-            ac.extend(sac)
+
+        ## Now do the fading ones additively
+        for mul, slc in fading:
+            for c, v in slc:
+                lc[c] = min(255, int(v*mul) + lc[c])
 
         return tuple(lc.items()), ac, data
 
diff --git a/blc2/functions/chaserstep.py b/blc2/functions/chaserstep.py
index 597b760..3d458a1 100644
--- a/blc2/functions/chaserstep.py
+++ b/blc2/functions/chaserstep.py
@@ -19,7 +19,7 @@ class ChaserStep(Function):
         super().__init__(w=chaser.w, id_=id_, name=name)
         self._function = None
         self._duration_mode = duration_mode
-        self._duration = duration
+        self._duration = duration + fade_in
         self._actual_duration = 0
         self._fade_out = fade_out
         self._fade_in = fade_in
@@ -117,7 +117,10 @@ class ChaserStep(Function):
     def fade_in(self, v):
         if v < 0:
             raise ValueError("Fades must be nonnegative")
+
+        self._duration -= self._fade_in
         self._fade_in = v
+        self._duration += v
         self._recalculate_duration()
 
     @property
@@ -189,7 +192,7 @@ class ChaserStep(Function):
             raise AttributeError("Can't set duration in inherit mode")
         elif value < 0:
             raise ValueError("Duration must be nonnegative")
-        self._duration = value
+        self._duration = value + self._fade_in
         self._recalculate_duration()
 
     @property
@@ -241,14 +244,13 @@ class ChaserStep(Function):
         if t < self.fade_in:
             mul *= min(1, t/self.fade_in)
         if fade_start < t <= data.actual_duration:
-            mul *= 1 - min(1, (t-fade_start)/self.fade_out)
+            mul *= -1*(1 - min(1, (t-fade_start)/self.fade_out))
 
         ## Render and fade cues
         lc, ac, data.data = self._function.render(t, data=data.data)
-        lc = tuple(((c, int(v*mul)) for c, v in lc))
         ac = tuple(((hash((data.audio_id, aid)), fname, st+data.start_time, max(self.fade_in, fin), min(fade_start, fstart), max(fout, self.fade_out)) for aid, fname, st, fin, fstart, fout in ac))
         
-        return lc, ac, data
+        return lc, ac, data, mul
 
     def serialize(self):
         e = et.Element(BXW+"step")
diff --git a/blc2/functions/join.py b/blc2/functions/join.py
index 9ab2f73..8c911bf 100644
--- a/blc2/functions/join.py
+++ b/blc2/functions/join.py
@@ -76,11 +76,11 @@ class Join(Function):
     def add_step(self, s):
         if s.id in (i.id for i in self._steps):
             raise ValueError("Already added")
-        self._steps.insert(-1, s)
+        self._steps.append(s)
         self._recalculate()
 
         self.w.register_function_change_callback(s, self._recalculate, self)
-        self.w.register_function_delete_ballback(s, self._recalculate, self)
+        self.w.register_function_delete_callback(s, self._recalculate, self)
 
     def delete_step(self, s, index=False):
         if index:
@@ -116,7 +116,7 @@ class Join(Function):
         e.set("name", self.name)
         for s in self.steps:
             se = et.SubElement(e, BXW+"member")
-            se.set("id", s.id)
+            se.set("id", str(s.id))
 
         return e
 
diff --git a/blc2/workspace.py b/blc2/workspace.py
index 1983ac7..799a6f3 100644
--- a/blc2/workspace.py
+++ b/blc2/workspace.py
@@ -223,6 +223,7 @@ class Workspace(XMLSerializable):
         from .functions.scene import Scene
         from .functions.audio import Audio
         from .functions.chaser import Chaser
+        from .functions.join import Join
 
         if e.tag != BXW+"workspace":
             raise LoadError("Root tag must be workspace")
@@ -254,6 +255,8 @@ class Workspace(XMLSerializable):
                 Scene.deserialize(w, function)
             elif type_ == CHASER:
                 Chaser.deserialize(w, function)
+            elif type_ == JOIN:
+                Join.deserialize(w, function)
             else:
                 raise LoadError("Unknown function type \"%s\"" % type_)
 
-- 
cgit v1.2.3