Ain't scripting fun. It was a breeze (well -- sort of) to get something up & running for InDesign ... Now translating it into the very different Javascript for Illustrator proved to be harder than I thought!
(Is there no way to vertically align a text into its frame? This is now sort-of, but it depends on the number of lines of text...)
Type in (or copy, or import) your values into a new text frame. Separate the label and the value by a single tab. The values will be sorted automatically, and the largest value will appear in the center; the rest will be distributed nicely around the edge. I noticed my mathematical distribution is not exactly the same as the sample you provided ... even when allowing for a pixel here or there. I wonder why?
For better or for worse, this is what it produces -- the first sub-item will always be at a 45 degrees angle, the rest is separated from each other by an exact distance of 5 points.
//DESCRIPTION:Krazy Circular Diagrams
// A Jongware Script 24-Sep-2010
// Uses a tab-separated set of String / value data
// which ought to be selected when running the script.
// Use but do not abuse, please.
if (app.documents.length == 0 || app.selection.length != 1 || !(app.selection[0].hasOwnProperty("baseline") || app.selection[0].hasOwnProperty("contents")))
{
alert ("Please select the text frame containing data");
} else
{
var dataArray;
var resultGroup;
var parent_diameter = 100;
var parent_position_x = app.activeDocument.activeView.centerPoint[0];
var parent_position_y = app.activeDocument.activeView.centerPoint[1];
var black = new GrayColor(); black.gray = 100;
var white = new GrayColor(); white.gray = 0;
dataArray = gatherValues(app.selection[0]);
if (dataArray.length == 0)
alert ("Unable to get sensible values .. please check");
else
{
calculateValues ();
resultGroup = app.activeDocument.groupItems.add();
drawCircles(resultGroup);
}
}
function gatherValues (fromItem)
{
var result = new Array();
var l, line, lines, dataSource;
if (fromItem.hasOwnProperty("baseline"))
dataSource = fromItem.parentStory.contents;
else
dataSource = fromItem.contents;
lines = dataSource.split ("\r");
for (l=0; l<lines.length; l++)
{
line = lines.split ("\t");
if (line.length == 2)
{
if (!isNaN(Number(line[1])))
result.push ([line[0], Number(line[1])]);
}
}
result.sort (sortByValue);
return result;
}
function calculateValues ()
{
var v;
// Calculate size per circle
for (v=0; v<dataArray.length; v++)
{
dataArray.push (Math.sqrt(parent_diameter*parent_diameter*dataArray[1]/dataArray[0][1]));
}
}
function drawCircles (addToGroup)
{
var color_p = new RGBColor();
var color_c = new RGBColor();
// Parent colors, in RGB
color_p.red=80;
color_p.green=130;
color_p.blue=190;
// Child colors, in RGB
color_c.red=74;
color_c.green=172;
color_c.blue=197;
// Draw parent circle
drawCircleAt (addToGroup, parent_position_x, parent_position_y, dataArray[0], color_p);
// Draw the other circles.
// First one starts at -45 degrees (0 degrees is straight to the right)
circleAngle = 45;
// The center of the circle is at parent_diameter/2 + (distance) + own_diameter/2
centerDistance = parent_diameter/2 + 5 + dataArray[1][2]/2;
for (nextCircle=1; nextCircle<dataArray.length; nextCircle++)
{
// and this is Math 1-0-1: finding its center position using the angle :-)
cx = parent_position_x + Math.cos(Math.PI * circleAngle/180)*centerDistance;
cy = parent_position_y + Math.sin(Math.PI * circleAngle/180)*centerDistance;
drawCircleAt (addToGroup, cx, cy, dataArray[nextCircle], color_c, parent_position_x,parent_position_y,parent_diameter/2, circleAngle);
if (nextCircle < dataArray.length - 1)
{
// Advance angle to make space for next circle.
// Let's type this while thinking, shall we?
// We have:
// a line from parent_center to child_center A at angle alpha
// radius of A (actually, diameter, but we don't care)
// radius of B (as above)
// some distance to put between A and B
// rad_A + distance + rad_B -> position of center of B (anywhere)
// oh, and distance from parent_center to child_center B is the same as to child_center A!
// checking on Wikipedia, http://en.wikipedia.org/wiki/Law_of_sines
// yields something like this ...
angle_diff = Math.asin ( ((dataArray[nextCircle+1][2]/2 + 5 + dataArray[nextCircle][2]/2)/2) / centerDistance); // in Radians
angle_diff = 2*angle_diff * 180 / Math.PI; // in Degrees
circleAngle = circleAngle - angle_diff;
}
}
}
function drawCircleAt (group, xpos, ypos, textAndSize, color, lineto_x, lineto_y, parentRad, angle)
{
var tframe, line;
// The connexion line, if any
if (lineto_x != undefined && lineto_y != undefined && parentRad != undefined)
{
angle = Math.PI * angle / 180.0;
line = group.pathItems.add();
line.setEntirePath ( [[lineto_x + parentRad*Math.cos(angle),lineto_y + parentRad*Math.sin(angle)], [xpos - textAndSize[2]/2*Math.cos(angle),ypos - textAndSize[2]/2*Math.sin(angle)]]);
line.filled = false;
line.stroked = true;
line.strokeWidth = 0.5;
line.strokeColor = black;
}
// The Circle
circle = group.pathItems.ellipse (ypos+textAndSize[2]/2, xpos-textAndSize[2]/2, textAndSize[2], textAndSize[2]);
circle.strokeColor = black;
circle.strokeWidth = 0.5;
circle.fillColor = color;
// The Text Frame
tframe = app.activeDocument.pathItems.rectangle(ypos+textAndSize[2]/6, xpos-textAndSize[2]/2, textAndSize[2], textAndSize[2]/2);
tframe = group.textFrames.areaText(tframe);
tframe.contents = textAndSize[0];
tframe.wrapInside = false;
tframe.wrapped = false;
tframe.textRange.hyphenation = false;
tframe.textRange.justification = Justification.CENTER;
tframe.textRange.textFont = app.textFonts.getByName("MyriadPro-Regular");
tframe.textRange.characterAttributes.size = textAndSize[2]/6;
tframe.textRange.fillColor = white;
}
function sortByValue (a,b)
{
a = Number(a[1]);
b = Number(b[1]);
return (a == b) ? 0 : (a < b ? 1 : -1);
}