按数字 Illustrator 脚本绘画

问题描述 投票:0回答:2

我来自彩色,我们决定将彩色矢量插图转换为按数字着色工作表。我们的输入文件是 svg 格式的彩色和轮廓图像。 轮廓版(如彩页)+彩色版

outline versioncolor version

我们想要得到的是以下内容

result

我们希望根据图像颜色版本的颜色数据在图像的轮廓版本下生成调色板。此外,与该调色板相对应的数字被放置在概述版本的每个颜色空间内。 我知道世界上没有任何脚本可以正确地做到这一点,但至少我正在努力减少编辑(人)在 Illustrator 中手动输入这些数字所花费的时间。我知道我们的颜色矢量图像可能有太多的颜色和阴影,因此我们需要以某种方式限制调色板的结果颜色(将它们融合成大组基本颜色)。

我搜索了所有 stackoverflow 解决方案,发现了一些巧妙的解决方案,例如 使用 Adobe Illustrator Javascript 绘制数字我正在寻找一个自动编号系统,用于在 Photoshop 中按数字套件自定义绘画(感谢 Yuri Khristich)。然而,它们并不完全适合我们的需求。 网络上的大多数脚本都会从彩色版本生成轮廓图像,但质量会受到影响。我们已经有了一个合适的轮廓版本,我们想将其用作按数字颜色工作表的基础。

javascript adobe adobe-illustrator
2个回答
1
投票

这里是为所选艺术品制作“调色板”的脚本。

正如您所知,这里是向所有填充区域添加颜色名称的脚本。

所以我采用了两个脚本,做了一些最小的调整,几乎得到了你想要的结果。编写脚本后,您所需要做的就是将带有数字和“调色板”的图层从彩色艺术品复制到轮廓版本。

脚本#1

// Modified version
// https://stackoverflow.com/questions/75344674/paint-by-number-illustrator-script

// Original: 
// https://productivista.com/make-a-list-of-colors-from-your-selection/

/*
  Date: July, 2020
  Author: Katja Bjerrum, email: [email protected], www.productivista.com
  ============================================================================
  NOTICE:
  This script is provided "as is" without warranty of any kind.
  Free to use, not for sale.
  ============================================================================
  Released under the MIT license.
  http://opensource.org/licenses/mit-license.php
  ============================================================================

*/

//@target illustrator

var doc = app.activeDocument;
var myLayer = doc.activeLayer;
app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;
var swGrps = doc.swatchGroups;
var mainSwGr = doc.swatchGroups[0];
var sel = doc.selection;

var actionSet = 'CreateSwatchGroup';
var actionName = 'ColourGroup';
var actionPath = Folder.myDocuments + '/Adobe Scripts/';

if (!Folder(actionPath).exists) Folder(actionPath).create();
//app.doScript("Colorgroup", "ToSwatchScript"); // Action, that creates swatch group

var actionDoc =
  [ '/version 3',
    '/name [' + actionSet.length  + ' ' + ascii2Hex(actionSet) + ']',
    '/isOpen 1',
    '/actionCount 1',
    '/action-1 {',
    '/name [' + actionName.length + ' ' + ascii2Hex(actionName) + ']',
    '   /keyIndex 0',
    '   /colorIndex 0',
    '   /isOpen 1',
    '   /eventCount 1',
    '   /event-1 {',
    '     /useRulersIn1stQuadrant 0',
    '     /internalName (ai_plugin_swatches)',
    '     /localizedName [ 8',
    '       5377617463686573',
    '     ]',
    '     /isOpen 0',
    '     /isOn 1',
    '     /hasDialog 1',
    '     /showDialog 1',
    '     /parameterCount 1',
    '     /parameter-1 {',
    '       /key 1835363957',
    '       /showInPalette 4294967295',
    '       /type (enumerated)',
    '       /name [ 15',
    '         4e657720436f6c6f722047726f7570',
    '       ]',
    '      /value 17',
    '     }',
    '   }',
    '}'].join('');

createAction(actionDoc, actionName, actionPath);

app.redraw();
app.doScript (actionName, actionSet);
app.redraw();
app.unloadAction(actionSet, '');


var convMM = 2.8346456692; // initialization of the variable to convert points to mm

var colorgroup = doc.swatchGroups[doc.swatchGroups.length - 1]; // Choose the last swatch group
var stY = -200; //
var stX = 20;
var recW = 25;
var recH = 25;

var offX = recW / 5;
var offY = recH / 4;
var textoffY = recH / 4;
var rows = 4;
var cols = 4;

var black = new GrayColor();
black.gray = 80;
var white = new GrayColor() ;
white.gray = 0;
var noStroke = doc.swatches[0].color;

if (swGrps.length <=1){
    alert ("Please create swatch group from your selection");
}
else if (sel <= 0){
    //docRef.placedItems[0].selected == false;
    alert ("Please make a selection");
    delSwatchGr(colorgroup); //delete swatch group
}
else{
    swatchGroupList(colorgroup, stY, stX);//create corlor list
    // delSwatchGr(colorgroup);//delete swatch group
}


//Function, that creates color list
function swatchGroupList(swatchGroup, stY, stX) {

    // Groups everything in the list
    var mainGroup = myLayer.groupItems.add();
    mainGroup.name = "Colors";
    mainGroup.moveToBeginning(myLayer);

    //Name of the color list
    var nameText = myLayer.textFrames.add();
    nameText.contents = swatchGroup.name; // the name of the swatch group
    nameText.position = [stX, stY + recH];

    var nameStyle = nameText.textRange.characterAttributes;
    nameStyle.size = 12;//size in punkt
    //nameStyle.textFont = textFonts.getByName("Avenir-Book");//the font
    nameStyle.capitalization = FontCapsOption.ALLCAPS;//ALL CAPITALS

    var swatches = swatchGroup.getAllSwatches();
    var swatchArray = [];

    for (i = swatches.length-1; i>=0; i--) {
        var mySwatch = swatches[i];
        mySwatch.name = i + 1;
        var subGroup = createSwatchGroup(mySwatch, textoffY);
        swatchArray.push(subGroup);
    }

    nameText.moveToEnd(mainGroup);
    var myGroup = swatchArray;
    var maxW = maxWidth(myGroup);


    for (var j = 0; j < myGroup.length; j++) {
        var mySubGroup = myGroup[j];
        mySubGroup.moveToBeginning(mainGroup);
    }

    for (var i = 0; i < mainGroup.groupItems.length; i++) {
        var mySubGroup = mainGroup.groupItems[i];

        if (mainGroup.groupItems.length > 7) {
            rows = 7;
            var c = i%rows;
            var r = Math.floor(i/rows);
            mySubGroup.position = [stX + r * (maxW + 10), stY - c * (recH + offY)];
        }
        else {
            rows = 7;
            var c = i % rows;
            var r = Math.floor(i / rows);
            mySubGroup.position = [stX, stY - c * (recH + offY)];
        }
    }
    // textSwatch.moveToBeginning(SubGroup);
    // path_ref.moveToBeginning(SubGroup);
    // SubGroup.position = [stX + c * 140, stY - r * (path_ref.height + offY)];
    subGroup.moveToBeginning(mainGroup);

}

function lightColor(c){
    if(c.typename)
    {
       switch(c.typename)
       {
            case "CMYKColor":
            return (c.black>=10 || c.cyan>10 || c.magenta>10 || c.yellow > 10) ? true : false;
            case "RGBColor":
            return (c.red<230  || c.green<230 || c.blue<230) ? true : false;
            case "GrayColor":
            return c.gray >= 10 ? true : false;
            case "SpotColor":
            return lightColor(c.spot.color);

            //return false;
       }
   }
}

function fitItem(item, itemW, itemH, diff) {
    var oldWidth = item.width
    var oldHeight = item.height

    if (item.width > item.height) {
      // landscape, scale height using ratio from width
      item.width = itemW - diff.deltaX
      var ratioW = item.width / oldWidth
      item.height = oldHeight * ratioW
    } else {
      // portrait, scale width using ratio from height
      item.height = itemH - diff.deltaY
      var ratioH = item.height / oldHeight
      item.width = oldWidth * ratioH
    }

  }

  function itemBoundsDiff(item) {
    var itemVB = item.visibleBounds

    var itemVW = itemVB[2] - itemVB[0] // right - left
    var itemVH = itemVB[1] - itemVB[3] // top - bottom

    var itemGB = item.geometricBounds

    var itemGW = itemGB[2] - itemGB[0] // right - left
    var itemGH = itemGB[1] - itemGB[3] // top - bottom

    var deltaX = itemVW - itemGW
    var deltaY = itemVH - itemGH

    var diff = { deltaX: deltaX, deltaY: deltaY }

    return diff
  }

  function delSwatchGr(swGr){

        var swGrSws = swGr.getAllSwatches();
        for (var j = 0; j < swGrSws.length; j++){
            var sw = swGrSws[j];
            sw.color = new CMYKColor();
        }
        swGr.remove();

}

//Function finds the max group width
function maxWidth(myGroup) {
    var maxFound = 0;
    for (var j = 0; j < myGroup.length; j++) {
        var GrWidth = myGroup[j].width;
        //var Widthmax = GrWidth.width;
        maxFound = Math.max(maxFound, GrWidth);
    }
    return maxFound;
}

function createSwatchGroup(sw, myOffset) {
    //Is "MyForm" path exists?
    try{
        var path_ref_ori = app.activeDocument.pathItems.getByName("MyForm" || "myform" || "MYFORM");
    }
    catch(e) {
        var path_ref_ori = false;
    }

    if (path_ref_ori) {
        myPath = path_ref_ori.duplicate();
        var boundsDiff = itemBoundsDiff(myPath);
        fitItem(myPath, recW, recH, boundsDiff);
        myPath.name = "NewForm";
        myPath.position = [0, 0];
    }
    else {
        var myPath = createMyPath()
    }

    myPath.fillColor = sw.color;
    myPath.stroked = true;
    myPath.strokeWidth = 0.3;
    myPath.strokeColor = lightColor(myPath.fillColor) ? noStroke : black;

    var textSwatch = myLayer.textFrames.add(); //swatch text
    textSwatch.contents = sw.name;

    textSwatch.position = [myPath.width + 1.3 * convMM, -myOffset];
    var textSwStyle = textSwatch.textRange.characterAttributes;
    textSwStyle.size = 10; //size in punkt
    //textSwStyle.textFont = textFonts.getByName("MyriadPro-Semibold"); //the font

    var SubGroup = myLayer.groupItems.add(); //groups path and text
    SubGroup.name = sw.name;
    SubGroup.position = [0, 0];

    textSwatch.moveToBeginning(SubGroup);
    myPath.moveToBeginning(SubGroup);

    return SubGroup;
}


function createMyPath(){
//Is "MyForm" path exists?
    try{
        var path_ref_ori = app.activeDocument.pathItems.getByName("MyForm" || "myform" || "MYFORM");
    }
    catch(e) {
        var path_ref_ori = false;
    }

    if (path_ref_ori) {
        path_ref = path_ref_ori.duplicate();
        var boundsDiff = itemBoundsDiff(path_ref);
        fitItem(path_ref, recW, recH, boundsDiff);
        path_ref.name = "NewForm";
        path_ref.position = [0, 0];
    }
    else {
        var path_ref = myLayer.pathItems.rectangle(0, 0, recW, recH); //swatch path item
    }

    return path_ref
};

function createAction(str, set, path) {
    var f = new File('' + path + '/' + set + '.aia');
    f.open('w');
    f.write(str);
    f.close();
    app.loadAction(f);
    f.remove();
};

function ascii2Hex(hex) {
    return hex.replace(/./g, function (a) { return a.charCodeAt(0).toString(16) });
};

输入(选择作品并运行脚本后):

结果(在底部添加了全局色板和“调色板”):

脚本#2

// Based on:
// https://stackoverflow.com/questions/73705368/paint-with-numbers-with-adobe-illustrator-javascript

var doc = app.activeDocument,
    lays = doc.layers,
    WORK_LAY = lays.add(),
    NUM_LAY = lays.add(),
    i = lays.length - 1,
    lay;

// main working loop
for (; i > 1; i--) {
    //process each layer
    lay = lays[i];
    lay.name = lay.name + " Num:" + (i - 1); // i-1 as 2 layers beed added.
    process(lay.pathItems, false);
    process(lay.compoundPathItems, true); // if any
}
//clean up
NUM_LAY.name = "Numbers";
WORK_LAY.remove();

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

    for (; j < items.length; j++) {
        // process each pathItem
        op = items[j];
        try { color = op.fillColor.spot.name } catch(e) { continue } // <-- HERE
        // 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)
            ) ? 20 : 40; // adjust font size for small area paths.
        add_nums(color, xy, s); // <--- HERE
    }
}

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

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;
}

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 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 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 strokeComPath(compoundPath) {
    var p = compoundPath.pathItems,
        l = p.length,
        i = 0;

    for (; i < l; i++) {
        // !p[i].closed && p[i].closed = true;
        // p[i].stroked = true;
        // p[i].filled = false;
    }
};

结果(运行脚本后添加了带有数字的图层):

带有数字和“调色板”的最终概述版本

注意:在运行脚本 #2 之前,您必须取消分组并取消屏蔽彩色图稿。

这是其余示例的结果:

如您所见,“最终”艺术品仍然需要大量额外的手动工作:移动或删除多余的数字。

减少原始彩色艺术品中的颜色数量是有意义的(也许在某种程度上也可以使用脚本来实现)。


0
投票

Script 2 它对我不起作用,它没有做任何事情。 任何人都可以帮忙可能出了什么问题吗?或者我破坏了什么?

© www.soinside.com 2019 - 2024. All rights reserved.