2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

InDesignで、オブジェクトを左上→右下の順にソートして、番号付けする。

Last updated at Posted at 2018-07-07

7156B44923668FB6C7DA2A5E6F9352A9.png

適当に並べられたオブジェクトを、左上→右下順にソートする方法を考える。
ss 2018-07-08 8.51.17.png

InDesignの場合、オブジェクトには
visibleBounds(見た目上、線幅等を含めたサイズ)と、
geometricBounds(線幅等を含めないサイズ)の数値が、
配列で [<上のy値>,<左のx値>,<下のy値>,<右のx値>] として入っている。

var sel = app.activeDocument.selection[0]; // 選択オブジェクトの1個目
$.writeln(sel.visibleBounds); // -> 40.5,34.2499999999008,59.5,52.7499999998016
$.writeln(sel.geometricBounds); // -> 41,34.7499999999008,59,52.2499999998016

今回は visibleBounds を使用する。
シンプルにするために、y方向の並び順についてだけ考える。
6F57CC8294AFE0B6DE6696E76305CB8D.png

各オブジェクトの上辺にシアンの接線を、下辺にマゼンタの接線を引く。
0571A3E25000CD45A1531785386A67D3.png

行になるのは、この範囲。 一番上にある上辺 から、 一番下にある下辺 まで。
0318E64C91E2B64AA1B9DF4DE7CC9F0A.png

次に、こういう並びだった場合を考える。
95FA7452064FB6BFBA4801C8185C37D9.png

さっきと同じように、上辺にシアンの接線を、下辺にマゼンタの接線を引く。
A10A8FABCF4D93B616C537CB70A8A73B.png

行になるのは、この範囲。今度は2行になる。
62CF365314F27DC880F09412C07EA286.png

つまり、行になるのは 下辺の次に現れる上辺 と、次に上辺が現れる前の下辺 まで。
そう考えると、

  1. 配列に、上辺の値と下辺の値を、上辺か下辺かのフラグをつけて収集
  2. それらを位置でソートする
  3. 下辺の次に現れる上辺と、次に上辺が現れる前の下辺の位置を収集

で、各行の範囲が取れるはず。
スクリプトを書く。

(function () {
    var ranges = getRowRanges(app.activeDocument.allPageItems);
    // $.writeln(uneval(ranges));
}());

function getRowRanges(objs) {
    // オブジェクトの配列から、行と列の範囲を返す
    var yPositions = [], bounds, i, len;
    for (i = 0, len = objs.length; i < len; i++) {
        bounds = objs[i].visibleBounds;
        // 上辺と下辺の位置を収集。0...上辺、1...下辺
        yPositions.push([0, bounds[0]]); // 上辺
        yPositions.push([1, bounds[2]]); // 下辺
    }
    return getRanges(yPositions);
}

function getRanges(positions) {
    // 収集したポジションから、行の範囲を返す
    var ranges = [],
        tmpRange = [],
        last, i;
    //位置でソート
    positions.sort(function (a, b) {
        return a[1] - b[1];
    });
    //最初の上辺の値をセット
    tmpRange[0] = positions[0][1];
    for (i = 1, last = positions.length - 1; i < last; i++) {
        // 今の値が下辺で、次が上辺だったら
        if (positions[i][0] && !positions[i + 1][0]) {
            // 下辺の値をセットしてpush
            tmpRange[1] = positions[i][1];
            ranges.push(tmpRange);
            // 次の上辺の値をセット
            tmpRange = [positions[i + 1][1]];
        }
    }
    //最後の下辺の値をセットしてpush
    tmpRange[1] = positions[last][1];
    ranges.push(tmpRange);
    return ranges;
}

これで、rangesにこんな感じのデータが返る。

[
  [35.7499999997482,52.9999999994964],
  [55.5,65.4999999997482],
  [68.7499999997482,73.9999999997482],
  [79.9999999997482,92.5],
  [95.5,110],
  [112.749999999748,134.249999999748]
]

列方向も同じ考えで良いので、行と列を一緒に処理するように変更。

(function () {
    var ranges = getRowAndColRanges(app.activeDocument.allPageItems);
    // $.writeln(uneval(ranges));
}());

function getRowAndColRanges(objs) {
    // オブジェクトの配列から、行と列の範囲を返す
    var yPositions = [], // 上辺と下辺
        xPositions = [], // 左辺と右辺
        bounds, i, len;
    for (i = 0, len = objs.length; i < len; i++) {
        bounds = objs[i].visibleBounds;
        yPositions.push([0, bounds[0]]); // 上辺
        xPositions.push([0, bounds[1]]); // 左辺
        yPositions.push([1, bounds[2]]); // 下辺
        xPositions.push([1, bounds[3]]); // 右辺
    }
    return {
        "row": getRanges(yPositions),
        "col": getRanges(xPositions)
    };
}

function getRanges(positions) {
    // ... 上記参照 ...
}

rangesに返るデータはこんな感じ。

{
  "row":[
    [35.7499999997482, 52.9999999994964],
    [55.5, 65.4999999997482],
    [68.7499999997482, 73.9999999997482],
    [79.9999999997482, 92.5],
    [95.5, 110],
    [112.749999999748, 134.249999999748]
  ],
  "col":[
    [30.7499999999008, 43.9999999998512],
    [44.9999999999008, 52.7499999998016],
    [54.7499999999008, 56.7499999999008],
    [63.5, 64.5],
    [68.5, 69.5],
    [75.4999999999008, 90.9999999999008],
    [95.5, 106.749999999901],
    [109.5, 116.999999999901]
  ]
}

塗り分けると、こんな感じになる。
D3B92022EA8ABF7D4DFF122EF7A10F02.png

あとは、重なり判定して位置を出し、ソートする。

(function () {
    var selObjs = app.activeDocument.allPageItems;
    var ranges = getRowAndColRanges(selObjs);
    // オブジェクトをZ順にソート
    var zSortFunc = function (a, b) {
        var aData = getPosition(a, ranges),
            bData = getPosition(b, ranges);
        if (aData.row == bData.row) {
            return aData.col - bData.col;
        }
        return aData.row - bData.row;
    }
    selObjs.sort(zSortFunc);
    for (var i = 0, len = selObjs.length; i < len; i++) {
        // フレームに番号を入れる
        selObjs[i].contents = (i + 1).toString();
    }
}());

function getPosition(obj, rowAndColRanges) {
    // オブジェクトのZ順での位置
    var bounds = obj.visibleBounds;
    var data = {};
    for (var r = 0, rLen = rowAndColRanges["row"].length; r < rLen; r++) {
        var rowRange = rowAndColRanges["row"][r];
        // オブジェクトが行に重なっていたら
        if (bounds[0] < rowRange[1] && bounds[2] > rowRange[0]) {
            data["row"] = r; break;
        }
    }
    for (var c = 0, cLen = rowAndColRanges["col"].length; c < cLen; c++) {
        var colRange = rowAndColRanges["col"][c];
        // オブジェクトが列に重なっていたら
        if (bounds[1] < colRange[1] && bounds[3] > colRange[0]) {
            data["col"] = c; break;
        }
    }
    return data;
}

function getRowAndColRanges(objs) {
    // ... 上記参照 ...
}

function getRanges(positions) {
    // ... 上記参照 ...
}

このスクリプトを実行すると、左上→右下順にオブジェクトに番号が入る。

5A152D72EA4E114D61AB157D1C0055F9.png

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?