Copy link to clipboard
Copied
I need a script to scale objects to the same area, in this case 4 sqaure inches. Find the area, reduce to square root, dicive 2 by that number and use that number to scale the object up or down. I've done a hundred or so and have soemthing 1700 left to go. Automating would be a big help.
1 Correct answer
4th attempt! Now handles GroupItems. Feel free to ignore attempt 3, I've left it just for learning purposes. There are two ways you may want to handle groups: 1) calculate area for each individual item inside the group, or 2) calculate the aggregate area of all the grouped items. If you want option 2) just comment out the itemsInsideGroupItems line.
Just for fun I also changed the way the parameters work for specifying the target area value and unit. I put them in a string. Example values are: '
...Explore related tutorials & articles
Copy link to clipboard
Copied
Hi kylwell, here's a rough first go. If the item has an area property (PathItems do have this) the script will use it, otherwise will use width and height to calculate the area, which is crude to say the least. There's certainly room for more sophistication here.
var items = app.activeDocument.selection;
var targetAreaInSquareInches = 4;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var areaInSquarePoints = item.area != undefined ? item.area : item.width * item.height;
var areaInSquareInches = areaInSquarePoints / (72 * 72);
var scaleFactor = (Math.sqrt(targetAreaInSquareInches / areaInSquareInches)) * 100;
item.resize(scaleFactor, scaleFactor);
}
- Mark
Copy link to clipboard
Copied
I'm getting a error with this:
Error 1242: Illegal argument - argument 1 - Numeric value expected
Line: 9
-> item.resize(scaleFactor, scaleFactor);
I have a script for getting the area if that helps"
var decimalPlaces = 3;
function calculateArea (obj) {
if (obj.typename == "PathItem") {
return obj.area; // could be negative
} else if (obj.typename == "CompoundPathItem") {
var totalArea = 0;
for (var i=0; i<obj.pathItems.length; i++) {
totalArea += calculateArea(obj.pathItems[i]); // could be negative
}
return Math.abs(totalArea); // make sure positive
} else if (obj.typename == "GroupItem") {
var totalArea = 0;
for (var i=0; i<obj.pathItems.length; i++) {
totalArea += Math.abs(calculateArea(obj.pathItems[i])); // make sure positive
}
for (var i=0; i<obj.compoundPathItems.length; i++) {
totalArea += calculateArea(obj.compoundPathItems[i]); // already positive
}
for (var i=0; i<obj.groupItems.length; i++) {
totalArea += calculateArea(obj.groupItems[i]); // already positive
}
return totalArea; // already positive
} else { // not path, compound path or group
return 0;
}
}
function convertArea (area) {
var ppi = 72;
var result = {};
result.inch = area/ppi/ppi;
result.cm = result.inch * 6.4516;
return result;
}
if (app.documents.length > 0) {
var objects = app.activeDocument.selection;
var display = ["Shape Area"];
// Collect info
var totalArea = 0;
for (var i=0; i<objects.length; i++) {
var area = Math.abs(calculateArea(objects[i])); // need absolute in case of PathItems
totalArea += area;
var conv = convertArea(area);
display.push(conv.inch.toFixed(decimalPlaces) + " in² / " + conv.cm.toFixed(decimalPlaces) + "cm²");
}
var conv = convertArea(totalArea);
display.push("Total Area: " + conv.inch.toFixed(decimalPlaces) + " in² / " + conv.cm.toFixed(decimalPlaces) + " cm²");
// Display
alert(display.join("\n"));
}
Copy link to clipboard
Copied
What if you change line 5 in @m1b 's script to this
var areaInSquarePoints = Math.abs(item.area != undefined ? item.area : item.width * item.height);
Copy link to clipboard
Copied
Hey that seemed to have fixed it.
Copy link to clipboard
Copied
Or so I thought. It works on non-compound path objects perfectly. An object with a compound path to using the objects width & heighth.
Copy link to clipboard
Copied
You are right! I'll fix it.
Copy link to clipboard
Copied
2nd attempt! 🙂
var items = app.activeDocument.selection;
var targetAreaInSquareInches = 4;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var areaInSquarePoints = areaOfItemInSquarePoints(item);
var areaInSquareInches = areaInSquarePoints / (72 * 72);
var scaleFactor = (Math.sqrt(targetAreaInSquareInches / areaInSquareInches)) * 100;
if (scaleFactor == NaN) continue;
item.resize(scaleFactor, scaleFactor);
}
function areaOfItemInSquarePoints(item) {
var area = 0;
if (item.typename == 'PathItem') {
area = item.area;
} else if (item.typename == 'CompoundPathItem') {
for (var j = 0; j < item.pathItems.length; j++) {
area += item.pathItems[j].area;
}
} else {
// for other types of items, resort to width x height
area = item.width * item.height;
}
return Math.abs(Math.round(area * 10000) / 10000);
}
- Mark
Copy link to clipboard
Copied
And 3rd attempt, this one better organised I think:
scaleSelectedItemsToMatchTargetArea(4, 'inch');
function scaleSelectedItemsToMatchTargetArea(targetAreaInSquareUnits, unitType) {
var items = app.activeDocument.selection;
for (var i = 0; i < items.length; i++) {
scaleToArea(items[i], targetAreaInSquareUnits, unitType);
}
}
function scaleToArea(item, targetAreaInSquareUnits, unitType) {
var itemAreaInSquareUnits = areaOfItem(item, unitType);
var scaleFactor = (Math.sqrt(targetAreaInSquareUnits / itemAreaInSquareUnits)) * 100;
if (scaleFactor != NaN) {
item.resize(scaleFactor, scaleFactor);
}
}
function areaOfItem(item, unitType) {
var itemAreaInSquareUnits;
var itemAreaInSquarePoints = areaOfItemInSquarePoints(item);
if (unitType == 'in' || unitType == 'inch') {
var ptsIn1inch = 72;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1inch * ptsIn1inch);
} else if (unitType == 'mm' || unitType == 'millimetres') {
var ptsIn1mm = 2.834645669;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1mm * ptsIn1mm);
} else {
// no unitType specified, use points
itemAreaInSquareUnits = itemAreaInSquarePoints;
}
return itemAreaInSquareUnits;
}
function areaOfItemInSquarePoints(item) {
var area = 0;
if (item.typename == 'PathItem') {
area = item.area;
} else if (item.typename == 'CompoundPathItem') {
for (var j = 0; j < item.pathItems.length; j++) {
area += item.pathItems[j].area;
}
} else {
// for other types of items, resort to width x height
area = item.width * item.height;
}
return Math.abs(Math.round(area * 10000) / 10000);
}
Still largely untested, but let me know how it goes! - Mark
Edit: took out my $.writelns!
Copy link to clipboard
Copied
4th attempt! Now handles GroupItems. Feel free to ignore attempt 3, I've left it just for learning purposes. There are two ways you may want to handle groups: 1) calculate area for each individual item inside the group, or 2) calculate the aggregate area of all the grouped items. If you want option 2) just comment out the itemsInsideGroupItems line.
Just for fun I also changed the way the parameters work for specifying the target area value and unit. I put them in a string. Example values are: '4 square inches', '4in','4 sq inch' will give same result. Other examples are:'100mm', '800 pt'. If the parameter is a number, not a string, it will be interpreted as points.
// start with the selected items
var items = app.activeDocument.selection;
// comment out the following line if you want each groups' area to be calculated, rather than the items inside each group individually
items = itemsInsideGroupItems(items);
// this is where you specify your target size ()
scaleItemsToArea(items, '4 square inches');
function scaleItemsToArea(items, targetString) {
// very simple parsing of targetString
targetString = String (targetString);
var targetAreaInSquareUnits = Number(targetString.match(/\d+/)[0]);
var unitType;
// note: only looks for 'in' or 'mm'
if (targetString.search(/(in|\")/) > -1) {
unitType = 'in';
} else if (targetString.search(/mm/) > -1) {
unitType = 'mm';
} else {
unitType = 'pt'
}
// scale each item
for (var i = 0; i < items.length; i++) {
scaleToArea(items[i], targetAreaInSquareUnits, unitType);
}
}
function scaleToArea(item, targetAreaInSquareUnits, unitType) {
var itemAreaInSquareUnits = areaOfItem(item, unitType);
var scaleFactor = (Math.sqrt(targetAreaInSquareUnits / itemAreaInSquareUnits)) * 100;
if (scaleFactor != NaN) {
item.resize(scaleFactor, scaleFactor);
}
}
function areaOfItem(item, unitType) {
var itemAreaInSquareUnits;
var itemAreaInSquarePoints = areaOfItemInSquarePoints(item);
if (unitType == 'in') {
var ptsIn1inch = 72;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1inch * ptsIn1inch);
} else if (unitType == 'mm') {
var ptsIn1mm = 2.834645669;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1mm * ptsIn1mm);
} else {
// no unitType specified, use points
itemAreaInSquareUnits = itemAreaInSquarePoints;
}
return itemAreaInSquareUnits;
}
function areaOfItemInSquarePoints(item) {
var area = 0;
if (item.typename == 'PathItem') {
area = item.area;
} else if (item.typename == 'CompoundPathItem') {
for (var j = 0; j < item.pathItems.length; j++) {
area += item.pathItems[j].area;
// $.writeln('item.pathItems[j].area = ' + item.pathItems[j].area + ' area = ' + area);
}
} else if (item.typename == 'GroupItem') {
for (var j = 0; j < item.pageItems.length; j++) {
area += areaOfItemInSquarePoints(item.pageItems[j]);
// $.writeln('item.pathItems[j].area = ' + item.pathItems[j].area + ' area = ' + area);
}
} else {
// for other types of items, resort to width x height
area = item.width * item.height;
}
return Math.abs(Math.round(area * 10000) / 10000);
}
function itemsInsideGroupItems(items) {
// returns an array containing items, including items found inside GroupItems
try {
var found = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.typename == 'GroupItem') {
found = found.concat(itemsInsideGroupItems(item.pageItems));
} else {
found.push(item);
}
}
return found;
} catch (e) {
return found;
}
}
As usual, not much testing done, so please let me know if it works as you'd expect. - Mark
Copy link to clipboard
Copied
gah-roovy
I'll let you know
Copy link to clipboard
Copied
Only error I can get it to make is when you have two or more seperated object joined as a compound path. Not that that's a common situation. Other than that seems to be flawless. And thanks for the note on how to alter it if need be.
Copy link to clipboard
Copied
I can't reproduce that problem. Any chance you could you post screenshot of layers palette, or link to sample file?

