Get the untransformed bounds of an object

Is there a method to get the untransformed bounds of an object?

For example, if I have a rectangle and it is at 10 x 10 and size is 50 x 100 I can get the bounds with:

var bounds = sceneNode.globalBounds; // {x:10, y:10, width: 50, height: 100}

Then when I change the rotation by 45’ the bounds are :-18.03 x 6.97

var bounds = sceneNode.globalBounds; // {x:10, y:10, width: 50, height: 100}
sceneNode.rotateAround(45, sceneNode.localCenterPoint);
// Update to original post: the next line has a typo in the size, corrected line after 
// var bounds = sceneNode.globalBounds; // {x:-18.03, y:6.97, width: 50, height: 100}
var bounds = sceneNode.globalBounds; // {x:-18.03, y:6.97, width: 106.1, height: 106.1}
sceneNode.rotateAround(-sceneNode.rotation, sceneNode.localCenterPoint);

Is it possible to get the original unrotated bounds? When I manually rotate the scene node back it works but if it is outside of the edit context I get an error:

Plugin Error: Plugin abc is not permitted to make changes from the background. Return a Promise to continue execution asynchronously.
    at convertPluginErrorToString (plugins/PluginErrorUtil.js:1:198)
    at internalFormatPluginError (plugins/PluginErrorUtil.js:1:503)
    at internalReportPluginError (plugins/PluginErrorUtil.js:1:610)
    at Object.reportPluginError (plugins/PluginErrorUtil.js:1:1015)
    at Object.checkAllowedToEdit (plugins/ScenegraphGuard.js:1:1097)
    at Line.<anonymous> (plugins/ScenegraphWrappers.js:1:2399)
    at exportLine (/Users/bob/Library/Application Support/Adobe/Adobe XD CC/develop/myplugin/main.js:100:8)

Is there a way to get the untransformed bounds:

var untransformedBounds = sceneNode.getUntransformedBounds();

@Velara There is no direct API to get the untransformed bounds at the moment. As a workaround, could you save the original bounds to a variable before transforming the objects?

Unfortunately, the objects are already transformed.

Is there enough information currently in the API to get the untransformed bounds?

Wouldn’t the untransformed bounds always be (0,0) - (sceneNode.width, sceneNode.height)? The offset (x, y) is a transformation itself (from 0,0).

That said, I don’t think every sceneNode has a width and height property. Ellipse, for example, just has X and Y radius properties.

Also, what’s the usecase here? Maybe that would help shed some light on what you’re trying to accomplish.

I’m not sure what you mean. Could you elaborate?

I did just noticed a typo in the bounds size:

In the original example the reported global bounds after rotation is 106 x 106

var bounds = sceneNode.globalBounds; // {x:10, y:10, width: 50, height: 100}
sceneNode.rotateAround(45, sceneNode.localCenterPoint);
var bounds = sceneNode.globalBounds; // {x:-18.03, y:6.97, width: 106.1, height: 106.1}
sceneNode.rotateAround(-sceneNode.rotation, sceneNode.localCenterPoint);

You can repeat the steps here:

  1. Add rectangle to stage at 10x10 with dimensions of 50x100
  2. Rotate 45
  3. Select rectange and group

The bounds are -18.03 x 6.97 and size is 106.1 x106.1.

I know that Lines do not have a height or width sometimes as reported by sceneNode.globalBounds but they do have a height or width with sceneNode.globalDrawBounds.

I’ve noticed an minor inconsistency in the UI slightly based around this post.

In the UI you have the width, height, x, y and rotation.

If you change the rotation the x and y are changed but the width and height do not.

If you check the global bounds of the object it contains the transformed width and height and the transformed x and y.

So the UI reflects the transformed x and y values but not the transformed width and height.

The code for the above information:

var localX = selectedItem.parent ? selectedItem.globalBounds.x - selectedItem.parent.globalBounds.x : 0;
var localY = selectedItem.parent ? selectedItem.globalBounds.y - selectedItem.parent.globalBounds.y : 0;
addRow("Position in Group", "(sceneNode.globalBounds.x - sceneNode.parent.globalBounds.x)", {x: localX, y: localY});
addRow("Bounds in Parent", "(sceneNode.boundsInParent)", selectedItem.boundsInParent);
addRow("Top Left in Parent", "(sceneNode.topLeftInParent)", selectedItem.topLeftInParent);
addRow("Global Bounds", "(sceneNode.globalBounds)", selectedItem.globalBounds);
addRow("Global Draw Bounds", "(sceneNode.globalDrawBounds)", selectedItem.globalDrawBounds);

@peterflynn Thoughts here? You’re probably most familiar with how the coordinate spaces work with the API, so any thoughts are most appreciated. :slight_smile:

I think what you’re looking for is localBounds, no?

The localBounds doesn’t seem to have the original dimensions (maybe width and height?).

The original unrotated rectangle values are:

left: 27
top: 30
width: 100
height: 100
localBounds.width: 100
localBounds.height: 100

When it’s rotated 40 degrees the values are:

left: 6.56
top: 9.56
width: 140.8
height: 140.8
transform: matrix(0.766,0.6428,-0.6428,0.766,70.8372,9.5584)
rotation: 40
localBounds.width: 100
localBounds.height: 100

If I can get the unrotated top and left values (27x30) that might solve the issues I’m running into.

PS I’ve attempted to unrotate the object to get the untransformed bounds but when I’m running my plugin process if I attempt to rotate the node back to 0 degrees I get the error mentioned above, “Plugin abc123 is not permitted to make changes from the background.”

I don’t have any quick test for this, but I wonder if

  node.transform.transformPoint(node.topLeftInParent)

wouldn’t do the trick?

That’s by analogy with this routine I’m using (successfully):

const centerNodeOnMaybeAlign = (newNode, node, align) => {
  const nodeCenterInParent = node.transform.transformPoint(node.localCenterPoint)
  const newNodeCenterInParent = newNode.transform.transformPoint(newNode.localCenterPoint)
  newNode.placeInParentCoordinates(newNodeCenterInParent, nodeCenterInParent)
  if (align && newNode.rotation != node.rotation)
    newNode.rotateAround(node.rotation - newNode.rotation, {
      x: newNode.localCenterPoint.x,
      y: newNode.localCenterPoint.y,
    })
}

with thanks (as usual) to @kerrishotts figuring this out originally.

Update: After testing, no, that doesn’t work.

1 Like

@cpryland The transform object has some methods on it that looks like it has what I need :smile:

It looks like this is returning the correct top and left:

var transform = item.transform.rotate(-item.rotation, item.localCenterPoint.x, item.localCenterPoint.y);

returns:

{ a: 0.9999998902960183,
b: -3.031767398242735e-8,
c: 3.031767398242735e-8,
d: 0.9999998902960183,
e: 27.00000528061757,
f: 29.999994245688796 }

The e and f are the same as the original untransformed x and y

Was just going to say the same after some experimentation.

(The e and f components are the translation part of the matrix, for bystanders.)

And if you look at that matrix, and round it it’s

{a: 1, b: 0, c: 0, d: 1, e: 27, f: 30 }