Skip to main content
Inspiring
October 8, 2024
Answered

• Small Javascript to reset to "zero" the rotate value of a placed item.

  • October 8, 2024
  • 2 replies
  • 2860 views

Hi -

 

I try to make a very "simple" Adobe Illustrator Javascript based on the below script (I found it somewhere years ago on internet, tbh can't remeber where).

 

The script should simply reset to zero (0°) the "rotate" value of the current selected placed item.

I tried already a lot of stuff, but can't make it works  :-P:

I found information like "app.getRotationMatrix(…)".  But yeah… How to make it work?   🤷🏻

 

 

Here is the source code (base), not the final script:

 

 

selection = app.activeDocument.selection;

for (i=0; i<selection.length; i++) {

    if(selection[i].typename == "TextFrame" || selection[i].typename == "PlacedItem" || selection[i].typename == "RasterItem") {
        get_matrix = selection[i].matrix;		

        if(selection[i].typename == "PlacedItem")
            get_matrix = concatenateScaleMatrix(get_matrix, 100, -100);

        invMatrix = invertMatrix(get_matrix);
        invMatrix.mValueTX = 0;
        invMatrix.mValueTY = 0;
        
        selection[i].transform(invMatrix);
    }
}

 

 

 

I tried something (w/ the help of ChatGPT), visually we are close to a result, but the placed item rotate value is just not reset to "zero" (0°):

 

 

 

function resetPlacedItemRotation() {
    var myDoc_DC = app.activeDocument;
    var IMG_selected = myDoc_DC.selection;

    if (IMG_selected.length === 0) {
        alert("Please select a placed item.");
        return;
    }

    for (var i = 0; i < IMG_selected.length; i++) {
        var item = IMG_selected[i];

        if (item.typename === "PlacedItem") {
            var confirmReset = confirm("Do you want to reset the rotation for the selected placed item?");
            
            if (confirmReset) {
                // Get the current transformation matrix
                var currentMatrix = item.matrix;

                // Calculate the current rotation angle in degrees
                var currentRotation = Math.atan2(currentMatrix.mValueB, currentMatrix.mValueA) * (180 / Math.PI);

                // Apply the exact opposite of the current rotation
                var rotationToApply = -currentRotation;  // Opposite direction

                // Rotate around the center of the placed item
                item.rotate(rotationToApply, true, true, true, true, Transformation.CENTER);

                alert("Rotation has been reset to 0° by applying " + rotationToApply.toFixed(3) + "°.");
            } else {
                alert("Operation canceled.");
            }
        } else {
            alert("Selected item is not a placed item.");
        }
    }
}

resetPlacedItemRotation();

 

 

 

 

Thank you,

Enjoy your day,

 

 

 - Dimitri

 

This topic has been closed for replies.
Correct answer renél80416020

Seulement rotation 0°

// JavaScript Document
// This script resets the rotation angle to 0 degrees for bitmap clips and Imported artwork.
  var info = false;
      testObjects(selection);
// -------
function testObjects(a) {
  var deg,
      tag,
      selLen = a.length;
    for (var c = 0; c < selLen; c++) {
        if(a[c].typename == 'GroupItem'){
          testObjects(a[c].pageItems);
        } else
            if(a[c].typename=='RasterItem' || a[c].typename == 'PlacedItem') {
              deg = getRotationAngle(a[c]);
              a[c].rotate(deg * -1);
              tag = getTag(a[c]);
              if (tag != undefined) tag.value = 0;
            }
    }
}
// -------
function getRotationAngle(item) {
    var mtx = item.matrix;
    var deg = Math.atan2(mtx.mValueB, mtx.mValueA) * 180 / Math.PI;
    return (item.typename == 'PlacedItem') ? deg * -1 : deg;
}
// -------
function getTag(obj)
{ // retourne le V.tag de nom "BBAccumRotation" si il existe
     try {
     var tag = obj.tags.getByName("BBAccumRotation");
     }   catch (e) {
          if (info) alert( "The specified tag doesn’t exist" );
        return undefined;
        }
     return tag;
}
// -------

René

2 replies

m1b
Community Expert
Community Expert
October 16, 2024

Hi @dimitri_cas, you are already well on your way to a solution, but I just remembered that I did a similar thing in a previous script I wrote. Maybe you could look at it and see if it sheds any light on the problem.

- Mark

Inspiring
October 17, 2024

Hi Mark -

Salut René renél80416020

 

Thanks to both of you for your help.

Strangely enough, currently I can't reproduce the "wrong behaviour" of the script (ie. That it flipped not to 0° the placed item, instead of just resetting the rotate value to 0°). And I don't know why…  😛

 

If I still encounter the "wrong behaviour" I'll post the file (PDF) as requested  😉

 

 

Thanks again, and

both of you, enjoy your day.

 

 

 - Dimitri

 

m1b
Community Expert
Community Expert
October 8, 2024

Hi @dimitri_cas, here is one approach—get the rotation from the placed item's matrix, then rotate it by that negative amount:

(function () {

    var doc = app.activeDocument,
        placedItem = doc.selection[0];

    if (!placedItem)
        return alert('Please select a placed item and try again.');

    var sr = getLinkScaleAndRotation(placedItem),
        scale = [sr[0], sr[1]],
        rotation = sr[2];

    placedItem.rotate(-rotation);

})();

/**
 * Return the scale, rotation and size
 * of a PlacedItem or RasterItem.
 * @author m1b
 * @version 2023-03-09
 * @param {PlacedItem|RasterItem} item - an Illustrator item.
 * @param {Boolean} round - whether to round numbers to nearest integer.
 * @returns {Array} [scaleX%, scaleY%, rotation°, width, height]
 */
function getLinkScaleAndRotation(item, round) {

    if (item == undefined)
        return;

    var m = item.matrix,
        rotatedAmount,
        unrotatedMatrix,
        scaledAmount;

    var flipPlacedItem = (item.typename == 'PlacedItem') ? 1 : -1;

    try {
        rotatedAmount = item.tags.getByName('BBAccumRotation').value * 180 / Math.PI;
    } catch (error) {
        rotatedAmount = 0;
    }
    unrotatedMatrix = app.concatenateRotationMatrix(m, rotatedAmount * flipPlacedItem);

    if (
        unrotatedMatrix.mValueA == 0
        && unrotatedMatrix.mValueB !== 0
        && unrotatedMatrix.mValueC !== 0
        && unrotatedMatrix.mValueD == 0
    )
        scaledAmount = [unrotatedMatrix.mValueB * 100, unrotatedMatrix.mValueC * -100 * flipPlacedItem];
    else
        scaledAmount = [unrotatedMatrix.mValueA * 100, unrotatedMatrix.mValueD * -100 * flipPlacedItem];

    if (scaledAmount[0] == 0 || scaledAmount[1] == 0)
        return;

    if (round)
        return [round(scaledAmount[0]), round(scaledAmount[1]), round(rotatedAmount)];
    else
        return [scaledAmount[0], scaledAmount[1], rotatedAmount];

};

 

There are other ways, eg. read the rotation value from a tag:

 

rotatedAmount = item.tags.getByName('BBAccumRotation').value * 180 / Math.PI;

 

 

I also wrote a function that derives the rotation value by minimizing the area of the rectangular bounds of the item:

/**
 * Returns the rotation amount in degrees
 * that the item needs to be rotated such
 * that it has a minimal bounding box area.
 * Assuming that `item` is a rectangular
 * object, such as a PlacedItem, RasterItem
 * or a rectangular path item, the resulting
 * rotation will rotate it so that the sides
 * of the rectangle align to a factor of 90°.
 * In other words, it will return the value
 * required to "unrotate" the item.
 * @author m1b
 * @version 2023-08-25
 * @param {PageItem} item - an Illustrator page item.
 * @returns {Number}
 */
function findRotationByMinimalBounds(item) {

    // we will rotate a copy and leave the original
    var workingItem = item.duplicate(),

        convergenceThreshold = 0.001,
        inc = 45, // the starting rotation increment
        rotationAmount = 0,
        prevArea = area(workingItem);

    while (Math.abs(inc) >= convergenceThreshold) {

        workingItem.rotate(inc);

        var newArea = area(workingItem);

        if (newArea < prevArea) {
            prevArea = newArea;
            rotationAmount -= inc;
            inc *= 0.5;
        }

        else {
            workingItem.rotate(-inc); // Undo the last rotation
            inc *= -0.5;
        }

    }

    // clean up
    workingItem.remove();

    return round(rotationAmount, 2);

    /**
     * Returns area of bounding box of `item`.
     * @param {PageItem} item
     * @returns {Number}
     */
    function area(item) {
        return item.width * item.height;
    };

};

/**
 * Rounds `n` to `places` decimal places.
 * @param {Number} n - the number to round
 * @param {Number} places - number of decimal places, can be negative
 * @returns {Number}
 */
function round(n, places) {
    var m = Math.pow(10, places != undefined ? places : 3);
    return Math.round(n * m) / m;
};

 

Each method has pros and cons. I suggest you try the first approach first and see how you go. It only works with an unskewed matrix. The second approach only works if the tag exists, and no rotations have been performed without updating it. The third option is probably the most robust but is a bit slower.

- Mark

Inspiring
October 8, 2024

Hi m1b -

 

Thanks for your reply.

First way/option seems to work perfectly  😉

It worked well the first time, I tried already in another document on another placed image and it didn't work.

The seoncd time, result is quite similar than mine… 😞

 

Thx again.

Enjoy your day.

 

  

 

- Dimitri

 

Kurt Gold
Community Expert
Community Expert
October 8, 2024

I think to get Mark's first script to run properly requires to reset the bounding box after each rotation to zero degree. You may want to modify that.

 

You may also have a look at this approach by Sky Chaser High which is pretty good.

 

/* ===============================================================================================================================================
   resetToFullScale

   Description
   This script resets the scale to 100% and the rotation angle to 0 degrees for the linked files.
   Embedded images is also supported.

   Usage
   Select the linked files or the embedded images, run this script from File > Scripts > Other Script...

   Notes
   In rare cases, if you continue to use the script, it may not work.
   In that case, restart Illustrator and try again.

   Requirements
   Illustrator CS6 or higher

   Version
   1.0.1

   Homepage
   github.com/sky-chaser-high/adobe-illustrator-scripts

   License
   Released under the MIT license.
   https://opensource.org/licenses/mit-license.php
   =============================================================================================================================================== */

(function() {
    if (app.documents.length > 0 && app.activeDocument.selection.length > 0) main();
})();


function main() {
    // work around a bug
    try {
        app.executeMenuCommand('Adobe Update Link Shortcut'); // Link Panel > Update Link
        app.redraw();
    }
    catch (err) { }

    var items = app.activeDocument.selection;
    var images = getImageItems(items);

    for (var i = 0; i < images.length; i++) {
        resetToFullScale(images[i]);
    }
}


function resetToFullScale(item) {
    var basis = {
        'left': item.left,
        'top': item.top,
        'width': item.width,
        'height': item.height
    };

    var filename = '';
    try {
        if (item.typename == 'PlacedItem') filename = item.file.name;
    }
    catch (err) { }

    // work around a bug
    var deg = getRotationAngle(item);
    item.rotate(deg * -1);
    var scale = getScale(item);
    item.resize(1 / scale.x * 100, 1 / scale.y * 100);

    item.matrix.mValueA = 1;
    item.matrix.mValueB = 0;
    item.matrix.mValueC = 0;
    item.matrix.mValueD = (/\.eps$/i.test(filename) || item.typename == 'RasterItem') ? 1 : -1; // invert the sign except for eps file and embedded image

    // workaround for the reflect object
    item.left = 0;
    item.top = 0;

    // reposition the object to the center
    item.left = basis.left - (item.width - basis.width) / 2;
    item.top = basis.top + (item.height - basis.height) / 2;
}


function getRotationAngle(item) {
    var matrix = item.matrix;
    var rad = Math.atan2(matrix.mValueB, matrix.mValueA);
    var deg = rad * 180 / Math.PI;
    return (item.typename == 'PlacedItem') ? deg * -1 : deg;
}


function getScale(item) {
    var matrix = item.matrix;
    var x = Math.sqrt(Math.pow(matrix.mValueA, 2) + Math.pow(matrix.mValueB, 2));
    var y = Math.sqrt(Math.pow(matrix.mValueC, 2) + Math.pow(matrix.mValueD, 2));
    return {
        x: x,
        y: y
    };
}


function getImageItems(items) {
    var images = [];
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        if (item.typename == 'PlacedItem' || item.typename == 'RasterItem') {
            images.push(item);
        }
        if (item.typename == 'GroupItem') {
            images = images.concat(getImageItems(item.pageItems));
        }
    }
    return images;
}