Skip to content

Instantly share code, notes, and snippets.

@sttk3
Created February 18, 2023 09:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sttk3/6e3f8c23de3954beb74875cfc1af17d7 to your computer and use it in GitHub Desktop.
Save sttk3/6e3f8c23de3954beb74875cfc1af17d7 to your computer and use it in GitHub Desktop.
/**
* @file 指定した数の色でランダムに塗るIllustratorスクリプト。
* 線と塗り両方に対応。テキスト,複合パス/グループの配下への適用可能。
* 元ネタは→[【Illustratorスクリプト】指定した数の色でランダムに塗るスクリプト。 - ふわふわぷかぷか](https://fuwafuwapukapuka.hatenablog.com/entry/2023/02/16/032354)
* @version 1.0.0
* @author sttk3.com
*/
//@target 'illustrator'
(function() {
$.localize = true ;
if(app.documents.length <= 0) {return ;}
var doc = app.documents[0] ;
var sel = doc.selection ;
if(sel.length <= 0) {return ;}
try {
showDialog(sel) ;
} catch(e) {
alert(e) ;
}
})() ;
handleChanging = null ;
/**
* RGBColorを生成して返す
* @param {Integer} r Red
* @param {Integer} g Green
* @param {Integer} b Blue
* @return {RGBColor}
*/
function makeRGBColor(r, g, b) {
var tmpColor = new RGBColor() ;
tmpColor.red = r ;
tmpColor.green = g ;
tmpColor.blue = b ;
return tmpColor ;
}
/**
* 0からmaxまでのランダムな整数を返す
* @param {Integer} max 最大値
* @return {Integer}
*/
function getRandomInt(max) {
return Math.floor(Math.random() * max) ;
}
/**
* ランダムなRGBカラーを生成して返す
* @return {RGBColor}
*/
function getRandomColor() {
var max = 255 ;
return makeRGBColor(getRandomInt(max), getRandomInt(max), getRandomInt(max)) ;
}
/**
* 対象のアイテムに色を適用する。文字属性と,複合パス・グループの配下への適用に対応する
* @param {PageItem} targetItem 対象のPageItem
* @param {Color} dstColor 適用する色
* @param {Boolean} applyToStroke strokeに色を適用するかどうか
* @param {Boolean} applyToFill fillに色を適用するかどうか
* @return {Boolean} 色の適用が1つでも行われたかどうか
*/
function applyColor(targetItem, dstColor, applyToStroke, applyToFill) {
var res = false ;
var itemClass = targetItem.constructor.name ;
var targetProps = [] ;
if(applyToStroke) {targetProps.push('strokeColor') ;}
if(applyToFill) {targetProps.push('fillColor') ;}
try {
for(var i = 0, len = targetProps.length ; i < len ; i++) {
switch(itemClass) {
case 'TextFrame':
targetItem.textRange[targetProps[i]] = dstColor ;
res = true ;
break ;
case 'CompoundPathItem':
var children = targetItem.pathItems ;
for(var j = 0, childrenLength = children.length ; j < childrenLength ; j++) {
if(applyColor(children[j], dstColor, applyToStroke, applyToFill)) {
res = true ;
}
}
break ;
case 'GroupItem':
var children = targetItem.pageItems ;
for(var j = 0, childrenLength = children.length ; j < childrenLength ; j++) {
if(applyColor(children[j], dstColor, applyToStroke, applyToFill)) {
res = true ;
}
}
break ;
default:
if(targetProps[i] in targetItem) {
targetItem[targetProps[i]] = dstColor ;
res = true ;
}
break ;
}
}
} catch(e) {
// skip
}
return res ;
}
/**
* 色割り当てメイン動作。対象のアイテムにランダムな色を適用する
* @param {Array} targetItems 対象のアイテム(複数)
* @param {String} colorLengthText 適用する色の数
* @param {Boolean} applyToStroke strokeに色を適用するかどうか
* @param {Boolean} applyToFill fillに色を適用するかどうか
* @return {Boolean} 色の適用が行われたかどうか
*/
function setColorTo(targetItems, colorLengthText, applyToStroke, applyToFill) {
var res = false ;
if( !(applyToStroke || applyToFill) ) {
return res ;
}
var colorLengthInt
try {
colorLengthInt = parseInt(colorLengthText, 10) ;
// 色の作成
var colorList = [] ;
for(var i = 0 ; i < colorLengthInt ; i++) {
colorList.push(getRandomColor()) ;
}
// 色の適用
for(var j = 0, itemLength = targetItems.length ; j < itemLength ; j++) {
applyColor(targetItems[j], colorList[getRandomInt(colorLengthInt)], applyToStroke, applyToFill) ;
}
res = true ;
} catch(e) {
//alert(e) ;
}
return res ;
}
/**
* このスクリプトによる変更があったときのみapp.undo()を実行する
* @param {Boolean} modified 変更したかどうか
* @return {}
*/
function undoIf(modified) {
if(modified) {app.undo() ;}
}
/**
* 全角数値などいくつかの文字を半角に変換する
* @param {String} srcText 対象の文字列
* @return {String}
*/
function toHankaku(srcText) {
return srcText.toString().replace(/[0-9A-Za-z]/g, function(s) {return String.fromCharCode(s.charCodeAt(0) - 0xFEE0)}) ;
}
/**
* 色数指定&適用ダイアログを開く
* @param {Array} targetItems 処理対象のアイテム
* @return {}
*/
function showDialog(targetItems) {
var defaultColorLength = '3' ;
var minimumColorLength = 1 ;
var lastInput = defaultColorLength ;
var modified = false ;
// DIALOG
// ======
var dialog = new Window('dialog') ;
dialog.text = 'Apply Random Color' ;
dialog.orientation = 'column' ;
dialog.alignChildren = ['center', 'center'] ;
dialog.spacing = 16 ;
dialog.margins = 16 ;
// PANELMAIN
// =========
var panelMain = dialog.add('panel', undefined, undefined, {name: 'panelMain'}) ;
panelMain.orientation = 'row' ;
panelMain.alignChildren = ['left', 'center'] ;
panelMain.spacing = 26 ;
panelMain.margins = 10 ;
// GROUPCOLORLENGTH
// ================
var groupColorLength = panelMain.add('group', undefined, {name: 'groupColorLength'}) ;
groupColorLength.orientation = 'row' ;
groupColorLength.alignChildren = ['left', 'center'] ;
groupColorLength.spacing = 6 ;
groupColorLength.margins = 0 ;
var label = groupColorLength.add('statictext', undefined, undefined, {name: 'label'}) ;
label.text = {ja: '色の数', en: 'Colors'} ;
var fieldColorLength = groupColorLength.add("edittext {justify: 'right', properties: {name: 'fieldColorLength'}}") ;
fieldColorLength.text = defaultColorLength ;
fieldColorLength.preferredSize.width = 82 ;
// GROUPTARGETPROP
// ===============
var groupTargetProp = panelMain.add('group', undefined, {name: 'groupTargetProp'}) ;
groupTargetProp.orientation = 'row' ;
groupTargetProp.alignChildren = ['left','center'] ;
groupTargetProp.spacing = 12 ;
groupTargetProp.margins = 0 ;
var checkStroke = groupTargetProp.add('checkbox', undefined, undefined, {name: 'checkStroke'}) ;
checkStroke.text = {ja: '線', en: 'Stroke'} ;
checkStroke.value = false ;
var checkFill = groupTargetProp.add('checkbox', undefined, undefined, {name: 'checkFill'}) ;
checkFill.text = {ja: '塗り', en: 'Fill'} ;
checkFill.value = true ;
// GROUPFOOTER
// ===========
var groupFooter = dialog.add('group', undefined, {name: 'groupFooter'}) ;
groupFooter.orientation = 'row' ;
groupFooter.alignChildren = ['left', 'center'] ;
groupFooter.spacing = 12 ;
groupFooter.margins = 0 ;
var buttonWidth = 80 ;
var buttonUpdate = groupFooter.add('button', undefined, undefined, {name: 'buttonUpdate'}) ;
buttonUpdate.text = {ja: '再適用', en: 'Apply'} ;
buttonUpdate.preferredSize.width = buttonWidth ;
var buttonOK, buttonCancel ;
var labelOK = 'OK' ;
var labelCancel = 'Cancel' ;
if(/^w/i.test($.os)) {
buttonOK = groupFooter.add('button', undefined, labelOK) ;
buttonCancel = groupFooter.add('button', undefined, labelCancel) ;
} else {
buttonCancel = groupFooter.add('button', undefined, labelCancel) ;
buttonOK = groupFooter.add('button', undefined, labelOK) ;
}
// 色数入力フィールドにフォーカスする
fieldColorLength.active = true ;
// 初回の適用
dialog.onShow = function() {
modified = setColorTo(targetItems, fieldColorLength.text, checkStroke.value, checkFill.value) ;
app.redraw() ; // ドキュメントを再描画
} ;
// 色数入力フィールド入力時プレビュー
var handleChanging = function() {
try {
var hankakuText = toHankaku(fieldColorLength.text) ;
var newInt = Math.max(parseInt(hankakuText, 10), minimumColorLength) ;
if(isNaN(newInt)) {
fieldColorLength.text = lastInput ;
} else {
fieldColorLength.text = newInt.toString() ;
}
lastInput = fieldColorLength.text ;
undoIf(modified) ;
modified = setColorTo(targetItems, lastInput, checkStroke.value, checkFill.value) ;
app.redraw() ;
} catch(e) {
// skip
}
} ;
fieldColorLength.onChanging = handleChanging ;
// 色数入力フィールドUp/Downキー動作
fieldColorLength.addEventListener('keyup', function(event) {
event.preventDefault() ;
var keyName = event.keyName ;
if(keyName === 'Up' || keyName === 'Down') {
var oldText = fieldColorLength.text ;
var addition = 1 ;
if(event.shiftKey) {addition = 10 ;}
var inverter = {'Up': 1, 'Down': -1} ;
try {
var currentInt = parseInt(oldText, 10) ;
var newInt = Math.max((Math.floor(currentInt / addition) * addition) + (addition * inverter[keyName]), minimumColorLength) ;
fieldColorLength.text = newInt.toString() ;
lastInput = fieldColorLength.text ;
handleChanging() ;
} catch(e) {
// skip
}
}
}) ;
// 再適用の処理
checkStroke.onClick = handleChanging ;
checkFill.onClick = handleChanging ;
buttonUpdate.onClick = handleChanging ;
// キャンセルボタンの処理
buttonCancel.onClick = function() {
undoIf(modified) ;
dialog.close() ;
} ;
// OKボタンの処理
buttonOK.onClick = function() {
dialog.close() ; // ダイアログを閉じる
} ;
dialog.center() ; // ダイアログ表示位置をモニターの中心に
dialog.show() ; // 作成したダイアログを表示
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment