PEN

02 February 2009, 02:15 PM

Any one know any good reference for how the math for the bend modifier works?

View Full Version : Math for a bend

PEN 02 February 2009, 02:15 PM Any one know any good reference for how the math for the bend modifier works? |

MatanH

02 February 2009, 02:35 PM

I think you should look into rotation transformation which is a kind of a linear transformation

http://www.kwon3d.com/theory/transform/rot.html

http://mathworld.wolfram.com/RotationMatrix.html

http://www.kwon3d.com/theory/transform/rot.html

http://mathworld.wolfram.com/RotationMatrix.html

PEN

02 February 2009, 03:11 PM

Thanks Matan, I have been trying this already but what I get it s spiral. If you consider the pivot of an object at the end of it and you bend the the object the last vert will end up back at the pivot. If you just rotate it, it circles the pivot. I'll keep playing with it how ever when I get a chance.

Thanks for the suggestions.

Thanks for the suggestions.

LoneRobot

02 February 2009, 03:17 PM

now there's a couple of pages of pain ;)

PEN

02 February 2009, 03:44 PM

It is actually easy in Max script as you can just use rotateX matrix and it will take care of all of it for you.

ZeBoxx2

02 February 2009, 04:39 PM

the bend modifier's source code seems to be part of the SDK - might be able to glean it from there :)

harefort

02 February 2009, 04:43 PM

I don't have a reference, but basically you'd have to do something like this:

-First sort the vertices along the bend-axis. (so you can travel along later)

-For each vertex in mesh, find closest point on bend-axis. This point is the vertex's pivot.

-Then travel along the bend-axis and rotate all remaining vertices and pivots by bend-angle/vertex-count around the current pivot.

Does that make sense?

-First sort the vertices along the bend-axis. (so you can travel along later)

-For each vertex in mesh, find closest point on bend-axis. This point is the vertex's pivot.

-Then travel along the bend-axis and rotate all remaining vertices and pivots by bend-angle/vertex-count around the current pivot.

Does that make sense?

PEN

02 February 2009, 05:07 PM

yes it does make sense but not sure how to go about that. I'll play around, nothing major I'm doing, just want to know how. Richard I will have a look in the SDK.

MatanH

02 February 2009, 08:47 PM

Hi Paul,

I found special interest in your problem since it requires me to implement

my current study subject which is linear transformations and matrices.

So I took some extra time looking into this problem and I think the key here

is to find the pivot of rotation.

The data you would probably need is:

* alpha = The angle of bend

* vertPos = the position of the vertex in local space

* center = The center of the bend modifier in local space

* maxDist = The distance along the axis that you would like to bend between the center of the bend modifier and the max point of the bounding box

* vertDist = The distance of the vertex from the center along the bend axis

Now you need to calculate a couple of things:

The rotation angle for the current vertex

beta = alpha * vertDist / maxDist

The radius of the rotation circle

rad = vertDist / beta in radians

The pivot of rotation (assuming we are trying to bend the z axis on the x,z plane)

piv = center + [beta / abs(beta) * rad,0,0]

The rotation matrix (assuming we are trying to bend the z axis on the x,z plane)

rotMatrix = matrix3 [cos(beta), 0, -sin(beta)] [0, 1, 0] [sin(beta), 0, cos(beta)] piv

The new position of the current vertex

newPos = vertPos * rotMatrix

I didn't try to implement this so I might have some mistakes along the way but this should be the way

Matan.

I found special interest in your problem since it requires me to implement

my current study subject which is linear transformations and matrices.

So I took some extra time looking into this problem and I think the key here

is to find the pivot of rotation.

The data you would probably need is:

* alpha = The angle of bend

* vertPos = the position of the vertex in local space

* center = The center of the bend modifier in local space

* maxDist = The distance along the axis that you would like to bend between the center of the bend modifier and the max point of the bounding box

* vertDist = The distance of the vertex from the center along the bend axis

Now you need to calculate a couple of things:

The rotation angle for the current vertex

beta = alpha * vertDist / maxDist

The radius of the rotation circle

rad = vertDist / beta in radians

The pivot of rotation (assuming we are trying to bend the z axis on the x,z plane)

piv = center + [beta / abs(beta) * rad,0,0]

The rotation matrix (assuming we are trying to bend the z axis on the x,z plane)

rotMatrix = matrix3 [cos(beta), 0, -sin(beta)] [0, 1, 0] [sin(beta), 0, cos(beta)] piv

The new position of the current vertex

newPos = vertPos * rotMatrix

I didn't try to implement this so I might have some mistakes along the way but this should be the way

Matan.

Bobo

02 February 2009, 09:16 PM

yes it does make sense but not sure how to go about that. I'll play around, nothing major I'm doing, just want to know how. Richard I will have a look in the SDK.

Here is a SIMPLE implementation (from top of my head without looking at the SDK)

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

)

on map i p do

(

if amount != 0 then

(

theRadius = 180/amount*extent.z/Pi

TM = rotateYMatrix (amount*p.z/extent.z)

TM.row4 = [theRadius,0,0]

[p.x-theRadius,p.y,0] * TM

)

else p

)

)

Here is the logic.

p is the position of the vertex in object space.

Let's say you have a cylinder. If you apply a Bend with 360 degrees, the TOP vertex (with Z equal to the height of the modifier context bounding box) will do a full rotation about a center outside of the cylinder and end up at the vertex with the same X and Y, but at the BOTTOM of the bounding box. The question is - WHERE is this center of rotation outside of the object that you have to rotate about?

The answer is surprisingly easy - if you have a line with a length L and you want to bend it to 360 degrees as described above, we have L = 2*Pi*R. We need to know what R is, and it is L/2/Pi. In other words, for 180 degrees, the Radius is L/Pi, for 90 degrees it will be 2*L/Pi and so on, growing as the angle gets smaller.

The amount parameter defines the actual angle we want to rotate at. So we divide 180 by the amount and multiply by the L (which is the Z component of the modifier bounding box's extent) divided by Pi. The result is the actual Radius we want to rotate with, which gives us the center of rotation that we need for all vertices. In other words, we used the top point of the modifier context to figure out the center of rotation, now we can apply a different rotation to each vertex based on its normalized position within the Z of the bounding box. A vertex at the base will not rotate at all, a vertex at the top will do the full rotation, a vertex at half the height will do half of the rotation.

So we create a Rotation matrix to rotate about the Y axis and use the normalized height of the vertex (p.z/extent.z) to multiply the amount in degrees. Once again, this gives us 0 degrees for a vertex with Z = 0 and 'amount' degrees for a vertex with p.z = Extents.z

Then we shift the transformation matrix' row4 to the center of rotation.

We take the projection of the vertex P in the XY plane and shift it along X by the radius so that the vector we will be rotating is defined by the new rotation center and the vertex's projection on the XY plane. The Y remains the same and the Z is 0. We multiply this ground plane projection of the vertex by the rotation angle and the vertex ends up on an arc with center the point we calculated in the beginning.

This modifier does not implement the Direction yet, it just rotates about the Y axis, but it shows the basic idea...

Since we have a division of 180 by the amount, we have to make sure we never perform this when the angle is 0. Thus the IF statement which returns the undeformed position if 0.

Here is a SIMPLE implementation (from top of my head without looking at the SDK)

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

)

on map i p do

(

if amount != 0 then

(

theRadius = 180/amount*extent.z/Pi

TM = rotateYMatrix (amount*p.z/extent.z)

TM.row4 = [theRadius,0,0]

[p.x-theRadius,p.y,0] * TM

)

else p

)

)

Here is the logic.

p is the position of the vertex in object space.

Let's say you have a cylinder. If you apply a Bend with 360 degrees, the TOP vertex (with Z equal to the height of the modifier context bounding box) will do a full rotation about a center outside of the cylinder and end up at the vertex with the same X and Y, but at the BOTTOM of the bounding box. The question is - WHERE is this center of rotation outside of the object that you have to rotate about?

The answer is surprisingly easy - if you have a line with a length L and you want to bend it to 360 degrees as described above, we have L = 2*Pi*R. We need to know what R is, and it is L/2/Pi. In other words, for 180 degrees, the Radius is L/Pi, for 90 degrees it will be 2*L/Pi and so on, growing as the angle gets smaller.

The amount parameter defines the actual angle we want to rotate at. So we divide 180 by the amount and multiply by the L (which is the Z component of the modifier bounding box's extent) divided by Pi. The result is the actual Radius we want to rotate with, which gives us the center of rotation that we need for all vertices. In other words, we used the top point of the modifier context to figure out the center of rotation, now we can apply a different rotation to each vertex based on its normalized position within the Z of the bounding box. A vertex at the base will not rotate at all, a vertex at the top will do the full rotation, a vertex at half the height will do half of the rotation.

So we create a Rotation matrix to rotate about the Y axis and use the normalized height of the vertex (p.z/extent.z) to multiply the amount in degrees. Once again, this gives us 0 degrees for a vertex with Z = 0 and 'amount' degrees for a vertex with p.z = Extents.z

Then we shift the transformation matrix' row4 to the center of rotation.

We take the projection of the vertex P in the XY plane and shift it along X by the radius so that the vector we will be rotating is defined by the new rotation center and the vertex's projection on the XY plane. The Y remains the same and the Z is 0. We multiply this ground plane projection of the vertex by the rotation angle and the vertex ends up on an arc with center the point we calculated in the beginning.

This modifier does not implement the Direction yet, it just rotates about the Y axis, but it shows the basic idea...

Since we have a division of 180 by the amount, we have to make sure we never perform this when the angle is 0. Thus the IF statement which returns the undeformed position if 0.

Bobo

02 February 2009, 09:28 PM

Edit: Here is the correct Direction version:

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

direction type:#float ui:spn_direction default:0

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1

)

on map i p do

(

if amount != 0 then

(

theDirMatrix = rotateZMatrix -direction

theRadius = 180/amount*extent.z/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM

)

else p

)

)

The main difference is that instead of using the positive X axis as is to project where the center of rotation will be, we take that X axis and rotate about the Z. Then we transform the previous (bend) rotation matrix by this new (direction) matrix, use the offset radius as the row4 and also use the transformed vector to offset the vertex position, then transform out of the direction matrix and bend using the bend matrix. In short, we generalize the previous X-only center by placing it around at the angle specified by the Direction parameter.

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

direction type:#float ui:spn_direction default:0

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1

)

on map i p do

(

if amount != 0 then

(

theDirMatrix = rotateZMatrix -direction

theRadius = 180/amount*extent.z/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM

)

else p

)

)

The main difference is that instead of using the positive X axis as is to project where the center of rotation will be, we take that X axis and rotate about the Z. Then we transform the previous (bend) rotation matrix by this new (direction) matrix, use the offset radius as the row4 and also use the transformed vector to offset the vertex position, then transform out of the direction matrix and bend using the bend matrix. In short, we generalize the previous X-only center by placing it around at the angle specified by the Direction parameter.

Bobo

02 February 2009, 11:06 PM

And here is one with Axis control:

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

direction type:#float ui:spn_direction default:0

axis type:#integer ui:rad_axis default:3

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1

radiobuttons rad_axis "Axis:" labels:#("X","Y","Z")

)

on map i p do

(

if amount != 0 then

(

case axis of

(

1: (

theDirMatrix = rotateXMatrix -direction

theRadius = 180/amount*extent.x/Pi

theRadiusOffset = [0,0,-theRadius] * theDirMatrix

TM = rotateYMatrix (amount*p.x/extent.x) * theDirMatrix

TM.row4 = theRadiusOffset

(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM

)

2: (

theDirMatrix = rotateYMatrix -direction

theRadius = 180/amount*extent.y/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateZMatrix (-amount*p.y/extent.y) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM

)

default: (

theDirMatrix = rotateZMatrix -direction

theRadius = 180/amount*extent.z/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM

)

)

)

else p

)

)

Same principle, just the axes and planes in which the calculations are performed are switched based on the radio buttons. This could be optimized to express in shorter form with less redundant code, but I might do that later.

plugin simpleMod MyBend

name:"MyBend"

classID:#(0x33635bb3, 0x5db238c1)

version:1

(

parameters main rollout:params

(

amount type:#float ui:spn_amount default:0

direction type:#float ui:spn_direction default:0

axis type:#integer ui:rad_axis default:3

)

rollout params "MyBend Parameters"

(

spinner spn_amount "Amount: " range:[-100000,100000,0] scale:1

spinner spn_direction "Direction: " range:[-100000,100000,0] scale:1

radiobuttons rad_axis "Axis:" labels:#("X","Y","Z")

)

on map i p do

(

if amount != 0 then

(

case axis of

(

1: (

theDirMatrix = rotateXMatrix -direction

theRadius = 180/amount*extent.x/Pi

theRadiusOffset = [0,0,-theRadius] * theDirMatrix

TM = rotateYMatrix (amount*p.x/extent.x) * theDirMatrix

TM.row4 = theRadiusOffset

(([0,p.y,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM

)

2: (

theDirMatrix = rotateYMatrix -direction

theRadius = 180/amount*extent.y/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateZMatrix (-amount*p.y/extent.y) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,0,p.z]-theRadiusOffset)*inverse theDirMatrix) * TM

)

default: (

theDirMatrix = rotateZMatrix -direction

theRadius = 180/amount*extent.z/Pi

theRadiusOffset = [theRadius,0,0] * theDirMatrix

TM = rotateYMatrix (amount*p.z/extent.z) * theDirMatrix

TM.row4 = theRadiusOffset

(([p.x,p.y,0]-theRadiusOffset)*inverse theDirMatrix) * TM

)

)

)

else p

)

)

Same principle, just the axes and planes in which the calculations are performed are switched based on the radio buttons. This could be optimized to express in shorter form with less redundant code, but I might do that later.

PEN

02 February 2009, 01:30 AM

That is great Bobo, thanks a million and thanks to every one else as well. I need to sit down and play with this now so that I get a full handle on what it is doing. I think I get the idea of how you are calculating it but it hasn't fully sunk in. Looks like you are having fun with it as well Bobo. I now have a sort of do it all modifier that does twist, taper, skew, wave, offsets, scaling and a other fun stuff. Looks like scripted modifiers that transform verts are now stable, they used to crash all the time. Speed is a huge problem how ever especialy when the modifier is active in the modifier stack. Once it is closed it isn't to bad but not great. I really need to learn C++.

PEN

02 February 2009, 02:38 AM

It always works this way, I mention that it is stable and what happens next:S I start getting garbage collections errors. If I had p=[0,0,0] in the map handler it would throw them if I do p.x=0;p.y=0;p.z=0 it is fine. Very odd.

CGTalk Moderation

02 February 2009, 02:38 AM

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.