[Bf-extensions-cvs] [89308ed] master: Fix T43259: Mocap Tools - Index out of range in "Samples to Beziers"

Bastien Montagne noreply at git.blender.org
Fri Jan 16 17:41:50 CET 2015


Commit: 89308ed082f58bb568fbf7a684d063448566b86b
Author: Bastien Montagne
Date:   Fri Jan 16 17:30:50 2015 +0100
Branches: master
https://developer.blender.org/rBA89308ed082f58bb568fbf7a684d063448566b86b

Fix T43259: Mocap Tools - Index out of range in "Samples to Beziers"

Main issue was that the addon did not handled 'sampled_points' at all.
Now it converts such curves back to keyframes ones first.
This might not be optimal, but avoids having too different read logic,
and we cannot do anything to sampled fcurves from python anyway...

Also did some other fixes/enhancements:
* Fixed behavior with groups of fcurves when they did not have same keyframes
  (was giving full crappy results with odd offsets).
* In `fitCubic()`, do not call `maxErrorAmount()` twice unless it's really needed.
* Use 'fast' remove/insert of keyframes!
* Use comprehensions to generate the init points data, saves some code lines
  and is usually quicker.

Note performance gains (though mesurable) are not so fancy, we could probably
optimize much more by avoiding to use that custom nD vector class and all
(and probably even much much more by using numpy), most of the time being
spent inside complex math helpers, but for now it should work as expected at least.

===================================================================

M	mocap/__init__.py
M	mocap/mocap_tools.py

===================================================================

diff --git a/mocap/__init__.py b/mocap/__init__.py
index fd69f89..5dca806 100644
--- a/mocap/__init__.py
+++ b/mocap/__init__.py
@@ -21,8 +21,8 @@
 bl_info = {
     "name": "Motion Capture Tools",
     "author": "Benjy Cook",
-    "blender": (2, 72, 0),
-    "version": (1, 0, 1),
+    "blender": (2, 73, 0),
+    "version": (1, 1, 1),
     "location": "Object UI > Mocap tools",
     "description": "Various tools for working with motion capture animation",
     "warning": "",
diff --git a/mocap/mocap_tools.py b/mocap/mocap_tools.py
index 91c92a9..3f91041 100644
--- a/mocap/mocap_tools.py
+++ b/mocap/mocap_tools.py
@@ -18,7 +18,7 @@
 
 # <pep8 compliant>
 
-from math import sqrt, radians
+from math import sqrt, radians, floor, ceil
 import bpy
 import time
 from mathutils import Vector, Matrix
@@ -108,6 +108,13 @@ class dataPoint:
         self.u = u
 
 
+# Helper to convert from a sampled fcurve back to editable keyframes one.
+def make_editable_fcurves(fcurves):
+    for fc in fcurves:
+        if fc.sampled_points:
+            fc.convert_to_keyframes(floor(fc.sampled_points[0].co[0]), ceil(fc.sampled_points[-1].co[0]) + 1)
+
+
 #Cross Correlation Function
 #http://en.wikipedia.org/wiki/Cross_correlation
 #IN:   curvesA, curvesB - bpy_collection/list of fcurves to analyze. Auto-Correlation is when they are the same.
@@ -233,7 +240,6 @@ def autoloop_anim():
 #         group_mode - boolean, indicating wether we should place bezier keyframes on the same x (frame), or optimize each individual curve.
 #OUT: None. Deletes the existing curves and creates the new beziers.
 def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
-
     #Calculates the unit tangent of point v
     def unitTangent(v, data_pts):
         tang = NdVector((0, 0, 0, 0, 0))
@@ -416,26 +422,17 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
 
     #Create data_pts, a list of dataPoint type, each is assigned index i, and an NdVector
     def createDataPts(curveGroup, group_mode):
-        data_pts = []
+        make_editable_fcurves(curveGroup if group_mode else (curveGroup,))
+
         if group_mode:
             print([x.data_path for x in curveGroup])
-            for i in range(len(curveGroup[0].keyframe_points)):
-                x = curveGroup[0].keyframe_points[i].co.x
-                y1 = curveGroup[0].evaluate(i)
-                y2 = curveGroup[1].evaluate(i)
-                y3 = curveGroup[2].evaluate(i)
-                y4 = 0
-                if len(curveGroup) == 4:
-                    y4 = curveGroup[3].evaluate(i)
-                data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4))))
+            comp_cos = (0,) * (4 - len(curveGroup))  # We need to add that number of null cos to get our 5D vector.
+            kframes = sorted(set(kf.co.x for fc in curveGroup for kf in fc.keyframe_points))
+            data_pts = [dataPoint(i, NdVector((fra,) + tuple(fc.evaluate(fra) for fc in curveGroup) + comp_cos))
+                        for i, fra in enumerate(kframes)]
         else:
-            for i in range(len(curveGroup.keyframe_points)):
-                x = curveGroup.keyframe_points[i].co.x
-                y1 = curveGroup.keyframe_points[i].co.y
-                y2 = 0
-                y3 = 0
-                y4 = 0
-                data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4))))
+            data_pts = [dataPoint(i, NdVector((kf.co.x, kf.co.y, 0, 0, 0)))
+                        for i, kf in enumerate(curveGroup.keyframe_points)]
         return data_pts
 
     #Recursively fit cubic beziers to the data_pts between s and e
@@ -460,8 +457,8 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
                 else:
                     bez = fitSingleCubic(data_pts, s, e)
 
-        #recalculate max error and point where it occurs
-        maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e)
+            #recalculate max error and point where it occurs
+            maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e)
 
         #repara wasn't enough, we need 2 beziers for this range.
         #Split the bezier at point of maximum error
@@ -479,37 +476,44 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
         if group_mode:
             for fcurve in curveGroup:
                 for i in range(len(fcurve.keyframe_points) - 1, 0, -1):
-                    fcurve.keyframe_points.remove(fcurve.keyframe_points[i])
+                    fcurve.keyframe_points.remove(fcurve.keyframe_points[i], fast=True)
         else:
             fcurve = curveGroup
             for i in range(len(fcurve.keyframe_points) - 1, 0, -1):
-                fcurve.keyframe_points.remove(fcurve.keyframe_points[i])
+                fcurve.keyframe_points.remove(fcurve.keyframe_points[i], fast=True)
 
-        #insert the calculated beziers to blender data.\
+        #insert the calculated beziers to blender data.
         if group_mode:
             for fullbez in beziers:
                 for i, fcurve in enumerate(curveGroup):
                     bez = [Vector((vec[0], vec[i + 1])) for vec in fullbez]
-                    newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y)
+                    newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y, options={'FAST'})
                     newKey.handle_right = (bez[1].x, bez[1].y)
 
-                    newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y)
+                    newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y, options={'FAST'})
                     newKey.handle_left = (bez[2].x, bez[2].y)
         else:
             for bez in beziers:
                 for vec in bez:
                     vec.resize_2d()
-                newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y)
+                newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y, options={'FAST'})
                 newKey.handle_right = (bez[1].x, bez[1].y)
 
-                newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y)
+                newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y, options={'FAST'})
                 newKey.handle_left = (bez[2].x, bez[2].y)
 
+        # We used fast remove/insert, time to update the curves!
+        for fcurve in (curveGroup if group_mode else (curveGroup,)):
+            fcurve.update()
+
     # indices are detached from data point's frame (x) value and
     # stored in the dataPoint object, represent a range
 
     data_pts = createDataPts(curveGroup, group_mode)
 
+    if not data_pts:
+        return
+
     s = 0  # start
     e = len(data_pts) - 1  # end
 
@@ -517,6 +521,7 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode):
 
     #begin the recursive fitting algorithm.
     fitCubic(data_pts, s, e)
+
     #remove old Fcurves and insert the new ones
     createNewCurves(curveGroup, beziers, group_mode)
 
@@ -607,6 +612,8 @@ def denoise(obj, fcurves):
     Implementation of non-linear blur filter.
     Finds spikes in the fcurve, and replaces spikes that are too big with the average of the surrounding keyframes.
     """
+    make_editable_fcurves(fcurves)
+
     for fcurve in fcurves:
         org_pts = fcurve.keyframe_points[:]



More information about the Bf-extensions-cvs mailing list