Understanding the effect of boolean operations on the .shape property

I created two shapes in OmniGraffle, a square and a circle.
I subtracted the circle from the square, then did a ‘copy as javascript.’
Next, I selected ‘Undo’ (to un-subtract), moved the circle a bit, then re-did the ‘copy as javascript’ and looked at the difference between the two files.

var canvas = document.windows[0].selection.canvas;
var g1 = canvas.newShape();
g1.shape = "BE8634A1-471D-4D3C-B0B3-49AC50F65A46-75347-00015195BDBD7C7A";
g1.shape = "10F6CA84-E927-4562-9762-163FEEA4B13E-75347-0001518001EEBE5A";

When the shape property is custom, how can we best understand these strings, parse them, etc? Thanks!

The only information they contain is a unique identifier.

In the GraphicsList Array of that Sheet, the identifier serves as a name.

In the ExportShapes Array of the same Sheet, you will find an entry with a ShapeName corresponding to that identifier. The entry for the identified shape includes a StrokePath dictionary, the keys of which include an elements array (a sequence of (MOVETO, (x, y)) tuples).

This is all in the .plist file which you will find inside the zipped package which constitutes a .graffle file.

For omniJS API access to the geometry of the custom shape which is bound to the name g1, try (for example in the Console):

g1.shapeVertices

Thank you - I’m guessing your example is mainly referencing the AppleScript. Are the JS commands equal to, or a subset of the AppleScript ones?

Is there a way to perform unite / intersect / subtract operations with code? If so, specifically with javascript?

Thanks!

Mmm … not quite sure what you mean there. No AppleScript involved.

There is still an AppleScript interface, but AppleScript itself is in sunset mode and only available on macOS. The actively maintained scripting interface is a JavaScript API (omniJS) which allows you to use the same code on macOS and iOS/iPadOS versions of OmniGraffle.

[ OmniGraffle omniJS API ]( https://omni-automation.com/omnigraffle )

The plist file is the main contents of the .graffle file itself.

If you select custom shapes and use Copy As JavaScript you will obtain code which binds names like g1, g2 etc to each selected graphic.

g1.shapeVertices is an omniJS (JavaScript) incantation which returns an Array of Point objects defining the custom geometry of the graphic g1.

More generally, when you perform a boolean operation on two shapes, the geometry of the custom shape is simply a list of points, and a long UUID string is attached to the custom shape as an identifier.

The new custom shape is amnesic – it contains no reference to its own history, or to the parent shapes which were used in the definition of its contours.

Is there something particular which you are trying to create / automate ?

The relevant section of Automation > API Reference, is, of course:

The short answer to your question is that boolean operations are used to define an entirely new shape (with a UUID for a name) and with a custom .shapeVertices Array of Point objects.

Are there omniJS methods that can be passed two or more references to shapes, and perform those same boolean operations on them?

Is there a distinct property exposed on the shape class that indicates that the shape is a boolean? If so, is there a method which can be called to uncombine them? (And presumably return a list of references to the unique member shapes, as a result?)

Thank you!

property exposed on the shape class that indicates that the shape is a boolean ?

There are boolean operations (defining shapes with a custom lists of shapeVertices) but under the hood there are no boolean shapes. Just custom shapes.

Nothing in a custom shape tells us how its particular list of shapeVertices was calculated.

(so, for example, no way of defining an uncombine beyond the ⌘Z Undo stack)

Do you want to show us an (input, output) pair illustrating what you want to be able to do ?

To implement shape cropping or combination, you would have to implement you own functions over pairs of shapeVertices lists.

i.e. you would need to look up an algorithm for deriving the intersection of two polygons from the lists of their vertices. Not trivial.

[math - A simple algorithm for polygon intersection - Stack Overflow](https://stackoverflow.com/questions/2272179/a-simple-algorithm-for-polygon-intersection)

[Weiler–Atherton clipping algorithm - Wikipedia](https://en.wikipedia.org/wiki/Weiler–Atherton_clipping_algorithm)

Given that such algorithms are clearly implemented somewhere in OG, you might also try writing to Omni Support and registering interest in having them exposed in the API.