Skip to main content
Participant
January 27, 2024
Answered

Select Layers Based on Location in a Photoshop Action

  • January 27, 2024
  • 2 replies
  • 574 views

I'm trying to create an action where I select all layers in the left half of the file, regardless of the layer names or how they are ordered in the layer manager.

 

My intuitive way of doing this was to use the Move tool, Shift, and drag over the left half of the file, which does select the correct layers. In the action menu, however, it records the layers by name ("Select layer "Rectangle 2"...), which does not generalize to the other files where I would like to repeat the action. Is there any way to get around this, such as using the selection tool? 

 

PS My intended use for this is to group all layers on the left half of a file, for all files with the same dimensions.

This topic has been closed for replies.
Correct answer Anton28125417w3bs

I looked into scripting since I'll need to use it for other tasks anyway and this is the solution I was able to throw together, using layer bounds just as you recommended. It's not perfect but works well for me and can hopefully be utilized by others. 🙂

 

// Check if a layer is in the left half of the canvas
function isLayerInLeftHalf(layer) {
    var bounds = layer.bounds;
    var centerX = bounds[0] + (bounds[2] - bounds[0]) / 2;
    // alert(layer.toString().concat(", ",centerX));
    var canvasWidth = activeDoc.width;
    return centerX < canvasWidth / 2;
}

// Add layer to appropriate LayersList
function makeLayersLists(layers, leftLayersList, rightLayersList){
    for (var i = 0; i < layers.length; i++) {
        // If layer is a group, search recursively
        if(layers[i].typename == 'LayerSet'){
            makeLayersLists(layers[i].layers,leftLayersList,rightLayersList);
        }
        else if (layers[i].name != 'Background') {
            if (isLayerInLeftHalf(layers[i])) {
                leftLayersList.push(layers[i]);
            }
            else{
                rightLayersList.push(layers[i]);
            }
        }
    }
}

function groupLayers(){
    var layers = activeDoc.layers;

    const leftLayersList = [];
    const rightLayersList = [];

    makeLayersLists(layers,leftLayersList,rightLayersList);

    // Create groups
    var leftLayerGroup = activeDoc.layerSets.add();
    leftLayerGroup.name = "left";
    var rightLayerGroup = activeDoc.layerSets.add();
    rightLayerGroup.name = "right";

    // Create dummy layers in groups to help ensure layer order stays same after move
    var tempLayerLeft = leftLayerGroup.artLayers.add();
    var tempLayerRight = rightLayerGroup.artLayers.add();

    // Move each layer from the lists into the appropriate group (before tempLayer)
    for (var i=0; i<leftLayersList.length; i++){
        leftLayersList[i].move(tempLayerLeft, ElementPlacement.PLACEBEFORE);
    }
    for (var i=0; i<rightLayersList.length; i++){
        rightLayersList[i].move(tempLayerRight, ElementPlacement.PLACEBEFORE);
    }

    // Remove dummy layers
    tempLayerLeft.remove();
    tempLayerRight.remove();
}

var activeDoc = app.activeDocument;

groupLayers();

 

2 replies

Stephen Marsh
Community Expert
Community Expert
January 28, 2024

I have had success with an action, but there are limitations and conditions...

 

Starting with the following layer structure:

 

 

The action is rather long and complex:

 

 

It crops and deletes unused layers, then groups them for both the left and right halves, then combines the two back into a new single document:

 

 

This is just my first attempt, I'm sure that it can be refined... But I'm left wondering if it may just be better if you do this manually as you have been!

Participant
January 28, 2024

Thank you Stephen! This is a great solution and I didn't even know you could make a snapshot action to then revert to.

Stephen Marsh
Community Expert
Community Expert
January 27, 2024
quote

I'm trying to create an action where I select all layers in the left half of the file, regardless of the layer names or how they are ordered in the layer manager.


By @Anton28125417w3bs


That's the problem.

 

Actions rely on names or consistent layer stacking order.

 

Although scripts are more powerful than actions, I'm having a tough time thinking of an easy answer for you. Layers are identified vertically, even if they visually appear as left or right content.

 

A script can detect the layer bounds (coordinates), so it should be possible to conditionally select all layers within the left hand side.

 

This may take someone with greater scripting knowledge than I possess.

Anton28125417w3bsAuthorCorrect answer
Participant
January 28, 2024

I looked into scripting since I'll need to use it for other tasks anyway and this is the solution I was able to throw together, using layer bounds just as you recommended. It's not perfect but works well for me and can hopefully be utilized by others. 🙂

 

// Check if a layer is in the left half of the canvas
function isLayerInLeftHalf(layer) {
    var bounds = layer.bounds;
    var centerX = bounds[0] + (bounds[2] - bounds[0]) / 2;
    // alert(layer.toString().concat(", ",centerX));
    var canvasWidth = activeDoc.width;
    return centerX < canvasWidth / 2;
}

// Add layer to appropriate LayersList
function makeLayersLists(layers, leftLayersList, rightLayersList){
    for (var i = 0; i < layers.length; i++) {
        // If layer is a group, search recursively
        if(layers[i].typename == 'LayerSet'){
            makeLayersLists(layers[i].layers,leftLayersList,rightLayersList);
        }
        else if (layers[i].name != 'Background') {
            if (isLayerInLeftHalf(layers[i])) {
                leftLayersList.push(layers[i]);
            }
            else{
                rightLayersList.push(layers[i]);
            }
        }
    }
}

function groupLayers(){
    var layers = activeDoc.layers;

    const leftLayersList = [];
    const rightLayersList = [];

    makeLayersLists(layers,leftLayersList,rightLayersList);

    // Create groups
    var leftLayerGroup = activeDoc.layerSets.add();
    leftLayerGroup.name = "left";
    var rightLayerGroup = activeDoc.layerSets.add();
    rightLayerGroup.name = "right";

    // Create dummy layers in groups to help ensure layer order stays same after move
    var tempLayerLeft = leftLayerGroup.artLayers.add();
    var tempLayerRight = rightLayerGroup.artLayers.add();

    // Move each layer from the lists into the appropriate group (before tempLayer)
    for (var i=0; i<leftLayersList.length; i++){
        leftLayersList[i].move(tempLayerLeft, ElementPlacement.PLACEBEFORE);
    }
    for (var i=0; i<rightLayersList.length; i++){
        rightLayersList[i].move(tempLayerRight, ElementPlacement.PLACEBEFORE);
    }

    // Remove dummy layers
    tempLayerLeft.remove();
    tempLayerRight.remove();
}

var activeDoc = app.activeDocument;

groupLayers();

 

Stephen Marsh
Community Expert
Community Expert
January 28, 2024

@Anton28125417w3bs 

 

Well done! This is great, I'm glad you managed to script this!

 

A couple of cosmetic suggestions, just for good form. I'd change:

 

var canvasWidth = activeDoc.width;

 

to:

 

var canvasWidth = activeDoc.width.value;

 

So that you have a numerical value without the ruler unit (this doesn't appear to affect your calculations).

 

You can also ensure that the script works in pixels, rather than the current ruler units, then set the ruler units back to what they were before the script run.

 

So, before the original code, you could add:

 

// Save the current ruler units and set to pixels
var savedRuler = app.preferences.rulerUnits;        
app.preferences.rulerUnits = Units.PIXELS;

 

Then at the end of the original code add:

 

// Restore the ruler units
app.preferences.rulerUnits = savedRuler; 

 

Something else to consider is wrapping the entire code in a function and then calling the function from a suspendHistory step so that there is only a single undo in the history panel.

 

P.S. For good form, you will also want to change const for var as ExtendScript is an old language:

 

var leftLayersList = [];
var rightLayersList = [];