OmniJS Create Objects with Properties?

Creating an object with properties ?

Does it look feasible to implement the Create Object with Properties syntax in OmniJS ?

e.g. as in JXA, something like:

var canvas = document.windows[0].selection.canvas
var g1 = canvas.newShape({
    textUnitRect : new Rect(0.14, 0.12, 0.75, 0.75),
    textVerticalPadding : 0,
    shape : "AdjustableStar",
    geometry : new Rect(571.18, 306.14, 120.00, 120.00),
    shadowColor : null
});

It might not allow much performance gain, but I do find that it allows for more readable code - grouped and indented, and less need to repeatedly type (or read) a reference to the shape.

1 Like

I realise now that a “with properties” style-record pattern is already possible within the omniJS interface syntax, enabled by the standard Object.assign method in JavaScript (Safari 9 JS onwards, so available to Yosemite JS + in JXA or omniJS).

    // First selected graphic, or a default new circle
    shpSeln = graphics.length > 0 ? graphics[0] : (
        Object.assign(
            canvas.newShape(), {
                shape: "Circle",
                geometry: new Rect(100, 50, 50, 50),
                shadowColor: null,
                strokeColor: Color.RGB(0.5, 0.5, 0.5)
            }
        )
    )

Given that this pattern:

  1. corresponds to the (possibly more familiar ?) pattern of the existing AppleScript/JXA interface to OmniGraffle
  2. enables a convenient single-record assignment of the same pre-defined style to several shapes

Perhaps it might make a good default for tutorial code on Omni-Automation ?

( I don’t have a strong view on this, pedagogically, but it is certainly a style which I will personally use, and which feels very familiar from the AppleScripting past :-)

For example, if we take the very nice selection to linked polygon examples in Sal’s sample plugin, which use a sequence of individual property assignments to set an arrow style:

[details=Series of individual in-line property assignments:]

    // connect lines function
    function connectObjects(startObj,endObj){
        cnvs = document.windows[0].selection.canvas
        aLine = cnvs.connect(startObj,endObj)
        aLine.lineType = LineType.Curved
        aLine.strokeColor = Color.black
        aLine.shadowColor = null
        aLine.strokeThickness = 4
        aLine.strokeType = StrokeType.Single
        aLine.headType = 'Arrow'
        aLine.tailType = 'None'
    }
```[/details]


We could alternatively define a style as a single record of key-value pairs, and pass that record (or alternative style records), to functions which create links and shapes:

```javascript
    // STYLING AN OBJECT BY ASSIGNING A STYLE RECORD (an OG AS analogy)

    // dctLineStyle :: Style for arrows between replicated shapes
    var dctLineStyle = {
        lineType: LineType.Curved,
        strokeColor: Color.red,
        shadowColor: null,
        strokeThickness: 4,
        strokeType: StrokeType.Single,
        headType: 'Arrow',
        tailType: 'None'
    };

    // styledLink :: Style Dictionary -> Canvas -> Shape -> Shape -> Line
    function styledLink(dctStyle, oCanvas, shapeFrom, shapeTo) {
        return Object.assign(
            oCanvas.connect(shapeFrom, shapeTo), // New link line,
            dctStyle                             // with assigned style
        );
    }

Full example of creating shapes in a 'with properties' style
    (function () {
        'use strict';

        // STYLING AN OBJECT BY ASSIGNING A STYLE RECORD (an OG AS analogy)

        // dctLineStyle :: Style for arrows between replicated shapes
        var dctLineStyle = {
            lineType: LineType.Curved,
            strokeColor: Color.red,
            shadowColor: null,
            strokeThickness: 4,
            strokeType: StrokeType.Single,
            headType: 'Arrow',
            tailType: 'None'
        };

        // styledLink :: Style Dictionary -> Canvas -> Shape -> Shape -> Line
        function styledLink(dctStyle, oCanvas, shapeFrom, shapeTo) {
            return Object.assign(
                oCanvas.connect(shapeFrom, shapeTo), // New link line,
                dctStyle // with assigned style
            );
        }

        // MAIN --------------------------------------------------------------

        // Number of polygon sides
        var intSides = 6,

            seln = document.windows[0].selection,
            cnv = seln.canvas,
            graphics = seln.graphics,

            // First selected graphic, or a default new circle
            shpSeln = graphics.length > 0 ? graphics[0] : (
                Object.assign(
                    cnv.newShape(), {
                        shape: "Circle",
                        geometry: new Rect(100, 50, 50, 50),
                        shadowColor: null,
                        strokeColor: Color.RGB(0.5, 0.5, 0.5)
                    }
                )
            ),
            objGeo = shpSeln.geometry,

            objC = objGeo.center,
            objH = objGeo.height,
            objW = objGeo.width,

            // Diameter and radius of a circle intersection selection
            diameter = objH * (intSides + 1),
            radius = diameter / 2,

            // the center of this circle.
            centerX = objC.x,
            centerY = objC.y + radius;

        // New large background circle
        var shpCircle = cnv.addShape(
                'Circle',
                new Rect(objGeo.midX - radius, objGeo.midY, diameter, diameter)
            )
            .orderBelow(shpSeln);

        // A fold / reduce over an array [1 .. intSides-1]
        // Initialising the reduce accumulator to the selected shape,
        // and:
        // 1. Updating the accumulator at each step to hold the previous shape,
        // 2. creating a styled link between previous shape and new shape.

        var shpFinal = enumFromTo(1, intSides - 1)
            .reduce(
                function (prev, n) {
                    var next = shpSeln.duplicateTo(
                        topLeftFromCenter( // X, Y of
                            objH, objW,
                            pointFromRadialVector( // next shape on circle.
                                centerX, centerY,
                                ((360 / intSides) * n) - 90,
                                radius
                            )
                        )
                    );
                    return (
                        // Styled link created,
                        styledLink(dctLineStyle, cnv, prev, next),
                        // And the accumulator updated to hold this new shape
                        // for the next step of the fold / reduce:
                        next
                    );
                },
                shpSeln // Initial value of the accumulator.
            );

        // Link between the final shape and the original shape,
        // closing the polygon.
        var linkFinal = styledLink(dctLineStyle, cnv, shpFinal, shpSeln);

        // (done) ...


        // GENERIC FUNCTIONS --------------------------------------------------
        // enumFromTo :: Int -> Int -> Maybe Int -> [Int]
        function enumFromTo(m, n, step) {
            var d = (step || 1) * (n >= m ? 1 : -1);
            return Array.from({
                length: Math.floor((n - m) / d) + 1
            }, function (_, i) {
                return m + (i * d)
            });
        }

        // pointFromRadialVector :: Real -> Real -> Real -> Real -> Point
        function pointFromRadialVector(xOrigin, yOrigin, angle, distance) {
            function delta(f, from) {
                return f(angle * Math.PI / 180) * distance + from;
            }
            return new Point(
                delta(Math.cos, xOrigin),
                delta(Math.sin, yOrigin)
            );
        }

        // Width -> Height -> center X -> center Y -> Point
        // topLeftFromCenter :: Real -> Real -> Point -> Point
        function topLeftFromCenter(width, height, center) {
            return new Point(
                center.x - (width / 2),
                center.y - (height / 2)
            );
        }
    })();