/* Author: A Jongware Script (Circle fill) Program version: Adobe Illustrator CC+ Name: fillinger.jsx; Modify and refactoring: Alexander Ladygin www.ladyginpro.ru Copyright (c) 2018 */ if (app.documents.length && app.selection.length < 2) { alert('Please select 2 or more objects'); } else { var scriptName = 'Fillinger', settingFile = { name: scriptName + '__setting.json', folder: Folder.myDocuments + '/LA_AI_Scripts/' }; function inputNumberEvents (ev, _input, min, max, callback){ var step, _dir = ev.keyName.toLowerCase().slice(0,1), _value = parseFloat(_input.text), units = (',px,pt,mm,cm,in,'.indexOf(_input.text.length > 2 ? (',' + _input.text.replace(/ /g, '').slice(-2) + ',') : ',!,') > -1 ? _input.text.replace(/ /g, '').slice(-2) : ''); min = (min === undefined ? 0 : min); max = (max === undefined ? Infinity : max); step = (ev.shiftKey ? 10 : (ev.ctrlKey ? .1 : 1)); if (isNaN(_value)) { _input.text = min; } else { _value = ( ((_dir === 'u') || (_dir === 'r')) ? _value + step : (((_dir === 'd') || (_dir === 'l')) ? _value - step : false) ); if (_value !== false) { _value = (_value <= min ? min : (_value >= max ? max : _value)) _input.text = _value; if (callback instanceof Function) callback(_value, _input, min, max, units); } } } var win = new Window('dialog', 'Fillinger \u00A9 www.ladyginpro.ru'); win.orientation = 'column'; win.alignChildren = ['fill', 'fill']; var globalGroup = win.add('group'); globalGroup.orientation = 'column'; globalGroup.alignChildren = ['fill', 'fill']; var sizePanel = globalGroup.add('panel', undefined, 'Items size in % of total size'), sizeGroup = sizePanel.add('group'); sizePanel.alignChildren = 'fill'; sizeGroup.orientation = 'row'; sizeGroup.alignChildren = ['fill', 'fill']; var maxValueGroup = sizeGroup.add('group'); maxValueGroup.orientation = 'row'; maxValueGroup.alignChildren = 'fill'; var maxValueLabel = maxValueGroup.add('statictext', undefined, 'Max:'), maxValue = maxValueGroup.add('edittext', [0, 0, 50, 25], '10'); maxValueLabel.justify = 'center'; var minValueGroup = sizeGroup.add('group'); minValueGroup.orientation = 'row'; minValueGroup.alignChildren = 'fill'; var minValueLabel = minValueGroup.add('statictext', undefined, 'Min:'), minValue = minValueGroup.add('edittext', [0, 0, 50, 25], '4'); minValueLabel.justify = 'center'; function checkMnMaxSize (val, item, min, max) { if (item === minValue) { if (val > parseFloat(maxValue.text)) { maxValue.text = (val >= max ? max : val); } } else if (item === maxValue) { if (val < parseFloat(minValue.text)) { minValue.text = (val < min ? min : val); } } } maxValue.addEventListener('keydown', function (e) { if (e.ctrlKey && e.keyName === 'Right') minValue.text = this.text; else inputNumberEvents(e, maxValue, 1, Infinity, checkMnMaxSize); }); minValue.addEventListener('keydown', function (e) { if (e.ctrlKey && e.keyName === 'Left') maxValue.text = this.text; else inputNumberEvents(e, minValue, 1, Infinity, checkMnMaxSize); }); var guttersResizeGroup = sizePanel.add('group'); guttersResizeGroup.orientation = 'row'; guttersResizeGroup.alignChildren = ['fill', 'fill']; var guttersGroup = guttersResizeGroup.add('group'); guttersGroup.orientation = 'column'; guttersGroup.alignChildren = 'fill'; var guttersLabel = guttersGroup.add('statictext', undefined, 'Min distance') guttersValue = guttersGroup.add('edittext', undefined, '0'); guttersValue.addEventListener('keydown', function (e) { inputNumberEvents(e, this, 0, Infinity); }); var resizeGroup = guttersResizeGroup.add('group'); resizeGroup.orientation = 'column'; resizeGroup.alignChildren = 'fill'; var resizeLabel = resizeGroup.add('statictext', undefined, 'Resize value') resizeValue = resizeGroup.add('edittext', undefined, '70'); resizeValue.addEventListener('keydown', function (e) { inputNumberEvents(e, this, 10, Infinity); }); var rotatePositionGroup = globalGroup.add('group'); rotatePositionGroup.orientation = 'row'; rotatePositionGroup.alignChildren = ['fill', 'fill']; var rotatePanel = rotatePositionGroup.add('panel', undefined, 'Rotate items:'); rotatePanel.alignChildren = 'fill'; var randomRadioGroup = rotatePanel.add('group'); randomRadioGroup.orientation = 'column'; var randomRotate = randomRadioGroup.add('radiobutton', undefined, 'Random'), rotateByValue = randomRadioGroup.add('radiobutton', undefined, 'By value'), rotateValue = rotatePanel.add('edittext', undefined, '0'); randomRotate.value = true; rotateValue.enabled = false; randomRotate.onClick = function() { rotateValue.enabled = false; } rotateByValue.onClick = function() { rotateValue.enabled = true; } var objectPositionPanel = rotatePositionGroup.add('panel', undefined, 'The item to fill is:'), objectPositionGroup = objectPositionPanel.add('group'); objectPositionGroup.orientation = 'column'; objectPositionGroup.alignChildren = ['fill', 'fill']; var objectPosTop = objectPositionGroup.add('radiobutton', undefined, 'On top'), objectPosBottom = objectPositionGroup.add('radiobutton', undefined, 'Below'), objectPosLayers = objectPositionGroup.add('checkbox', undefined, 'As in [Layers]'); objectPosTop.value = true; var groupResult = globalGroup.add('checkbox', undefined, 'Group all items after executing'), randomItems = globalGroup.add('checkbox', undefined, 'Random items (if items in the group)'), removeTopElement = globalGroup.add('checkbox', undefined, 'Remove the item to fill after executing'); var winButtons = globalGroup.add('group'); winButtons.orientation = 'row'; winButtons.alignChildren = ['fill', 'fill']; winButtons.margins = 0; var cancel = winButtons.add('button', undefined, 'Cancel'); cancel.helpTip = 'Press Esc to Close'; cancel.onClick = function () { win.close(); } var ok = winButtons.add('button', undefined, 'OK'); ok.helpTip = 'Press Enter to Run'; ok.onClick = function (e) { startAction(); win.close(); }; ok.active = true; var progressBar = win.add('progressbar'), progressBarCounter = 100; progressBar.value = 0; progressBar.minvalue = 0; progressBar.maxvalue = progressBarCounter; progressBar.maximumSize = [1000, 5]; /* random rotate rotate from value */ function startAction(){ globalGroup.enabled = false; var __rotateValue = Number(rotateValue.text); maxCircleSize = Number(maxValue.text); if (maxCircleSize < 0.01 || maxCircleSize > 100){maxCircleSize = 20;} minCircleSize = Number(minValue.text); if (minCircleSize < 0.01 || minCircleSize > maxCircleSize){minCircleSize = maxCircleSize/2;} maxCircleSize /= 100; minCircleSize /= 100; maxCircleSize /= 2; minCircleSize /= 2; minDistanceToOtherCircles = Number(guttersValue.text); var items = selection.concat(); if (!objectPosLayers.value) items.sort(function (a, b) { return a.geometricBounds[1] <= b.geometricBounds[1]; }); if (objectPosBottom.value) items.reverse(); var object = items[0]; if (object.typename !== 'PathItem' && object.typename !== 'CompoundPathItem') { return alert('The filling object must be PathItem or CompoundPathItem! [' + object.typename + ']'); } items.splice(0,1); var placeObject = (items.length === 1 ? (items[0].typename === 'GroupItem' ? items[0].pageItems : [items[0]]) : (!items.length ? [] : items)); if (!placeObject.length) { return alert('No items to fill!'); } var placeObjectResizeValue = (isNaN(parseFloat(resizeValue.text)) ? parseFloat(resizeValue.text) : 70), innerpaths = [], outerPath = null groupItems = (groupResult.value ? activeDocument.groupItems.add() : false); if (groupItems) groupItems.move(object, ElementPlacement.PLACEBEFORE); function getNode() { return ((!randomItems.value) ? placeObject[0] : placeObject[ Math.floor(Math.random() * placeObject.length) ]); } if (object.constructor.name == "CompoundPathItem"){ for (p=0; p maxx){x = minx;minx = maxx;maxx = x;} if (miny > maxy){y = miny;miny = maxy;maxy = y;} maxwide = maxx - minx; maxhigh = maxy - miny; totalArea = Math.abs(object.area); filledArea = 0; Math_Epsilon = 0.0001; joinedPath = outerPath; triangleIndexList = Triangulate (joinedPath, innerpaths); triangleList = []; for (p=0; p a_rnd) {break;} } pt = getRandomPoint (triangleList[q]); d = distanceToClosestEdge (pt, edgeList); if (d >= radiiList[rad]){ for (c=0; c= __placeObject.height ? 'width' : 'height'), placeObjectSizeReverse = (placeObjectSize === 'width' ? 'height' : 'width'), __radius = 2 * nrad, __size = __radius * (placeObjectResizeValue / 100); __ratio = __size * 100 / __placeObject[placeObjectSize] / 100; __placeObject.move(groupResult.value ? groupItems : object, ElementPlacement[groupResult.value ? 'INSIDE' : 'PLACEBEFORE']); __placeObject[placeObjectSize] = __size; __placeObject[placeObjectSizeReverse] *= __ratio; __placeObject.position = [pt[0]-nrad + ((__radius - __placeObject.width) / 2), pt[1]+nrad - ((__radius - __placeObject.height) / 2)]; if (randomRotate.value) __placeObject.rotate(Math.floor(Math.random() * 360)); else if (rotateByValue.value && __rotateValue) __placeObject.rotate(__rotateValue); progressBar.value += progressBarCounter; win.update(); } } if (removeTopElement.value) object.remove(); } function drawLine (a,b){var p = app.activeDocument.pathItems.add();try {p.setEntirePath ([ a,b ]);p.strokeWidth = 0.1;} catch (e){alert ("Bad line:\ra="+a+"\rb="+b);}return p;} function distanceFromPointToPoint(A, B){return Math.sqrt ( ((A[0]-B[0]) * (A[0]-B[0])) + ((A[1]-B[1]) * (A[1]-B[1])) );} function flattenPath(obj){var newpath = new Array(),curveList,pt, nextpt,isFlattened = false;if (!obj.hasOwnProperty ("pathPoints")){return null;}for (pt=0; pt=0; i--){m_points.splice (closestpt, 0, h[i]);}for (var i=h.length-1; i > miny; i--){m_points.splice (closestpt, 0, h[i]);}}}var n = m_points.length;if (n < 3) {return indices;}var V = new Array(n);if (Area(m_points) > 0){for (var v = 0; v < n; v++)V[v] = v;}else{for (var v = 0; v < n; v++){V[v] = (n - 1) - v;}} var nv = n,count = 2 * nv;for (var m = 0, v = nv - 1; nv > 2;){if ((count--) <= 0){return indices;}var u = v;if (nv <= u){u = 0;}v = u + 1;if (nv <= v){v = 0;}var w = v + 1;if (nv <= w){w = 0;}if (Snip(u, v, w, nv, V, m_points)){var a, b, c, s, t;a = V[u];b = V[v];c = V[w];indices.push(a);indices.push(b);indices.push(c);m++;for (s = v, t = v + 1; t < nv; s++, t++){V[s] = V[t];}nv--;count = 2 * nv;}}indices.reverse();return indices;} function Area(m_points){var n = m_points.length,A = 0.0;for (var p = n - 1, q = 0; q < n; p = q++){var pval = m_points[p],qval = m_points[q];A += pval[0] * qval[1] - qval[0] * pval[1];}return (A * 0.5);} function Snip(u, v, w, n, V, m_points){var p,A = m_points[V[u]],B = m_points[V[v]],C = m_points[V[w]];if (Math_Epsilon > (((B[0] - A[0]) * (C[1] - A[1])) - ((B[1] - A[1]) * (C[0] - A[0])))){return false;}for (p = 0; p < n; p++) {if ((p == u) || (p == v) || (p == w)){continue;}var P = m_points[V[p]];if (InsideTriangle(A, B, C, P)){return false;}}return true;} function InsideTriangle(A, B, C, P){var ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy,cCROSSap, bCROSScp, aCROSSbp;ax = C[0] - B[0]; ay = C[1] - B[1];bx = A[0] - C[0]; by = A[1] - C[1];cx = B[0] - A[0]; cy = B[1] - A[1];apx = P[0] - A[0]; apy = P[1] - A[1];bpx = P[0] - B[0]; bpy = P[1] - B[1];cpx = P[0] - C[0]; cpy = P[1] - C[1];aCROSSbp = ax * bpy - ay * bpx;cCROSSap = cx * apy - cy * apx;bCROSScp = bx * cpy - by * cpx;return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0));} function plotRandomPoint(triangle){var Ax,Ay,Bx,By,Cx,Cy, a,b,c, Px,Py;Ax = triangle[0][0];Ay = triangle[0][1];Bx = triangle[1][0];By = triangle[1][1];Cx = triangle[2][0];Cy = triangle[2][1];a = Math.random();b = Math.random()*(1-a);c = 1-a-b;Px = a*Ax + b*Bx + c*Cx;Py = a*Ay + b*By + c*Cy;app.activeDocument.pathItems.ellipse (Py+1,Px-1,2,2);} function getRandomPoint(triangle){var Ax,Ay,Bx,By,Cx,Cy, a,b,c, Px,Py;Ax = triangle[0][0];Ay = triangle[0][1];Bx = triangle[1][0];By = triangle[1][1];Cx = triangle[2][0];Cy = triangle[2][1];do{a = Math.random();b = Math.random();} while (a + b >= 1);c = 1-a-b;Px = a*Ax + b*Bx + c*Cx;Py = a*Ay + b*By + c*Cy;return [Px,Py];} function getCircleThru(v1, v2, v3){var x1 = v1[0],y1 = v1[1],x2 = v2[0],y2 = v2[1],x3 = v3[0],y3 = v3[1],s = 0.5*((x2 - x3)*(x1 - x3) - (y2 - y3)*(y3 - y1)),sUnder = (x1 - x2)*(y3 - y1) - (y2 - y1)*(x1 - x3);if (Math.abs(sUnder) < 0.001){return null;}s /= sUnder;var xc = 0.5*(x1 + x2) + s*(y2 - y1),yc = 0.5*(y1 + y2) + s*(x1 - x2);var radius = Math.sqrt((xc-x1)*(xc-x1)+(yc-y1)*(yc-y1));return [ xc, yc, radius ];} function distanceToClosestEdge(pt, edgelist){var d,p, d2;d = ClosestPointOnLine (pt, [ edgelist[0][0], edgelist[0][1] ]);d = d[1];for (p=1; p= 1){nx = X2;ny = Y2;} else{nx = X1 + t * dx;ny = Y1 + t * dy;}}dx = px - nx;dy = py - ny;return [ [nx, ny], Math.sqrt (dx * dx + dy * dy) ]} function point(arr){this.x = arr[0];this.y = arr[1];this.distance = function (pt) { return Math.sqrt ( (this.x-pt.x)*(this.x-pt.x) + (this.y-pt.y)*(this.y-pt.y) ) }} function saveSettings() { var $file = new File(settingFile.folder + settingFile.name), data = [ maxValue.text, minValue.text, guttersValue.text, resizeValue.text, rotateValue.text, randomRotate.value, rotateByValue.value, objectPosTop.value, objectPosBottom.value, objectPosLayers.value, randomItems.value, removeTopElement.value, groupResult.value ].toString(); $file.open('w'); $file.write(data); $file.close(); } function loadSettings() { var $file = File(settingFile.folder + settingFile.name); if ($file.exists) { try { $file.open('r'); var data = $file.read().split('\n'), $main = data[0].split(','); maxValue.text = $main[0]; minValue.text = $main[1]; guttersValue.text = $main[2]; resizeValue.text = $main[3]; rotateValue.text = $main[4]; randomRotate.value = ($main[5] === 'true'); rotateByValue.value = ($main[6] === 'true'); objectPosTop.value = ($main[7] === 'true'); objectPosBottom.value = ($main[8] === 'true'); objectPosLayers.value = ($main[9] === 'true'); randomItems.value = ($main[10] === 'true'); removeTopElement.value = ($main[11] === 'true'); groupResult.value = ($main[12] === 'true'); rotateValue.enabled = !randomRotate.value; } catch (e) {} $file.close(); } } win.onClose = function() { saveSettings(); return true; } function checkSettingFolder() { var $folder = new Folder(settingFile.folder); if (!$folder.exists) $folder.create(); } checkSettingFolder(); loadSettings(); win.center(); win.show(); }