4
$\begingroup$

I have two sets of points. The first has defined positions, and an ID number attribute. The second set is a subset of the first's ID numbers, but doesn't have any position values. What I'm hoping for is a clean way to take the second set, have it search through the first set for the point that matches its ID, and then copy the position from that point, but I haven't been able to find a way to make that happen yet.

Spreadsheet view of the primary set of points, with their position values.Spreadsheet view of the secondary set.

In this case, the first set's HIP value should match to one of the Start values in the second set, though it is possible that some of the start values will not have a match, based on filtering that's happening earlier in the node setup.

$\endgroup$
0

2 Answers 2

7
+100
$\begingroup$

First of all, hopefully the devs will let us add this in soon 😉
👉 https://projects.blender.org/blender/blender/pulls/104621 .

Anyway, until then there are several ways you can do this, or at least two methods that I know of:

  1. Sample nearest
  2. Accumulate field
  3. Geometry proximity (4.2+)

Preface

First let's think about what we want. We have two IDs (or keys); the one you are sampling from and the one you are transferring to. Based on these IDs matching we want to transfer an attribute from the A to B (the sampled to the sampler).

What problem could we face? I could think of two that we would need to account for:

  1. What if there doesn't exist a match for an ID in the sampler from sampled geometry?
  2. What if there are multiple matches for an ID in the sampler from sampled geometry?

In regards to sampling the attribute it's best to go with the index, meaning our match ID operation will produce the index of the point you want to sample.

Now to address the potential problems I mentioned above: first, we'll output a boolean value as to whether or not there is a match (true = there is a match). Second, we could either use the first match, or (what I will do) you can have an option to specify on the sample geometry which match to sample from (first, second, third, etc...). And just as a bonus I like to add the total number of matches.

Here is a setup for the first way - sample nearest:

-image-

and this is how the node looks:

The idea here is to use the position as the ID (or the ID as the position) and take advantage of the source position (otherwise you could just set the position of both).

  • We set the position of the target geometry (the sampled geo) to its ID
  • Then we sample the nearest position while setting the sampler geometry's position also to be its ID.

So now the nearest point should be the one with the same ID.

To solve the issue of no matches we sample the ID at that index to see if it is a true match.

And for duplicate matches we offset any duplicates in the y direction by its "local index" within its ID - using the accumulate field - and add out sort index [which is an offset to pick which match to use] to the samplers y position. Lastly I sample the total from the accumulate field, which gives me how many duplicates there are.

One last issue is that the sort index isn't constrained by the total duplicates. For this you could sample a second time and modulo it by the total. (not recommended, because this is just too many sample nearest's for my liking)
btw that looks like this: enter image description here

Here is a setup for the second way - accumulate field:

enter image description here

and this is how the node looks:

The trick with this one is to combine both geometries and use the IDs as a group (for accumulate field). We take the total Index of the group excluding the sampler, which leaves us with the index of the point on the target geo that matches the one on the sampler.

From this point you could either capture the index and delete the extra (target) geo, or sample the result back to the original geo. I went with the latter one, but both are fine.

some points to think about:

  • If you are gonna sample the result back to the original geo (like I did) then you need to make sure that the sampler geometry is the top input of the join geometry.
  • To deal with duplicate matches I added more group IDs, one for each duplicate. I did this by multiplying the "local index" per ID with the max ID and then adding that to the IDs [keys]; What this does is append these new IDs the the end of the other IDs. Then I do the same thing to the sort index except I wrap them by the total duplicate amount.
  • What if there doesn't exist a match? Since I take the total already, if there is no match then the total matches are 0, so I just compare that.
  • with the total, since I take it before I specify a sort index, so I just take it from the first point. The rest of the points are shoved into another group and ignored for this calculation - this is where I have the switch with the top integer being an arbitrary improbable (negative) number.

I have more, but that is for another day 😉.


Geometry proximity method

Now in 4.2+ they added a group ID to the geometry proximity node so we could use that so basically circumvent the proximity part, and just use it to to get the data from the matching element.

This doesn't have some of the sort index thing like we implemented for the sample nearest, but it's mysh faster so I would take this over the other one.

One thing I use it for a lot is to transfer a boolean, which is the easiest. Just make the group ID to -1 or something and use the index and the sample ID and use the is valid output. I used this all the time, and it's very straight forward easy and fast. I even hide the extra inputs and it look really clean and like a new node 😄. enter image description here

here i used it for Instance on points based on probability: enter image description here

If you want to send data across then you would set position of the geometry (input) to the data you want and set the sample position to 0, and the output position will be the data you set. enter image description here

here is a sort index, but on the sources side: enter image description here

You could also average the value per ID that goes into the set position. or pass the index through and use with sample index, if you needed to sample a lot of values etc. I'll leave the rest up to you.

If they add this ID option to the sample nearest node then that will basically be the same as the proposed "sample by ID" node; and I'm not sure how to feel about that. I'm still a fan of the index map node; hope we get it back to consideration 🤷‍♂️.


PS: A native node like this has been in blender development for some time. There are two projects and they just got merged
There is the original by iliya which is called the 'index map' node
and there us one called Sample by Group ID by Simon Thommes.
I this the 'Index Map' design is much better and we should go with that.

I suggest, if you desire this node in blender, to join the discussion or leave and emoji or something letting the developers know you want it. It has been too long and we need the community's interest to get this node into blender sooner.

$\endgroup$
4
  • $\begingroup$ @MarkusvonBroady thanks for the spell check. my browser was acting funny and only spell checking some of the time. and the way it gets with these long answers is that you'll inevitably mistype something and then it'll be hard to find it etc... $\endgroup$
    – shmuel
    Commented Sep 3, 2023 at 19:29
  • $\begingroup$ The sample nearest method (I had trouble getting my head around the accumulate field method) worked for what I needed, but oddly enough there seems to be an issue with the first Accumulate Field node in the group, it's outputting 0 for all values, so the Exists check isn't functioning. I'm not 100% sure why, but I think there's still something fundamental about passing values into and out of groups that I'm not getting. $\endgroup$
    – Valbatross
    Commented Sep 5, 2023 at 18:05
  • $\begingroup$ Ok, I'm reasonably certain I solved my issue, but it meant using the To ID field instead of the From ID in the Sample Index node to match the values. I'm guessing it has something to do with how I'm inputting the IDs to the group in the first place. $\endgroup$
    – Valbatross
    Commented Sep 5, 2023 at 19:12
  • $\begingroup$ @Valbatross can you attach an image? $\endgroup$
    – shmuel
    Commented Sep 5, 2023 at 19:48
3
$\begingroup$

I've colored some vertices on a triangulated, subdivided plane, and used the same colors on some triangles. The plane, and the triangles are separate objects:

I've set the color attribute to vertex domain, and named it HIP on the plane, and Start on the triangles. Then I made this geonodes setup for the triangles:

  1. Load the other object (grid), store original position in "Capture Attribute" and use the color HIP to set new position (if you have a scalar - single value attribute, you can just plug it to the vector and it will work too, with the value triplicated for each axis).

  2. Move the triangles to a new position: for each vertex, read its Start attribute, treat it as a position, find the nearest vertex to this position within the just prepared geometry in p. 1. Once its index is returned, read its saved attribute (original position), and set self position to that.

Because since Blender 3.3 you need to first find the index, and then sample the actual value by index, the second sample can be done from the original geometry before the "Set Position" node. This means the Capturing doesn't even have to be done:

Duel the shmuel

Just kidding, I search for good elaborate answers to promote them with bounties, but while waiting for the 24h non-bounty period, I decided to share how I would approach generalizing the idea of sampling by attribute:

Using the above custom group, and a Suzanne mesh with Group vertex group, with a vertex on one eye with weight 0.1, a vertex on another eye 0.2 and a vertex on nose 0.3:

As you can see the non-matching nose wasn't moved, but it will if you increase the Max Distance (a.k.a. εpsilon):

  • I don't implement additional sorting, if multiple points satisfy the criteria (match within the $ε$), it is your job to remove the duplicates first, or, if you want to find based on multiple criteria¹, combine them in a vector, e.g. $x=$original_criterion, $y=$index. You can do this, because…
  • …All field attribute types are supported except RGBA color, which I imagine ignores the alpha component, but I didn't test it; this is because all attributes are treated as vectors, meaning that scalar values (float and integer) are triplicated
  • …Which means the distance between $0$ scalar, and $1$ scalar isn't $1$, it's ${\sqrt3} = 1.73205080756887$. This comes from the Pythagorean Theorem for a diagonal of $xy$, and then a diagonal of that and $z$. So remember, if you want to use this node group with an accurate εpsilon for a scalar value, multiply the value by the square root of 3:
>>> Vector((1, 1, 1)).length
1.7320508075688772

>>> sqrt(3)
1.7320508075688772

>>> sqrt(sqrt(1**2 + 1**2)**2 + 1**2)
1.7320508075688774

>>> Vector((2, 2, 2)).length
3.4641016151377544

>>> 2 * sqrt(3)
3.4641016151377544

>>> sqrt(sqrt(2**2 + 2**2)**2 + 2**2)
3.464101615137755

¹ - after I slept on it, I realized I was wrong (which I would also realize if I tested it), this technique of combining XYZ can work, but only if the data is prepared in a particular way, e.g. two integer attributes can be prepared by turning another into a factorial $1\over x+2$, this way the "nearest" will always find the closest integer attribute, because even if the other attribute is the highest possible (${1\over 0+2} = {1\over 2} = 0.5$), it won't make it further away than the next closest attribute… But what if the attributes are float? They would need to be mapped based on minimum and maximum, which actually isn't that hard to do, but my answer is incomplete.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .