21 Replies Latest reply on Nov 27, 2011 2:21 PM by Michael L Hale

    How to measure the distance using script

    bala281972 Level 1

      Hi

       

      Is there any way to measure the distance of any photoshop document using the measure tool, by using the script. Given below the steps required by me. Hope i'm clear in my communications.

       

      I searched the forums but couldnt find any suitable answers.

       

      1.Open Photoshop document

      2. Allow the user to draw the line using measure tool

      3. alert the distance

       

      Rgs

      Anish

        • 1. Re: How to measure the distance using script
          JJMack Most Valuable Participant

          I would think that if the measure tool were recordable which it is not you would need to set the two end points.  If you have the two end points you do not need the measure tool for its then simple math the length is the square root of the sum of the width length squared and the height length squared.

           

          With the line tool set to create a path you could daw a line path and have  a script to get the bounds of the path or path turned into a selection and do the above math. To automate the process you could record an interactine action to create the line path then have the action use the script that calulayes the line length after the interactive path step..

          • 2. Re: How to measure the distance using script
            Michael L Hale Level 5

            You can have the user mark the part of the image to be measured with the pen or line shape tool. But if all the script is going to do is alert the user the distance, they could get that from using the measure tool and the info panel without a script.

             

            At any rate here is a function to find the distance and angle of a line path made with either the pen or line tool.

            //the code below is for the pen tool. If the path was made with the line tool replace pathPoints[1] with pathPoints[3] 
              function measureLine(path){
               var res = new Array;
               var lineStart = path.subPathItems[0].pathPoints[0].anchor; 
               var lineEnd = path.subPathItems[0].pathPoints[1].anchor;
               var a = Math.max(lineStart[0],lineEnd[0])-Math.min(lineStart[0],lineEnd[0]);
               var o = Math.max(lineStart[1],lineEnd[1])-Math.min(lineStart[1],lineEnd[1]);
               var c = Math.round(Math.sqrt((a*a)+(o*o)));
               res.push(c);
               var ang = (180/Math.PI) * Math.atan2(o,a);
               if(lineStart[1] < lineEnd[1]){//negative angle 
                  ang = -ang; 
               };
               res.push(ang);
               return res;
               }
            
            
            • 3. Re: How to measure the distance using script
              Michael L Hale Level 5

              JJMack,

               

              I have never been able to find a way that allows the user to make the path using the line tool while the script is running. I will admit I never thought of actions but I can't get it it work with actions either. Can you post the steps to create an interactive action using the line tool?

              • 4. Re: How to measure the distance using script
                JJMack Most Valuable Participant

                Your most likely will not like my reply for its not fully automated. Many things in Photoshop can not be recorded or scripted. These thing need to be done manually by a user. All an Action of script can do in these cases is help with the manual process.  This normally means selecting the tools and set it to the properties you want the user to use. Then inserting a stop with a message instructing the user what to do and that after they do it to click Play to continue the action.  In the cast of the line tool you need to install or have the user define a line tool preset with a specific name like "Line Tool 1".  The action would have a recorded step where the preset manager was used to select tool preset "Line Tool 1".  That step would set the current tool to be the line tool with no arrow heads and be 1 pixel wide path. The stop would tell the user to draw out a line then click play.  IMO the pen tool could be used but the Pen tool can also draw curved lines that twist back over the path and the bounds of a selection made from the work path might not be what one wants. Also the pen tool wants to continue drawing after you draw out the first line.

                 

                And thank you for the function.....

                • 5. Re: How to measure the distance using script
                  Michael L Hale Level 5

                  I mis-understood your post. I thought you said you could use an action to automate the process. I didn't think it was possible but thought I would ask.

                  • 6. Re: How to measure the distance using script
                    JJMack Most Valuable Participant

                    Some boiler plate to make measurement pixel and insure a document open and catch no path the action look like this

                    linelength.jpg

                     

                    // enable double-clicking from Mac Finder or Windows Explorer

                    #target photoshop // this command only works in Photoshop CS2 and higher

                      // bring application forward for double-click events

                    app.bringToFront();

                      // ensure at least one document open

                    if (!documents.length) alert('There are no documents open.', 'No Document');

                    else {

                              main(); // at least one document exists proceed

                    }

                    ///////////////////////////////////////////////////////////////////////////////

                    //                            main function                                  //

                    ///////////////////////////////////////////////////////////////////////////////

                    function main() {

                              // declare local variables

                              var orig_ruler_units = app.preferences.rulerUnits;

                              app.preferences.rulerUnits = Units.PIXELS;          // Set the ruler units to PIXELS

                              try { alert(measureLine(app.activeDocument.pathItems.getByName("Work Path"))); }

                              // display error message if something goes wrong

                              catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }

                              app.preferences.rulerUnits = orig_ruler_units;          // Reset units to original settings

                    }

                    ///////////////////////////////////////////////////////////////////////////////

                    //                           main function end                               //

                    ///////////////////////////////////////////////////////////////////////////////

                    //the code below is for the Line tool.

                    function measureLine(path){

                              var res = new Array;

                              var lineStart = path.subPathItems[0].pathPoints[0].anchor;

                              var lineEnd = path.subPathItems[0].pathPoints[3].anchor;

                              var a = Math.max(lineStart[0],lineEnd[0])-Math.min(lineStart[0],lineEnd[0]);

                              var o = Math.max(lineStart[1],lineEnd[1])-Math.min(lineStart[1],lineEnd[1]);

                              var c = Math.round(Math.sqrt((a*a)+(o*o)));

                              res.push(c);

                              var ang = (180/Math.PI) * Math.atan2(o,a);

                              if(lineStart[1] < lineEnd[1]){//negative angle

                                        ang = -ang;

                              };

                              res.push(ang);

                              return res;

                    }

                     

                    1 person found this helpful
                    • 7. Re: How to measure the distance using script
                      JJMack Most Valuable Participant

                      No it was my wording "record an interactine action" To most that means turning on a dialog in a step like a levels adjustment.  I loosely consider an inserted stop as a dialog. The hard part is getting the user to click play to continue the action after they complete the stop message directions...

                      • 8. Re: How to measure the distance using script
                        bala281972 Level 1

                        Hey JJ its awesome . . . it works perfectly with the straight lines, but when the lines are angled it gives wrong measurement. Can we do something abt it . . . may be a different algorithm because the measuring points are angled and i guess the algorithm needs to be modified for angled lines . . .

                        • 9. Re: How to measure the distance using script
                          JJMack Most Valuable Participant

                          It should work correctly after all it just a right triangle. You know Pythagorean theorem - The sum of the areas of the two squares on the legs (a and b) equals the area of the square on the hypotenuse (c).  Try adding a delet Work Path step at the beginning of the action so yoy cam play it over and over. And use this script with the trialgle spelled out for as an alert.

                          // enable double-clicking from Mac Finder or Windows Explorer

                          #target photoshop // this command only works in Photoshop CS2 and higher

                           

                           

                          // bring application forward for double-click events

                          app.bringToFront();

                           

                           

                          // ensure at least one document open

                          if (!documents.length) alert('There are no documents open.', 'No Document');

                          else {

                                    main(); // at least one document exists proceed

                          }

                          ///////////////////////////////////////////////////////////////////////////////

                          //                            main function                                  //

                          ///////////////////////////////////////////////////////////////////////////////

                          function main() {

                                    // declare local variables

                                    var orig_ruler_units = app.preferences.rulerUnits;

                                    app.preferences.rulerUnits = Units.PIXELS;          // Set the ruler units to PIXELS

                                    try { alert(measureLine(app.activeDocument.pathItems.getByName("Work Path"))); }

                                    // display error message if something goes wrong

                                    catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }

                                    app.preferences.rulerUnits = orig_ruler_units;          // Reset units to original settings

                          }

                          ///////////////////////////////////////////////////////////////////////////////

                          //                           main function end                               //

                          ///////////////////////////////////////////////////////////////////////////////

                          //the code below is for the Line tool.

                          function measureLine(path){

                                    var res = new Array;

                                    var lineStart = path.subPathItems[0].pathPoints[0].anchor;

                                    var lineEnd = path.subPathItems[0].pathPoints[3].anchor;

                                    var a = Math.max(lineStart[0],lineEnd[0])-Math.min(lineStart[0],lineEnd[0]);

                                    var o = Math.max(lineStart[1],lineEnd[1])-Math.min(lineStart[1],lineEnd[1]);

                                    var c = Math.round(Math.sqrt((a*a)+(o*o)));

                                    alert("Triangle Height = " + o + " Width = " + a + " Hypotenuse = " + c );

                                    res.push(c);

                                    var ang = (180/Math.PI) * Math.atan2(o,a);

                                    if(lineStart[1] < lineEnd[1]){//negative angle

                                              ang = -ang;

                                    };

                                    res.push(ang);

                                    return res;

                          }

                           

                          • 10. Re: How to measure the distance using script
                            Michael L Hale Level 5

                            Sorry if these points are obvious to you but they have tripped up other users.

                             

                            Make sure the line tool is not using arrowheads.

                             

                            The path needs to be made from left to right.

                             

                            Right-left directon and Arrowheads change  the path points of the line. Arrowheads adds points so the script reads the head instead to the shaft. Right to left changes the order of the points so that the line weight effects the angle.

                            • 11. Re: How to measure the distance using script
                              JJMack Most Valuable Participant

                              Michael L Hale wrote:

                               

                              Make sure the line tool is not using arrowheads.

                               

                              The path needs to be made from left to right.

                               

                              Right-left directon and Arrowheads change  the path points of the line. Arrowheads adds points so the script reads the head instead to the shaft. Right to left changes the order of the points so that the line weight effects the angle.

                              When I made the preset I set the line tool to NOT have arrow heads the line width 1 pixel and to draw in path mode then I saved the new preset. For I did not know what a wide line would do would it be a line or a rectangle or what effect arrow heads might cause.

                               

                              I do not think the path needs to be made from left to right all that that effect is the sign of the angle.  If I draw a path starting lets say bottom left and proceed to the right and up at a 45 angle the results is the line length and +45.  If on the other hand I draw the exact same path but start from the upper right and proceed to the left and down at a 45 degree angle the result is the same line length and -45 the sign more or less indicate direction the path was drawn in. If I draw a vertical path straight up the angle is +90 if I draw it straight down it -90 straight  to the right +0 to the left -0.

                               

                              The problem is that there are two +45 and two -45 infact there are  two of every angle  when dealing only with 0-90 not 0-360. So if you make it a pratice to start from the left you know +45 is up and to the right and not up and to the left. And -45 is down and to the right and not down and to the left.

                              • 12. Re: How to measure the distance using script
                                Michael L Hale Level 5

                                Sorry maybe I wasn't clear. If you want the angle reported by the script to match what would be reported if the user used the line tool you do need to make the path left to right.

                                • 13. Re: How to measure the distance using script
                                  JJMack Most Valuable Participant

                                  Michael L Hale wrote:

                                   

                                  Sorry maybe I wasn't clear. If you want the angle reported by the script to match what would be reported if the user used the line tool you do need to make the path left to right.

                                  That would only be half right as you can use the ruler measure tool from left to right and right to left a full 360 the angles reported will be in the range of 0 to +|-180 if you limit it to left to right the angle range will be from +90 to -90.

                                   

                                   

                                  I understand your statement for "var ang = (180/Math.PI) * Math.atan2(o,a);" will only generate the angle for a right triangle 0 through 90.   That is why I wrote the sign was an indication of direction meaning up or down but the problem is that there are two -45 and two +45  the other directiom left or right is only known by the line tool user. I do not understand path points you use lineStart[1] < lineEnd[1]to find out up or down perhaps some other comparison can find out left or right and then adjust the angle if to the right by to the range from +|-90 to +|-180.

                                   

                                  I think I did it now matches the line tool left to right or right to left the angle now matches the ruler measure tool when ruler unites is set to pixels

                                  linemeasuretool.jpg

                                  // enable double-clicking from Mac Finder or Windows Explorer

                                  #target photoshop // this command only works in Photoshop CS2 and higher

                                   

                                   

                                  // bring application forward for double-click events

                                  app.bringToFront();

                                   

                                   

                                  // ensure at least one document open

                                  if (!documents.length) alert('There are no documents open.', 'No Document');

                                  else {

                                            main(); // at least one document exists proceed

                                  }

                                  ///////////////////////////////////////////////////////////////////////////////

                                  //                            main function                                  //

                                  ///////////////////////////////////////////////////////////////////////////////

                                  function main() {

                                            // declare local variables

                                            var orig_ruler_units = app.preferences.rulerUnits;

                                            app.preferences.rulerUnits = Units.PIXELS;          // Set the ruler units to PIXELS

                                            try { alert(measureLine(app.activeDocument.pathItems.getByName("Work Path"))); }

                                            // display error message if something goes wrong

                                            catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }

                                            app.preferences.rulerUnits = orig_ruler_units;          // Reset units to original settings

                                  }

                                  ///////////////////////////////////////////////////////////////////////////////

                                  //                           main function end                               //

                                  ///////////////////////////////////////////////////////////////////////////////

                                  //the code below is for the Line tool.

                                  function measureLine(path){

                                            var res = new Array;

                                            var lineStart = path.subPathItems[0].pathPoints[0].anchor;

                                            var lineEnd = path.subPathItems[0].pathPoints[3].anchor;

                                            var a = Math.max(lineStart[0],lineEnd[0])-Math.min(lineStart[0],lineEnd[0]);

                                            var o = Math.max(lineStart[1],lineEnd[1])-Math.min(lineStart[1],lineEnd[1]);

                                            var c = Math.round(Math.sqrt((a*a)+(o*o)));

                                            //alert("Triangle Height = " + o + " Width = " + a + " Hypotenuse = " + c );

                                            res.push(c);

                                            var ang = (180/Math.PI) * Math.atan2(o,a);

                                            if(lineStart[0] > lineEnd[0]){//To the left

                                                      ang = 180-ang;

                                            };

                                            if(lineStart[1] < lineEnd[1]){//Down

                                                      ang = -ang;

                                            };

                                            res.push(ang);

                                            return res;

                                  }

                                   

                                   

                                  Message was edited by: JJMack

                                  • 14. Re: How to measure the distance using script
                                    Michael L Hale Level 5

                                    I understand what you are saying and I agree that to match the measure tool the function would need to be changed. What I was trying to say was that it was originally written to work with left to right paths. The angle of a path made from left to right( that is pathPoint[0].anchor[0] is less than of pathPoint[3].anchor[0] when using a line tool path ) can only have an angle of +/- 90.

                                     

                                    A path made from right to left needs to use different pathpoints and requries different math. Another factor for the mismatch is the measure tool rounds to the nearest whole pixel. Paths can use fractions of a pixel. The angle may not match exactly because of that rounding.

                                     

                                    It's true all of those issues could be fixed and you are free to make any modifications you like.

                                    • 15. Re: How to measure the distance using script
                                      Michael L Hale Level 5

                                      This only works with CS5 as it now uses the ruler tool instead of a path. But it fixes all the issues talked about in this thread and well as some other differences between the original funciton and the data in the info panel.

                                       

                                      It returns a custom object with angle and length properties if there is a ruler tool line in the active document and undefined it not. I was unsure if the length should be a unitValue or a number in the ruler units settings. I went with number because the info panel has different number of decimal places for the different units. And like the info panel if the ruler setting are set to percent the length value is blank

                                       

                                      function getRulerToolLengthAndAngle(){

                                          var points = [];

                                          points.start = [];

                                          points.end = [];

                                          var desc = new ActionDescriptor();

                                          var ref = new ActionReference();

                                          ref.putProperty( charIDToTypeID('Prpr'), charIDToTypeID('RrPt') );

                                          ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );

                                          desc.putReference( charIDToTypeID('null'), ref );

                                          var desc = executeAction( charIDToTypeID('getd'), desc, DialogModes.NO );

                                          if( desc.hasKey( charIDToTypeID('Pts ') ) ){

                                              var pointList = desc.getList( charIDToTypeID('Pts ') );

                                              var startPointDesc = pointList.getObjectValue(0);

                                             

                                              points.start.push( startPointDesc.getUnitDoubleValue( charIDToTypeID( 'X   ' ) ) );

                                              points.start.push( startPointDesc.getUnitDoubleValue( charIDToTypeID( 'Y   ' ) ) );

                                              var endPointDesc = pointList.getObjectValue(2);

                                              points.end.push( endPointDesc.getUnitDoubleValue( charIDToTypeID( 'X   ' ) ) );

                                              points.end.push( endPointDesc.getUnitDoubleValue( charIDToTypeID( 'Y   ' ) ) );

                                             

                                              var res = {};

                                              res.toString = function(){ return 'RulerTool Info';};

                                              var pointA = points.start;

                                              var pointB = points.end;

                                              if( pointA[0]<pointB[0] ){

                                                  var a = Math.max(pointA[0],pointB[0])-Math.min(pointA[0],pointB[0]);

                                                  var o = Math.max(pointA[1],pointB[1])-Math.min(pointA[1],pointB[1]);

                                                  var ang = (180/Math.PI) * Math.atan2(o,a);

                                                  if(pointA[1] < pointB[1]){//negative angle

                                                      ang = -ang;

                                                  };

                                                  res.angle = ang.toFixed(1);

                                              }else{

                                                  var a = Math.max(pointA[1],pointB[1])-Math.min(pointA[1],pointB[1]);

                                                  var o = Math.max(pointA[0],pointB[0])-Math.min(pointA[0],pointB[0]);

                                                  var ang = 180-((180/Math.PI) * Math.atan2(a,o));

                                                  if(pointA[1] < pointB[1]){//negative angle

                                                      ang = -ang;

                                                  };

                                                  res.angle = ang.toFixed(1);

                                              }

                                                  if( app.preferences.rulerUnits == Units.PERCENT ){

                                                      res.length = "";

                                                  }else{

                                                      var c = Math.sqrt((a*a)+(o*o));

                                                      var length = new UnitValue( c, 'px');

                                                      length.baseUnit = new UnitValue((1/app.activeDocument.resolution),'in');

                                                      switch( app.preferences.rulerUnits ){

                                                          case Units.PIXELS: res.length = length.as('px').toFixed(2); break;

                                                          case Units.INCHES : res.length = length.as('in').toFixed(3); break;

                                                          case Units.CM: res.length = length.as('cm').toFixed(2); break;

                                                          case Units.MM: res.length = length.as('mm').toFixed(1); break;

                                                          case Units.POINTS: res.length = length.as('pt').toFixed(1); break;

                                                          case Units.PICAS: res.length = length.as('pc').toFixed(2); break;

                                                      }

                                                  }

                                              return res;

                                          }

                                      }

                                      var info = getRulerToolLengthAndAngle();

                                      if(info != undefined ) alert('The ruler tool angle is: '+info.angle+'\nThe length is: '+info.length);

                                      • 16. Re: How to measure the distance using script
                                        JJMack Most Valuable Participant

                                        Thank you

                                         

                                        I learned quite a bit in this thread however the thread is a bit of a waste for there is no clear way to make a clean interactive script or action+script where a user can interactivly set two point so the script can then use these to help process the active document.  The process must be stopped the points set and then the process need to be resumed.

                                        • 17. Re: How to measure the distance using script
                                          bala281972 Level 1

                                          u guys are rocking ! ! !

                                           

                                          Amazing amount of interaction and understanding what was required by me so clearly without 1% variation

                                           

                                          hats off to u guys (bow)

                                           

                                          I have tried the script and it works so smoothly . . . the results were extraordinary . . .

                                           

                                          thanks for the valuable inputs and hope over a period of time i'll master the skills in JS

                                           

                                          thanks onceagain for ur patience and curiosity in answering this thread.

                                          • 18. Re: How to measure the distance using script
                                            Michael L Hale Level 5

                                            JJMack, sorry that you find the function a waste because it is not interactive or can't be used in a batch process. There are a lot of what I consider very useful scripts that are not interactive. The idea to use the ruler tool came from Adobe's straighten script which is not interactve.

                                             

                                            It is a bit of a waste to have a script that only shows in a dialog what the user can see in the info panel but the alert is to demo the function.

                                            • 19. Re: How to measure the distance using script
                                              JJMack Most Valuable Participant

                                              Always go with Michael's code he is very knowledge when it comes to object oriented coding he's an expert with javascript  and Photoshop.  He and a few other give us their expertise here for free. I feel Adobe should reward them for they provide better support for Adobe's customers then Adobe does.

                                               

                                              I'm just a hacker when it comes to Photoshop scripting.  While I see what Michael's code does that it uses the Acton Manager to get the ruler tool points if set. If point are set I see he get the opposite and adjacent triangle sides length in pixels without setting the ruler unites to pixels and calculated the other triangle side length in pixels.  If need be he converts the calculated length from a pixel length to number ruler units to the proper decimal place for the users current ruler units. Though I don't fully understand the code for  my knowledge about objects is limited at best creating a custom object is beyond my knowledge I can see where things are being done.  My high school math is just fading memory all I seem to recall is something like "Saddle  Our Horses, Cant Away Happily, To Our Adventures" I take his arctangent angle code on faith....  what the heck is an arctangent???

                                              • 20. Re: How to measure the distance using script
                                                JJMack Most Valuable Participant

                                                I did not write that the script was a waste In fact I stated I learned a lot from this thread and I always seem to learn something from whatever you write.  You constant participation here amazes me and I'm very grateful, but not to the point where I going to send you some money Adobe should do that....

                                                 

                                                IMO the op seemed to be asking for an automated interactive script or action

                                                1.Open Photoshop document

                                                2. Allow the user to draw the line using measure tool

                                                3. alert the distance

                                                 

                                                Your script absolutly does #3.    I felt the requester actually wanted to replace #3 with code that would use the distance to do something else. Not mearly put out a message with a number which would match visible information that is in Photoshop Tool option bar as well as the Info Pallet.

                                                • 21. Re: How to measure the distance using script
                                                  Michael L Hale Level 5

                                                  JJMack wrote:

                                                   

                                                  what the heck is an arctangent???

                                                  LOL, I have no idea what it is either. When I created the original function a while back I had to do a Google search to find the math needed. Math.atan2 did what I needed but returns radians instead of degrees. The (180/Math.PI) * part of the line that stores the angle is there to convert to degrees( again from a Google search ). It's very possible that someone with better math skills could come up with a function that uses fewer lines or doesn't need the left-right if statement.

                                                   

                                                  Sorry if I came off sounding grumpy.

                                                   

                                                  Also for others looking at this thread the ruler tool points seem to always be stored in the descriptor as charIDToTypeID( '#Rlt' ) regardless of the ruler unit settings. That is why I didn't use getUntiDoubleType to get the unit from the descriptor. So the points coordinates are always in pixels unless converted as I did in the switch statement.

                                                   

                                                  It might also be more useful to store the start and end points in the custom object so the function will return them as well. That way you could get the X, Y of the end point, something that the info panel doesn't show.