Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
1

Mirroring group of objects horizontally to the exact central point of the figure

New Here ,
Feb 29, 2024 Feb 29, 2024

Copy link to clipboard

Copied

Over the years of working with flat sketching i found the way how to mirror group of objects horizontally to the exact central point of the figure. In the video below you can see that hack of mine. The question is... how to make it faster? Working with a huge ammount of documets daily and doing the same steps every time I need to mirror objects takes so much time! 
Is there any options how to make it easyer? I wish I would be able do it in just one click! 

TOPICS
Draw and design , How-to , Scripting

Views

742
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 3 Correct answers

Community Expert , Feb 29, 2024 Feb 29, 2024

or if you want to repeat your method as shown in the video

 

after selecting your shapes

1. select the Reflect Tool 

2. Alt/Option + Click on the Guide (or one of the Anchors)

 

CarlosCanto_0-1709276432839.pngexpand image

 

that will bring up the Reflect Dialog, click on Copy button

CarlosCanto_1-1709276506637.pngexpand image

 

Votes

Translate
Community Expert , Mar 01, 2024 Mar 01, 2024

Another way; use a Transform Effect to the group in the Appearance panel. You can make it a Graphic Style to apply with a single click.

Screenshot 2024-03-01 at 09.36.05.pngexpand image

Votes

Translate
Community Expert , Mar 01, 2024 Mar 01, 2024

Hi @Coco_K, you have the best answers already, but I'm a scripter and I was intrigued by this challenge from a scripting perspective.

 

So, I've written a script called "Close Path By Mirroring". Select one or more open paths and run script and here's what it looks like:

demo.gifexpand image

Full script is below (sorry, it's long!).

- Mark

 

/**
 * Close Path By Mirroring.js
 *
 * Usage:
 * 1. select one or more *open* path items
 * 2. run script
 *
 * @author m1b
 * @discussion https://community.adobe.com/t5/illus
...

Votes

Translate
Adobe
Community Expert ,
Feb 29, 2024 Feb 29, 2024

Copy link to clipboard

Copied

have you tried Mirror Repeat?

 

1. select your objects

CarlosCanto_0-1709275467949.pngexpand image

2. go to Object->Repeat->Mirror

CarlosCanto_1-1709275560830.pngexpand image

 

3. double click on an empty area to exit edit mode

Votes

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
Community Expert ,
Feb 29, 2024 Feb 29, 2024

Copy link to clipboard

Copied

or if you want to repeat your method as shown in the video

 

after selecting your shapes

1. select the Reflect Tool 

2. Alt/Option + Click on the Guide (or one of the Anchors)

 

CarlosCanto_0-1709276432839.pngexpand image

 

that will bring up the Reflect Dialog, click on Copy button

CarlosCanto_1-1709276506637.pngexpand image

 

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Another way; use a Transform Effect to the group in the Appearance panel. You can make it a Graphic Style to apply with a single click.

Screenshot 2024-03-01 at 09.36.05.pngexpand image

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Hi @Coco_K, you have the best answers already, but I'm a scripter and I was intrigued by this challenge from a scripting perspective.

 

So, I've written a script called "Close Path By Mirroring". Select one or more open paths and run script and here's what it looks like:

demo.gifexpand image

Full script is below (sorry, it's long!).

- Mark

 

/**
 * Close Path By Mirroring.js
 *
 * Usage:
 * 1. select one or more *open* path items
 * 2. run script
 *
 * @author m1b
 * @discussion https://community.adobe.com/t5/illustrator-discussions/mirroring-group-of-objects-horizontally-to-the-exact-central-point-of-the-figure/m-p/14458608
 */
(function () {

    var doc = app.activeDocument,
        items = doc.selection;

    closePathByMirroring(items);

})();

/**
 * Close item's paths by flipping path across
 * the axis of its first and last points.
 * @author m1b
 * @version 2024-03-01
 * @param {PathItem|CompoundPathItem} item - the item to mirror.
 */
function closePathByMirroring(item) {

    // process an array of items
    if ('Array' === item.constructor.name) {
        for (var i = 0; i < item.length; i++)
            closePathByMirroring(item[i]);
        return;
    }

    if (
        item.hasOwnProperty('pathItems')
        && item.pathItems.length > 0
    ) {
        for (var i = 0; i < item.pathItems.length; i++)
            closePathByMirroring(item.pathItems[i]);
        return;
    }

    if (
        !item.hasOwnProperty('pathPoints')
        || item.pathPoints.length < 2
    )
        return;

    if (true === item.closed)
        // ignore closed paths
        return;

    var len = item.pathPoints.length - 1,
        pFirst = item.pathPoints[0],
        pLast = item.pathPoints[len],
        fulcrum = pFirst.anchor,
        angle = angleBetweenPoints(pFirst.anchor, pLast.anchor, true) + 1.5707963268;

    // matrix to move fulcrum to origin, straighten and flip (and move back)
    var flip = multiplyMatrices([
        translationMatrix([-fulcrum[0], -fulcrum[1]]),
        rotationMatrix(-angle, false),
        scaleMatrix(-1, 1),
        rotationMatrix(angle, false),
        translationMatrix([fulcrum[0], fulcrum[1]]),
    ]);

    // add mirrored path points
    for (var i = len, p; i >= 0; i--) {

        if (len === i) {
            item.pathPoints[i].rightDirection = transformPoint(item.pathPoints[i].leftDirection, flip);
        }

        else if (0 === i) {
            item.closed = true;
            item.pathPoints[0].leftDirection = transformPoint(item.pathPoints[i].rightDirection, flip);
        }

        else {
            p = item.pathPoints.add();
            p.anchor = transformPoint(item.pathPoints[i].anchor, flip);
            p.leftDirection = transformPoint(item.pathPoints[i].rightDirection, flip);
            p.rightDirection = transformPoint(item.pathPoints[i].leftDirection, flip);
        }

    }

};

/**
 * Returns angle between two points
 * and horizontal.
 * @param {Array} p1 - a point array [x, y].
 * @param {Array} p2 - a point array [x, y].
 * @param {Boolean} asRadians - whether the result should be in radians (default: false).
 * @returns {Number} - the angle.
 */
function angleBetweenPoints(p1, p2, asRadians) {

    var delta = differenceBetweenPoints(p1, p2),
        theta = Math.atan2(-delta[1], -delta[0]); // radians

    if (asRadians)
        return theta;

    return theta * (180 / Math.PI);

};

/**
 * Returns x, y difference between two points.
 * @param {Array} p1 - a point array [x, y].
 * @param {Array} p2 - a point array [x, y].
 * @returns {Array} - [dx, dy].
 */
function differenceBetweenPoints(p1, p2) {

    return [-(p2[0] - p1[0]), -(p2[1] - p1[1])];

};

/**
 * Returns a translation matrix.
 * @param {Array<Number>} v - the translation vector [tx, ty].
 * @returns {Array<Number>} - 3x3 matrix.
 */
function translationMatrix(v) {

    return [
        [1, 0, v[0]],
        [0, 1, v[1]],
        [0, 0, 1],
    ];

};

/**
 * Returns a matrix to scale by `sx` x `sy`.
 * @param {Number} sx - the horizontal scale factor [0..1].
 * @param {Number} sy - the vertical scale factor [0..1].
 * @returns {Array<Number>} - 3x3 matrix.
 */
function scaleMatrix(sx, sy) {

    return [
        [sx, 0, 0],
        [0, sy, 0],
        [0, 0, 1],
    ];

};

/**
 * Returns a matrix with `angle` rotation.
 * @param {Number} angle - the rotation amount.
 * @param {Boolean} angleIsDegrees - whether the angle unit is degrees (default: radians).
 * @returns {Array<Number>} - 3x3 matrix.
 */
function rotationMatrix(angle, angleIsDegrees) {

    if (angleIsDegrees)
        angle *= (Math.PI / 180);

    return [
        [Math.cos(angle), -Math.sin(angle), 0],
        [Math.sin(angle), Math.cos(angle), 0],
        [0, 0, 1],
    ];

};

/**
 * Multiply two or more 3x3 matrices.
 * @param {Array<matrix>} matrices - a 2D matrix [[a,b,tx], [d,e,ty], [0,0,1]].
 * @returns {matrix}
 */
function multiplyMatrices(matrices) {

    if (
        undefined == matrices
        || 0 === matrices.length
    )
        throw Error('Mat.multiplyMatrices: no matrices supplied.');

    // initialize result matrix
    var result,
        len = matrices.length - 1;

    // multiply each subsequent matrix with the result matrix
    for (var i = len; i >= 0; i--) {

        var m = matrices[i],
            rowCount = m.length,
            columnCount = m[0].length;

        if (
            rowCount !== 3
            || columnCount !== 3
        )
            throw Error('Mat.multiplyMatrices: matrix ' + i + ' is bad.');

        if (len === i) {
            // initialise result
            result = m;
            continue;
        }

        var temp = new Array(rowCount);

        for (var r = 0; r < rowCount; ++r) {
            temp[r] = new Array(columnCount);

            for (var c = 0; c < columnCount; ++c) {
                temp[r][c] = 0;

                for (var k = 0; k < columnCount; ++k)
                    temp[r][c] += result[r][k] * m[k][c];

            }

        }

        result = temp;
    }

    return result;

};

/**
 * Transformation a point with a matrix.
 * @param {Array<Number>} point - a point [x, y].
 * @param {Array<Array<Number>>} matrix - a 3x3 matrix.
 * @returns {Array<Number>} - [tx, ty].
 */
function transformPoint(point, matrix) {

    // add 1 to the point for homogeneity with the matrix
    var p = multiplyMatrixVector(matrix, [point[0], point[1], 1]);

    return [p[0], p[1]];

};

/**
 * Multiplies a matrix by a vector,
 * returning the resulting vector.
 * Note: will throw error if the vector
 * length is less than the number of
 * columns in the matrix.
 * @param {Array<Array<Number>>} matrix - the matrix to multiply.
 * @param {Array<Number>} vector - the vector to multiple.
 * @returns {Array<Number>}
 */
function multiplyMatrixVector(matrix, vector) {

    var result = [];
    for (var i = 0; i < matrix.length; i++) {

        result[i] = 0;
        for (var j = 0; j < vector.length; j++)
            result[i] += matrix[i][j] * vector[j];

    }

    return result;

};

/**
 * Transforms a path item using a 3x3 matrix.
 * @author m1b
 * @version 2024-03-01
 * @param {Object} options
 * @param {PathItem|CompoundPathItem} options.item - a path item or compoundPathItem.
 * @param {Array<Array<Number>>} options.matrix - a 3x3 matrix.
 * @param {Array<Number>} [options.netTranslationVector] - described a pre-transform and post-transform translation, which serves to specify the `fulcrum` of the transform.
 */
function transformPathItem(options) {

    options = options || {};

    var item = options.item,
        matrix = options.matrix;

    for (var i = 0, p, l = item.pathPoints.length; i < l; i++)
        transformPathPoint(item.pathPoints[i], matrix);

};

/**
 * Transform `pathPoint` with `matrix`.
 * @author m1b
 * @version 2024-03-01
 * @param {PathPoint} pathPoint - the PathPoint to transform.
 * @param {matrix} matrix - the matrix (3x3 array).
 */
function transformPathPoint(pathPoint, matrix) {

    pathPoint.anchor = transformPoint(pathPoint.anchor, matrix);
    pathPoint.leftDirection = transformPoint(pathPoint.leftDirection, matrix);
    pathPoint.rightDirection = transformPoint(pathPoint.rightDirection, matrix);

};

 

Edit 2024-03-01: Fixed bug where closing point's left control was not mirrored. Thanks @Ton Frederiks!

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

I like the script, but see a problem when a direction handle is used at an endpoint.

Screenshot 2024-03-01 at 11.22.17.pngexpand image

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Good spotting! I'll fix.

- Mark

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Since we have 2 scripters here, I would like to ask an offtopic question.

I see as the suggested name for the script: Close Path By Mirroring.js

Most scripts I see have the .jsx extension, but they cannot be Quickpreviewed  in MacOS. Files with a .js extension preview fine. I don't think so, but are there any disadvantages when scripts are renamed to .js instead of .jsx?

Votes

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
Guide ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

I've not looked into it in any detail, but my understanding is that JSX is transpiled into JS anyway, so there should be no disadvantage of using JS. 

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Thanks, I thought so.

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Hi @Ton Frederiks and @femkeblanco the choice to use .js or .jsx (at this stage—may change in future) makes no practical difference. Femke there is no transpiling going on—they are both interpreted as ExtendScript by Adobe apps.

 

Personally, I always use .js because .jsx is the standard for Javascript React files and it would be confusing to me if I used .jsx.

- Mark

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

Thanks Marc, and the content can be quickpreviewed with the spacebar, instead of showing an icon.

Screenshot 2024-03-01 at 11.44.57.pngexpand image

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

That's true Ton, and it will affect which app gets launched when you double-click also. I've only used .js for at least 10 years and never come across a reason to do otherwise.

Votes

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
Community Expert ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

The difference between js and jsx is what Mark said, when you double click on a script to launch it, it will be handled by the default application.

 

if jsx, the file gets opened by the ESTK app and if the script has a target directive, it will be handled by the right application (Illustraor, Photoshop, etc)

Votes

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
Advocate ,
Mar 01, 2024 Mar 01, 2024

Copy link to clipboard

Copied

LATEST

Bonjour, 

C'est pourtant simple:

Sélectionnez les objets
Touche de raccourci <o>
1 Clic sur un des points situés sur l’axe de symétrie
par exemple le point C

(si vous avez tracé un axe de symétrie clic sur l"axe).
2 Faire glisser vers la droite <MAJ>+<ALT>

symetrie.pngexpand image

Ou encore plus simple:

Clic sur le point C

appuyez sur <ALT> et clic sur un deuxième point D.

symétrie 2.pngexpand image

Même méthode si l'axe est incliné.

symetrie 3.pngexpand image

Avec un axe clic à deux endroits sur l'axe (1 <ALT> puis 2)

symétrie 4.pngexpand image

Remarque: l'axe n'appartient pas à la sélection, cela peut être un segment d'un tout autre objet.

symétrie 5.pngexpand image

 

 

 

 

Votes

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