Group by @Cd

   1171   13   3
User Avatar
Member
17 posts
Joined: Nov. 2018
Offline
Hi I'm trying to group primitives by their @Cd rgb value:

@Cd.r=1&&@Cd.g=0&&@Cd.b=0

Trying this in the group node but no luck with all 3, each one individually is working though. Can anyone tell me where I'm going wrong with all 3 together?
Edited by BenWWilson - Jan. 26, 2024 11:36:46
User Avatar
Member
2536 posts
Joined: June 2008
Offline
Isn't a single equal symbol used for assignment and double equals for comparison?
Have you tried (@Cd.r==1&&@Cd.g==0&&@Cd.b==0)

You can also do this inside a point wrangle with code like this...
if(v@Cd=={1,0,0}){i@group_red_color=1;}
Using Houdini Indie 20.0
Windows 11 64GB Ryzen 16 core.
nVidia 3050RTX 8BG RAM.
User Avatar
Member
466 posts
Joined: Aug. 2014
Offline
Gentlemen, I'd like to draw your attention to the fact that vectors are comprised of floating point components. And that floating point comparison for equality is not a good idea.

float a = 0.15 + 0.15;
float b = 0.1 + 0.2;
int equal = 0;
if(a == b)
    equal = 1;
else
    equal = 0;
printf("%f == %f? --> %d\n", a, b, equal);

0.300000 == 0.300000? --> 0
User Avatar
Member
442 posts
Joined: Aug. 2019
Offline
Does VEX has a similar function like Python's math.isclose()?
User Avatar
Member
2536 posts
Joined: June 2008
Offline
For your example, try...
float a = 0.15 +0.15;
float b = 0.1+0.2;
int equal = 0;
float threshold = 0.0001;
if(abs(a-b)<threshold)
    equal = 1;
else
    equal = 0;
printf("%f == %f? --> %d\n", a, b, equal);
Edited by Enivob - Jan. 27, 2024 19:23:26
Using Houdini Indie 20.0
Windows 11 64GB Ryzen 16 core.
nVidia 3050RTX 8BG RAM.
User Avatar
Member
466 posts
Joined: Aug. 2014
Offline
Just some additional thoughts.

Following suggestions from The Floating-Point Guide [floating-point-gui.de], we could also use:

if ((abs(a-b) / b) < threshold)

which would safeguard the operation against improportionate threshold value in relation to values of a and b.

Even though they say it's a "nay" because the divisor or both values can be zero, according to docs VEX has some [www.sidefx.com] guards [www.sidefx.com] that address this.

Docs
In traditional programming, these are generated by 0/0 or sqrt(-1), but in VEX most such operations are guarded so normally VEX will not produce NANs.

Docs
In traditional programming, these are generated by 1/0 or -1/0, but in VEX most such operations are guarded so normally VEX will not produce +Inf or -Inf.

When we're dealing with colors and if 8-bit precision is enough, I guess we could also resort to integer arithmetic. Because conversion from fractional 0-1 to integer 0-255 space can be done with a linear function, we can simply fitthe original floating point range of the color into integers.

int rgb8repr(float f){
    return (int) (fit(f, 0, 1, 0, 255));
}

if (@elemnum == 0)
    @Cd = set(0.29999 + 0.00001, 0.1 + 0.2, 0.15 + 0.15);
if (@elemnum == 1){
    @Cd = set(0.3, 0.3, 0.15 * 2);
}
float fr = @Cd.r;
float fg = @Cd.g;
float fb = @Cd.b;

float r = rgb8repr(fr);
float g = rgb8repr(fg);
float b = rgb8repr(fb);

i[]@RGB = array(r, g, b);

printf("%d: (%f, %f, %f) --> (%d, %d, %d)\n",  @elemnum, fr, fg, fb, r, g, b);

// Second wrangle
int fequal, iequal = 0;
int rgb[] = i[]@RGB;
if (rgb == array(76, 76, 76))
    iequal = 1;
float t = 0.3;
if (@Cd.r == t && @Cd.g == t && @Cd.b == t)
    fequal = 1;
printf("%d: %d, %d, %d? %d\n", @elemnum, rgb[0], rgb[1], rgb[2], iequal);
printf("%d: %f, %f, %f? %d\n", @elemnum, @Cd.r, @Cd.g, @Cd.b, fequal);

Output (note that @primnum==2 was set to (0.3, 0.3, 0.3) outside of wrangles):

0: (0.300000, 0.300000, 0.300000) --> (76, 76, 76)
1: (0.300000, 0.300000, 0.300000) --> (76, 76, 76)
2: (0.300000, 0.300000, 0.300000) --> (76, 76, 76)
3: (0.000000, 0.873151, 0.861095) --> (0, 222, 219)
4: (0.974217, 0.515668, 0.000000) --> (248, 131, 0)
5: (0.000000, 0.984340, 0.193957) --> (0, 251, 49)
0: 76, 76, 76? 1
1: 76, 76, 76? 1
2: 76, 76, 76? 1
3: 0, 222, 219? 0
4: 248, 131, 0? 0
5: 0, 251, 49? 0
0: 0.300000, 0.300000, 0.300000? 1
1: 0.300000, 0.300000, 0.300000? 1
2: 0.300000, 0.300000, 0.300000? 1
3: 0.000000, 0.873151, 0.861095? 0
4: 0.974217, 0.515668, 0.000000? 0
5: 0.000000, 0.984340, 0.193957? 0

Interestingly, floating point comparison turned out to be working pretty well here. In fact it seems to be quite resilient to errors. I wonder why.
Edited by ajz3d - Jan. 28, 2024 15:40:32
User Avatar
Member
17 posts
Joined: Nov. 2018
Offline
Hey everyone I didn't appreciate how much of a coding conundrum this would be and I appreciate all of your help in thinking about solutions.

Just to add one that I've found is to have a group for each value (r,g,b) set merge type to intersect with existing on g and b. Filters down to the correct selection.

Just another way of getting the desired outcome.
Edited by BenWWilson - Jan. 29, 2024 04:06:52
User Avatar
Member
331 posts
Joined: April 2018
Offline
You can use a Wrangle to build a string attribute with the RGB values, and then use a Groups From Name SOP to generate groups based on that.

Like this:
s@colour_string = sprintf("%rgb_%f_%f_%f", @Cd.x, @Cd.y, @Cd.z);




Side rant:
There used to be a Partition SOP that did this out-of-the-box, but it was very irritatingly and unceremoniously removed (I think from H19 onwards).

I understand why they did it - it's not performance-friendly, and can result in an unmanageable number of groups if you aren't careful, and they recommend using attributes instead.

But this is supposed to be Houdini - let the user take responsibility - just give us the tools.

Meanwhile, you have the Resample SOP that will happily freeze your computer if you set the Length too low (by just dragging the slider, or scrolling the parameter).
Edited by eikonoklastes - Jan. 29, 2024 10:29:26
User Avatar
Member
180 posts
Joined: Aug. 2018
Online
Meanwhile, you have the Resample SOP that will happily freeze your computer if you set the Length too low (by just dragging the slider, or scrolling the parameter).

I'm sure I've put in an RFE requesting some form of a guard against this. Fingers crossed I'm not the only one.
Hope springs eternal.
User Avatar
Member
2040 posts
Joined: Sept. 2015
Offline
Mike_A
I'm sure I've put in an RFE requesting some form of a guard against this.

Hopefully such an RFE doesn't materialize. Creating 'guards' like this begins to make the process cumbersome to allways have to untick/remove a default gaurd before using the node, when one is already familiar with their workflow and node usage.

Houdini already has a default process that can be used as a gaurd for all nodes.

When laying down a node and you are not sure of the 'consequences' beforehand, before wiring it up set the bypass flag(yellow one furthest to the left on a node), wire it up.

Then look at your parameter settings and make adjustments so you are sure it's not going to set a 'run away' process.

Then just unset your bypass flag.
User Avatar
Member
331 posts
Joined: April 2018
Offline
BabaJ
Mike_A
I'm sure I've put in an RFE requesting some form of a guard against this.

Hopefully such an RFE doesn't materialize. Creating 'guards' like this begins to make the process cumbersome to allways have to untick/remove a default gaurd before using the node, when one is already familiar with their workflow and node usage.

Houdini already has a default process that can be used as a gaurd for all nodes.

When laying down a node and you are not sure of the 'consequences' beforehand, before wiring it up set the bypass flag(yellow one furthest to the left on a node), wire it up.

Then look at your parameter settings and make adjustments so you are sure it's not going to set a 'run away' process.

Then just unset your bypass flag.

This is strawman. The RFE wasn't to put in arbitrary guards everywhere. It was to prevent the app from literally freezing when you use the most common slider on one of the most common nodes. That's just bad UI/UX design and needs to be fixed.
User Avatar
Member
2040 posts
Joined: Sept. 2015
Offline
eikonoklastes
It was to prevent the app from literally freezing when you use the most common slider on one of the most common nodes. That's just bad UI/UX design and needs to be fixed.

As stated before - a gaurd is already available. Use the bypass flag.
User Avatar
Member
331 posts
Joined: April 2018
Offline
BabaJ
eikonoklastes
It was to prevent the app from literally freezing when you use the most common slider on one of the most common nodes. That's just bad UI/UX design and needs to be fixed.

As stated before - a gaurd is already available. Use the bypass flag.

The whole point of dragging the slider or scrolling the parameter is to see a change interactively. Your suggestion is an unintuitive hack that breaks the interactivity and puts the onus on the user to tiptoe around a behaviour that should never exist.
Edited by eikonoklastes - Jan. 29, 2024 11:49:20
User Avatar
Member
93 posts
Joined: Dec. 2019
Offline
Hello @BenWWilson

Just wanted to answer your initial question :

BenWWilson
Trying this in the group node but no luck with all 3, each one individually is working though. Can anyone tell me where I'm going wrong with all 3 together?

Quick answer

Use Group Expression node, not Group Create

Long answer

In the Group Create node, you can provide string pattern to describe the desired selection, such as :
  • 0-18
  • piece_*
  • @Cd.r==1

If you provide multiple space-separated patterns, this will give the union of these patterns. You can not do intersections with this method. But, you can still do exclusion by adding "^" before the pattern you want to exclude. In your example, you will need to exclude all values but zero for .g and .b components, this should look like this in Group Create node :

@Cd.r==1 ^@Cd.g>0 ^@Cd.b>0

However, having an expression with "and/or" logical operators like "@Cd.r==1 && @Cd.g==0 && @Cd.b==0" can be much more intuitif, and the Group Expression node is more appropriate for this, as it expects that you provide an expression with a boolean result.

I built a hip file with examples on this, using Group Create and Group Expression nodes. I also did some examples with "distance between vectors" using the Group Expression node.

Hope this helps.

Cheers,

Attachments:
GroupByColor.hiplc (180.6 KB)

Houdini Pipeline Supervisor @ TAT Studio
  • Quick Links