6
$\begingroup$

I can find a first nearest point. How can I find a second nearest point?

PS: The screenshot is from Blender 3.2. Feel free to use any Blender version.

enter image description here

$\endgroup$
7
  • $\begingroup$ No time to try it, but just an idea: maybe there's a way with the Delete Geometry node... find the nearest, delete it and find the nearest in the reduced geometry... that should be the second nearest. Of course you must somehow preserve the original points and their indices... $\endgroup$ Commented Nov 24, 2022 at 10:22
  • $\begingroup$ Although depending on how you implement that it could be problematic, because for one point the second nearest might be one first nearest of a different point but then it's gone... maybe there's a way of feeding the distance to nearest into a math node set to greater than and then find the nearest which has a larger distance, that should be second nearest too. $\endgroup$ Commented Nov 24, 2022 at 10:27
  • 2
    $\begingroup$ The only way to do this is sorting: for each point you need to find a list of points sorted by distance to that point - and pick the 2nd. How can I re-sort the points/indexes of an object in geometry nodes? $\endgroup$ Commented Nov 24, 2022 at 12:02
  • $\begingroup$ Personally, I find the two suggestions from MarkusvonBroady and GordonBrinkman very useful and both should actually work. However, I would prefer the sorting suggested by @MarkusvonBroady. $\endgroup$ Commented Nov 24, 2022 at 18:31
  • 2
    $\begingroup$ @quellenform speak for yourself, I understood 😜 in his screenshot for each point coming from "Distribute Points", he is finding the nearest point from "Grid" node, so I'm pretty sure he wants to find 2nd nearest point for each point. Since TA doesn't have an option to find 2nd nearest, you need to remove 1st nearest for it to find the 2nd nearest, but since 1st nearest is potentially different for each point… You would have to duplicate the geometry as many times as many points it has… Very similar to my quadratic sorting, which as I understand is obsolete now. $\endgroup$ Commented Nov 26, 2022 at 14:32

3 Answers 3

3
$\begingroup$

Geometry Explosion technique

The functionality of octrees in Geometry Nodes is quite limited and so basically for problems like that you need an all-to-all interaction like here, and the problem is actually conceptually similar to:

Geometry Nodes: How to find the 2 vertices that are furthest apart?

So here's how I implemented this using a 'geometry explosion':

Imgur mirror (SE image hosting has problems)

"Which Nearest?" is simply $2$ to answer OP's question, but could be any number. I don't check if it's not too big.

For clarity, the Grid geometry (called "Target" in Takapapatapaka's answer) is colored dark blue when it's in its original form, bright blue when it is exploded, and black when all points but winners are removed (the domain size returns to original, but it's a different geometry in nature hence black not dark blue).

The logic should be clear: Duplicate the "Target" geometry, the grid, as many times as the number of points ("Source") seeking the 2nd nearest. Now there's one duplicate for each "Source" point, and each such duplicate is sorted by distance to the relevant "Source" point. Then to get only the nearest points, you would get the 1st result for each grid, but that makes no sense as "Vanilla Functionality" already does that much more efficiently. But you can take 2nd result for each grid for 2nd nearest, 3rd result for 3rd nearest etc.

Since for each duplicate grid I'm leaving behind only a single vertex, and the order of grids was the order of relevant points, the mapping of "Source" point to "Target" vertex is 1:1. If you wanted to simultaneously calculate e.g. 2nd and 3rd nearest, the right-most "Sample Index: Index" logic would change.

This solution should be faster than takapakapaka's answer, but sadly despite not using a repeat zone the timing is well over 50% of his.

Find Nearest (octrees instead of sorting)

The performance bottleneck in the solution above is clearly the sorting. So below is a solution that deletes the 1st nearest, and then again searches for the nearest point. The problem is, because 1st nearest is different for each point, you still need geometry explosion to have a separate copy for each sampling ("Source") point. Then "Sample Nearest" doesn't provide a way to separate geometry into groups, therefore I temporarily join the exploded geometry with sampling points (and mark that geometry with cyan color) and use "Index of Nearest" which provides such functionality:

Imgur mirror

This is slower than the 1st solution, now very close to takapakatapapaka's answer in performance. The new, worse bottleneck is the "Index of Nearest" which creates a separate octree for each group and apparently that's slower than sorting. If you think you could use a single octree by offsetting duplicates - I tried it, and the "Sample Nearest" reported much worse performance (~5 times slower, see the file).

v.4.4.0.

$\endgroup$
3
$\begingroup$

Not sure if this is okay to unearth an old thread, but I found it while searching for the same thing, and it put me on the way of a solution not detailed anywhere else, so I thought it might be useful anyway.

I originally was trying to achieve something via Delete Geometry, but it would require to have a duplicate of the target geometry for each element in the source geometry. Searching for a way to achieve this, I found the Repeat Zone.

Using Repeat Zone

New (4.0) node Repeat Zone makes a quite simple solution to this problem, though it may be computationally intensive. Simply put, it enables us to iterate over all elements of the target geometry, and store them if they are closer than the previous ones. Since we have a hand in the iteration, we can make it so it does not store only the closest, but also the second closest.

This kinda is brute force iteration, and there is not much optimization in my solution, so it is slow (around half a second on my machine) for the configuration provided in this question (comparing 1k points against a grid of ~10k vertices). For comparison, the node tree proposed in the question takes only 2ms on my machine. So its applicability may vary depending on the amount of geometry you need to use.

Here is a screenshot of my setup, along with the blendfile with multiple setups (see below for the different options) : Geometry Nodes setup in Blender allowing two store the two closest point as attributes

Step by step explanation :

  1. Identify how many iterations we need (how many elements there are in the target geometry). Here it is a simple Attribute Statistics on the index property, set to max.

If the indexes are starting from 0 and there is no gap in their progression, it should give the correct amount of elements. From what I know, geometry nodes recalculates indices when changes are made (like deleting some geometry), so apart from inputing external geometry (as in edited by hand or created in another program) which has weird indexes, it should be relatively safe to use. If it had weird indexes, it possibly could try to reach for way too many elements, which could lead in longer process time or crash if memory runs out. 2. In the Repeat Zone, retrieve the indexes of the current element, the first closest and second closest, and calculate their distance to the source geometry position property. 3. Compare the distances, giving 3 options according to the state of the current element :

  • not closer : do nothing.
  • closer than the 2nd closest : replace the 2nd closest with the current element.
  • closer than the 1st closest : replace the 2nd closest with the 1st, and then the 1st with the current element. (That's why we need to store the 2nd Nearest first, otherwise we cannot "save" the value of the old 1st Nearest as the new 2nd Nearest).

Notes on optimizing Store Attributes

I tried various set-ups to optimize the process time, see below for a list of my attempts. Some keypoints :

  • My first version took ~800ms, the quickest I got was ~320ms, the solution i propose in the screenshot is ~420/340ms (see below for the double timing). For a quicker solution (~200ms in comparison), see Markus' answer.
  • Some parts have more impact than other : calculating the distance seems to have little to no impact. Storing the attributes is not that long, and is quicker than using the "inputs/outputs" of the Repeat Zone (Repeat Items). The logic between calculating distances and storing the results seems to be the most important part.
  • I was surprised to discover that Reroute nodes have an impact on performance when used with Repeat Zone (see Markus' comment below) : i tried a new idea in a quickly made nodetree, got better performance. Blender crashed, so i had to do it again, but i could not obtain the good timings i got. The only difference was me rerouting everything to have a better looking nodetree : i ended up recreating it from scratch, with no reroute, and the good timings came back. I thought Reroutes were mostly visual and did not matter computer-side, but in this case they can change the processing time greatly (10% to 40% percent longer). I think iterating 10k times makes it more visible than usual. I made a different version of each node tree, one with readable noodles and the other without reroutes. I reported both timings in two separate columns.
Attempt Time w/o Reroute Description
Storing results as Repeat Items 800ms 660ms Using the Repeat Zone inputs/outputs to store the nearest points, and assigning them to Attributes afterwards
Using switch before Store Attributes 550ms 410ms Using Store Attributes inside the Repeat Zone and a set of switches beforehand to decide what to store (either re-storing the previous one, either storing the current element if closer)
Using the selection input of Store Attributes 460ms 320ms Selecting only the new values when storing attributes, with a single switch for the 2nd Nearest
Using 3 Store Attributes instead of 2 430ms 340ms (current solution) Duplicating the 2ndNearest Store Attribute node, one in case the current element is closer than the 2nd only, and one in case it's closer than both
Using boolean logic to avoid overwriting 450ms 400ms (inefficient) Using a Not->And boolean nodes before the first 2ndNearest Store Attribute, to avoid storing it 2 times in some cases
Storing distance of 1st and 2nd closest 450ms 370ms (inefficient) Storing as attributes the distance to the current 2 closest point, to avoid re-calculating it multiple times
$\endgroup$
3
  • 1
    $\begingroup$ I think the reason reroutes are important here, is that you use a "repeat zone". Repeat zones seem to just evaluate grouped nodes the specified number of times, so it is as if you did something like a python loop evaluating a Geometry Nodes modifier. In fact, according to my tests a while back, a repeat zone is slower than a Python loop (and Python is known for having slow loops). So if the nodes are being evaluated (some stack machine probably) on each iteration of a repeat zone, that makes the layout actually matter. $\endgroup$ Commented Apr 6 at 14:33
  • $\begingroup$ Thanks for the explanation on Repeat Zone performances, it would make perfect sense. I made some tests with reroute and I can confirm that in a nodetree without Repeat Zone, having 1k+ reroutes or none did not make any difference, even on very high poly geometry. I also tried with a For Each Element zone, and there's no difference either. Introducing a Repeat Zone indeed brings the performance difference back. $\endgroup$ Commented Apr 6 at 18:56
  • $\begingroup$ Hi Takapapatapaka, i add answer because have other approach to solve this, and i found one mistake in your setup! $\endgroup$ Commented Apr 6 at 23:18
1
$\begingroup$

Problem in other answer

This issue recently came up for me as well, and since I had to deal with it, I'd like to share my approach! I'm adding this response because when trying to understand the previous approach, I identified a problem. The Sample Nearest node is actually receiving the original mesh and not the generated points, which causes it to return incorrect values. I've included a screenshot showing this issue.

enter image description here

If you analyze point "0", you'll see it returns "101" as the closest, but in reality it should be point "13".

Regarding my approach:

enter image description here I used the 'For Each Element' to calculate the nearest point using Sample Nearest, but it was always returning the same point as the closest one.

I needed to make it "self-ignore" in each loop. Then, since I was going to delete the mesh, I needed to save the original indices, as GN always recalculates them (as others have mentioned).

I chose to store them as an ID for convenience, but it could be any attribute.

Next, I could use Sample Nearest and use the ID to transfer this information to my current point, with the Sample Index and store it as an attribute.

After that, I just repeated this logic, but now I also needed to delete the "First Nearest" from my verification, which allowed me to find the Second Nearest."

enter image description here

$\endgroup$
2
  • 1
    $\begingroup$ I thought this is intended to have separate "Source" and "Target" meshes. So you sample from one onto another. $\endgroup$ Commented Apr 7 at 0:09
  • $\begingroup$ Actually, is exactly this! $\endgroup$ Commented Apr 7 at 0:15

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.