8 Replies Latest reply: Aug 5, 2012 10:53 PM by Johnathan A. M. RSS

    export multiple images with random layers

    Johnathan A. M.

      I have this texture file that has many different variations to it. (3 different kinds of pants, different hairpieces, some optional items, etc...)

      What i want to do, is export it with random layers selected. (there are also folders inside, it's fine if i have to merge the folder into a single layer, but it would be better if it could treat the folders as a layer)

      There are 2 layers that must always be on,

      then there are 6 other layers that can be mixed and matched completely at random.

      Finally: there are 3 layers that only one of them can be active at one time (if one is active, the other 2 cannot be). Those last 3 layers I wouldn't mind if i had to go through manually and remove the combinations that couldn't work, but it would be much better to have it automatic.

      Is there any way to script this so that it exports all of these textures as *filename*_*variation#*.png

        • 1. Re: export multiple images with random layers
          c.pfaffenbichler Community Member

          What i want to do, is export it with random layers selected.

          What difference will selecting a Layer make for Exporting? Did you mean »visible«?

           

          Is there any way to script this so that it exports all of these textures as *filename*_*variation#*.png

          One can Script that, but how experienced are you with JavaScript?

           

          Please post a screenshot of the Layers Panel or the file itself.

          • 2. Re: export multiple images with random layers
            Johnathan A. M.

            Yes, I meant visible, mixed up the words, and I've been working with a program that a part is only visible when selected.

             

            I'm sure the numbering would be rather trivial compared to the other part of the script. I havn't touched javascript in about 5 years now, so I would definitely need some helplayer panel.png

            The first and last layers are not to ever be visible (the first one is a texturemap, the last is to be a separate file completely) The folders are just layers made of multiple parts for organizational purposes. the rules are here if the image cannot be read:

            "

            -Layer 6 must always be visible

             

             

            -layers 1,2,3,5,9, and 11 can be randomly mixed and matched

             

             

            -if layer 5 is visible, layer 4 can be visible, but does not have to be

             

             

            -is layer 9 is visible, layer 3 cannot be.

             

             

            -layers 7,8, and 10: only 1 can be visible at one time, but one must always be visible "

             

            I obsured the actual layer names and previews for a number of reasons. Ultimately I wouldn't expect it to make a major difference.

            • 3. Re: export multiple images with random layers
              c.pfaffenbichler Community Member

              The screenshot shows the results I get with the Script farther down below on the last file in the screenshot (each number being on a layer of its own, 1 being the lowest of them, 9 the topmost).

               

              The function needs these arguments

              • an array of layers (and arrays),

              • the minimum number of layers,

              • the maximum number of layers (of the elements in the array at position 0) and

              • a number to give the index of the layer in the array that must be shown – if no specific layer is needed insert some String.

              If the array contains an array that needs like content as the function will be tun on that.

               

              In the example the array contains layers 6, 5, 4, 3, 2 and an array with an array containing the layers 9, 8, 7 and baseLayer 0 (referring to the first of those), so if that array is used 9 will be visible.

               

              minimal and maximal only refer to the array and don’t take the contents of additional arrays into account.

               

              So you can set up the arrays according to your needs and give it a try (if you haven’t already performed the task manually).

               

              Edit: Existing files of the same resulting names will be overwritten without prompting.

              randomLayerVisibilityScr.jpg

              // 2012, use it at your own risk;
              #target photoshop
              if (app.documents.length > 0) {
              var myDocument = app.activeDocument;
              // getting the name and location;
              var docName = myDocument.name;
              var basename = docName.match(/(.*)\.[^\.]+$/)[1];
              var docPath = myDocument.path;
              // set up the arrays of layers;
              var theLayers = myDocument.layers;
              var theArray2 = [[theLayers[0], theLayers[1], theLayers[2]], 1, 3, 0];
              var theArray = [theArray2, theLayers[3], theLayers[4], theLayers[5], theLayers[6], theLayers[7]];
              // randomize and save png;
              for (var p = 1; p < 21; p++) {
                        RandomShow (theArray, 1, 5, "no");
                        savePNG (myDocument, docPath, basename, p)
                        }
              };
              ////// randomly show layers; baselayer is the index in thearray of a layer that has to be shown //////
              function RandomShow (theArray, minimal, maximal, baseLayer) {
              var theCounter = 0;
              if (minimal > theArray.length || maximal > theArray.length) {alert ("nonsense")}
              else {
              // create array of numbers without baselayer;
                        var numbersArray = new Array;
                        for (var m = 0; m < theArray.length; m++) {
                                  if (m != baseLayer) {
                                            numbersArray.push(m);
                                            };
                                  };
              //  hide layers;
                        hideLayers(theArray);
              // show the baselayer;
              if (baseLayer.constructor == Number) {
                        theArray[baseLayer].visible = true;
                        maximal--
                        };
              // number of layers to show;
                        var theNumber = minimal + Math.floor( Math.random() * (maximal - minimal + 1) );
              // shuffle the array of numbers;
                        arrayShuffle(numbersArray);
              // take the first x numbers;
                        for (var n = 0; n < theNumber; n++) {
                                  var theObject = theArray[numbersArray[n]];
                                  if (theObject.constructor != Array) {
                                            theObject.visible = true;
                                            }
                                  else {
                                            RandomShow (theObject[0], theObject[1], theObject[2], theObject[3])
                                            }
                                  };
                        }
              };
              ////// hide the layers in an array of layers //////
              function hideLayers (anArray) {
                        try {
                                  for (var o = 0; o < anArray.length; o++) {
                                            if (anArray[o].constructor != Array) {anArray[o].visible = false}
                                            else {hideLayers (anArray[o])}
                                            }
                                  }
                        catch (e) {}
                        };
              ////// shuffle the items in an array //////
              // http://www.hardcode.nl/subcategory_1/article_317-array-shuffle-function
              function arrayShuffle(theArray) {
                        var len = theArray.length;
                        var i = len;
                        while (i--) {
                                  var p = parseInt(Math.random()*len);
                                  var t = theArray[i];
                                  theArray[i] = theArray[p];
                                  theArray[p] = t;
                        }
              };
              ////// function to png //////
              function savePNG (myDocument, docPath, basename, theSuffix) {
              // weboptions;
              var webOptions = new ExportOptionsSaveForWeb();
              webOptions.format = SaveDocumentType.PNG;
              webOptions.PNG8 = false; 
              webOptions.transparency = true;
              webOptions.interlaced = 0;
              webOptions.includeProfile = false;
              webOptions.optimized = true;
              myDocument.exportDocument(new File(docPath+"/"+basename+"_"+theSuffix+".png"), ExportType.SAVEFORWEB, webOptions);
              };
              
              • 4. Re: export multiple images with random layers
                Johnathan A. M.

                that script was great! it output 20 different versions, a couple erroneous (but i can't exactly expect perfection when i didn't give proper layer names, can I?), but overall, it worked great. I would just like to see if I fully understand exactly what it is doing so when i have more layers I can modify it without breaking it.

                so this is how I understand it:

                -the first 8 lines just make sure there is a valid document open, and if there is, find out the file path and everything

                 

                -next you set up the layers, but here I'm confused: Why is thearray2 put in thearray, but then never used separately? is it to join those layers? For the layer numbers: I can just add more, right? and the numbers go from the bottom of the layer panel to the top, right?

                 

                -line 14 just makes sure it outputs no more than 20 images (which i find odd. the texture files are extremely small ~6.82 kb each, so loading them won't be a problem. it would be good if it got stuck in an infinite loop)

                -15-18 tell it to randomly select layers from the layer array, from 1 to 5 layers visible, then send it to the save function at the bottom

                -20 defines randomshow as a function, 21 resets the document number, and 22 makes sure i dont give it an invalid range

                 

                -baselayer is defined (but i can't find out where to input what it is, do i put it in place of the "no" on line 15?)

                 

                -hides all layers, then makes the randomly selected layers visible

                 

                -39 makes sure there is always at least 1 layer being shown

                 

                -all the layers are taken, shuffled, and a random ammont of the first x numbers are taken, these are made visible, the others are hidden.

                 

                -savepng is defined with all the save to web options

                 

                 

                 

                this is as far as I can understand it, so a couple things to ask:

                -for the layer array do I put in the actual layer names?

                -I have 14 layers currently, what should i bump up to be able to randomize these as well?

                -If I currently have layers hidden, how would i tel it to not add those to be randomly shuffled and made visible (it was randomizing the texturemap and the other layer that is there for future reference into the compiled pngs)

                 

                other than those few things i don't understand, the script worked great

                 

                I probably wouldn't export this manually. 14 layers is a lot to do by hand. i mean: the script got to 20 versions before it automatically ended.

                overwriting of images is actually great. I make changes to the texture every now and then, and having to tell it it can overwrite each and every time would be a pain. It would be nice to have it output to a folder, but I'm not THAT incompetent, i can have it make a folder with the same name as the document and have it export into there (edit: apparently I AM that incompetent. the folder has to exist before it can save there... excuse me as i go find out how to make it make a folder...)

                thanks for all your help, I really needed it.  and thanks for commenting the code, that will really help me when trying to modify it. though: knowing me, I'm gonna have at least a paragraph of documentation so i don't mess anything up. I will probably also just make a blurb at the top of the file with all the options so I have less of a change of messing everything up while modifying it (ever modify a file, and accidentally delete 1 extra closer } and have the compile completely fail, and you end up counting out where the opener or closer is that you missed?)

                 

                also: i really don't intend for you to script this, I have the best intentions to work this out on my own, but the problem is: I'm quite incompetent at a newer language (I've used JS before, but it was limited usage, mostly web-based, so: making a window open without the popup blocker catching it, and making stuff compatable with IE, and it was a while ago, and some parts of this are completely new to me) unless somebody is there walking me through it. I'm attempting to read though the documentation, but it's very difficult for me to learn what things do without actual sample code of it working with other parts

                • 5. Re: export multiple images with random layers
                  c.pfaffenbichler Community Member

                  -the first 8 lines just make sure there is a valid document open, and if there is, find out the file path and everything

                  Correct.

                  But I cut corners, if I were more diligent I would also check for the existence of more than one layer and that the file has been saved at all (otherwise it has no path and an error occurs) etc.

                   

                  -line 14 just makes sure it outputs no more than 20 images (which i find odd. the texture files are extremely small ~6.82 kb each, so loading them won't be a problem. it would be good if it got stuck in an infinite loop)

                  Feel free to change it to a higher number.

                  To create 100 files change to

                  for (var p = 1; p < 101; p++) {
                  

                   

                  Why is thearray2 put in thearray, but then never used separately?

                  The function RandomShow checks if the elements in the Array are Arrays themselves

                  if (theObject.constructor != Array) {
                  

                  and just assumes it is a Layer if it is not an Array.

                  But when it is an Array the function RandomShow is run with that Arrays elements as arguments.

                  This is intended to enable the various rules – for example if you have two or more Layers of which only one may be visible you could put them in such an Array with minimal and maximal set to 1, which should only make one visible.

                  But – and again I should have included checks for this – you should not put a Layer in more than one Array.

                   

                  In the example changing theArray2 to

                  var theArray2 = [[theLayers[0], theLayers[1], theLayers[2]], 1, 1, "no"];
                  

                  would mean that 7, 8 and 9 would only be visible individually.

                  But as theArray2 is itself part of theArray it may be visible or not unless it is set as the baseLayer.

                  Or rather it would if I had not forget to check the baseLayer item for being an Array itself …

                  So please change the affected if-clause (should be about line 34) to

                  // show the baselayer;
                  if (baseLayer.constructor == Number) {
                            var theObject = theArray[baseLayer];
                  // show baselayer if layer;
                            if (theObject.constructor != Array) {
                                      theObject.visible = true;
                                      }
                  // run function if baselayer is an array;
                            else {
                                      RandomShow (theObject[0], theObject[1], theObject[2], theObject[3])
                                      };
                            maximal--
                            };
                  

                   

                   

                  -baselayer is defined (but i can't find out where to input what it is, do i put it in place of the "no" on line 15?)

                  The function takes four arguments,

                  • the Array

                  • the minimum number of visible elements

                  • the maximum number of visible elements

                  • a layer that should always be visible identified by its place in the Array

                  So when invoking the function RandomShow and wanting the first layer in the Array to be visible it would be:

                  RandomShow (theArray, 1, 5, 0);
                  

                  -for the layer array do I put in the actual layer names?

                  If you want to address a layer with teh name »Layer 1« by name (top level layers that is, layers in Groups are filial to the Group) you can use

                  myDocument.layers.getByName("Layer 1")
                  

                   

                  -If I currently have layers hidden, how would i tel it to not add those to be randomly shuffled and made visible (it was randomizing the texturemap and the other layer that is there for future reference into the compiled pngs)

                  Whether a Layer is visible or not is irrelevant in the curren tform of the Script, the Layers are addressed in the Array.

                  Introducing a check for visibility would be possible, but make the function a bit more convoluted as this would also affect the maximum number for example.

                   

                  I don’t think I answered all questions, but maybe you’ve made progress on your own already and you can always post again if matters are still unclear.

                  Maybe the function is more complicated than it would need to be or maybe it will not be able to accomodate all your needs currently, but one could for example extend the baseLayer argument to an Array itself to have more than one element (either Layer or another Array) that will always be visible … there are still possibilities but the issue might be a bit complicated.

                  • 6. Re: export multiple images with random layers
                    c.pfaffenbichler Community Member

                    I noticed an error – I forgot to remove the baseLayer from the array, so it could account for two »visibilities« in effect reducing the number of actually shown layers/elements.

                    Forget it, the line

                    if (m != baseLayer) {
                    

                    took care of it …

                    • 7. Re: export multiple images with random layers
                      c.pfaffenbichler Community Member

                      I’ve adapted the Script somewhat …

                      // 2012, use it at your own risk;
                      #target photoshop
                      if (app.documents.length > 0) {
                      var myDocument = app.activeDocument;
                      // getting the name and location;
                      var docName = myDocument.name;
                      var basename = docName.match(/(.*)\.[^\.]+$/)[1];
                      var docPath = myDocument.path;
                      // set up the arrays of layers;
                      var theLayers = myDocument.layers;
                      var theArray2 = [[theLayers[0], theLayers[1], theLayers[2]], 1, 1, []];
                      var theArray3 = [[theLayers[3], theLayers[4], theLayers[5]], 1, 2, []];
                      // randomize and save png;
                      for (var p = 1; p < 21; p++) {
                                RandomShow ([theLayers[6], theLayers[7], theLayers[8]], 3, 5, [theArray2, theArray3]);
                                savePNG (myDocument, docPath, basename, p)
                                }
                      };
                      ////// randomly show layers; baselayer is the index in thearray of a layer that has to be shown //////
                      function RandomShow (theArray, minimal, maximal, baseLayer) {
                      // baselayers;
                      for (var q = 0; q < baseLayer.length; q++) {
                                var theObject = baseLayer[q];
                      // show baselayer if layer;
                                if (theObject.constructor != Array) {
                                          theObject.visible = true;
                                          }
                      // run function if baselayer is an array;
                                else {
                                          if (theObject.length != 0) {
                                                    RandomShow (theObject[0], theObject[1], theObject[2], theObject[3])
                                                    }
                                          };
                                maximal--;
                                minimal--
                                };
                      // process the purely random layers;
                      if (minimal > theArray.length || maximal > theArray.length) {alert ("nonsense")}
                      else {
                      // create array of numbers without baselayer;
                                var numbersArray = new Array;
                                for (var m = 0; m < theArray.length; m++) {
                                          numbersArray.push(m);
                                          };
                      //  hide layers;
                                hideLayers(theArray);
                      // number of layers to show;
                                var theNumber = minimal + Math.floor( Math.random() * (maximal - minimal + 1) );
                      // shuffle the array of numbers;
                                arrayShuffle(numbersArray);
                      // take the first x numbers;
                                for (var n = 0; n < theNumber; n++) {
                                          var theObject = theArray[numbersArray[n]];
                                          if (theObject.constructor != Array) {
                                                    theObject.visible = true;
                                                    }
                                          else {
                                                    RandomShow (theObject[0], theObject[1], theObject[2], theObject[3])
                                                    }
                                          };
                                }
                      };
                      ////// hide the layers in an array of layers //////
                      function hideLayers (anArray) {
                                try {
                                          for (var o = 0; o < anArray.length; o++) {
                                                    if (anArray[o].constructor != Array) {anArray[o].visible = false}
                                                    else {hideLayers (anArray[o])}
                                                    }
                                          }
                                catch (e) {}
                                };
                      ////// shuffle the items in an array //////
                      // http://www.hardcode.nl/subcategory_1/article_317-array-shuffle-function
                      function arrayShuffle(theArray) {
                                var len = theArray.length;
                                var i = len;
                                while (i--) {
                                          var p = parseInt(Math.random()*len);
                                          var t = theArray[i];
                                          theArray[i] = theArray[p];
                                          theArray[p] = t;
                                }
                      };
                      ////// function to png //////
                      function savePNG (myDocument, docPath, basename, theSuffix) {
                      // weboptions;
                      var webOptions = new ExportOptionsSaveForWeb();
                      webOptions.format = SaveDocumentType.PNG;
                      webOptions.PNG8 = false; 
                      webOptions.transparency = true;
                      webOptions.interlaced = 0;
                      webOptions.includeProfile = false;
                      webOptions.optimized = true;
                      myDocument.exportDocument(new File(docPath+"/"+basename+"_"+theSuffix+".png"), ExportType.SAVEFORWEB, webOptions);
                      };
                      

                      In this version the nine layer file gets results like these:

                      randomLayerVisibilityScr2.jpg

                      The lowest three layers (1, 2, 3) are random but at least one will be visible, of the middle ones (4, 5, 6) one or two can be visible, of the topmost three (7, 8, 9) only one will be shown.

                       

                      The baseLayer-argument needs to be an Array now which can contain layers (which is basically useless as if one wants a layer to always be visible one need not include it in the function at all) or arrays.

                      • 8. Re: export multiple images with random layers
                        Johnathan A. M.

                        wow. you are being incredibly helpful (sorry I didn't get back to you sooner)

                        I'll see what I can do with this new script (I'm pretty sure with a bit of tweaking I can get it fully working! I got the backend code of the model funtioning correctly (let me tell you, that was a pain) so now the main concern is the textures. thanks for all your help