Is there a way to get/set a document’s grid properties?
At least for AppleScript, the scripting dictionary shows the following properties for the grid of a canvas - therefore there is a way…
grid n [inh. [item]] : The grid of a canvas
properties
draws in front (boolean) : Does the grid draw in front of all shapes?
major (boolean) : Does the grid have ‘major’ lines?
major spacing (integer) : The number of minor grid lines for each major line
script grid color ([color]) : Color of the grid
script major grid color ([color]) : Color of major lines
snaps (boolean) : Do points snap to the grid?
spacing (real) : Number of pixels between minor grid lines
visible (boolean) : Is the grid visible?
Reading them through the omniJS interface, you could paste and enter something like this in the automation console:
(() => {
'use strict';
const
grid = canvases[0].grid,
ks = Object.getOwnPropertyNames(grid.constructor.prototype)
.filter(k => 'constructor' !== k)
return ks.reduce((a, k) => {
const v = grid[k];
return a + k + '\t' + (
'object' !== typeof v ? (
v + '\n'
) : 'RGB: (' + [v.red, v.green, v.blue]
.map(x => 255 * x)
.join(', ') + ')\n'
);
}, '');
})();
to obtain a result analogous to:
drawsInFront true
majorSpacing 5
majorColor RGB: (47.93519541621208, 47.936570942401886, 47.935833781957626)
minorColor RGB: (143.72830599546432, 152.28140383958817, 193.3756971359253)
spacing 4
snaps false
visible true
And, of course, you can update (and re-check) properties by reversing this process:
(() => {
'use strict';
const
grid = canvases[0].grid,
ks = ['drawsInFront', 'majorSpacing', 'majorColor', 'minorColor',
'spacing', 'snaps', 'visible'
];
return (
// Effects
grid.drawsInFront = true,
grid.majorSpacing = 25,
grid.majorColor = Color.blue,
grid.minorColor = Color.gray,
grid.spacing = 5,
grid.snaps = true,
grid.visible = true,
// Values
ks.reduce((a, k) => {
const v = grid[k];
return a + k + '\t' + (
'object' !== typeof v ? (
v + '\n'
) : 'RGB: (' + [v.red, v.green, v.blue]
.map(x => 255 * x)
.join(', ') + ')\n'
);
}, '')
);
})();
returning the list of updated grid properties:
drawsInFront true
majorSpacing 25
majorColor RGB: (0, 0, 255)
minorColor RGB: (127.5, 127.5, 127.5)
spacing 5
snaps true
visible true
Documentation concerning grids are also found at the Omni Automation website:
https://omni-automation.com/omnigraffle/grid-00.html
Cheers!
P.S. Try the toothpick examples!
@Sal thanks! Is this page new? I totally missed it. Anyway, I see the Grid is a read only property, any ETA on when it can be set also?
Thx
BTW, whats in the pipe line for omni-automation? I’d be rather interested to hear what you guys plan to cook up in the near future. Tell us what you can please!
The Grid object is a read-only reference, but all of its properties are RW
(you can test the setting of grid properties with the code above in
https://discourse-test.omnigroup.com/t/grid-properties/44324/4?u=draft8)
Greetings! That page has been up for a while. I updated it last year with the toothpick examples in the hope it would make it a bit more fun and interesting. Making the grid writable is on the wish list for sure!
As to what is coming up for Omni Automation, I”ll leave announcements to Ken ;-)
HINT: very very very cool stuff!
We all optimise for different things in the way we write our code.
For my taste, the code at omni-automation.com is a bit heavy-going on the poor reader, especially if they are a beginner – looking for the basic structures and concepts.
It seems, in particular:
- rather monolithic and unchunked,
- a bit repetitive and demanding on the poor typist,
- out of sync with current JS learning materials like the first half of Eloquent Javascript, which (sensibly and helpfully) use ES6
let
andconst
etc rather than the trickier and less predictable ES5var
.
(Remember that omniJS only runs on macOS versions which have ES6 interpreters – any argument for ‘backward-compatibility’ would have to appeal more to personal nostalgia and the joys of retro aesthetics than to practicalities for beginners).
There are lots of ways of re-writing the omni-automation
code examples, here is one way of doing it for the second toothpick example of omniJS grid-setting.
Javascript is entirely built around dictionaries of key-value pairs ("Objects
" in the documentation), and I think it can be clarifying and simplifying to introduce them straight away, together with the Object.assign() method, which (very helpfully for omniJS scripting) lets us add the key values of one dictionary/‘Object’ to another:
(() => {
'use strict';
// A less repetitive, and perhaps more structured, variant
// of the ToothPick Puzzle #2 code at:
// https://omni-automation.com/omnigraffle/grid-00.html
const main = () => {
// A reference to the front canvas,
const cnvs = document.windows[0].selection.canvas;
// key-value pairs for the line style that we want,
const dictLineStyle = {
strokeThickness: 10,
shadowColor: null,
strokeCap: LineCap.Butt,
strokeColor: Color.RGB(0.647059, 0.647059, 0.647059)
};
// key-value pairs for the the grid style that we want,
const dictGridStyle = {
majorSpacing: 10,
spacing: 10,
majorColor: Color.blue,
minorColor: Color.white,
snaps: true,
visible: true,
drawsInFront: false
};
// (From, To) pairs for the line coordinates that we want.
// fromTos :: [((Float, Float), (Float, Float))]
const fromTos = [
Tuple([400.00, 210.00], [400.00, 390.00]),
Tuple([200.00, 210.00], [200.00, 390.00]),
Tuple([0.00, 210.00], [0.00, 390.00]),
Tuple([400.00, 10.00], [400.00, 190.00]),
Tuple([200.00, 10.00], [200.00, 190.00]),
Tuple([0.00, 10.00], [0.00, 190.00]),
Tuple([210.00, 400.00], [390.00, 400.00]),
Tuple([10.00, 400.00], [190.00, 400.00]),
Tuple([210.00, 200.00], [390.00, 200.00]),
Tuple([10.00, 200.00], [190.00, 200.00]),
Tuple([210.00, 0.00], [390.00, 0.00]),
Tuple([10.00, 0.00], [190.00, 0.00]),
]
// A cleared canvas with the grid features required,
cnvs.graphics.forEach(x => x.remove());
cnvs.canvasSizingMode = CanvasSizingMode.Infinite
Object.assign(cnvs.grid, dictGridStyle)
// a new line created, positioned, and styled for each of our
// pairs of (From, To) points.
fromTos.forEach((pair, index) =>
Object.assign(
// A fresh line:
cnvs.newLine(),
// with the required start and end points,
{
points: Array.from(pair)
.map(x => new Point(...x))
},
// the style defined in our dictionary of keys and values,
dictLineStyle,
// and a name that varies with the position in the sequence.
{
name: 'Toothpick ' + chr(ord('L') - index)
}
)
)
};
// GENERIC FUNCTIONS ----------------------------------
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// chr :: Int -> Char
const chr = x => String.fromCodePoint(x);
// ord :: Char -> Int
const ord = c => c.codePointAt(0);
// ---
return main();
})();
You make some excellent points about modern JavaScript.
I could imagine that some progress could be made if Omni open sourced all their OmniJS documentation and examples in a Github repo and accepted pull requests.
It does all feel just a little silo-ed and stagnant at the moment – I’m not seeing much omniJS in the wild …
Opening it up a bit (on Github, for example) might well infuse fresh energy and exchange.
(The https://github.com/JXA-Cookbook/JXA-Cookbook/wiki model did harvest more attention and generate more resource and editorial range in the early stages of JavaScript for Automation than the https://omni-automation.com model seems able to).
Opening up documentation is just a first step. To have more omniJS in the wild, I believe there is some more things that would need to happen:
- make automation a standard feature on all platforms so there’s more developers and more demand for automation
- provide access to NPM to leverage all the javascript code out there
- support frictionless development (connect a debugger, support updating and reloading plugins without manual interaction etc.)
I hope that is so obvious that at least some of it is amongst the upcoming “very very very cool stuff” Sal hinted at in January.
A footnote – just looked at the 101 example at omni-automation.com
I think I would tend to:
-
skip all the var var var (use of
var
would puzzle readers of Eloquent JavaScript coming to JS for the first time, and only gets us into trouble anyway. EJ chapter 2: “we’ll rarely use var in this book because it has some confusing properties.”) [^footnote] -
chunk the structure a bit more to ease the cognitive load of a big rectangular slab of steps.
-
Add
use strict
to get more informative error messages.
Perhaps sth closer to:
(() => {
'use strict';
const dictStyle = {
strokeThickness: 12,
strokeColor: Color.red,
fillColor: Color.RGB(0, 0, 1, 1)
};
Object.assign(
document.windows[0].selection.canvas
.addShape(
'Circle',
new Rect(100, 100, 200, 200)
),
dictStyle
);
})();
as an alternative to:
var aRect = new Rect(100,100,200,200)
var aFillColor = Color.RGB(0, 0, 1, 1)
var aStrokeColor = Color.red
cnvs = document.windows[0].selection.canvas
var aShape = cnvs.addShape('Circle',aRect)
aShape.strokeThickness = 12
aShape.fillColor = aFillColor
aShape.strokeColor = aStrokeColor
[^footnote]: Confusing properties of var
:
- Unlike let and const,
var
leaks scope out of{ ... }
blocks, up to the whole of the enclosing function, or, in the absence of a function wrapper, out into the global namespace. - The accidents that this can cause are compounded by the fact that
var
bindings (again unlike let and const bindings) trigger no warning at all when you have accidentally used the same name twice in the same scope, or in what you had quite reasonably imagined to be two different scopes. - Misuse of
var
as the default pattern for binding names to values also makes mutability a default, and default mutability has an explosive (and quite unnecessary) effect on the program-state complexity and risk of bugs that a beginner has to navigate. const is always better than let, and there is never any need forvar
. Hard to think of a messier or more expensive candidate for a default.