SceneNode.moveTo method relative to parent node

Add moveTo(parentObject, x, y) API.

Currently the method selectedItem.moveInParentCoordinates() accepts delta values.

So if you want to move an object to 10x10 and it’s positioned at 2938.20 x 2780 you have to do something like:

selectedItem.moveInParentCoordinates(-object.boundsInParent.x+10, -object.boundsInParent.y+10);

to move it by -2928.20 x -2770 pixels.

A moveTo method could be created to set the x and y to a value relative to the parent supplied:

// move it to 10 x 10 in it's parent group
selectedItem.moveTo(bounds.parent, 10, 10);

// move it to 10 x 10 in it's parent artboard
var artboard = getArtboard(selectedItem);
selectedItem.moveTo(artboard, 10, 10);

It’s a bit verbose, but does placeInParentCoordinates do the trick?

@peterflynn Thoughts here on convenience functions to make this a bit easier?

1 Like

placeInParentCoordinates() does exactly what you’re looking for, just not with the name you were looking for :slight_smile:

You’ll note that it requires you to specify two points though, not just one: a point in the parent, plus which point in the child you want to move to that location. Because the child node can have rotation, we want the plugin author to explicitly tell us which part of the child node should align with that location in the parent. Is it the center? Its local bounds origin? Its local bounds top left? The top left of its axis-aligned bounding box in the parent’s coordinates? Etc. XD can’t guess this for you, so it’s important to make the plugin explicitly indicate the intent here.

The question title says, “SceneNode.moveTo method relative to parent node” and it is about that but also it could have been written, “SceneNode.moveTo method relative to a parent node”:

You could pass in another node and it would calculate the position relative to the node supplied.

So you would say I want to position this rectangle at 10 x 10 in the artboard it is in and it’s the 10th artboard. You would write, element.moveTo(10, 10, element.artboard).

Or if it is grouped and it is nested in 10 groups you would write, element.moveTo(10, 10, element.parent) and it would be at 10 x 10 in the direct parent not the artboard.

If you left the third parameter out it would be positioned in the direct parent, not parent artboard, of the element.

Here is what I’m using now that positions an element in it’s direct parent. It uses the top left x and y position as the registration point.

function moveTo(element, x, y) {
    var bounds = getBoundsInParent(element);
    element.moveInParentCoordinates(-bounds.x+x, -bounds.y+y);
}

function getArtboard(item) {

    if (item instanceof Artboard) return item;
    var parent = item.parent;
    while (parent!=null) {
        if (parent instanceof Artboard) return parent;
        parent = parent.parent;
    }
}

function getBoundsInParent(item) {
    var bounds = {};
    var x = 0;
    var y = 0;
    var parentX = 0;
    var parentY = 0;
    var parentWidth= 0;
    var parentHeight = 0;
    var width= 0;
    var height = 0;
    var offsetX = 0;
    var offsetY = 0;
    var centerX = 0;
    var centerY = 0;
    var centerDeltaX = 0;
    var centerDeltaY = 0;
    var globalCenterX = 0;
    var globalCenterY = 0;
    var globalDeltaX = 0;
    var globalDeltaY = 0;
    var isLine = getType(item)==item.Line; // or use item instanceof Line
    var sizeAdjusted = false;
    var artboard = null;
    var xInArtboard = 0;
    var yInArtboard = 0;
    var parentXInArtboard = 0;
    var parentYInArtboard = 0;
    var parent = null;

    if (item.parent) {
        artboard = getArtboard(item);
        parent = item.parent;

        x = item.globalBounds.x;
        y = item.globalBounds.y;
        width = item.globalBounds.width;
        height = item.globalBounds.height;
        xInArtboard = artboard ? item.globalBounds.x - artboard.globalBounds.x : 0;
        yInArtboard = artboard ? item.globalBounds.y - artboard.globalBounds.y : 0;

        parentXInArtboard = artboard ? parent.globalBounds.x - artboard.globalBounds.x : 0;
        parentYInArtboard = artboard ? parent.globalBounds.y - artboard.globalBounds.y : 0;

        if (isLine && width==0) {
            width = item.strokeWidth;
            sizeAdjusted = true;
        }
        
        if (isLine && height==0) {
            height = item.strokeHeight;
            sizeAdjusted = true;
        }

        parentX = parent.globalBounds.x;
        parentY = parent.globalBounds.y;
        parentWidth = parent.globalBounds.width;
        parentHeight = parent.globalBounds.height;

        // center cartisian position
        centerX = parentWidth/2 - width/2;
        centerY = parentHeight/2 - height/2;

        offsetX = x - parentX;
        offsetY = y - parentY;
        
        globalCenterX = parentX + centerX;
        globalCenterY = parentY + centerY;
        
        centerDeltaX = centerX - offsetX;
        centerDeltaY = centerY - offsetY;
        
        globalDeltaX = x + centerDeltaX;
        globalDeltaY = y - centerDeltaY;

        bounds.xInArtboard = xInArtboard;
        bounds.yInArtboard = yInArtboard;

        bounds.parentXInArtboard = parentXInArtboard;
        bounds.parentYInArtboard = parentYInArtboard;

        bounds.x = offsetX;
        bounds.y = offsetY;

        bounds.globalX = item.globalBounds.x;
        bounds.globalY = item.globalBounds.y;

        bounds.xInGroup = offsetX;
        bounds.yInGroup = offsetY;
        
        bounds.centerX = centerX;
        bounds.centerY = centerY;
        
        bounds.width = item.globalBounds.width;
        bounds.height = item.globalBounds.height;

        bounds.centerDeltaX = centerDeltaX;
        bounds.centerDeltaY = centerDeltaY;

        bounds.globalDeltaX = globalDeltaX;
        bounds.globalDeltaY = globalDeltaY;

        bounds.globalCenterX = globalCenterX;
        bounds.globalCenterY = globalCenterY;

        bounds.sizeAdjusted = sizeAdjusted;

        bounds.computedCenterX = getCenterPoint(item).x;
        bounds.computedCenterY = getCenterPoint(item).y;

        bounds.parentWidth = parentWidth;
        bounds.parentHeight = parentHeight;

        bounds.parentX = parentX;
        bounds.parentY = parentY;

        bounds.offsetX = offsetX;
        bounds.offsetY = offsetY;
    }

    return bounds;
}


/**
 * 
 * @param {SceneNode} node 
 */
function getType(node) {
	var type = node && node.constructor && node.constructor.name;

	switch(type) {
	 
		case Types.ELLIPSE:
		   break;
		case Types.RECTANGLE:
		   break;
		case Types.PATH:
		   break;
		case Types.LINE:
		   break;
		case Types.TEXT:
		   break;
		case Types.GROUP:
		   break;
		case Types.BOOLEAN_GROUP:
		   break;
		case Types.REPEAT_GRID:
		   break;
		case Types.SYMBOL_INSTANCE:
		   break;
		case Types.ARTBOARD:
		   break;
		default:
	}

	return type;

}

function getCenterPoint(node) {
	return {
		x: node.boundsInParent.x + node.boundsInParent.width/2,
		y: node.boundsInParent.y + node.boundsInParent.height/2
	}
}

The getBoundsInParent adds additional properties most have been validated but if you use this a few values you may want to verify the values are correct.