Calculating Area of Rectilinear Polygons using AppleScript

I am looking for a method of calculating the area enclosed by a custom shape (for example the example of union of 2 shapes below).

The area of a custom shape is difficult to calculate using shape properties I can query using AppleScript. The size attribute specifies the width/height of the entire bounding box, but does not specify any information regarding the notch. Thus I am having trouble coming up with the area for this shape using a script

If I manually enable “edit points” for a shape, the shape changes to “Bezier” and i can now query an attribute called point list using AppleScript. Since the point list defines the real shape of the polygon I can easily use it to calculate the enclosed area.

However, it is not guaranteed that all objects on my canvas are “Bezier”. With that in mind I have the following questions:
1. Is it possible to automate the process of making points editable using AppleScript? (i.e. not have to manually select the checkbox for each object)
2. Is there any other automated way of deriving a point list for a custom shape using AppleScript?

Thanks in advance for the help.

  • M

I wonder whether you could dump the object out as a B&W gif to a true image analysis app, get in return the relative area of black or white pixels, multiply that by the total pixel count, and viola be done.

It seems otherwise like a royal pain to go through all the steps you want to take.


JJW

What data is copied onto the clipboard? There might be some useful additional data you could use.

If I use a “Bezier” to Edit -> Copy As -> AppleScript, I get this:

tell application id “com.omnigroup.OmniGraffle6”
tell canvas of front window
make new shape at end of graphics with properties {name: “Bezier”, fill: 0, draws shadow: false, text: {size: 16, alignment: center, font: “HelveticaNeue”, text: “pd_
<%UserData area%>”}, user data: {polyUsed: “true”, area: “3.206”}, point list: {{214.82496, 591.3907200000001}, {214.82496, 591.3907200000001}, {236.3904, 591.3907200000001}, {236.3904, 591.3907200000001}, {236.3904, 591.3907200000001}, {236.3904, 521.30304}, {236.3904, 521.30304}, {236.3904, 521.30304}, {268.73856, 521.30304}, {268.73856, 521.30304}, {268.73856, 521.30304}, {268.73856, 474.02496}, {268.73856, 474.02496}, {268.73856, 474.02496}, {107.8272, 474.02496}, {107.8272, 474.02496}, {107.8272, 474.02496}, {107.8272, 591.3907200000001}, {107.8272, 591.3907200000001}, {107.8272, 591.3907200000001}, {214.82496, 591.3907200000001}}, fill color: {1.000000, 0.568627, 0.301961}}
end tell
end tell

The point list is the attribute I can query using set myPoints to point list of self. Once I know this, I can calculate the of this shape.

However, If I uncheck the “Edit Points” button, the same “Bezier” shape becomes a “Custom” shape with the following AppleScript

tell application id “com.omnigroup.OmniGraffle6”
tell canvas of front window
make new shape at end of graphics with properties {name: “38E86AD2-DC5C-4AA8-A51B-6794FDAAC570-2779-0000D2BDBEACF8DC”, fill: 0, draws shadow: false, size: {160.911360, 117.365760}, text: {size: 16, alignment: center, font: “HelveticaNeue”, text: “pd_
<%UserData area%>”}, origin: {106.997760, 465.750000}, fill color: {1.000000, 0.568627, 0.301961}}
end tell
end tell

There is no readily available attribute I can query to get the area. size is the width/height of the entire bounding box, but does not include any notches.

Since I have a canvas with a mix-and-match of both “Bezier” & “Custom” shape, I am running into issues calculating accurate areas for “Custom” shapes.

In case this kind of thing comes up again, you may get a better point list from the GraphicType content of the clipboard, which is designed to be pasteable into other graphic applications.

For example, from Script Editor, with language selector at top left set to JavaScript,

  • copy a graphic
  • run the script or Keyboard Maestro macro below:

Copy from OmniGraffle 7 as GraphicType JSONvB.kmmacros.zip (10.5 KB)

(() => {
    'use strict';

    ObjC.import('AppKit');

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });

    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });

    // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
    const bindLR = (m, mf) =>
        m.Right !== undefined ? (
            mf(m.Right)
        ) : m;

    // elem :: Eq a => a -> [a] -> Bool
    const elem = (x, xs) => xs.includes(x);

    // isLeft :: Either a b -> Bool
    const isLeft = lr =>
        lr.type === 'Either' && lr.Left !== undefined;

    // String copied to general pasteboard
    // copyText :: String -> IO Bool
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;
        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            )
        );
    };

    // MAIN ----------------------------------------------------------
    const
        ogType = 'com.omnigroup.OmniGraffle.GraphicType',
        pBoard = $.NSPasteboard.generalPasteboard;

    const lrResult = bindLR(
        elem(ogType,
            ObjC.deepUnwrap(pBoard.pasteboardItems.js[0].types)
        ) ? Right(
            JSON.stringify(
                ObjC.deepUnwrap(
                    pBoard.propertyListForType(ogType)
                ),
                null, 2
            )
        ) : Left('OG7 graphics not found on clipboard'),
        strJSON => (
            copyText(strJSON),
            Right('Clipboard now contains JSON-encoded OG7 graphics')
        )
    );

    return lrResult.Right || lrResult.Left
})();