Highlighted

## [CC 2018][JS] How to get length of path

Participant ,
Jan 25, 2018

Copied

Hi,

Does anyone know if it's possible to get the total length of a path drawn with the pen tool?

I can't find a property which contains this value. Is this something which can be calculated?

Or is there a Indesign plugin available somewhere which support this property so it can be retrieved using scripting ?

Thanks

Valorous Hero
| Valorous Hero

Ok, so here is a possible approach :

`//A script from Loic Aigon @ ozalto.com//retrieves the length of a selected path within InDesign//Such as what Illustrator provides in the info panel.//Widely inspired from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//length is not warrantied as perfect as curve length computation are always approximations//Pretty compliant to Illustrator results though.#include "Bezier.jsinc"//Main routinevar main = function() {//VARS================================================//var doc = app.properties.activeDocument, path, pps,sum = 0, isClosed, p1, p2, i = 0;if ( !doc ) return;if ( app.selection.length!=1 || app.selection[0].properties.paths )  {alert("You must select a path please.");return;}//Retrieving path points datapath = app.selection[0].paths[0],pps = path.entirePath;n = pps.length;//Is the path closed ?isClosed = path.pathType == PathType.CLOSED_PATH;//Looping through InDesign path pointswhile ( i<n ) {//If path is not closed and last point is reached, loop breaks.//Otherwise we compute and add the length between endpoint and startpoint to the final resultif ( i+1==n && !isClosed ) break;//p1 is the current point//p2 the following point if any or the first one if the path is closedp1 = pps;p2 =  pps[i+1]?  pps[i+1]  : pps[0];//Iteratingly adding curves length to the final path length resultsum+=getPathLength(p1,p2);//Keeping on movingi++;}//Yelling the resultalert( "This measures about "+Math.round (sum*1000)/1000+" units" );}function getPathLength (pA,pB) {var sqA = typeof ( pA[0] ) == "number",sqB = typeof ( pB[0] ) == "number",x1 = sqA? pA[0] : pA[1][0],x2 = sqA? pA[0] : pA[2][0],x3 = sqB? pB[0] : pB[0][0],x4 = sqB? pB[0] : pB[1][0],y1 = sqA? pA[1] : pA[1][1],y2 = sqA? pA[1] : pA[2][1],y3 = sqB? pB[1] : pB[0][1],y4 = sqB? pB[1] : pB[1][1],p1 = {x:x1,y:y1},cp1 = {x:x2,y:y2},cp2 = {x:x3,y:y3},p2 = {x:x4,y:y4},d = 0;return Bezier.getLength ( p1, cp1, cp2, p2 );}var u;app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );`

And the Bezier.jsinc lib :

`//Bezier.jsinc by Loic Aigon @ ozalto.com//A library to compute Bezier Cubic curves items properties//Highly derivated from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//More infos on Bezier Cubic curves here : https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curvesvar Bezier = (function(){return {//Retrieve [x,y] point on a cubic bezier curve//percent stands for the location of the point regarding of the curve length expressed in %//0 means at the beginning of the curve//1 means at the end of the curvegetBezier:function(percent,p1,cp1,cp2,p2) {//Specific algoritms factor computationsfunction b1(t) { return t*t*t }function b2(t) { return 3*t*t*(1-t) }function b3(t) { return 3*t*(1-t)*(1-t) }function b4(t) { return (1-t)*(1-t)*(1-t) }//Return x,y values for the point at "percent" lcoation of the curvereturn {x: p1.x*b1(percent) + cp1.x*b2(percent) + cp2.x*b3(percent) + p2.x*b4(percent),y: p1.y*b1(percent) + cp1.y*b2(percent) + cp2.y*b3(percent) + p2.y*b4(percent) };},//Retrieves curve length//p1, p2, cp1 & cp2 with specifi structure://{x:…,y:…} where values are real numbersgetLength:function(p1,cp1,cp2,p2) {var i = 0, max = 100,bz, pvBz, d = 0;//Compute x/y location for the 100 points between start point and end pointwhile ( i<=max ) {//Sets initial bezier pointif ( !pvBz ) {pvBz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);}//Sets next point a,d compute distance regarding to previous pointelse {bz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);//Simple trigonometry to compute distance between two points (x1,y1 to x2,y2)d+=Math.sqrt (Math.pow(bz.x-pvBz.x,2)+Math.pow(bz.y-pvBz.y,2));pvBz = bz;}i++;}//return computed distancereturn d;}}})();`

Voilà.

I have this feeling that area computation is just 1.000.000 steps higher.

Topics

Scripting

Views

3.3K

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

## [CC 2018][JS] How to get length of path

Participant ,
Jan 25, 2018

Copied

Hi,

Does anyone know if it's possible to get the total length of a path drawn with the pen tool?

I can't find a property which contains this value. Is this something which can be calculated?

Or is there a Indesign plugin available somewhere which support this property so it can be retrieved using scripting ?

Thanks

Valorous Hero
| Valorous Hero

Ok, so here is a possible approach :

`//A script from Loic Aigon @ ozalto.com//retrieves the length of a selected path within InDesign//Such as what Illustrator provides in the info panel.//Widely inspired from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//length is not warrantied as perfect as curve length computation are always approximations//Pretty compliant to Illustrator results though.#include "Bezier.jsinc"//Main routinevar main = function() {//VARS================================================//var doc = app.properties.activeDocument, path, pps,sum = 0, isClosed, p1, p2, i = 0;if ( !doc ) return;if ( app.selection.length!=1 || app.selection[0].properties.paths )  {alert("You must select a path please.");return;}//Retrieving path points datapath = app.selection[0].paths[0],pps = path.entirePath;n = pps.length;//Is the path closed ?isClosed = path.pathType == PathType.CLOSED_PATH;//Looping through InDesign path pointswhile ( i<n ) {//If path is not closed and last point is reached, loop breaks.//Otherwise we compute and add the length between endpoint and startpoint to the final resultif ( i+1==n && !isClosed ) break;//p1 is the current point//p2 the following point if any or the first one if the path is closedp1 = pps;p2 =  pps[i+1]?  pps[i+1]  : pps[0];//Iteratingly adding curves length to the final path length resultsum+=getPathLength(p1,p2);//Keeping on movingi++;}//Yelling the resultalert( "This measures about "+Math.round (sum*1000)/1000+" units" );}function getPathLength (pA,pB) {var sqA = typeof ( pA[0] ) == "number",sqB = typeof ( pB[0] ) == "number",x1 = sqA? pA[0] : pA[1][0],x2 = sqA? pA[0] : pA[2][0],x3 = sqB? pB[0] : pB[0][0],x4 = sqB? pB[0] : pB[1][0],y1 = sqA? pA[1] : pA[1][1],y2 = sqA? pA[1] : pA[2][1],y3 = sqB? pB[1] : pB[0][1],y4 = sqB? pB[1] : pB[1][1],p1 = {x:x1,y:y1},cp1 = {x:x2,y:y2},cp2 = {x:x3,y:y3},p2 = {x:x4,y:y4},d = 0;return Bezier.getLength ( p1, cp1, cp2, p2 );}var u;app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );`

And the Bezier.jsinc lib :

`//Bezier.jsinc by Loic Aigon @ ozalto.com//A library to compute Bezier Cubic curves items properties//Highly derivated from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//More infos on Bezier Cubic curves here : https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curvesvar Bezier = (function(){return {//Retrieve [x,y] point on a cubic bezier curve//percent stands for the location of the point regarding of the curve length expressed in %//0 means at the beginning of the curve//1 means at the end of the curvegetBezier:function(percent,p1,cp1,cp2,p2) {//Specific algoritms factor computationsfunction b1(t) { return t*t*t }function b2(t) { return 3*t*t*(1-t) }function b3(t) { return 3*t*(1-t)*(1-t) }function b4(t) { return (1-t)*(1-t)*(1-t) }//Return x,y values for the point at "percent" lcoation of the curvereturn {x: p1.x*b1(percent) + cp1.x*b2(percent) + cp2.x*b3(percent) + p2.x*b4(percent),y: p1.y*b1(percent) + cp1.y*b2(percent) + cp2.y*b3(percent) + p2.y*b4(percent) };},//Retrieves curve length//p1, p2, cp1 & cp2 with specifi structure://{x:…,y:…} where values are real numbersgetLength:function(p1,cp1,cp2,p2) {var i = 0, max = 100,bz, pvBz, d = 0;//Compute x/y location for the 100 points between start point and end pointwhile ( i<=max ) {//Sets initial bezier pointif ( !pvBz ) {pvBz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);}//Sets next point a,d compute distance regarding to previous pointelse {bz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);//Simple trigonometry to compute distance between two points (x1,y1 to x2,y2)d+=Math.sqrt (Math.pow(bz.x-pvBz.x,2)+Math.pow(bz.y-pvBz.y,2));pvBz = bz;}i++;}//return computed distancereturn d;}}})();`

Voilà.

I have this feeling that area computation is just 1.000.000 steps higher.

Topics

Scripting

Views

3.3K

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
19 Replies
Most Valuable Participant ,
Jan 25, 2018

Copied

No built-in function but of course it can be calculated. Straight lines only, or curves as well? Straight lines is as straightforward as possibly can (assuming basic maths skills), but if you do a quick Google search on "bézier curve length" you will find that that is rather more difficult.

Alternative: use Illustrator for your vector operations. Its local version of the Info Panel can show the total length of a selected path.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 25, 2018

Copied

Another idea to get the length of a path is to add a text path to the path and measure it's length by looking at the value of endBracket. But this is no precise business because there could be a little gap between startBracket and endBracket endBracket and startBracket. You'd also need some text on the path formatted with justification fully justified.

Regards,
Uwe

// Some edits…

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 25, 2018

Copied

There are two panels that may be what you're looking for.

Transform panel.

Or you could use the Measure tool. Drag over the line with the measure tool. The

The Info panel will appear.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 25, 2018

Copied

tmmls  said:

 Does anyone know if it's possible to get the total length of a path drawn with the pen tool?

Hi Barbara,

I think the shape of a path that should be measured in length is more like that:

pathItem.length will return a number in Points.

pathItem.area will return the area of a path in square points.

Both properties are missing in the InDesign document object model.

My suggestion would be to add a text path to the shape. Add some text, eg. "a b c", set it's formatting to fully justified and read out the values for startBracket and endBracket:

`// Arbitrary shape selected.// Add text path to selection:var textPath = app.selection[0].textPaths.add();// Add text with at least two blanks to the text path:textPath.texts[0].contents = "a b c";// Set the formatting of the text to fully justified:textPath.texts[0].justification = Justification.FULLY_JUSTIFIED;// The endBracket statement should show the length of the path in points:alert(    "Length:"+"\r"+    "startBracket:"+" "+textPath.startBracket +"\r"+    "endBracket:"+" "+textPath.endBracket);`

Screenshot of the result:

Details of startBracket and endBracket:

Regards,
Uwe

Likes

1 Like

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 25, 2018

Copied

Thanks for the clarification, Uwe. Of course, it wouldn't be that simple.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 25, 2018

Copied

Even with my workaround it's not that simple, because the two brackets could have a gap when a text path is applied to a closed shape. And sometimes it's the startBracket value that counts. That will depend in which order the path points are drawn:

Regards,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Participant ,
Jan 26, 2018

Copied

Hi Uwe,

Many thanks for your feedback. Your provided solutions works great. I'm using something like this ...

`var textPath = app.selection[0].textPaths.add();textPath.texts[0].contents = "";textPath.texts[0].justification = Justification.FULLY_JUSTIFIED;var myLength = 0;if (textPath.endBracket > textPath.startBracket){    myLength = textPath.endBracket * 0.3528;}else{    myLength = textPath.startBracket * 0.3528;}alert(myLength + "mm");`

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 26, 2018

Copied

Hi tmmls ,

to get maximum precision possible I would create a new spline item on the basis of the entirePath array of the selected spline item and open the path of that with the menu command "\$ID/mOpen Path" and measure that. Then the value for startBracket will be always 0. ( At least all my tests with various items suggesting this.) And you will not run into trouble, if the selected item already has a text path…

Note: If you rotate an object like a rectangle, values will differ compared to an unrotated rectangle.

The values for an unrotated rectangle will be more accurate.

Regards,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 26, 2018

Copied

Here an example with a rectangle 100 x 100 pt where the path was opened by menu action "\$ID/mOpen Path":

Compare this values with the ones of answer #6 .

Regards,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Valorous Hero ,
Jan 28, 2018

Copied

Hi,

Possibly doable with some bezier algoritm implementation.

Not sharing the code at this time because it's really half baked for now (will do if I can make it cleaner or shall i say shareable).

The results seem compliant to what is measured inside Illustrator.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 29, 2018

Copied

 Loic.Aigon  wrote… Possibly doable with some bezier algoritm implementation.

Hi Loic,

I bet it is and you'll proof it 🙂

Thank you very much for working on a solution!

Regards,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Valorous Hero ,
Jan 29, 2018

Copied

Ok, so here is a possible approach :

`//A script from Loic Aigon @ ozalto.com//retrieves the length of a selected path within InDesign//Such as what Illustrator provides in the info panel.//Widely inspired from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//length is not warrantied as perfect as curve length computation are always approximations//Pretty compliant to Illustrator results though.#include "Bezier.jsinc"//Main routinevar main = function() {//VARS================================================//var doc = app.properties.activeDocument, path, pps,sum = 0, isClosed, p1, p2, i = 0;if ( !doc ) return;if ( app.selection.length!=1 || app.selection[0].properties.paths )  {alert("You must select a path please.");return;}//Retrieving path points datapath = app.selection[0].paths[0],pps = path.entirePath;n = pps.length;//Is the path closed ?isClosed = path.pathType == PathType.CLOSED_PATH;//Looping through InDesign path pointswhile ( i<n ) {//If path is not closed and last point is reached, loop breaks.//Otherwise we compute and add the length between endpoint and startpoint to the final resultif ( i+1==n && !isClosed ) break;//p1 is the current point//p2 the following point if any or the first one if the path is closedp1 = pps;p2 =  pps[i+1]?  pps[i+1]  : pps[0];//Iteratingly adding curves length to the final path length resultsum+=getPathLength(p1,p2);//Keeping on movingi++;}//Yelling the resultalert( "This measures about "+Math.round (sum*1000)/1000+" units" );}function getPathLength (pA,pB) {var sqA = typeof ( pA[0] ) == "number",sqB = typeof ( pB[0] ) == "number",x1 = sqA? pA[0] : pA[1][0],x2 = sqA? pA[0] : pA[2][0],x3 = sqB? pB[0] : pB[0][0],x4 = sqB? pB[0] : pB[1][0],y1 = sqA? pA[1] : pA[1][1],y2 = sqA? pA[1] : pA[2][1],y3 = sqB? pB[1] : pB[0][1],y4 = sqB? pB[1] : pB[1][1],p1 = {x:x1,y:y1},cp1 = {x:x2,y:y2},cp2 = {x:x3,y:y3},p2 = {x:x4,y:y4},d = 0;return Bezier.getLength ( p1, cp1, cp2, p2 );}var u;app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );`

And the Bezier.jsinc lib :

`//Bezier.jsinc by Loic Aigon @ ozalto.com//A library to compute Bezier Cubic curves items properties//Highly derivated from http://www.iscriptdesign.com/?sketch=tutorial/splitbezier//More infos on Bezier Cubic curves here : https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curvesvar Bezier = (function(){return {//Retrieve [x,y] point on a cubic bezier curve//percent stands for the location of the point regarding of the curve length expressed in %//0 means at the beginning of the curve//1 means at the end of the curvegetBezier:function(percent,p1,cp1,cp2,p2) {//Specific algoritms factor computationsfunction b1(t) { return t*t*t }function b2(t) { return 3*t*t*(1-t) }function b3(t) { return 3*t*(1-t)*(1-t) }function b4(t) { return (1-t)*(1-t)*(1-t) }//Return x,y values for the point at "percent" lcoation of the curvereturn {x: p1.x*b1(percent) + cp1.x*b2(percent) + cp2.x*b3(percent) + p2.x*b4(percent),y: p1.y*b1(percent) + cp1.y*b2(percent) + cp2.y*b3(percent) + p2.y*b4(percent) };},//Retrieves curve length//p1, p2, cp1 & cp2 with specifi structure://{x:…,y:…} where values are real numbersgetLength:function(p1,cp1,cp2,p2) {var i = 0, max = 100,bz, pvBz, d = 0;//Compute x/y location for the 100 points between start point and end pointwhile ( i<=max ) {//Sets initial bezier pointif ( !pvBz ) {pvBz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);}//Sets next point a,d compute distance regarding to previous pointelse {bz = this.getBezier ( (i)/100,p1,cp1,cp2,p2);//Simple trigonometry to compute distance between two points (x1,y1 to x2,y2)d+=Math.sqrt (Math.pow(bz.x-pvBz.x,2)+Math.pow(bz.y-pvBz.y,2));pvBz = bz;}i++;}//return computed distancereturn d;}}})();`

Voilà.

I have this feeling that area computation is just 1.000.000 steps higher.

Likes

4 Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Participant ,
Jan 29, 2018

Copied

Hi Loic,

Awesome job! Your code is like a piece of art. Beauty at its highest form 🙂 🙂 🙂

It works great ...

Thank you very much

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Jan 29, 2018

Copied

Hi Loic,

thank you very much for this!

Just tested a bit with some arbitrary shapes.

Ran your script, tested the copied shape in Illustrator with app.selection[0].length and also ran my scripting approach with a text path.

Just one example:

Loic: 479.344

Uwe: 479.346436822862

AI: 479.367309570312

For now I see more small differences in the results from AI than with my approach using a text path or your script.

Hi tmmls ,

I would suggest that you mark Loic's answer #12 as correct, because his approach is less interfering to the measured item compared to mine.

Regards,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Valorous Hero ,
Jan 29, 2018

Copied

 Loic: 479.344Uwe: 479.346436822862AI: 479.367309570312For now I see more small differences in the results from AI than with my approach using a text path or your script.

Indeed, the "precision" factor could be increased by reducing the iteration factor (from 1 to 1e-n)* in the "Bezier" jsinc lib / getLength function. The more you add segments, the more precise it will get. At some point, you can have a result higher than AI. One could think then that the code is wrong but as computing length is no matter what an approximation (excl. regular shapes), I am not too much concerned. I ended thinking that it was approximative enough at this stage of the script.

As for the reward, no problem, I only did it as a personal challenge and a reason to practicise those algorithms. I have been digging Area Computation too and it seems a lot more challenging. Hopefully I can get into that too. Next step.

*replace i++ by i+=.1 and you will notice the diiference I am talking about

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Participant ,
Jan 29, 2018

Copied

Great work Loic. Regarding the area computation, I posted this topic a few days ago ([Javascript] Calculate area coverage polygon object). It would be nice If this could be fixed also. Currently, I solved the issue by converting it to a text frame and inserting a character which represents a 2mm2. By checking for offset I get a rough indication of the surface area. The idea is based on an idea Laubender presented. He suggested adding inline frames and checking for offset, but that script took more time to process. Today, I found this post, suggesting to convert a path shape to a polygon shape, and then calculating the area of the polygon shape. (javascript - How can I calculate the area of a bezier curve? - Stack Overflow)  Because my math skills are very poor, this is quit out of my comfort zone ...

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Valorous Hero ,
Jan 29, 2018

Copied

Yeah I know. Indeed, I started wondering about area computation first (see delaunay triangulation). That lead me to computing additional points location on a bezier curve which lead me to length computation.

Area is tricky because, you could have "holes" or "overlaps". It's a very tough one and in prepress industry, apps that compute ink coverage actually flattens files. So eventually exporting to image formats and look after pixels is all that's left.

But for sure ExtendScript wouldn't be that fast. Using compiled libraries may be necessary.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Feb 07, 2019

Copied

Hi guys, there's actually something built into Illustrator that does what the OP is asking for.

Window / Document Info /  (open up the options in this new window) select Object

Voila - any path you need to measure will be calculated.

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
LATEST
Feb 07, 2019

Copied

Hi lordchops ,

thanks for that hint.

Actually tmmls was asking if InDesign ( not Illustrator ) is able to calculate this using JavaScript.

Best,
Uwe

Likes

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Resources
Learn and Support
Resources
Features