Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Moho allows users to write new tools and plugins. Discuss scripting ideas and problems here.

Moderators: Víctor Paredes, Belgarath, slowtiger

Post Reply
User avatar
antonyng
Posts: 3
Joined: Fri Jun 02, 2023 7:47 am

Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by antonyng »

Hi all, I try to write a script that create shape that follow the bounds of another layer, I have get this working. But strangely if the layer has both non-zero origin AND non-one scale, the result with be incorrect. But if just either factor is true, the result is also correct. I somehow know that the origin affects the bounds x and y, and no idea how the result should depend on the origin and scale. Can anyone tell me the solution and the relationship of shape bounds, layer scale and translation and origin? Thank you very much!

function FollowBounds:Run(moho)
local document = moho.document

local noTransformLayer = document:LayerByName("No Transform")
noTransformLayer = moho:LayerAsVector(noTransformLayer)

local withTransformLayer = document:LayerByName("With Transform")
withTransformLayer = moho:LayerAsVector(withTransformLayer)

local frameZero = 0

local noTransformLayerBounds = noTransformLayer:Bounds(frameZero)

local noTransformLayerBoundsMinimumX = noTransformLayerBounds.fMin.x
local noTransformLayerBoundsMinimumY = noTransformLayerBounds.fMin.y
local noTransformLayerBoundsMaximumX = noTransformLayerBounds.fMax.x
local noTransformLayerBoundsMaximumY = noTransformLayerBounds.fMax.y

local withTransformLayerBounds = withTransformLayer:Bounds(frameZero)

local withTransformLayerBoundsMinimumX = withTransformLayerBounds.fMin.x
local withTransformLayerBoundsMinimumY = withTransformLayerBounds.fMin.y
local withTransformLayerBoundsMaximumX = withTransformLayerBounds.fMax.x
local withTransformLayerBoundsMaximumY = withTransformLayerBounds.fMax.y

local withTransformLayerScale = withTransformLayer.fScale:GetValue(frameZero)

local withTransformLayerScaleX = withTransformLayerScale.x
local withTransformLayerScaleY = withTransformLayerScale.y

local withTransformLayerTranslation = withTransformLayer.fTranslation:GetValue(frameZero)

local withTransformLayerTranslateX = withTransformLayerTranslation.x
local withTransformLayerTranslateY = withTransformLayerTranslation.y

local withTransformLayerOrigin = withTransformLayer:Origin()

local withTransformLayerOriginX = withTransformLayerOrigin.x
local withTransformLayerOriginY = withTransformLayerOrigin.y

local newShapeLayer = document:LayerByName("New Shape")
newShapeLayer = moho:LayerAsVector(newShapeLayer)


local newShapeLayerOrigin = newShapeLayer:Origin()

local newShapeLayerOriginX = newShapeLayerOrigin.x
local newShapeLayerOriginY = newShapeLayerOrigin.y

local newShapeLayerMesh = newShapeLayer:Mesh()

local pointPosition = LM.Vector2:new_local()

local function setPointPosition(x, y)
pointPosition.x = x
pointPosition.y = y
end


local function addPointToNewShapeLayer(x, y)
setPointPosition(x, y)

newShapeLayerMesh:AddPoint(pointPosition, -1, frameZero)
end

local function appendPointToNewShapeLayer(x, y)
setPointPosition(x, y)

newShapeLayerMesh:AppendPoint(pointPosition, frameZero)
end

addPointToNewShapeLayer(noTransformLayerBoundsMinimumX, noTransformLayerBoundsMaximumY)
appendPointToNewShapeLayer(noTransformLayerBoundsMaximumX, noTransformLayerBoundsMaximumY)
appendPointToNewShapeLayer(noTransformLayerBoundsMaximumX, noTransformLayerBoundsMinimumY)
appendPointToNewShapeLayer(noTransformLayerBoundsMinimumX, noTransformLayerBoundsMinimumY)

addPointToNewShapeLayer(withTransformLayerTranslateX + withTransformLayerBoundsMinimumX * withTransformLayerScaleX, withTransformLayerTranslateY + withTransformLayerBoundsMaximumY * withTransformLayerScaleY)

appendPointToNewShapeLayer(withTransformLayerTranslateX + withTransformLayerBoundsMaximumX * withTransformLayerScaleX, withTransformLayerTranslateY + withTransformLayerBoundsMaximumY * withTransformLayerScaleY)

appendPointToNewShapeLayer(withTransformLayerTranslateX + withTransformLayerBoundsMaximumX * withTransformLayerScaleX, withTransformLayerTranslateY + withTransformLayerBoundsMinimumY * withTransformLayerScaleY)

appendPointToNewShapeLayer(withTransformLayerTranslateX + withTransformLayerBoundsMinimumX * withTransformLayerScaleX, withTransformLayerTranslateY + withTransformLayerBoundsMinimumY * withTransformLayerScaleY)
end


Image
User avatar
antonyng
Posts: 3
Joined: Fri Jun 02, 2023 7:47 am

Re: Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by antonyng »

The green shape is in no transform layer, and red shape is in with transform layer, you can see the points follow red shape layer shape is wrong.

Image
User avatar
synthsin75
Posts: 9979
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by synthsin75 »

The layer origin offsets the coordinate system, with the origin always at 0,0. So you'd need to subtract the origin offset between the controlled layer and the bounding layer.

For layer scale, you'd need to do a matrix transformation.
User avatar
antonyng
Posts: 3
Joined: Fri Jun 02, 2023 7:47 am

Re: Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by antonyng »

Thank you for your reply. But what confused me is I don't understand why I don't need to consider origin at all if the scale x/y is reset to 1, but it becomes incorrect once both origin and scale x/y is altered?
synthsin75 wrote: Sat Jun 03, 2023 3:33 pm The layer origin offsets the coordinate system, with the origin always at 0,0. So you'd need to subtract the origin offset between the controlled layer and the bounding layer.

For layer scale, you'd need to do a matrix transformation.
User avatar
avv
Posts: 2
Joined: Thu Aug 03, 2023 1:49 pm

Re: Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by avv »

antonyng wrote: Mon Jun 12, 2023 8:53 am Thank you for your reply. But what confused me is I don't understand why I don't need to consider origin at all if the scale x/y is reset to 1, but it becomes incorrect once both origin and scale x/y is altered?
I'm confused by this as well. My observation is that Moho subtracts the layer's transform first by the origin * scale, then adds origin back after the rotation. If scale = 1, then these two terms cancel out, thus leaving it unaffected. I have code here that transforms layer bounds and works for image layers but not for group layers, unsure why.
Either way, it's really confusing. LostMarble should address this in their documentation.
User avatar
synthsin75
Posts: 9979
Joined: Mon Jan 14, 2008 11:20 pm
Location: Oklahoma
Contact:

Re: Make a shape follow any existing shape bounds not work when source layer has non-zero origin AND non-one scale

Post by synthsin75 »

It has to do with transform matrix order of operations: https://learn.microsoft.com/en-us/dotne ... esktop-4.8
One reason order is significant is that transformations like rotation and scaling are done with respect to the origin of the coordinate system. Scaling an object that is centered at the origin produces a different result than scaling an object that has been moved away from the origin. Similarly, rotating an object that is centered at the origin produces a different result than rotating an object that has been moved away from the origin.
Post Reply