Insert node to the center of the user's view

Hi there,

I’m creating a content plugin that displays various images.

If the user has selected a shape, I’m filling the shape with ImageFill, but if no node is selected I’m creating one.

The problem is that if I’m creating a node, the shape is added at the center of the document and the user will have to scroll there to get it (not solved with viewport.scrollToCenter(0, 0); either).

Is there a way to insert the node exactly where the user is looking at? Here’s what I have:

else {
        let node = new Rectangle();

        node.height = imageFill.naturalHeight;
        node.width = imageFill.naturalWidth;
        node.strokeEnabled = false;
        node.fill = imageFill;

        //Trying to detect the current view here 
        let viewportHeight =
          (viewport.bounds.height * viewport.zoomFactor) / 2;
        let viewportWidth =
          (viewport.bounds.width * viewport.zoomFactor) / 2;

        let parentCenter = { x: viewportHeight, y: viewportWidth };

        let nodeBounds = node.localBounds; // node's bounds in its own local coordinates
        let nodeTopLeft = { x: nodeBounds.x / 2, y: nodeBounds.y / 2 }; // node's top left corner in its own local coordinates

        selection.insertionParent.addChild(node);
        node.placeInParentCoordinates(nodeTopLeft, parentCenter);
      }

This still adds the node at the center of the document, if someone has figured this out, I’d appreciate some help.

Thank you very much!

The viewport is expressed in global coordinates, which is often a different coordinate space from the parent you’re inserting the node into.

If you want to handle the full range of cases possible in XD, including nested groups and groups that might be rotated, converting between these coordinate spaces is a little tricky. You’ll need to walk from the insertionParent up to the scenegraph root and accumulate together each node’s transform matrix. At the end, you’ll have a matrix which represents the conversion from the parent’s coordinates to global coordinates. Take the inverse of this matrix (invert()) and call transformPoint() on it to convert from global coordinates back to the parent’s coordinates – that will give you the point you want to use with placeInParentCoordinates().

A couple other things to watch out for:

  • No need to multiply by the zoomFactor. Viewport width/height in document coordinates already takes that into account.
  • Your nodeTopLeft shouldn’t be dividing by 2. The top left is just (localBounds.x, localBounds.y) directly. If you want to exactly center it in the viewport though, use node.localCenterPoint instead.
2 Likes

Do you mean that to evaluate the users’ current view I have to:

  1. Loop through each node present in the scenegraph.
  2. Add their transform matrix together.
  3. invert() and call transformPoint() on it.

And then use that point with placeInParentCoordinates()?

This doesn’t look trivial, my scope is really limited to insert a node where the user is looking at.