// Random Distribute — Adobe Illustrator compatible
// Port of randomdistribute.js (Eike Menijn, scriptographer.org).
//
// WHAT IT DOES: scatters the SELECTION over the canvas. Every selected object is moved to a
// position drawn at random — but the draw is made over the range that keeps the object
// ENTIRELY VISIBLE, exactly as in the original:
//
//     position = random(0, documentSize - itemBounds.size) + itemBounds.size / 2
//
// per axis (Scriptographer's `position` is the CENTRE of an item's bounds, hence the
// + size/2). So a wide object gets a narrower range than a small one and NOTHING ends up
// hanging off the artboard. Only the position changes: size, rotation, colour, stacking
// order and everything else are left alone.
//
// USE: select the objects, run the script. The whole run is ONE undo step, so a single ⌘Z
// puts every object back exactly where it was. Run it again for a different scatter.
//
// AN OBJECT BIGGER THAN THE CANVAS cannot be placed entirely inside it: the range collapses
// to zero (Math.max below) and the object is parked at the top-left corner, which is the
// closest thing to "as visible as possible".
#target illustrator

var doc = app.activeDocument;

// The artboard: this DOM has exactly one, and it is the document box (Document.width /
// Document.height, i.e. svgcontent's width/height — the canvas the user sees).
var docW = doc.width, docH = doc.height;

// Snapshot the selection ONCE — doc.selection is a live getter.
var sel = doc.selection;
var items = [];
for (var i = 0; sel && i < sel.length; i++) if (sel[i]) items.push(sel[i]);

if (!items.length)
    throw new Error('Random Distribute needs a selection: select the objects you want to ' +
                    'scatter over the canvas, then run the script again.');

// Bounds of an item in DOCUMENT coordinates — i.e. WITH its own transform folded in.
// Why not just `item.geometricBounds`: that is built on getBBox(), which on both engines
// (as in every browser) reports the LOCAL, PRE-transform box. An object that already carries
// a transform — it was rotated with the cage, or this very script moved it on an earlier run,
// since translate() only APPENDS to `transform` — would then be measured where it is NOT, and
// the move would be off by the transform. Reading through the fork's own transform-aware pair
// (svgedit.math.getMatrix + transformBox, present on the WKWebView engine and mirrored by the
// native scripting_dom.js shim) makes "run it twice" land inside the canvas both times.
// Units: the default AIConfig mapping is 1 point = 1 user unit, origin top-left (topdown),
// so AI coordinates and SVG user coordinates coincide — which is what this app ships.
function docBounds(item) {
    var g = item.geometricBounds;                       // [left, top, right, bottom]
    var box = { x: g[0], y: g[1], width: g[2] - g[0], height: g[3] - g[1] };
    try {
        var m = svgedit.math.getMatrix(item._el);
        var t = svgedit.math.transformBox(box.x, box.y, box.width, box.height, m).aabox;
        return { x: t.x, y: t.y, width: t.width, height: t.height };
    } catch (e) { return box; }                         // no transform-aware pair: local box
}

for (var j = 0; j < items.length; j++) {
    var item = items[j];
    var b = docBounds(item);

    // The original, verbatim: draw inside the range that keeps the object fully on the
    // artboard, and read the result as the CENTRE of its bounds.
    var rx = Math.random(), ry = Math.random();
    var cx = rx * Math.max(0, docW - b.width) + b.width / 2;
    var cy = ry * Math.max(0, docH - b.height) + b.height / 2;

    // …and move it there. `position` in this DOM is the top-left of the bounds, so the move
    // is expressed as the translation between the two centres — which is also the form that
    // survives an item that already has a transform (see docBounds above).
    item.translate(cx - (b.x + b.width / 2), cy - (b.y + b.height / 2));
}

app.redraw();
$.writeln('Random Distribute: scattered ' + items.length + ' object(s) over ' +
          docW + 'x' + docH + '.');
