[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [35765] trunk/blender: Graph Editor: Euler Filter ported from Py to C

Joshua Leung aligorith at gmail.com
Fri Mar 25 04:58:22 CET 2011


Revision: 35765
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=35765
Author:   aligorith
Date:     2011-03-25 03:58:21 +0000 (Fri, 25 Mar 2011)
Log Message:
-----------
Graph Editor: Euler Filter ported from Py to C

Ported joeedh's Euler Filter code from Python to C so that this is
more in line with the other Graph Editor tools - i.e. joeedh's version
only worked on the active bone's curves, while standard tools could
work with multiple bones/objects at the same time.

To use this new version of this operator:
1) Select all the F-Curves for all 3 of the components (XYZ) for the
euler rotations you wish to clean up. In the Graph Editor, they must
be one after the other (i.e. you can't have "RotX, RotY, something
else, RotZ")
2) Activate the operator from the Key menu in the Graph Editor


In an old test file I have floating around, this method did not appear
to be good enough to fix a very clear discontinuity in the middle of
the action, so I'll test some additional methods too

Modified Paths:
--------------
    trunk/blender/source/blender/editors/space_graph/graph_edit.c
    trunk/blender/source/blender/editors/space_graph/graph_intern.h
    trunk/blender/source/blender/editors/space_graph/graph_ops.c

Removed Paths:
-------------
    trunk/blender/release/scripts/startup/bl_operators/fcurve_euler_filter.py

Deleted: trunk/blender/release/scripts/startup/bl_operators/fcurve_euler_filter.py
===================================================================
--- trunk/blender/release/scripts/startup/bl_operators/fcurve_euler_filter.py	2011-03-25 02:12:44 UTC (rev 35764)
+++ trunk/blender/release/scripts/startup/bl_operators/fcurve_euler_filter.py	2011-03-25 03:58:21 UTC (rev 35765)
@@ -1,78 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-#  This program is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU General Public License
-#  as published by the Free Software Foundation; either version 2
-#  of the License, or (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software Foundation,
-#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-# <pep8 compliant>
-
-import bpy
-
-
-def main(context):
-    from math import pi
-
-    def cleanupEulCurve(fcv):
-        keys = []
-
-        for k in fcv.keyframe_points:
-            keys.append([k.handle_left.copy(), k.co.copy(), k.handle_right.copy()])
-
-        for i in range(len(keys)):
-            cur = keys[i]
-            prev = keys[i - 1] if i > 0 else None
-            next = keys[i + 1] if i < len(keys) - 1 else None
-
-            if prev is None:
-                continue
-
-            th = pi
-            if abs(prev[1][1] - cur[1][1]) >= th:  # more than 180 degree jump
-                fac = pi * 2.0
-                if prev[1][1] > cur[1][1]:
-                    while abs(cur[1][1] - prev[1][1]) >= th:  # < prev[1][1]:
-                        cur[0][1] += fac
-                        cur[1][1] += fac
-                        cur[2][1] += fac
-                elif prev[1][1] < cur[1][1]:
-                    while abs(cur[1][1] - prev[1][1]) >= th:
-                        cur[0][1] -= fac
-                        cur[1][1] -= fac
-                        cur[2][1] -= fac
-
-        for i in range(len(keys)):
-            for x in range(2):
-                fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
-                fcv.keyframe_points[i].co[x] = keys[i][1][x]
-                fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
-
-    flist = bpy.context.active_object.animation_data.action.fcurves
-    for f in flist:
-        if f.select and f.data_path.endswith("rotation_euler"):
-            cleanupEulCurve(f)
-
-
-class DiscontFilterOp(bpy.types.Operator):
-    """Fixes the most common causes of gimbal lock in the fcurves of the active bone"""
-    bl_idname = "graph.euler_filter"
-    bl_label = "Filter out discontinuities in the active fcurves"
-
-    @classmethod
-    def poll(cls, context):
-        return context.active_object != None
-
-    def execute(self, context):
-        main(context)
-        return {'FINISHED'}

Modified: trunk/blender/source/blender/editors/space_graph/graph_edit.c
===================================================================
--- trunk/blender/source/blender/editors/space_graph/graph_edit.c	2011-03-25 02:12:44 UTC (rev 35764)
+++ trunk/blender/source/blender/editors/space_graph/graph_edit.c	2011-03-25 03:58:21 UTC (rev 35765)
@@ -1501,12 +1501,13 @@
  * of values to -180 degrees to 180 degrees.
  */
 
-#if 0 // XXX this is not ready for the primetime yet
- 
 /* set of three euler-rotation F-Curves */
 typedef struct tEulerFilter {
+	struct tEulerFilter *next, *prev;
+	
 	ID *id;							/* ID-block which owns the channels */
-	FCurve (*fcurves)[3];			/* 3 Pointers to F-Curves */				
+	FCurve *(fcurves[3]);			/* 3 Pointers to F-Curves */	
+	char *rna_path;					/* Pointer to one of the RNA Path's used by one of the F-Curves */
 } tEulerFilter;
  
 static int graphkeys_euler_filter_exec (bContext *C, wmOperator *op)
@@ -1518,7 +1519,8 @@
 	int filter;
 	
 	ListBase eulers = {NULL, NULL};
-	tEulerFilter *euf= NULL;	
+	tEulerFilter *euf= NULL;
+	int groups=0, failed=0;
 	
 	/* get editor data */
 	if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1528,7 +1530,7 @@
 	 * 	 1) Sets of three related rotation curves are identified from the selected channels,
 	 *		and are stored as a single 'operation unit' for the next step
 	 *	 2) Each set of three F-Curves is processed for each keyframe, with the values being
-	 * 		processed according to one of several ways.
+	 * 		processed as necessary
 	 */
 	 
 	/* step 1: extract only the rotation f-curves */
@@ -1542,45 +1544,134 @@
 		 *	- only rotation curves
 		 *	- for pchan curves, make sure we're only using the euler curves
 		 */
-		if (strstr(fcu->rna_path, "rotation_euler") == 0)
+		if (strstr(fcu->rna_path, "rotation_euler") == NULL)
 			continue;
+		else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) {
+			BKE_reportf(op->reports, RPT_WARNING,
+				"Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)", 
+				(ale->id)? ale->id->name:"<No ID>", fcu->rna_path, fcu->array_index);
+			continue;
+		}
 		
-		/* check if current set of 3-curves is suitable to add this curve to 
-		 *	- things like whether the current set of curves is 'full' should be checked later only
-		 *	- first check if id-blocks are compatible
+		/* optimisation: assume that xyz curves will always be stored consecutively,
+		 * so if the paths or the ID's don't match up, then a curve needs to be added 
+		 * to a new group
 		 */
-		if ((euf) && (ale->id != euf->id)) {
-			/* if the paths match, add this curve to the set of curves */
-			// NOTE: simple string compare for now... could be a bit more fancy...
-			
+		if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path)==0)) {
+			/* this should be fine to add to the existing group then */
+			euf->fcurves[fcu->array_index]= fcu;
 		}
 		else {
 			/* just add to a new block */
 			euf= MEM_callocN(sizeof(tEulerFilter), "tEulerFilter");
 			BLI_addtail(&eulers, euf);
+			groups++;
 			
 			euf->id= ale->id;
+			euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */
 			euf->fcurves[fcu->array_index]= fcu;
 		}
 	}
 	BLI_freelistN(&anim_data);
 	
+	if (groups == 0) {
+		BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
+		return OPERATOR_CANCELLED;
+	}
+	
 	/* step 2: go through each set of curves, processing the values at each keyframe 
 	 *	- it is assumed that there must be a full set of keyframes at each keyframe position
 	 */
 	for (euf= eulers.first; euf; euf= euf->next) {
+		int f;
 		
+		/* sanity check: ensure that there are enough F-Curves to work on in this group */
+		// TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same?
+		if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
+			/* report which components are missing */
+			BKE_reportf(op->reports, RPT_WARNING,
+				"Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
+				(euf->fcurves[0]==NULL)? "X":"",
+				(euf->fcurves[1]==NULL)? "Y":"",
+				(euf->fcurves[2]==NULL)? "Z":"",
+				euf->id->name, euf->rna_path);
+				
+			/* keep track of number of failed sets, and carry on to next group */
+			failed++;
+			continue;
+		}
+		
+		/* simple method: just treat any difference between keys of greater than 180 degrees as being a flip */
+		// FIXME: there are more complicated methods that will be needed to fix more cases than just some
+		for (f = 0; f < 3; f++) {
+			FCurve *fcu = euf->fcurves[f];
+			BezTriple *bezt, *prev=NULL;
+			unsigned int i;
+			
+			/* skip if not enough vets to do a decent analysis of... */
+			if (fcu->totvert <= 2)
+				continue;
+			
+			/* prev follows bezt, bezt = "current" point to be fixed */
+			for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, prev=bezt, bezt++) {
+				/* our method depends on determining a "difference" from the previous vert */
+				if (prev == NULL)
+					continue;
+				
+				/* > 180 degree flip? */
+				if (fabs(prev->vec[1][1] - bezt->vec[1][1]) >= M_PI) {
+					/* 360 degrees to add/subtract frame value until difference is acceptably small that there's no more flip */
+					const double fac = 2.0 * M_PI;
+					
+					if (prev->vec[1][1] > bezt->vec[1][1]) {
+						while (fabs(bezt->vec[1][1] - prev->vec[1][1]) >= M_PI) {
+							bezt->vec[0][1] += fac;
+							bezt->vec[1][1] += fac;
+							bezt->vec[2][1] += fac;
+						}
+					}
+					else /* if (prev->vec[1][1] < bezt->vec[1][1]) */ {
+						while (fabs(bezt->vec[1][1] - prev->vec[1][1]) >= M_PI) {
+							bezt->vec[0][1] -= fac;
+							bezt->vec[1][1] -= fac;
+							bezt->vec[2][1] -= fac;
+						}
+					}
+				}
+			}
+		}
 	}
 	BLI_freelistN(&eulers);
 	
-	return OPERATOR_FINISHED;
+	/* updates + finishing warnings */
+	if (failed == groups) {
+		BKE_report(op->reports, RPT_ERROR, 
+			"No Euler Rotations could be corrected. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
+		return OPERATOR_CANCELLED;
+	}
+	else {
+		if (failed) {
+			BKE_report(op->reports, RPT_ERROR,
+				"Some Euler Rotations couldn't be corrected due to missing/unselected/out-of-order F-Curves. Ensure each rotation has keys for all components, and that F-Curves for these are in consecutive XYZ order and selected.");
+		}
+		
+		/* validate keyframes after editing */
+		ANIM_editkeyframes_refresh(&ac);
+		
+		/* set notifier that keyframes have changed */
+		WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME|NA_EDITED, NULL);
+		
+		/* done at last */
+		return OPERATOR_FINISHED;
+	}
 }
  
 void GRAPH_OT_euler_filter (wmOperatorType *ot)
 {
 	/* identifiers */
-	ot->name= "Euler Filter";
+	ot->name= "Euler Discontinuity Filter";
 	ot->idname= "GRAPH_OT_euler_filter";
+	ot->description= "Fixes the most common causes of gimbal lock in the selected Euler Rotation F-Curves";
 	
 	/* api callbacks */
 	ot->exec= graphkeys_euler_filter_exec;
@@ -1590,8 +1681,6 @@
 	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-#endif // XXX this is not ready for the primetime yet
-
 /* ***************** Jump to Selected Frames Operator *********************** */
 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list