Browse Source

[PropertyBinding] Support for '.' in node names, and unit tests.

Don McCurdy 8 years ago
parent
commit
18bdfdc7d7
2 changed files with 260 additions and 32 deletions
  1. 64 30
      src/animation/PropertyBinding.js
  2. 196 2
      test/unit/src/animation/PropertyBinding.js

+ 64 - 30
src/animation/PropertyBinding.js

@@ -102,47 +102,81 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	parseTrackName: function ( trackName ) {
+	parseTrackName: function () {
 
-		// matches strings in the form of:
-		//    nodeName.property
-		//    nodeName.property[accessor]
-		//    nodeName.material.property[accessor]
-		//    uuid.property[accessor]
-		//    uuid.objectName[objectIndex].propertyName[propertyIndex]
-		//    parentName/nodeName.property
-		//    parentName/parentName/nodeName.property[index]
-		//    .bone[Armature.DEF_cog].position
-		//    scene:helium_balloon_model:helium_balloon_model.position
-		// created and tested via https://regex101.com/#javascript
+		// Parent directories, delimited by '/' or ':'. Currently unused, but must
+		// be matched to parse the rest of the track name.
+		var directoryRe = /((?:[\w-]+[\/:])*)/;
 
-		var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
-		var matches = re.exec( trackName );
+		// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'
+		// characters, but must begin and end with a word character.
+		var nodeRe = /(\w(?:[\w-\.]*\w)?)?/;
 
-		if ( ! matches ) {
+		// Object on target node, and accessor. May contain only word characters,
+		// and must be a member of the supportedObjectNames whitelist. Accessor may
+		// contain any non-bracket characters.
+		var objectRe = /(?:\.([\w-]+)(?:\[(.+)\])?)?/;
 
-			throw new Error( "cannot parse trackName at all: " + trackName );
+		// Property and accessor. May contain only word characters. Accessor may
+		// contain any non-bracket characters.
+		var propertyRe = /\.([\w-]+)(?:\[(.+)\])?/;
 
-		}
+		var trackRe = new RegExp(''
+			+ '^'
+			+ directoryRe.source
+			+ nodeRe.source
+			+ objectRe.source
+			+ propertyRe.source
+			+ '$'
+		);
 
-		var results = {
-			// directoryName: matches[ 1 ], // (tschw) currently unused
-			nodeName: matches[ 2 ], 	// allowed to be null, specified root node.
-			objectName: matches[ 3 ],
-			objectIndex: matches[ 4 ],
-			propertyName: matches[ 5 ],
-			propertyIndex: matches[ 6 ]	// allowed to be null, specifies that the whole property is set.
-		};
+		var supportedObjectNames = [ 'material', 'materials', 'bones' ];
 
-		if ( results.propertyName === null || results.propertyName.length === 0 ) {
+		return function ( trackName ) {
 
-			throw new Error( "can not parse propertyName from trackName: " + trackName );
+				var matches = trackRe.exec( trackName );
 
-		}
+				if ( ! matches ) {
 
-		return results;
+					throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );
 
-	},
+				}
+
+				var results = {
+					// directoryName: matches[ 1 ], // (tschw) currently unused
+					nodeName: matches[ 2 ], 	// allowed to be null, specified root node.
+					objectName: matches[ 3 ],
+					objectIndex: matches[ 4 ],
+					propertyName: matches[ 5 ],
+					propertyIndex: matches[ 6 ]	// allowed to be null, specifies that the whole property is set.
+				};
+
+				var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );
+
+				if ( lastDot !== undefined && lastDot !== -1 ) {
+
+					var objectName = results.nodeName.substring( lastDot + 1 );
+
+					if ( supportedObjectNames.indexOf( objectName ) !== -1 ) {
+
+						results.nodeName = results.nodeName.substring( 0, lastDot )
+						results.objectName = objectName;
+
+					}
+
+				}
+
+				if ( results.propertyName === null || results.propertyName.length === 0 ) {
+
+					throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );
+
+				}
+
+				return results;
+
+			};
+
+	}(),
 
 	findNode: function ( root, nodeName ) {
 

+ 196 - 2
test/unit/src/animation/PropertyBinding.js

@@ -2,5 +2,199 @@
  * @author TristanVALCKE / https://github.com/TristanVALCKE
  */
 
-//Todo
-console.warn("Todo: Unit tests of PropertyBinding")
+QUnit.module( 'BufferAttribute' );
+
+QUnit.test( 'parseTrackName' , function( assert ) {
+
+	var paths = [
+
+		[
+			'.property',
+			{
+				nodeName: undefined,
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'nodeName.property',
+			{
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'a.property',
+			{
+				nodeName: 'a',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.de.Name.property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.d-e.Name.property',
+			{
+				nodeName: 'no.d-e.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'nodeName.property[accessor]',
+			{
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'nodeName.material.property[accessor]',
+			{
+				nodeName: 'nodeName',
+				objectName: 'material',
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'no.de.Name.material.property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: 'material',
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.de.Name.material[materialIndex].property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: 'material',
+				objectIndex: 'materialIndex',
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'uuid.property[accessor]',
+			{
+				nodeName: 'uuid',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'uuid.objectName[objectIndex].propertyName[propertyIndex]',
+			{
+				nodeName: 'uuid',
+				objectName: 'objectName',
+				objectIndex: 'objectIndex',
+				propertyName: 'propertyName',
+				propertyIndex: 'propertyIndex'
+			}
+		],
+
+		[
+			'parentName/nodeName.property',
+			{
+				// directoryName is currently unused.
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'parentName/no.de.Name.property',
+			{
+				// directoryName is currently unused.
+				nodeName: 'no.de.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'parentName/parentName/nodeName.property[index]',
+			{
+				// directoryName is currently unused.
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'index'
+			}
+		],
+
+		[
+			'.bone[Armature.DEF_cog].position',
+			{
+				nodeName: undefined,
+				objectName: 'bone',
+				objectIndex: 'Armature.DEF_cog',
+				propertyName: 'position',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'scene:helium_balloon_model:helium_balloon_model.position',
+			{
+				nodeName: 'helium_balloon_model',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'position',
+				propertyIndex: undefined
+			}
+		]
+	];
+
+	paths.forEach( function ( path, i ) {
+
+		assert.smartEqual(
+			THREE.PropertyBinding.parseTrackName( path[ 0 ] ),
+			path[ 1 ],
+			'Parses track name: ' +  path[ 0 ]
+		);
+
+	} );
+});