scripting effects properties

Find out why the . goes before the /

Moderator: Paul Tuersley

w_m0zart
Posts: 20
Joined: June 6th, 2005, 1:52 pm
Location: Hengelo, Netherlands
Contact:

This script offers an easy way to add any effect with properties and values to a layer.

It can be done in 3 simple steps. Before doing so, make sure to add the class EffectsObj (see line 1 in example for declaration) somewhere in your code. It will provide easy methods for dealing with effects. Then:

1. Create an object from EffectsObj. This will hold all data for the effect to be applied
2. Pass all data to the object with:
------object.FFArray[];------Effectdata. see the example below
------object.proj;------------your current project
------object.layr;------------the layer in your project to which the effect with settings will be applied.
3. object.Apply();-----------this will add the effect with the effectdata to the layer.

for example:

Code: Select all

01> F      = new EffectsObj;
02> F.proj = app.project;
03> F.layr = F.proj.activeItem.selectedLayers[0];
04> F.FFArray=["ADOBE_EffectName=Remove Grain"   ,
05>            "ADOBE_Property_1=Viewing Mode"   , "3",
06>            "ADOBE_Property_2=Preview Region" , \\ <-------- Optional; The property holds no value.
07>            "ADOBE_Property_3=Center"         , "[553.5,443]",
08>            "ADOBE_Property_4=Width"          , "123",
09>            "ADOBE_Property_5=Height"         , "135"]
10> F.Apply();
11>
12> if (F.guru) alert(F.guru); \\ <-------- Optional
Last line is optional; it will inform you about succes of the previous method. When there was a problem, F.guru holds a string, concerning the specific problem, If F.Apply() was succesful, F.guru will be false.

advantage of objects and arrays
Using objects, gives us the advantage of dealing with several effect settings at the same time. And using an array with strings with effectdata, makes editing uncomplicated and easy to understand.

Dependency problem
When parsing a .tfx file, I discovered the order in which values on certain effectproperties are set, may have impact on previous applied values. (The same problem exists also during manually changing effect values.)
Let's observe the following. Assume: FFbase=this_layer("Effects").addProperty("Remove Grain");
When we set Viewing Mode (1) to Blending Matte (3) with:

Code: Select all

FFbase.property(1).setValue(3);
Now, after we try to set Sample Selection (40) to Manual (2) with:

Code: Select all

FFbase.property(40).setValue(2)
suddenly also the previous setting had been modified -unwantedly- from Blending Matte into Noise Samples

There are several ways to solve this problem, but probably most easy way is after setting values, start a multipass loop in which values are compared with the array. When a difference is found, the specific value is written again. The loop will be ended untill all values match.

Creating a .tfx file
In order to discover all properties and values from an effect and to facilitate the procedure of stuffing an array with data, you should use the GenerateTFX script, which creates from a selected layer with an effect a text file, which can be easily manipulated and read in and transferred to the array.

Reading in effect properties and values from a file
For reading a .tfx file into an array the FFDialog() function is available. See example for usage:

Code: Select all

var pv = new Array();
pv=FFDialog();
When user pressed cancel in the file selector box, or when there had been an error during opening, FFDialog returns [-1]

The structure of a .tfx file is:

Code: Select all

-Each line is seperated with CR+LF (standard readable with readln())
-everything following a semicolon (;) is regarded as comment
-comment should be on a separate line
-before all properties and data, there should be ADOBE_EffectName=
       for example: ADOBE_EffectName=Grain Surgery
-Effectproperties must start with "ADOBE_Property_" continued by a number and "="
       for example: ADOBE_Property_4=Width
-single values are written without brackets, just plain numbers
       for example: 1.523
-arrays are in the format [x,y,..,z]
       for example: [0.5,1.0,0.8]
To find all details from a specific effect, please analyze a .tfx file, created with the GenerateTFX script.

Code: Select all

//###############################################################################
//# ApplyTFX.jsx script for adding an effect to layer.
//# This script offers an easy method to add an effect with properties and
//# values to a certain layer.
//#
//# It's use is quite straightforward:
//#
//# 1. Create an object from EffectsObj. This will hold all data for the effect to be applied
//# 2. Pass all data to the object with:
//#     -object.FFArray[]; Effectdata. see the example below
//#     -object.proj;      your current project
//#     -object.layr;      the layer in your project to which the effect with settings will be applied.
//# 3. object.Apply()      this will add the effect with the effectdata to the layer.
//#
//# for example:
//#
//# F      = new EffectsObj;
//# F.proj = app.project;
//# F.layr = F.proj.activeItem.selectedLayers[0];
//# F.FFArray=["ADOBE_EffectName=Remove Grain"          ,
//#            "ADOBE_Property_1=Viewing Mode", "3"     ,
//#            "ADOBE_Property_3=Center" , "[553.5,443]",
//#            "ADOBE_Property_4=Width"  , "123"        ,
//#            "ADOBE_Property_5=Height" , "135"         ]
//# F.Apply();
//#
//# if (F.guru) alert(F.guru); \\ <-------- Optional
//###############################################################################
//# Written in July 2005, by Marc Nijdam. Though probably some code from others 
//# may be found in here as well.
//###############################################################################

//###############################################################################
//# Code Entry point
//###############################################################################
{ 
     F      = new EffectsObj;         // F is an object from EffectsObject; init + do the constructor
     F.proj = app.project;            // set project

     F=prereq(F);                     // retrieve a selected layer + array; javascript does not treat variable by reference but by value!
     if (F.succes) F.Apply();
     if (F.guru) alert(F.guru);

//###############################################################################
//# Code end point
//###############################################################################

     function EffectsObj()            // Object + constructor
     {
          this.EFFSTR  = "ADOBE_Property" + "_";
          this.EFFNAME = "ADOBE_EffectName";
          this.succes  = false;
          this.guru    = "";          //this string might hold error after processing
          this.Apply   = FFApply;     // call to function FFApply();

          String.prototype.trim = trimSpaces;              // .trim()             removes leading and trailing spaces
          String.prototype.parse = parseLine;              // .parse(_chr_)       starting at pos. 0, search for _chr_, return what's after it
          String.prototype.isFfPrp = isEffectProperty;     // .isFfPrp()          returns true if this line has = sign, false if it hasn't
          String.prototype.cntFfDat = countData;           // .cntFfDat()         returns the amount of numbers on this line. (e.g. "[1.0,5.4]".cntFfDat()=2)
          String.prototype.getFfIDat = getIndexedData;     // .getIndexedData(n)) returns the n-th number on this line. (e.g. "[1.0,5.4]".getFfIDat(2)=5.4)
          String.prototype.isFfComment = isComment;        // .isFfComment()      true if this line is regarded as comment (starts with ;)
          String.prototype.getFfPrpNr = getPropertyNumber; // .getFfPrpNr()       returns the number, which follows after EFFSTR (e.g. ADOBE_Property_2=Point B will give 2)
          String.prototype.getFf2Array = getLine2Array;    // .getFf2Array()      returns array from a given string (e.g. [1,2,3,4,5] -> [0] = 1, [1]=2,.., [4]=5)
          Array.prototype.reOrder = reOrderArray;          // .reOrder()          removes comment and unneccessary properties which have no value
     }

     function FFApply()
     {
          var FFename;
          var FFedata;
          var FFvalue;
          var FFbase;

          var FFi=0;
          var FFj;
          var FF1;
          this.FFArray.reOrder();                             // optimize array; might cause some incompatibilities
          while ( ( this.FFArray[FFi].indexOf(this.EFFNAME) == -1) && (FFi < this.FFArray.length-1) ) FFi++; // search for occurence of string EFFNAME

          if (FFi < this.FFArray.length-1)
          {
               FFename=this.FFArray[FFi++].parse("=").trim(); // effectsname to add,,,

               if ( this.layr("Effects")!=null )              // Check for effects group in the layer. Some layers don't have one, like camera and light layers.
               {
                    if ( this.layr("Effects").canAddProperty(FFename) )    // Check whether this specific effect can be added to this layer.
                    {
                         FFbase=this.layr("Effects").addProperty(FFename); // FFbase points to the root of current effect properties and values
                         if (FFbase != null)
                         {
                              while ( (FFi < this.FFArray.length) )                // iterate through (FFi,...,FFArray.length)
                              {
                                   FF1=this.FFArray[FFi];
                                   if (! FF1.isFfComment() )                       // ignore lines with comment
                                   {
                                        if (FF1.isFfPrp())
                                        {
                                             FFedata=FF1.trim().getFfPrpNr();      //this.FFArray[FFi].parse("=").trim(); 
                                        }
                                        else
                                        {
                                             if (FF1.cntFfDat() >= 1) FFvalue=FF1; // it can be any number
                                             {
                                                  if ( FFvalue.cntFfDat() == 1 )   // "non-array"-value
                                                  {
                                                       FFbase.property(FFedata).setValue( FFvalue.getFfIDat() );
                                                  }
                                                  else // array with values
                                                  {
                                                       FFbase.property(FFedata).setValue(FFvalue.getFf2Array());
                                                  }
                                             }
                                        }
                                   }
                                   FFi++; // search for effectname
                              }
                              var FFpass=this.FFArray.length; // maximum attempts to try to change values..
                              var FFsimilar;
                              do
                              {
                                   FFsimilar=true;            // check properties and change when different; (multipass approach)
                                   FFi=0;
                                   while ( (FFi < this.FFArray.length) ) // iterate through (FFi,...,FFArray.length)
                                   {
                                        FF1=this.FFArray[FFi];
                                        if (! FF1.isFfComment() )        // ignore lines with comment
                                        {
                                             if (FF1.isFfPrp())
                                             {
                                                  FFedata=FF1.trim().getFfPrpNr();      //or: this.FFArray[FFi].parse("=").trim(); 
                                             }
                                             else
                                             {
                                                  if (FF1.cntFfDat() >= 1) FFvalue=FF1; // it can be any number
                                                  {
                                                       if ( FFvalue.cntFfDat() == 1 )   // "non-array"-value
                                                       {
                                                            if ( r3d(FFbase(FFedata).value) != r3d(FFvalue.getFfIDat()) )
                                                            {
                                                                 FFbase.property(FFedata).setValue( FFvalue.getFfIDat() );
                                                                 FFsimilar=false;
                                                            }
                                                       }
                                                       else // array with values
                                                       {
                                                            for (FFj=0; FFj<FFvalue.getFf2Array().length; FFj++)
                                                            {
                                                                 if ( r3d(FFvalue.getFf2Array()[FFj]) != r3d(FFbase(FFedata).value[FFj]) )
                                                                 {
                                                                      FFbase.property(FFedata).setValue(FFvalue.getFf2Array());
                                                                      FFsimilar=false;
                                                                 }
                                                            }
                                                       }
                                                  }
                                             }
                                        }
                                        FFi++; // search for effectname
                                   }
                              FFpass--;
                              } while ( (!FFsimilar) && (FFpass > 0) )
                         }
                         else
                         {
                              this.guru="Error when trying to add " + FFename + " to the current layer. Check your layer.";
                              this.succes=false;
                         }
                    }
                    else
                    {
                         this.guru=FFename + " cannot be added to the current layer. Check your layer.";
                         this.succes=false;
                    }
               }
               else
               {
                    this.guru="This layer is not suitable for adding any effect. Is this a camera or a light layer?";
                    this.succes=false;
               }
          }
          else
          {
               this.guru="Could not find the name for the effect which should be applied. Does the .tfx file contains the word " + this.EFFNAME + "=?";
               this.succes=false;
          }
     }

     function trimSpaces() // remove spaces
     {
          var tSstr=this;  // 'this' is only readable...
          while (tSstr.charAt(0) == ' ') tSstr = tSstr.substring(1);
          while (tSstr.charAt(tSstr.length - 1) == ' ') tSstr = tSstr.substring(0, tSstr.length - 1);
          return tSstr.toString(); // force stringtype. We'll get a lot of troubles if we return the object tSstr instead of the string.
     }

     function parseLine(pLq) // extract part after character pLq
     {
          var pLstr=this;    // 'this' is only readable...
          while ( (pLstr.charAt(0) != pLq) && (pLstr.length>0)) pLstr = pLstr.substring(1);
          return pLstr.substring(1);
     }
     
     function isEffectProperty() // does it start with EFFSTR and has this line the '=' character? if so, return true, else false
     {
          if ( this.indexOf("=") == -1) return false; // first check for presence of = sign;

          iEP= new EffectsObj;   // need temporarily EFFSTR

          if ( this.substring(0,iEP.EFFSTR.length) == iEP.EFFSTR )
          {
               iEP=null;
               return true;
          }
          iEP=null;
          return false;
     }

     function countData() // count the amount of numbers
     {
          if (this.length==0) return 0;
          var cDi;
          var cDNaN=false;
          var cDBrB=0;
          var cDBrE=0;
          var cDcnt=1;
          for (cDi=0; cDi<this.length; cDi++) // check for invalid characters or invalid structure
          {
               if ( ("0123456789., []".indexOf(this.charAt(cDi)) ) == -1 ) cDNaN=true;
               if ( this.charAt(cDi) == "," ) cDcnt++;
               if ( this.charAt(cDi) == "[" ) cDBrB++;
               if ( this.charAt(cDi) == "]" ) cDBrE++;
          }
          if ( (cDNaN) || (cDBrB>1) || (cDBrE>1) || (cDBrB != cDBrE) ) return 0;
          return cDcnt;
     }

     function getIndexedData(gID) // will return the n-th number on this line
     {
          if (gID==0) return -1;  // index =0 is not valid
          if (gID>this.cntFfDat()) return -1;     // out of range
          if (this.cntFfDat()==1)
          {
               if ( (this.trim().charAt(0) != "[") || (this.trim().charAt(this.trim().length-1) != "]") ) return parseFloat(this.trim()); // there's only one number
          }

          if ( this.trim().length<=2) return 0;   // or write 0.0? anyway, check length
          if ( (this.trim().charAt(0) != "[") || (this.trim().charAt(this.trim().length-1) != "]") ) return -1; // wrong array notation; string should be like [1,2,..,n]
          
          var gIDstr=this.trim().substring(1, this.trim().length-1).trim(); // remove [ and ]
          if (gIDstr.cntFfDat()==1) return parseFloat(gIDstr); // someone put a single number in an array notation e.g. [20.5]
          
          for (var gIDi=1; gIDi<gID; gIDi++)
          {
               if (gID != 1) gIDstr=gIDstr.substring(gIDstr.indexOf(",")+1).trim();
          }
          if ( gIDstr.indexOf(",") >= 1) return parseFloat( gIDstr.substr( 0 , gIDstr.indexOf(",") ) ); // trim the rest of the numbers on this line
          return parseFloat(gIDstr); // there's only one number
     }

     function isComment()
     {
          if (this.trim().charAt(0)==";") return true;
          return false;
     }
     function getPropertyNumber() // will return number between second '_' character and '=' character
     {
          if (this.length==0) return -1;
          var gPNi;
          var gPNEqs=0;
          var gPNUsc=0;
          for (gPNi=0; gPNi<this.length; gPNi++) // check for invalid characters or invalid structure
          {
               if ( this.charAt(gPNi) == "_" ) gPNUsc++;
               if ( this.charAt(gPNi) == "=" ) gPNEqs++;
          }
          if ( (gPNUsc >= 1) && (gPNEqs == 1) )
          {
               if ( this.trim().substring(this.trim().lastIndexOf("_")+1,this.trim().indexOf("=") ).trim().cntFfDat() == 1 ) return parseInt(this.trim().substring(this.trim().lastIndexOf("_")+1,this.trim().indexOf("=") ).trim());
          }
          return -1;
     }
     
     function getLine2Array() // will return an array with all data from a string
     {
          if (this.length == 0) return [-1];
          if (this.cntFfDat() == -1) return [-1];

          var gL2A = new Array( this.cntFfDat() );
          var gL2Ai;
          for (gL2Ai=0; gL2Ai<=this.cntFfDat()-1; gL2Ai++)
          {
               gL2A[gL2Ai]=this.toString().getFfIDat(gL2Ai+1);
          }
          return gL2A;
     }

     function reOrderArray()
     {
          if (this.length==0) return this;

          var FFrOA=this;
          var FFrOi;
          var FFrOstr="";

          var FFrOj=0;
          for (FFrOi = 0; FFrOi < this.length; FFrOi++) // remove all lines with comment
          {
               if ( this[FFrOi].toString().isFfComment()) ;
               else FFrOA[FFrOj++]=this[FFrOi];
          }
          FFrOA.length=FFrOj;     // correct now the Array length

          FFrOEP= new EffectsObj; // need temporarily EFFSTR
          if ( FFrOA[0].indexOf(FFrOEP.EFFNAME) == -1) return FFrOA; // First line should contain the string EFFNAME
          FFrOEP=null;            // discard object

          if (FFrOj==1) return FFrOA; // The current Array holds only item; the line with string EFFNAME, but no further property or data; so finished for now!

          FFrOj=1;
          var FFrOprOrd = new Array();
          for (FFrOi = 1; FFrOi < FFrOA.length; FFrOi++) // remove all properties without values
          {
               if (FFrOA[FFrOi].isFfPrp()) FFrOstr=FFrOA[FFrOi]; // make sure we've stored the line with property data on it
               else if ( FFrOA[FFrOi].cntFfDat()>0 )
               {
                    FFrOA[FFrOj++]=FFrOstr;
                    FFrOA[FFrOj++]=FFrOA[FFrOi];
               }
          }
          FFrOA.length=FFrOj;
          return FFrOA;
     }
     
     function r3d(r3dnr)
     {
          return (Math.round(r3dnr*1000)/1000);
     }

//###############################################################################
//# FFDialog
//#
//# Asks user for filename for effect template
//# --> nothing
//# <-- Array with effect + effectdata or -1 (User pressed cancel or empty file)
//#
//###############################################################################
     function FFDialog()
     {
          var _ffx2;
          _DffArray = new Array();
          _ffx2     = fileGetDialog("Please select the Effect Template, or cancel to quit", "TEXT tfx");
          if (_ffx2 != null)
          {
               var curLine=0;
               readString=new File(_ffx2);
               readString.open("r");
               while (!readString.eof)
               {
                    _DffArray[curLine++]=readString.readln();
               }
               readString.close();
               if (_DffArray.length>=1) _DffArray.length--;
               else
               {
                    alert("The file you gave me is quite empty...");
                    return [-1];
               }
               return _DffArray;
          }
          else return [-1];
     }

//###############################################################################
//# prereq
//#
//# prereq initializes object F with values
//# ---> object F
//# <--- object F
//#      F.layr     : target layer
//#      F.FFArray  : array with all effectdata
//#      F.guru     : possible error message
//#      F.succes   : true; everything went fine/false: had been some kind of problem
//###############################################################################
     function prereq(pv)
     {
          if (pv.proj)
          {
               if (pv.proj.numItems !=0)
               {
                    if ( (pv.proj.activeItem != null) && (pv.proj.activeItem instanceof CompItem) && (pv.proj.activeItem.selectedLayers.length == 1) )
                    {
                         pv.layr=pv.proj.activeItem.selectedLayers[0];
                         pv.FFArray=FFDialog(); // Ask user for File

                         if (pv.FFArray != -1) pv.succes=true;
                         else pv.succes=false;
                         pv.guru=false;
                         return pv;
                    }
                    else pv.guru="select (only) one layer in a composition please...";
               }
               else pv.guru="pv.is an empty project; please add comp with footage in it";
          }
          else pv.guru="Can't process anything; there is no project available";
          pv.succes=false;
          return pv;
     }
}
Post Reply