<commit_before>/**
 *
 * A track bound to a real value in the scene graph.
 * 
 * @author <NAME> / http://clara.io/
 * @author <NAME> / http://lighthaus.us/
 */

THREE.PropertyBinding = function ( rootNode, trackName ) {

	this.rootNode = rootNode;
	this.trackName = trackName;

	var parseResults = THREE.PropertyBinding.parseTrackName( trackName );

	this.directoryName = parseResults.directoryName || null;
	this.nodeName = parseResults.nodeName;
	this.propertyName = parseResults.propertyName || null;
	this.propertyArrayIndex = parseResults.propertyArrayIndex || -1;

	this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName );

};

THREE.PropertyBinding.prototype = {

	constructor: THREE.PropertyBinding,

	set: function( value ) {

		 console.log( "PropertyBinding.set( " + value + ")" );

 		// ensure there is a value node
		if( ! this.node ) {
			console.log( "  trying to update node for track: " + this.trackName + " but it wasn't found." );
			return;
		}

 		// ensure there is a value property on the node
		var nodeProperty = this.node[ this.propertyName ];
		if( ! nodeProperty ) {
			console.log( "  trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", this );				
			return;
		}

		// access a sub element of the property array (only primitives are supported right now)
		if( this.propertyArrayIndex >= 0 ) {
			console.log( '  update property array ' + this.propertyName + '[' + this.propertyArrayIndex + '] via assignment.' );				
			nodeProperty[ this.propertyArrayIndex ] = value;
		}
		// must use copy for Object3D.Euler/Quaternion		
		else if( nodeProperty.copy ) {
			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via a set() function.' );				
			nodeProperty.copy( value );
		}
		// otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
		else {
			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via assignment.' );				
			this.node[ this.propertyName ] = value;	
		}

		// trigger node dirty			
		if( this.node.needsUpdate !== undefined ) { // material
			console.log( '  triggering material as dirty' );
			this.node.needsUpdate = true;
		}			
		if( this.node.matrixWorldNeedsUpdate !== undefined ) { // node transform
			console.log( '  triggering node as dirty' );
			this.node.matrixWorldNeedsUpdate = true;
		}

	},

	get: function() {

		throw new Error( "TODO" );

	}

};


THREE.PropertyBinding.parseTrackName = function( trackName ) {

	// matches strings in the form of:
	//    nodeName.property
	//    nodeName.property[accessor]
	//    uuid.property[accessor]
	//    parentName/nodeName.property
	//    parentName/parentName/nodeName.property[index]
	// created and tested via https://regex101.com/#javascript

	var re = /^(([\w]+\/)*)([\w-]+)?(\.([\w]+)(\[([\w]+)\])?)?$/; 
	var matches = re.exec(trackName);

	if( ! matches ) {
		throw new Error( "cannot parse trackName at all: " + trackName );
	}

    if (matches.index === re.lastIndex) {
        re.lastIndex++;
    }

	var results = {
		directoryName: matches[0],
		nodeName: matches[3], 	// allowed to be null, specified root node.
		propertyName: matches[5],
		propertySubElement: matches[7]	// allowed to be null, specifies that the whole property is set.
	};

	console.log( "PropertyBinding.parseTrackName", trackName, results, matches );

	if( results.propertyName === null || results.propertyName.length === 0 ) {
		throw new Error( "can not parse propertyName from trackName: " + trackName );
	}

	return results;

};

// TODO: Cache this at some point
THREE.PropertyBinding.findNode = function( root, nodeName ) {

	console.log( 'AnimationUtils.findNode( ' + root.name + ', nodeName: ' + nodeName + ')');
	
	if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {

		console.log( '  root.' );
		return root;<commit_msg>add support for binding to materials.<commit_after>/**
 *
 * A track bound to a real value in the scene graph.
 * 
 * @author <NAME> / http://clara.io/
 * @author <NAME> / http://lighthaus.us/
 */

THREE.PropertyBinding = function ( rootNode, trackName ) {

	this.rootNode = rootNode;
	this.trackName = trackName;

	var parseResults = THREE.PropertyBinding.parseTrackName( trackName );

	this.directoryName = parseResults.directoryName || null;
	this.nodeName = parseResults.nodeName;
	this.material = parseResults.material;
	this.propertyName = parseResults.propertyName || null;
	this.propertyArrayIndex = parseResults.propertyArrayIndex || -1;

	this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName );

};

THREE.PropertyBinding.prototype = {

	constructor: THREE.PropertyBinding,

	set: function( value ) {

		 console.log( "PropertyBinding.set( " + value + ")" );

		 var targetObject = this.node;

 		// ensure there is a value node
		if( ! targetObject ) {
			console.log( "  trying to update node for track: " + this.trackName + " but it wasn't found." );
			return;
		}

		if( this.material ) {
			targetObject = targetObject.material;
		}

 		// ensure there is a value property on the node
		var nodeProperty = targetObject[ this.propertyName ];
		if( ! nodeProperty ) {
			console.log( "  trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", this );				
			return;
		}

		// access a sub element of the property array (only primitives are supported right now)
		if( this.propertyArrayIndex >= 0 ) {
			console.log( '  update property array ' + this.propertyName + '[' + this.propertyArrayIndex + '] via assignment.' );				
			nodeProperty[ this.propertyArrayIndex ] = value;
		}
		// must use copy for Object3D.Euler/Quaternion		
		else if( nodeProperty.copy ) {
			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via a set() function.' );				
			nodeProperty.copy( value );
		}
		// otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
		else {
			console.log( '  update property ' + this.name + '.' + this.propertyName + ' via assignment.' );				
			targetObject[ this.propertyName ] = value;	
		}

		// trigger node dirty			
		if( targetObject.needsUpdate !== undefined ) { // material
			console.log( '  triggering material as dirty' );
			this.node.needsUpdate = true;
		}			
		if( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
			console.log( '  triggering node as dirty' );
			targetObject.matrixWorldNeedsUpdate = true;
		}

	},

	get: function() {

		throw new Error( "TODO" );

	}

};


THREE.PropertyBinding.parseTrackName = function( trackName ) {

	// matches strings in the form of:
	//    nodeName.property
	//    nodeName.property[accessor]
	//    nodeName.material.property[accessor]
	//    uuid.property[accessor]
	//    uuid.material.property[accessor]
	//    parentName/nodeName.property
	//    parentName/parentName/nodeName.property[index]
	// created and tested via https://regex101.com/#javascript

	var re = /^(([\w]+\/)*)([\w-]+)?(\.material)?(\.([\w]+)(\[([\w]+)\])?)?$/; 
	var matches = re.exec(trackName);

	if( ! matches ) {
		throw new Error( "cannot parse trackName at all: " + trackName );
	}

    if (matches.index === re.lastIndex) {
        re.lastIndex++;
    }

	var results = {
		directoryName: matches[0],
		nodeName: matches[3], 	// allowed to be null, specified root node.
		material: ( matches[4] && matches[4].length > 0 ),
		propertyName: matches[6],
		propertySubElement: matches[8]	// allowed to be null, specifies that the whole property is set.
	};

	console.log( "PropertyBinding.parseTrackName", trackName, results, matches );

	if( results.propertyName === null || results.propertyName.length === 0 ) {
		throw new Error( "can not parse propertyName from trackName: " + trackName );
	}

	return results;

};

// TODO: Cache this at some point
THREE.PropertyBinding.findNode = function( root, nodeName ) {

	console.log( 'AnimationUtils.findNode( ' + root.name + ', nodeName: ' + nodeName + ')');
	
	if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {

		console.log( '  root.' );
		return root;
{'fraction_non_alphanumeric': 0.14499521072796934, 'fraction_numerical': 0.003232758620689655, 'mean_word_length': 3.272634271099744, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 10, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3808954', 'n_tokens_mistral': 2811, 'n_tokens_neox': 2551, 'n_words': 695}