Add rectangle into existing group (solved)

I’m attempting to create objects into a group, but mask.addChild() doesn’t work (“Plugin made a change outside the current edit context”).

I have been trying to access the mask several ways, but none of them work.

See second to last line in code here

  // rename current selection
  let frame = selection.items[0]
  frame.name = 'mask'

  //  create first element
  const rect1 = new Rectangle()
  rect1.name = 'rect1'
  rect1.width = 100
  rect1.height = 200
  rect1.fill = new Color('red')
  selection.insertionParent.addChild(rect1)
  rect1.moveInParentCoordinates(10,20)

  // bring original frame to the front
  selection.items = [frame]
  commands.bringToFront()

  // mask rectangle with frame
  selection.items = [frame,rect1]
  commands.createMaskGroup()

  // from now on, use mask as reference point
  let mask = selection.items[0]
  mask.name = 'Timeline'
  console.log('is mask a group?', mask instanceof Group);

  // create another rectangle and add it into the mask
  const rect3 = new Rectangle()
  rect3.name = 'rect3'
  rect3.width = 20
  rect3.height = 22
  rect3.fill = new Color('lime')
  mask.addChild(rect3) // THIS LINE
  rect3.moveInParentCoordinates(75,100)
1 Like

@PramUkesh suggested in another thread to change the selection to the group/mask and adding into that. doesn’t work for me either:

  selection.items = [mask]
  selection.addChild(rect3)

I’m sure I’m just not understanding the concept of parent and childs … Or edit contexts yet?

I only ever end up with the mask successfully created with the red rect1 inside, but the green rect3 is never added (sometimes fails silently in the console)

Hi Johannes,

To make sure I’m following your steps exactly, what are you expecting the first selection item to be?

let frame = selection.items[0]

Is it a rectangle, or something else?

Yes, it’s a rectangle the user creates. That will be used as a mask and I’ll be creating dozens of elements that should be placed inside the mask

Hi @johannes,
so:
• user creates a rectangle;
• your plugin adds some elements into the same artboard;
• your plugin masks all these elements with the rectangle.
Is this what you want to achieve?

  • User creates rectangle
  • Plugin creates first new object first
  • Selects object and rectangle
  • Creates group at this point (since you can’t create groups organically)
    then
  • Plugin will continue to create objects that are supposed to go into the group

I know I can create all the elements first, and then mask only in the end, but code-wise that’s a pain because I’d have to keep track of all the objects created in the meantime and then create a huuuuge selection and then mask in the end.

I thought it would be so much more efficient to create the group/mask as soon as possible and then keep adding into it with addChild()

This may not be 100% what you’re looking for, but it may clear some stuff up (explanation to follow):

function myPluginCommand(selection) {
  // Go to Plugins > Development > Developer Console to see this log output
  console.log("Plugin command is running!");

  console.log(selection.items[0]);

  //  create first element
  const rect1 = new Rectangle();
  rect1.name = "rect1";
  rect1.width = 100;
  rect1.height = 200;
  rect1.fill = new Color("red");
  selection.insertionParent.addChild(rect1);
  rect1.moveInParentCoordinates(10, 20);

  const rect3 = new Rectangle();
  rect3.name = "rect3";
  rect3.width = 20;
  rect3.height = 100;
  rect3.fill = new Color("lime");
  selection.insertionParent.addChild(rect3);
  rect3.moveInParentCoordinates(75, 100);

  commands.bringToFront(); // Bring the current selection (the frame) to the front
  selection.items = [selection.items[0], rect1, rect3]; // Order doesn't matter
  commands.createMaskGroup(); // Uses as mask shape the front-most object

  console.log(
    selection.items[0] instanceof Group && selection.items[0].mask
      ? "Masked group"
      : "Plain group"
  );
}

If you are sure the artboard contains just objects you want to mask you can easily select them all, then mask them once.

1 Like

In your example above, it appears that you’re attempting to add rect3 to the mask, but rect3 doesn’t overlap with the mask shape.

If that’s the case, this operation isn’t possible in the app UI from what I could tell. In the API, a better error message would be helpful here.

In my example above, I’ve changed the .height of rect3 to ensure it overlaps, and it works. (Although I recognize that the order is different than what you originally had; more on that in a sec.)

In real life, if you don’t have control over where the elements land in relation to the mask shape (for example, maybe you’re randomly adding elements programmatically), you could do some collision detection by doing math on the bounds of the elements.


As for the order of creating elements, would expanding on the above suffice, or do you need to add more elements after the mask is initially created?

Ah, and here’s my visual output from the above:

34

I appreciate your reply @ashryan and @PaoloBiagini

I realize that I can wait until the very end of the command and then select everything that I created and group/mask at that point.

But that’s impractical for two reasons:

  • during the command, I need to create individual groups, so I’m going to change selection and use the group command
  • then I’d have to keep track of all the objects I created and change the selection again, in a very very complex selection.items = [] before I can group again

Besides, what if I want to add something to a group that the user selected before executing the command?

I don’t have a problem creating groups or masks (and thanks Ash for the tip to make sure objects are inside the mask before creating the mask). I have a problem adding into an existing group.

Conceptually, I would expect to be able to run group.addChild() as documented here: https://adobexdplatform.com/plugin-docs/reference/scenegraph.html#groupaddchildnode-index

but something (edit context?) is preventing me from executing that method. It’s the only practical way I see besides managing a long array of all the objects and groups I created (which is impractical because I cannot assume that all elements have been created, since they depend on other conditions)

As for the order of creating elements, would expanding on the above suffice, or do you need to add more elements after the mask is initially created?

yes — this is my original question

I chatted offline with @ashryan — and this is really only about the edit context. Now that I understand it better, there’s not a way to add a new node into a group that the script created, because the content of that group are off limits.

Without the ability to change the edit context programmatically, my only option is to create the mask/group at the very end of running the script

2 Likes