Last active
June 6, 2023 22:30
-
-
Save cwgw/5d4aa43350c7df9babb1d9f6ee459232 to your computer and use it in GitHub Desktop.
A simple script for Adobe Illustrator CC that draws Metatron's Cube to your active artboard.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Title: Metatron's Cube | |
* Author: Charlie Wright | |
* | |
* A simple script for Adobe Illustrator CC that draws Metatron's Cube to your active artboard. | |
* To use, select File > Scripts > Other Script... then navigate to and open this file. | |
* | |
* For Illustrator scripting reference, see the official documentation at: | |
* https://www.adobe.com/devnet/illustrator/scripting.html | |
*/ | |
var MetatronsCube = function() { | |
/* if there is no open document, then bail early */ | |
if (app.documents.length < 1) { | |
alert("You must have an open document to use this script."); | |
return null; | |
} | |
/* get options */ | |
var options = getUserInput(); | |
/* make it! */ | |
if (options) { | |
return drawCube(options); | |
} | |
/** | |
* getUserInput | |
* | |
* Prompts user to provide parameters with which to draw the cube. | |
* | |
* @return {Object} Options object formatted for use by drawCube() | |
*/ | |
function getUserInput() { | |
/* create a new dialog window for our inputs */ | |
var w = new Window("dialog", "Metatron's Cube") | |
/* input labels and default values */ | |
var inputs = { | |
majorRadius: { | |
label: 'Major Radius', | |
defaultValue: 90, | |
}, | |
minorRadius: { | |
label: 'Minor Radius', | |
defaultValue: 45, | |
}, | |
iterationCount: { | |
label: 'Depth', | |
defaultValue: 2, | |
}, | |
sideCount: { | |
label: 'Sides', | |
defaultValue: 6, | |
}, | |
}; | |
/* add an input group, label and text field for each input item */ | |
for (item in inputs) { | |
if (inputs.hasOwnProperty(item)) { | |
var inputGroup = w.add("group"); | |
inputGroup.alignment = "right"; | |
inputGroup.add("statictext", undefined, inputs[item].label+":"); | |
/* save the edittext control so we can retrieve its value later */ | |
inputs[item].value = inputGroup.add("edittext", undefined, inputs[item].defaultValue); | |
inputs[item].value.characters = 12; | |
} | |
} | |
/* add "OK" and "Cancel" buttons to the dialog */ | |
var submitGroup = w.add("group"); | |
submitGroup.alignment = "right"; | |
var cancel = submitGroup.add("button", undefined, "Cancel"); | |
var submit = submitGroup.add("button", undefined, "OK") | |
/* make OK button focused as is convention */ | |
submit.active = true; | |
/* on "OK" return user input */ | |
if (w.show() === 1) { | |
var params = {}; | |
/* parse and cleanup input values */ | |
for (item in inputs) { | |
if (inputs.hasOwnProperty(item)) { | |
if (inputs[item].value && inputs[item].value.text) { | |
params[item] = parseInt(inputs[item].value.text); | |
} | |
} | |
} | |
return params; | |
} | |
return false | |
} | |
/** | |
* getArtboardCenterPoint | |
* | |
* Returns the coords of the center of a given artboard | |
* | |
* The property artboardRect is an array-like object of the form [ x1, y1, x2, y2 ] | |
* where (x1,y1) represents the top-left corner of the artboard and (x2,y2) the | |
* bottom-right corner. | |
* | |
* @param {Object} artboard An Illustrator document artboard | |
* | |
* @return {array} A coordinate pair of the form [ x, y ] | |
*/ | |
function getArtboardCenterPoint(artboard) { | |
var rect = artboard.artboardRect; | |
return [ | |
(rect[2] - rect[0]) / 2 + rect[0], | |
(rect[3] - rect[1]) / 2 + rect[1] | |
]; | |
} | |
/** | |
* drawCube | |
* | |
* Draw the thing! | |
* | |
* @param {Object} o Options | |
* @param {Object} o.doc Illustrator Document | |
* @param {Array} o.center Coords to be used as the center of the cube | |
* @param {Number} o.majorRadius The primary unit of sizing for the cube (how far apart the circles are). | |
* @param {Number} o.minorRadius The secondary unit of sizing for the cube (how big the circles are). | |
* @param {Number} o.sideCount The number of sides of the base polygon. This is | |
* traditionally six, but feel free to change it. | |
* @param {Number} o.iterationCount The depth of the cube. Traditionally, Metatron's cube is | |
* drawn as two concentric hexagons with an ellipse at each | |
* vertex (and one in the center). Wanna see it with three | |
* or four or n concentric hexagons? Why not? | |
* | |
* @return {Object} A GroupItem containing the new Metatron's cube, its | |
* constituent parts separated into sub-groups. | |
*/ | |
function drawCube(o) { | |
var options = {}; | |
options.doc = o.doc || app.activeDocument; | |
options.center = o.center || getArtboardCenterPoint(options.doc.artboards[options.doc.artboards.getActiveArtboardIndex()]); | |
options.majorRadius = o.majorRadius || 90; | |
options.minorRadius = o.minorRadius || options.majorRadius / 2; | |
options.iterationCount = o.iterationCount || 2; | |
options.sideCount = o.sideCount || 6; | |
/* at the document level, turn off fill and turn on stroke */ | |
options.doc.defaultFilled = false; | |
options.doc.defaultStroked = true; | |
/* the group that will hold everything */ | |
var rootGroup = options.doc.groupItems.add(); | |
rootGroup.name = 'MetatronsCube'; | |
/* the group of original polygons (hexagons) */ | |
var polyGroup = rootGroup.groupItems.add(); | |
polyGroup.name = 'BaseShapes'; | |
/* the group of ellipses */ | |
var ellipseGroup = rootGroup.groupItems.add(); | |
ellipseGroup.name = 'Ellipses'; | |
/* the group of lines */ | |
var lineGroup = rootGroup.groupItems.add(); | |
lineGroup.name = 'Lines'; | |
/* an array for all of our vertices */ | |
var points = []; | |
/* create a bunch of ellipses */ | |
for (var k = options.iterationCount; k > 0; k--) { | |
/* start with a polygon with sides equal to options.sides, radius equal to options.majorRadius and center at position options.center */ | |
var poly = polyGroup.pathItems.polygon(options.center[0], options.center[1], options.majorRadius * k, options.sideCount); | |
/* create a group within ellipseGroup to hold the ellipses at each iteration */ | |
var ellipseSubGroup = ellipseGroup.groupItems.add(); | |
/* at each vertex in poly, create an ellipse with diameter equal to options.minorRadius */ | |
for (var l = 0; l < poly.pathPoints.length; l++) { | |
var point = poly.pathPoints[l].anchor; | |
/* args for the ellipse method are (top, left, width, height) */ | |
var ellipse = ellipseSubGroup.pathItems.ellipse( | |
point[1] + (options.minorRadius), | |
point[0] - (options.minorRadius), | |
options.minorRadius * 2, | |
options.minorRadius * 2 | |
); | |
ellipse.rotate((360 / options.sideCount) * (l + 1)); | |
/* save this vertex for later */ | |
points.push(poly.pathPoints[l]) | |
} | |
} | |
/* add one last ellipse at the center */ | |
ellipseGroup.pathItems.ellipse( | |
options.center[1] + (options.minorRadius), | |
options.center[0] - (options.minorRadius), | |
options.minorRadius * 2, | |
options.minorRadius * 2 | |
); | |
/* instantiate the var that will point to each of our groups of lines */ | |
var lineSubGroup; | |
/* draw a line between each point in our points array and every other point in the array */ | |
for (var i = 0; i < points.length; i++) { | |
/* create a new group within lineGroup for each major iteration */ | |
lineSubGroup = i % options.sideCount === 0 ? lineGroup.groupItems.add() : lineSubGroup; | |
for (var j = points.length - 1; j > i; j--) { | |
/* the PathItem method setEntirePath accepts an array of coordinate pairs ([x, y]) and connects them with a line */ | |
lineSubGroup.pathItems.add().setEntirePath([ | |
[ points[i].anchor[0], points[i].anchor[1] ], | |
[ points[j].anchor[0], points[j].anchor[1] ] | |
]); | |
} | |
} | |
rootGroup.rotate(360 / (options.sideCount * 2)); | |
/* let's select the whole thing for ease of use */ | |
rootGroup.selected = true; | |
return rootGroup; | |
} | |
}() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was a dope find. Thanks for sharing your work!