Script to scale by area... or script to scale everyting to have the same area

Explorer ,
Jan 05, 2021 Jan 05, 2021

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.

TOPICS
Scripting

Views

165

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct Answer

Engaged , Jan 09, 2021 Jan 09, 2021
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: '...

Likes

translate

Translate

Translate
Engaged ,
Jan 05, 2021 Jan 05, 2021

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

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 06, 2021 Jan 06, 2021

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"));

}

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jan 08, 2021 Jan 08, 2021

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);

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 08, 2021 Jan 08, 2021

Copy link to clipboard

Copied

Hey that seemed to have fixed it.

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 09, 2021 Jan 09, 2021

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.

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2021 Jan 09, 2021

Copy link to clipboard

Copied

You are right! I'll fix it.

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2021 Jan 09, 2021

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

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2021 Jan 09, 2021

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!

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2021 Jan 09, 2021

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

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 09, 2021 Jan 09, 2021

Copy link to clipboard

Copied

gah-roovy

I'll let you know

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 09, 2021 Jan 09, 2021

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.

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2021 Jan 09, 2021

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?

Likes

translate

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines