# Points, Groups and Ramps... a tragedy in 3(d) acts!

## Recommended Posts

Hi guys!

It all started with me trying to understand how to change a point position in a line (for example) with values from a Ramp float.

I started to place a line, used a Null to "mimic" a master Interface, with a slider set to control the number of points in the line and in the null placed the Ramp float.

At this point my point problems started (pun... this is despair talking...). I have to address to each point in order to transform it. But how can I do that?

I thought about groups... But how can I create a group_0 for point 0, a group_1 for point 1 and so on?

And on the transform, how can I address them? Ok, ok... in "group" I could write something like group_`stamp("../jddjdj","groupNumber",0)` to change the group number...

But... How to write the stamp value? I can't address the points by \$PT (and I tried it... more then once, to see if Houdini was distracted!)

So I decided to start again, this time with the Add SOP.

I generated the points with a copy (ch() referenced the Null - Number of Points) and created a stamp()->\$CY to increment the points position in Z. Then I wanted to change the points position and... it all started again!

If I use the Xform above the copy I can use the stamp() function to create a rule based on the \$CY or \$NCY and run the points... except that there aren't any! They're created below... But if I move the Xform, I lose the stamp() hability...

So, full circle, back to: How can I group or address to each point in turn?

Finally... after being able to move them... how can I use the Ramp?

When I used the chramp() I've noticed that for each point the ramp generates a rampLineX (being x the number of teh point)position; rampLineX Value and rampLineX interp...

Sorry for this long email. But I had to explain my doubts but above all show my line of though. The error may be there!

I'll attach my file... pay no attention to the expressions in some places... I was desperate! Ramp_and_LineControl.hipnc

##### Share on other sites

Hi guys!

It all started with me trying to understand how to change a point position in a line (for example) with values from a Ramp float.

There are lots of ways to achieve this. The attached shows you two methods, one using SOPs (no copy-stamping involved), and the other one using VOPs (vex). They both provide the same controls as the Line SOP, but they also add the ability to shape the line with a ramp, as well as twist and roll the resulting shape. The VOP method will be more efficient when processing lots of points.

Ramp_and_LineControl_mgm.hipnc

HTH,

Cheers!

##### Share on other sites

Thanks Mario!

Your example is amazing. Truth be told lot's of that stuff are still out of my league, but I can always look them up and try to understand... and I also got an insight on organization, labeling and so on...

Just two questions more, if possible...

- When you create the attribute u, you gave in a value of \$PT/max(1, \$NPT-1). This will be 100 (points) / 99, right? So the attribute will have a value of 1 or this will be calculated in a per point base? First point will be 1/99, second will be 2/99 and so on..

- How could I create a group per point?

Thanks again for all your help!

##### Share on other sites

To get a stronger understanding of expressions in Houdini, first go to the help and check out the Expression Cookbook. It takes you through a tour of expressions modifying grids.

Another fantastic resource is the Point SOP's help card. All nodes have specific help (if not a bug and please submit). Many have specific example files that contain copious operator comment notes. Nodes with comments have a blue label instead of the default grey/black. Just Middle Mouse hold on a node to read the note.

So have a look at the many direct examples of the Point SOP. Remember that as soon as you type in a variable, that SOP will now work looping through all the points, each one in turn.

- When you create the attribute u, you gave in a value of \$PT/max(1, \$NPT-1). This will be 100 (points) / 99, right? So the attribute will have a value of 1 or this will be calculated in a per point base? First point will be 1/99, second will be 2/99 and so on..

I like to read through the expressions. So \$PT/max(1, \$NPT-1) would go something like this:

Take the current point number and divide this by the maximum of 1 or the total number of points.

Because point numbers start at 0 and not 1, \$NPT has to be subtracted by one in order to index properly. That's why you have the \$NPT-1. Just housekeeping.

Again I put copious notes like this on pretty much all the nodes in the help cards to help new users who aren't programmers to get in to the beauty and simplicity of expressions. Not hard. Just takes a few hours then the a-ha moment happens and a whole new world opens up.

- How could I create a group per point?

To do this you need to use the Partition SOP. Set it to points. In the expression field use pointgroup_\$PT. This will create as many groups as there are points called pointgroup_1, pointgroup_2, ... , pointgroup_n-1, pointgroup_n.

I bet that's not what you want though as it is difficult for novices to loop through point groups to do stuff as it is not a very efficient way to process points in Houdini. Again go through the Point SOP help card and scroll to the bottom and study all the example files and read all the comments on the nodes. Also take the tour through the Expression Cookbook in the Houdini help.

##### Share on other sites

I am in the process of going through the Expression cookbook (I have one from Houdini 6 that was on the web and the new one... The old one is more slow paced and has more examples of certain things...)

About the expression reading, your answer enlightened me. Not because the \$NPT -1 (I do have some basis of programming and Houdini reads arguments almost like... lists/arrays, with index beginning at 0)but what I was confused was about 2 things (one is pretty stupid I know...):

- cicling through all the points; and the "ha! factor (and slap on the head) \$NPT-1 is equal to 99 (Max returns the larger of 2 values. But it is used because it's all being made procedurally (I know I know... stupid! But this is something that I still have to get used to...)

Thanks for the info about grouping. It may not be what I really wanted (like you said) but you know when you have a doubt and can't walk away from it? Now I can get it of my head! Really a big thank you.

I was thinking about cicling throught the point groups with a stamp expression or something...

Anyway, I'm off to study the Point SOP and see if I can end the Expression cookbook. Thumbs up for both of you!

By the way, one good way to study expressions (at least one that I'm using more and more...) is to use the exhelp in textport

Have a nice Sunday

Edited by J.Santos
##### Share on other sites

Just so there's no lingering doubt as to why the expression \$PT/max(1,\$NPT-1) was written the way it was, here's a detailed explanation:

1. The "parametric u" attribute is meant to run from 0 to 1 (inclusive at both ends) along the line -- this could also be done with one of the UV projection sops, but I chose an expression simply out of habit (again, there are many ways to do any one of these steps).

2. The reason for wanting u to span [0,1] is that we'll be using it to index into the ramp (via the expression function chramp()), which expects the lookup index to be in [0,1]. So this [0,1] parametrization is a requirement for using the ramp this case. (again, I could have achieved this using a uvtexture sop, but I chose an expression instead, so now I have to explain it, but it's not the *only* way to do this -- though if you want to familiarize yourself with expressions, it's a good way to start )

3. The job of the expression \$PT/max(1,\$NPT-1) is to map point numbers to parametric u (i.e: to fulfill the ramp's indexing requirement). As Jeff mentioned (and you already know I think), point numbers (\$PT in the Point SOP) in Houdini start at 0 (VOPs, SOPs, everywhere), but the total number of points (\$NPT in the Point SOP) is just a count, and so starts at 1 (because you have to be able to say: "I have zero points"), so...

4. Recalling that \$PT starts at 0 then, if we go ahead and write \$PT/\$NPT as a first attempt at the point-number-to-[0,1] mapping, and assuming that there are, say, a total of 100 points in the line (\$NPT=100), you'll notice that we end up producing the sequence: 0/100, 1/100, 2/100....99/100 -- that is: 0, 0.01, 0.02....0.99, or more simply, the range [0,0.99]. The beginning of which is exactly what we want (namely, zero), but the end isn't -- we're getting 0.99 where we'd like a 1.

5. To produce a 1 at the end of the sequence, we need both the numerator and denominator to be the same number (x/x=1) for that last fraction of the sequence. For 100 points, \$PT will end at 99 (because the numbering starts at 0); for 50 points it will end at 49, and so on -- in general, the last point number (\$PT) in a sequence of \$NPT points will always be (\$NPT-1).

Therefore, to make that last fraction in the sequence be equal to one (to have both numerator and denominator be the same number), we have to use \$PT/(\$NPT-1). Notice that the first fraction is still zero, as before: 0/99, 1/99....99/99 => [0,1].

6. And now the mapping is exactly what we want... so why then complicate things with a max() expression?

Whenever you have a division in any expression, there is always a possibility that the denominator may take on the value 0, and you may recall that division by zero is undefined in math -- it can't be expressed as a number and so will likely generate some error or possibly unexpected results in whatever context you're using it.

With that in mind, let's now go back to our golden expression: \$PT/(\$NPT-1). That's a division (read: "dangerous"), so, out of good habit, we inspect its denominator: (\$NPT-1) -- can this little sub-expression ever take on the value 0 (and wreck our expression)?

The answer is of course "Yes; whenever the user wants a line with one point" (silly, perhaps, but still possible). In this case, \$NPT=1 and so (\$NPT-1)=0 and so \$PT/(\$NPT-1) is suddenly undefined and the train wreck begins

7. Which brings us to the last bit: the max() function.

We determined that we needed to change the initial denominator of \$NPT to (\$NPT-1) to make the resulting sequence work the way we wanted it to, but It was precisely this modification that introduced the possibility of a division by zero (a "bug"). If you think back to that sequence, you'll see that we want the denominator to be exactly in the range 1 to (\$NPT-1) -- nothing more and nothing less. To guarantee that this will always be true (and knowing that there's a chance it might not be), we could clamp the denominator: clamp(\$NPT-1,1,\$NPT-1). This will work, as the denominator won't be allowed to take on any value outside those two limits (type "exhelp clamp" in a textport to see the help for the clamp function).

But, we know that, by construction, \$NPT will never be less than (\$PT+1), so the top limit of the clamp is really not necessary -- that is: we only need to watch out for the lower limit in this case. Enter the max() function: max(1,\$NPT-1) ensures that the expression (\$NPT-1) will never be allowed to take on a value less than 1 (and we leave the top of the range alone because we're safe on that end in this case).

...leaving us with the final (safer) version: \$PT/max(1,\$NPT-1)

Hope that makes some sense.

Cheers.

##### Share on other sites

Wow...

It makes perfect sense thank you!

A very nice explanation of all the steps!

The first 2 points and the last one were really important to me, the why use the u attribute and the max() instead of any other...

The other points also show a very good way to think and analise all... I'm really learning alot from this!

I see that I posted an error when I read the expression because I wrote 99 in the last part but 1 in the beginning... I was thinking in the value of the point and not its index number... shame on me!

though if you want to familiarize yourself with expressions, it's a good way to start

Yeap, I really want that! Using Houdini and not taking advantage of its procedural power would be like having a car and using it to store stuff in the trunk (not that I don't do it, but I also drive it... )

Thanks again to both of you!

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×