The correct use of backward loops

Find out why the . goes before the /

Moderator: Paul Tuersley

Post Reply
jesseHeinsenberg
Posts: 6
Joined: December 26th, 2013, 4:38 am

June 28th, 2014, 1:35 am

Hi dear aenhancers,
I'm trying to simply remove the selected masks of a layer. So I'm trying to use a backward loop to remove them but so far the script only removes the last item of the array.

Code: Select all

function theAlert(){alert("Select one or several masks to remove.");return};

    var myComp = app.project.activeItem;
    //check if a comp is selcted
    if(myComp == null){theAlert()}else{
        var theLayer=myComp.selectedLayers[0];
        //check if a layer is selected
        if(theLayer == null){theAlert()}else{
                var masksGroup = theLayer.property("ADBE Mask Parade");
                //check if the layer has masks
                if(masksGroup.numProperties == 0 || myComp.selectedProperties[0].isMask == false){theAlert()}else{
                   
                         //remove masks
                         var numMasks=masksGroup.numProperties;
                         
                         app.beginUndoGroup("Mask removal");

                         for (  i = numMasks; i >= 1; i--){
                             if(masksGroup.property(i).selected)
                            masksGroup.property(i).remove();
                            }

                    app.endUndoGroup();

                }//end check if the layer has masks
            }//end check if a layer is selected
        }//end check if a comp is selected  
Actually this loop is meant to be a part of a larger script in which I've already created an array for the selected masks.
So I've tried a sort of comparison between my two arrays in order to remove one form the other. But this doesn't work at all. I don't know if it's a good idea to put a backward loop inside a forward loop.

Code: Select all

function theAlert(){alert("Select one or several masks to remove.");return};

    var myComp = app.project.activeItem;
    //check if a comp is selcted
    if(myComp == null){theAlert()}else{
        var theLayer=myComp.selectedLayers[0];
        //check if a layer is selected
        if(theLayer == null){theAlert()}else{
                var masksGroup = theLayer.property("ADBE Mask Parade");
                //check if the layer has masks
                if(masksGroup.numProperties == 0 || myComp.selectedProperties[0].isMask == false){theAlert()}else{
                    
                    //create selected Masks Array
                    var numMasks=masksGroup.numProperties;
                     var selectedMasks = new Array();
                      
                    for (m= 1; m <= numMasks; m++){
                    if (masksGroup.property(m).selected){
                    selectedMasks[selectedMasks.length]=masksGroup.property(m);}
                    };
                   
                         //remove selected masks
                         
                         app.beginUndoGroup("Mask removal");
                         
                         for (j = 1; j <= numMasks; j++){
                             var curMask = masksGroup.property(j);
                             
                             for (i = selectedMasks.length; i >0; i--){
                            if (curMask.name == selectedMasks[i].name)
                            curMask.remove();
                            }
                        
                        }

                    app.endUndoGroup();

                }//end check if the layer has masks
            }//end check if a layer is selected
        }//end check if a comp is selcted  
I guess this kind of problem might have been discussed somewhere on the forum, but I haven't found it yet so maybe a link to similar topic should fix it. Any help would be very appreciated.
beginUndoGroup
Posts: 81
Joined: November 27th, 2012, 6:41 am

June 28th, 2014, 10:53 am

Not clear why you need 2 loops.

Removing stuff looses the properties selection so you need to store indices before:

Code: Select all

var myComp = app.project.activeItem, theLayer, n, indices;
if ( myComp instanceof CompItem &&  myComp.selectedLayers.length>0 && myComp.selectedLayers[0].mask && myComp.selectedLayers[0].mask.numProperties>0){
	theLayer=myComp.selectedLayers[0];
	indices = [];
	for (n = theLayer.mask.numProperties; n>0; n--) if (theLayer.mask(n).selected) indices.unshift(n);
	n = indices.length;
	while (n--) theLayer.mask(indices[n]).remove();
	}
else alert("Select one or several masks to remove.");
But apparently you want something else, like removing masks with redundant names among selected masks?

Xavier
jesseHeinsenberg
Posts: 6
Joined: December 26th, 2013, 4:38 am

June 29th, 2014, 12:03 pm

Hi Xavier.
Thanks for your help. Now my script works but I still don't really understand why.
Here's a copy of the full script:

Code: Select all

//This script works on ShapeLayers. It converts Masks into Shapes. The converted shapes will be stored in a group, in which a stroke and a fill effect will be added.
//I know that AI files can be converted into Shapelayers in After effects CS6, but I 'm working on CS5. So...

 function theAlert(){alert("Select one or several masks.");return}  
    var myComp = app.project.activeItem;
    //check if a comp is selcted
    if(myComp == null){theAlert()}else{
        var theLayer=myComp.selectedLayers[0];
        //check if a layer is selected
        if(theLayer == null){theAlert()}else{
                var masksGroup = theLayer.property("ADBE Mask Parade");
                //check if the layer has masks
                if(masksGroup.numProperties == 0 ){theAlert()}else{
                    //convert masks to shapes
                   
                    if (myComp.selectedProperties[0].isMask == false){theAlert();}else{// end check if masks are selected
                     
                     var selectedMasks = new Array();
                     var selMasksId = new Array();
                      
                    for (m= 1; m <= masksGroup.numProperties; m++){
                        if (masksGroup.property(m).selected){
                        selectedMasks[selectedMasks.length]=masksGroup.property(m);
                        selMasksId[selMasksId.length] = m;
                        };
                    };
                    
                    app.beginUndoGroup("Bullseye");
                    
                    
                    var group = theLayer.content.addProperty("ADBE Vector Group");
                    
                    //add shapes                   
                    for(i=0;i<selectedMasks.length;i++){
                        myShape = selectedMasks[i].maskPath.value;
                        var shapeGroup = group.content.addProperty("ADBE Vector Shape - Group"); 
                        var shape = shapeGroup.path.setValue(myShape);
                        }
                        //add stroke
                         var stroke = group.content.addProperty("ADBE Vector Graphic - Stroke");
                         strokeColor=[0,0,0];
                         stroke.property("ADBE Vector Stroke Color").setValue(strokeColor);
                         stroke.property("ADBE Vector Stroke Width").setValue(6);
                         stroke.property("ADBE Vector Stroke Line Cap").setValue(2);
                         stroke.property("ADBE Vector Stroke Line Join").setValue(2);

                         
                         //add fill
                         var fill = group.content.addProperty("ADBE Vector Graphic - Fill");
                         fillColor=[1,1,1];
                         fill.property("ADBE Vector Fill Color").setValue(fillColor);
                         
                         //remove masks
                         var j = selMasksId.length;

                         while (j--){
                            theLayer.mask(selMasksId[j]).remove();
                            };

                        }// end check if masks are selected

                    app.endUndoGroup();

                }//end check if the layer has masks
            }//end check if a layer is selected
        }//end check if a comp is selected    
So far I've never used while loops, because I've read somewhere that while loops and for loops does exactly the same. So I got used to write for loops. But It seems that while loops are more commonly used to remove properties. Still, I don't understand why this works:

Code: Select all

while (j--){theLayer.mask(selMasksId[j]).remove();}
and why this doesn't work :

Code: Select all

for ( j = selMasksId.length; j >0; j--){theLayer.mask(selMasksId[j]).remove();}
beginUndoGroup
Posts: 81
Joined: November 27th, 2012, 6:41 am

July 1st, 2014, 12:36 am

You can you use a for loop, it's indeed the same. But since selMasksId is an array (not a collection like layer.mask) you must start right at the last entry and end at the first included:

Code: Select all

for ( j = selMasksId.length-1; j >=0; j--){theLayer.mask(selMasksId[j]).remove();}
The while loop works like this: j=selMasksId.length; while(j--){ /*do stuff/*};
For each j, while(j--) evaluates j (as boolean). If j is 0, break, else j is decremented by 1 and the /*do stuff*/ thingy is executed.
j=selMasksId.length : j===0 ? break otherwise j-- (so j=selMasksId.length-1) >>> remove mask(selMaskId[selMasksId.length-1]);
j=selMasksId.length-1 : j===0 ? break otherwise j-- (so j=selMasksId.length-2) >>> remove mask(selMaskId[selMasksId.length-2]);
and so on until j=1 (j is decremented by 1, so j=0, mask(selMaskId[0]) is removed, then the loop breaks.

Its more compact but quite risky. If you start with a negative j, or j=0.9999999999999999999999999999, you never reach 0 and generate an infinite loop.
jesseHeinsenberg
Posts: 6
Joined: December 26th, 2013, 4:38 am

July 1st, 2014, 12:27 pm

Allright, now I clearly understand my mistake. Thank you very much.
Post Reply