Skip to main content
Inspiring
January 18, 2018
Answered

[Javascript] Calculate area coverage polygon object

  • January 18, 2018
  • 5 replies
  • 7996 views

Hi,

Does anyone know if it's possible to calculate the total coverage of an irregular polygon shape in Adobe Indesign?

Something like this:

Is there a way to calculate the area of a shape?

Thanks

This topic has been closed for replies.
Correct answer Marc Autret

Hi all,

In case your splines do not auto-intersect, try this:

var pathArea = function(/*[x,y][3][]*/ep,  r,n,i,P,Q,xa,ya,xb,yb,xc,yc,xd,yd)

//----------------------------------

// Based on http://www.gust.org.pl/bachotex/2011-en/presentations/JackowskiB_3c_2011

// Return AREA x 20 ; result is signed.

{

    for( r=0, n=ep.length, i=-1 ; ++i < n ; )

    {

        3 == (P=ep).length ?

            ( xa=P[1][0], ya=P[1][1], xb=P[2][0], yb=P[2][1] ) :

            ( xa=xb=P[0], ya=yb=P[1]);

        3 == (Q=ep[(1+i)%n]).length ?

            ( xc=Q[0][0], yc=Q[0][1], xd=Q[1][0], yd=Q[1][1] ) :

            ( xc=xd=Q[0], yc=yd=Q[1]);

       

        r += (xb-xa)*(10*ya + 6*yb + 3*yc +    yd)

          +  (xc-xb)*( 4*ya + 6*yb + 6*yc +  4*yd)

          +  (xd-xc)*(   ya + 3*yb + 6*yc + 10*yd);

    }

    return r;

};

var splineArea = function(/*SplineItem*/o,  a,r,i,n,t)

//----------------------------------

// Visit every closed path and calculate the total area.

{

    const CLOSED = +PathType.CLOSED_PATH;

    a = o.paths.everyItem().getElements();

    for( r=0, n=a.length, i=-1 ; ++i < n && (t=a).pathType==CLOSED ; r+=pathArea(t.entirePath) );

    if( i < n ) throw "All paths must be closed.";

    return (r/=20), 0 < r ? r : -r;

};

var getArea = function(/*obj|obj[]*/a,t,mu,i,r)

//----------------------------------

// Main function. Return the area in the form "<value> <unit>²".

// This function does not address splines that auto-intersect.

{

    callee.Q||(callee.Q={

        Q:               'q',

        U:               'u',

        POINTS:          'pt',

        PIXELS:          'px',

        PICAS:           'p',

        MILS:            'mils',

        MILLIMETERS:     'mm',

        INCHES_DECIMEL:  'in',

        INCHES:          'in',

        HA:              'ha',

        CICEROS:         'c',

        CENTIMETERS:     'cm',

        BAI:             'bai',

        AMERICAN_POINTS: 'ap',

        AGATES:          'ag',

        });

    (a instanceof Array) || (a=);

    // Set consistent measurement unit.

    // ---

    while( 1 )

    {

        if( !(t=a[0]) || 'function' != typeof t.toSpecifier ) throw "Invalid input."

        t = t.toSpecifier().match(/^\/document\[@id=\d+\]/);

        if( !t || !(t=resolve(t[0])) || !(t instanceof Document) ) throw "Invalid input.";

        t = t.viewPreferences.horizontalMeasurementUnits;

        app.scriptPreferences.measurementUnit = t;

        mu = callee.Q[t.toString()]||'';

        mu && (mu = ' ' + mu + '\xB2');

        break;

    }

   

    for( r=0, i=a.length ; i-- ; (o=a)&&o.hasOwnProperty('paths')&&(r+=splineArea(o)) );

   

    return String(r) + mu;

};

// TEST. (Assuming something is selected.)

//========================================

var sel = app.selection;

alert( getArea(sel) );

Hope that helps.

Best,

Marc

Community Expert
January 23, 2020

Hi pjleveno,

I guess another bug with moving this thread to the new InDesign forum.

Better look into Marc's original code:

 

Nice Function for Computing Polygon Areas

Marc Autret

http://www.indiscripts.com/post/2019/06/indesign-scripting-forum-roundup-13#hd2sb2

 

Regards,
Uwe Laubender

( ACP )

Participating Frequently
January 23, 2020

Thanks! It works perfectly. I'm trying to locate unlabeled icons. Maybe I can find them based on their area, unless someone has another way to identify a path. Thanks again for these powerful tools and also your quick responses! 

Participating Frequently
January 23, 2020

Hello! I'm trying to test out the script, but I get an error. It seems I'm missing a value after "=" in the following line:

(a instanceof Array) || (a=);

What can I do to fix it? Thanks again!

tmmlsAuthor
Inspiring
February 1, 2018

Hi,

I just would like to mention that using the script on a default circle shape, the calculations seems not as accurate.

If you would like to get the area coverage of a circle, consider using pi * radius 2

For all other usages, the script Marc provided is awesome...

Peter Kahrel
Community Expert
Community Expert
February 1, 2018

Set the circle's stroke to zero and its area is calculated correctly.

P.

tmmlsAuthor
Inspiring
February 1, 2018

Hi Peter,

I have created a circle, 100x100 mm. When calculating the surface area using 50 * 50 * 3,141592653, the result doesn't match with the script output. The stroke is set to zero.

tmmlsAuthor
Inspiring
January 29, 2018

Hi,

Is this something that can be used to calculate the surface area in Indesign, without using any additional applications ?

javascript - How can I calculate the area of a bezier curve? - Stack Overflow

Thanks

Marc Autret
Marc AutretCorrect answer
Legend
January 30, 2018

Hi all,

In case your splines do not auto-intersect, try this:

var pathArea = function(/*[x,y][3][]*/ep,  r,n,i,P,Q,xa,ya,xb,yb,xc,yc,xd,yd)

//----------------------------------

// Based on http://www.gust.org.pl/bachotex/2011-en/presentations/JackowskiB_3c_2011

// Return AREA x 20 ; result is signed.

{

    for( r=0, n=ep.length, i=-1 ; ++i < n ; )

    {

        3 == (P=ep).length ?

            ( xa=P[1][0], ya=P[1][1], xb=P[2][0], yb=P[2][1] ) :

            ( xa=xb=P[0], ya=yb=P[1]);

        3 == (Q=ep[(1+i)%n]).length ?

            ( xc=Q[0][0], yc=Q[0][1], xd=Q[1][0], yd=Q[1][1] ) :

            ( xc=xd=Q[0], yc=yd=Q[1]);

       

        r += (xb-xa)*(10*ya + 6*yb + 3*yc +    yd)

          +  (xc-xb)*( 4*ya + 6*yb + 6*yc +  4*yd)

          +  (xd-xc)*(   ya + 3*yb + 6*yc + 10*yd);

    }

    return r;

};

var splineArea = function(/*SplineItem*/o,  a,r,i,n,t)

//----------------------------------

// Visit every closed path and calculate the total area.

{

    const CLOSED = +PathType.CLOSED_PATH;

    a = o.paths.everyItem().getElements();

    for( r=0, n=a.length, i=-1 ; ++i < n && (t=a).pathType==CLOSED ; r+=pathArea(t.entirePath) );

    if( i < n ) throw "All paths must be closed.";

    return (r/=20), 0 < r ? r : -r;

};

var getArea = function(/*obj|obj[]*/a,t,mu,i,r)

//----------------------------------

// Main function. Return the area in the form "<value> <unit>²".

// This function does not address splines that auto-intersect.

{

    callee.Q||(callee.Q={

        Q:               'q',

        U:               'u',

        POINTS:          'pt',

        PIXELS:          'px',

        PICAS:           'p',

        MILS:            'mils',

        MILLIMETERS:     'mm',

        INCHES_DECIMEL:  'in',

        INCHES:          'in',

        HA:              'ha',

        CICEROS:         'c',

        CENTIMETERS:     'cm',

        BAI:             'bai',

        AMERICAN_POINTS: 'ap',

        AGATES:          'ag',

        });

    (a instanceof Array) || (a=);

    // Set consistent measurement unit.

    // ---

    while( 1 )

    {

        if( !(t=a[0]) || 'function' != typeof t.toSpecifier ) throw "Invalid input."

        t = t.toSpecifier().match(/^\/document\[@id=\d+\]/);

        if( !t || !(t=resolve(t[0])) || !(t instanceof Document) ) throw "Invalid input.";

        t = t.viewPreferences.horizontalMeasurementUnits;

        app.scriptPreferences.measurementUnit = t;

        mu = callee.Q[t.toString()]||'';

        mu && (mu = ' ' + mu + '\xB2');

        break;

    }

   

    for( r=0, i=a.length ; i-- ; (o=a)&&o.hasOwnProperty('paths')&&(r+=splineArea(o)) );

   

    return String(r) + mu;

};

// TEST. (Assuming something is selected.)

//========================================

var sel = app.selection;

alert( getArea(sel) );

Hope that helps.

Best,

Marc

tmmlsAuthor
Inspiring
January 31, 2018

Hi Marc,

I've tested your code and it works great. Many thanks for sharing this script with us.

Awesome job! This is very helpful.

Community Expert
January 18, 2018

Maybe the best I can offer is cross-scripting with Illustrator.
There should be an area property, I think.

For that you would need to copy the polygon over to Illustrator.

With InDesign you would do that in a very time-consuming way. Just possible workarounds, no direct way.

One would be:

1. Do a text wrap with the shape.

2. Move a tiny text frame square with tiny contents step by step along.

3. Check after every move if the text frame is overset.

This works like a scanning mechanism and you are counting squares that add up to an area.

Regards,
Uwe

Community Expert
January 18, 2018

Another idea: Export to a pixel format with high resolution and count drawing pixels in PhotoShop.
That should also be possible by scripting.

Search for BridgeTalk here in the Scripting forum.

Kasyan Servetsky has some nice examples for cross-scripting InDesign with PhotoShop.

Regards,
Uwe