Skip to main content
Participating Frequently
February 19, 2024
Question

Paint by numbers script help

  • February 19, 2024
  • 1 reply
  • 571 views

Hello. What can be done in this script to put more than one number depending on the area of the object?

#target illustrator
    
var doc = app.activeDocument;
function main() {
    var _pageItems = doc.pageItems;
    var _layersCollection = [];
    for (var p = 0; p < _pageItems.length; p++) {
        collectItemsBasedOnColors(_pageItems[p], _layersCollection);
    }
    lays = doc.layers;
    WORK_LAY = lays.add();
    NUM_LAY = lays.add();

    for (var l = 0; l < _layersCollection.length; l++) {
        process(_layersCollection[l].pathItems, false, (l + 1));
    }
}

function process(items, isCompound, _num) {
    var j = 0,
        b, xy, s, p, op;

    for (; j < items.length; j++) {
        // process each pathItem
        op = items[j];
        // add stroke
        if (isCompound) {
            strokeComPath(op);
        } else {
            !op.closed && op.closed = true;
            op.filled = false;
            op.stroked = true;
        };
        b = getCenterBounds(op);
        xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
        s = (
            Math.min(op.height, op.width) < 20 ||
            (op.area && Math.abs(op.area) < 150)
        ) ? 4 : 6; // adjust font size for small area paths.
        add_nums(_num, xy, s);
    }
}

function collectItemsBasedOnColors(myCurrentObject, _layersCollection) {
    var myLayerName = "Unknown";
    if (myCurrentObject.typename == "PathItem") {
        if (myCurrentObject.filled == true) {
            if (myCurrentObject.fillColor == "[CMYKColor]") {
                myLayerName = "CMYK: " + Math.round(myCurrentObject.fillColor.cyan) + "," + Math.round(myCurrentObject.fillColor.magenta) + "," + Math.round(myCurrentObject.fillColor.yellow) + "," + Math.round(myCurrentObject.fillColor.black);
            }
        }
        try {
            var _layer = doc.layers.getByName(myLayerName);
            myCurrentObject.move(_layer, ElementPlacement.PLACEATBEGINNING);
        }

        catch (e) {
            var _layer = doc.layers.add()
            _layer.name = myLayerName
            myCurrentObject.move(_layer, ElementPlacement.PLACEATBEGINNING);
            _layersCollection.push(_layer);
        }
    }
}

function getCenterBounds(op) {
    var originalMinSize = getMinVisibleSize(op.visibleBounds);

    var p = applyOffset(op, false);

    if (getMinVisibleSize(p.visibleBounds) > originalMinSize) {
        // in some cases, path p becomes larger for some unknown reason
        p.remove();
        p = applyOffset(op, true);
    }

    var b = p.visibleBounds;

    if (getMinVisibleSize(b) > 10) {
        activeDocument.selection = [p];
        executeMenuCommand("expandStyle");
        p = activeDocument.selection[0];
        if (p.typename == "CompoundPathItem") {
            b = findBestBounds(op, p);
        }
    }

    p.remove();
    return b;
}

function getMinVisibleSize(b) {
    var s = Math.min(b[2] - b[0], b[1] - b[3]);
    return Math.abs(s);
}

function add_nums(n, xy, s) {
    var txt = NUM_LAY.textFrames.add();

    txt.contents = n;
    txt.textRange.justification = Justification.CENTER;
    txt.textRange.characterAttributes.size = s;
    txt.position = [xy[0] - txt.width / 2, xy[1] + txt.height / 2];
}


function applyOffset(op, checkBounds) {
    var p = op.duplicate(WORK_LAY, ElementPlacement.PLACEATBEGINNING),
        // offset value the small the better, but meantime more slow. 
        offset = function () {
            var minsize = Math.min(p.width, p.height);
            if (minsize >= 50) {
                return '-1'
            } else if (20 < minsize && minsize < 50) {
                return '-0.5'
            } else {
                return '-0.2' // 0.2 * 2 (both side ) * 50 (Times) = 20
            }
        },
        xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
            .replace('#offset', offset()),
        TIMES = 100; // if shapes are too large, should increase the value.

    if (checkBounds) {
        // check its size only if it needs, because it's too slow
        while (TIMES-- && getMinVisibleSize(p.visibleBounds) > 3) p.applyEffect(xmlstring);
    } else {
        while (TIMES--) p.applyEffect(xmlstring);
    }
    return p;
}

function getGeometricCenter(p) {
    var b = p.geometricBounds;
    return [(b[0] + b[2]) / 2, (b[1] + b[3]) / 2];
}

// returns square of distance between p1 and p2
function getDist2(p1, p2) {
    return Math.pow(p1[0] + p2[0], 2) + Math.pow(p1[1] + p2[1], 2);
}

// returns visibleBounds of a path in a compoundPath p
// which is closest to center of the original path op
function findBestBounds(op, p) {
    var opc = getGeometricCenter(op);
    var idx = 0,
        d;
    var minD = getDist2(opc, getGeometricCenter(p.pathItems[0]));
    for (var i = 0, iEnd = p.pathItems.length; i < iEnd; i++) {
        d = getDist2(opc, getGeometricCenter(p.pathItems[i]));
        if (d < minD) {
            minD = d;
            idx = i;
        }
    }
    return p.pathItems[idx].visibleBounds;
}

main();
This topic has been closed for replies.

1 reply

Participating Frequently
February 19, 2024

Ich möchte, dass es so ist

 

CarlosCanto
Community Expert
Community Expert
February 19, 2024

can you split the large pieces into smaller chunks?

Participating Frequently
February 20, 2024

Can something be added to the script to put more numbers?

Tnks