26

I wonder if there is a possibility to re-sort the indexes of a mesh according to a certain criterion?

Again and again in Geometry Nodes unfavorable situations arise where the point distribution or their indexes are in irregular order.

For example, I want to re-sort the points distributed on a grid with the node Distribute Points on Faces along a certain axis so that I can process them in order.

If I convert the distributed points for example with the node Mesh Line into a curve, a totally confused pattern comes out, because the indexes are distributed in a random order:

Re-sort points - Screen 1

However, I would like the indexes to be sorted and numbered from right to left so I can create a line along those points:

Re-sort points - Screen 2

Bonus task: The whole thing should at best also work with three-dimensional objects, because just a grid is sometimes pretty bland.

PS: I know there are "hacky solutions" out there, but that's what I really want to avoid and find a solid and as simple as possible logic based technique.


Techniques comparison

Even though it's not a contest here, I'm providing an overview of the answers below.

Each approach is very different, but each also has its justification. How, when, which technique is best depends very much on the situation.

I have now combined and modified all five techniques in such a way that they are meaningfully comparable, and run according to the following criteria:

  • All get as input any test value (float/integer)
  • All process points in the point domain
  • All provide the sorted index as result for further processing

enter image description here

(Native Sorting, Quadratic Sorting, Resolution Sorting, Circular Sorting, Ramp Sorting)

Features & Capabilities

Native Sorting Quadratic Sorting Resolution Sorting Circular Sorting Ramp Sorting
Reliability Perfect Perfect (?)
(Review pending)
Acceptable
(Depends on density & resolution settings)
Bad
(at higher density)
Acceptable
(Depends on density)
Performance assessment Perfect Acceptable * Good *
(Depends on resolution settings)
Perfect
(if you don't care about missed points)
Bad (!!!)
Can handle identical values Yes Yes Yes, partially
(Depends on resolution settings)
No No

*With a few points (below ~400), Quadratic Sorting is actually faster than Resolution Sorting. However, Resolution Sorting shows its strength with many values to be sorted and clearly outperforms Quadratic Sorting!

Note: I tried to make a comparison of the timings, but since the variants work too differently in the end and that depends on too many factors, it's not really possible to do that in a meaningful way.

Advantages & Disadvantages

Advantages Disadvantages
Native Sorting Captures all points,
Best performance
-
Quadratic Sorting Captures all points (?)
(Review pending)
Average performance
Resolution Sorting Performance depends on adjustable resolution Performance/Reliability depends on adjustable resolution
Has a small error rate at high density
Circular Sorting Is ultra-fast * "Swallows" points very easily at higher density
Ramp Sorting - Is computationally intensive at high density
Does not work reliably at high density

*The "Circular Sorting Technique" is so fast mainly because at high density the more points are discarded and thus no real comparison to other methods can be made!

Use cases

  • If you set a high value on precision and no point should be lost: Native Sorting
  • If you set a high value on precision and you have less then ~5000 (?) Points: Quadtratic Sorting
  • If you need a good result, but can live with a small error rate: Resolution Sorting
  • If you need it really fast, and if you don't care about precise detection: Circular Sorting
  • If you are totally crazy and feel like experimenting: Ramp Sorting

Download the comparison and try it yourself


(Blender 3.2+, all methods except "Native Sorting")


(Blender 3.4+, all five methods)

These files are all adapted for use on point clouds (!)

quellenform
  • 35,177
  • 10
  • 50
  • 133
  • 1
    nice comparison, though circular sorting doesn't get much slower because it just diregards more and more points. For my test of 5000 points, 4585 (91.7%) remained unsorted: https://i.imgur.com/9reNsbg.gif - so it's like taking my solution, removing 91.7% points, and then sorting 500 points instead of 5000 points. Still, it's a problem in itself to figure which points to remove, so I might actually use the Circular Sorting in the future... Also consider this: $\bbox[red, 5px]{\color{white}{\mathbf{NO}}}$ (color table) – Markus von Broady May 30 '22 at 11:13
  • @quellenform if it wouldn't take to much of your time, could you update the question with "resolution based sorting". I would like to see how it compares to other methods. :) – jst kiko Jun 06 '22 at 08:54
  • @jstkiko ...Thanks for your contribution, I'll do that of course! – quellenform Jun 06 '22 at 10:39
  • Nice update! However I find resolution sorting definitely more performant than quadratic sorting. – Markus von Broady Jun 06 '22 at 19:18
  • 1
    @MarkusvonBroady ...ups, that was indeed a typo! Thanks! – quellenform Jun 06 '22 at 19:21
  • Thank you all for this great Q&A. I can't seem to figure out how to use these though, could someone point out what I'm doing wrong in this setup, for ex: https://i.imgur.com/TDa1FpN.png ? I expect a new order that goes from left to right, but I get this seemingly random one instead. I get the same order from all other method node groups as well so obviously I'm messing something up. Thanks! (File) – Kuboå Dec 01 '22 at 17:14
  • 1
    @Kuboå This setup is NOT a reorganization of the points, but only a mapping of the indices. Have a look at the file with the comparisons. There you see, for example, that in the group "_Visualize" I transfer these mapped indices with Sample Index to the positions of a line. So the sorted index only says which point should put at this index, and it depends on you what you do with this information. Does this make more sense to you? – quellenform Dec 01 '22 at 18:24
  • @quellenform Yes that makes sense, thank you. Although I can follow and recreate the sampling position example just fine, I still got stumped with my scenario above, however. I think if you can tell me what I'm doing wrong trying to create a selection for the left-most point by (the now sorted) index here, it'll finally click. – Kuboå Dec 01 '22 at 19:44
  • 1
    @Kuboå So you want to know which point is on the far left? After sorting, the lowest value is the point with the index $15$, which is why it is assigned to point $0$. therefore you have to use Sample Index to fetch the sorted value, and make a comparison with the current index of the points. Something like this: https://i.stack.imgur.com/o8TQ3.png – quellenform Dec 02 '22 at 09:06
  • @quellenform Yeah that makes it clear, thank you very much. – Kuboå Dec 02 '22 at 11:03
  • @Kuboå You are welcome! If the solution "Native Sorting" helped you, please consider an upvote below. ;-) Thank you! – quellenform Dec 02 '22 at 12:29

5 Answers5

14

"Circular Sorting Technique"

enter image description here

How does this work?

Roughly speaking, I assign a range of the value to be sorted to a range from $0$ to $\pi$, which results in the distribution of points on a semicircle.

On the resulting shape, I apply a mesh trick that gives me a curve with ordered points.

This may sound rather muddled now, but it is really quite easy to understand if you represent it with pictures...

For example, if you distribute points on a grid with the node Distribute Points on Faces, and display the value of the X-axis on a semicircle, it looks like this:

enter image description here

If you now create a convex hull from the points on the semicircle and convert them back into curves, you get nicely sorted points. This works basically with arbitrary values.

Here you can see an example of sorting along the X-axis and along the Y-axis:

enter image description here

enter image description here

And this is how it looks, for example, with a grid rotating on the Z-axis:

enter image description here


Step by step to the solution

  1. First of all, you define by which value you want to sort. In this example I use the X-axis and separate it from the single points.

    enter image description here

  2. Next I use the node Attribute Statistics to determine the lowest and the highest value, which serves as input range for the node Map Range.

    I then map the values used here to a semicircle, so that the previously captured value of the X-axis is distributed along this semicircle.

    enter image description here

  3. Then I create a line with the node Mesh Line, whose subdivision corresponds to the number of the original points, and set their positions to the previously created positions on the semicircle.

    I achieve this by simply rotating a vector with the previously obtained angle.

    enter image description here

  4. And now comes the crucial step: I first capture the index of each point, so that I can later determine the original index, and then I use the nodes Convex Hull and Mesh to Curve.

    The ingenious thing about this is that Convex Hull forms a mesh hull around the points previously distributed on the semicircle, but this does not yet lead to the goal.

    Only by using the node Mesh to Curve, this mesh is transformed into a curve, whose indexes are ordered continuously from left to right.

    Since the original index was previously captured with the node Capture Attribute, a connection between the new ordered indexes and the original indexes can be established with Transfer Attribute.

    enter image description here

  5. With the help of this index now arbitrary values can be fetched and used in another mesh/object with the node Transfer Attribute.

    enter image description here


Download the file and try it yourself

Important Notes: Since this technique is based on the node Convex Hull, but this ignores distances between vertices below a certain value and combines the points arbitrarily, this technique may lead to unexpected results.

For less densely distributed points it works great, but as soon as the vertices are very close to each other along the sort (or even identical), they are ignored.

in this particular case: if the points on the circle are distributed with an angle of less than $0.01°$, they will not be recognized. (Thanks to @Robin Betts for the attentive reading).

Q & A

Q: It does not work at all with my mesh! Why?
A: Since this technique counts the points in the domain Points with the node Domain Size, and a mesh returns Vertices and is not a Point Cloud, you have two options:

  • Either you apply the node Mesh to Points before, which converts your mesh to points.
  • Or you switch the contained node Domain Size from Points to Mesh.

Q: Why actually a semicircle and not a whole circle?
A: Because the numbering is finally done by converting the convex hull into a curve with the node Mesh to Curve, which uses the longest edge as a reference point for the first vertex.

quellenform
  • 35,177
  • 10
  • 50
  • 133
  • Hi, Q! I really want this to work. (I once posted a solution very similar to this, and deleted it, because I found that without too much difficulty, I could kick it into failing ). Convex Hull has a nasty habit of deciding to merge vertices, where it doesn't think it needs the resolution. I wonder if there's a way to space out samples, interleaving dummy concave edges, or something, to improve this from convex-hull accuracy (not so great) to floating-point accuracy, (which is the unavoidable limit)? – Robin Betts May 28 '22 at 07:16
  • 1
    As always - a really nice solution :-) - Another approach for sorting is described by @JstKiko in the thread Sort points by distance from object. – André Zmuda May 28 '22 at 07:24
  • @RobinBetts Damn, you are right! If the difference of the angles is lower than $0.01°$, then the node Convex Hull simply skips the point. I would hate to delete this answer though, it was a lot of work and I still think it might help some ...what do you think? I will definitely add the hint though. – quellenform May 28 '22 at 08:54
  • +2 (q+a) that's amazing!! – Chris May 28 '22 at 08:58
  • @quellenform Oh, definitely not looking for deletion! Just interested in whether any of us can come up with an improvement.. The other solution you've been pointed at has a similar limitation. There might be some way of spreading samples evenly? That could be another Q. – Robin Betts May 28 '22 at 09:57
  • 1
    @RobinBetts Haha, this is catch-22: actually, you can only distribute something evenly if you also have continuous indexes, right? ;-) – quellenform May 28 '22 at 10:04
  • @quellenform That's what I've been trying to get around :D – Robin Betts May 28 '22 at 10:08
  • @quellenform Maybe I misunderstood something - but on windows 11 with blender 3.1.0 I don't lose any vertices even with 0.001 degree and a radius of 0.001 with this setup: . Only if I set the angle to 0, so that points are exactly the same, they will be skipped. - So, how can I reproduce the skipping behaviour of the Convex Hull node? – André Zmuda May 28 '22 at 21:57
  • @AndréZmuda This is because the input field requires a value in degrees, but when you feed the input with a value from another node, it must be in radians! I know it's really confusing sometimes, but that's why it worked for you ;-) – quellenform May 29 '22 at 14:01
  • 1
    For testing float accuracy, you might find this snippet useful (paste it into Python console): import numpy as np; start = 1; f"{np.nextafter(np.float16(start), np.float16(inf))-start:.10f}" - it displays the smallest possible step in float16 that Blender uses. For start = 1 it's about 0.001, but for start = 10 it's significantly bigger at 0.0078125, and for start = 0 it's only 0.000000059604644775390625 (5.96...e-08) – Markus von Broady May 29 '22 at 14:32
  • @quellenform Yes, you are right, I missed that. When I change the step to 1/10000 (rad) which is about 0.0057°, I can reproduce it. – André Zmuda May 29 '22 at 14:45
  • 1
    Actually it's float32 not float16, Blender does use float16 in some places, but in geonodes it uses float32, so the actual numbers for start = 0, 1, 10 are roughly 1.4e-45, 1.19e-07, 9.54e-07 quite more precision. – Markus von Broady May 29 '22 at 15:20
  • 1
    @AndréZmuda By the way, there is a math node that includes the "to Radians" option. – quellenform May 29 '22 at 18:05
  • @quellenform Good to know, this reduces my brainload ;-) – André Zmuda May 29 '22 at 18:06
11

"Native Sorting Technique"

Blender 3.4+

In version 3.4 there is an interesting new node, which allows a native sorting of the points of a curve based on a weighting.

The node is called Points of Curve, and returns the index of a specific point of the curve depending on the value passed.

To achieve sorting with this, first the point cloud is converted into a curve, and the sorting criterion is used as Weights for Points of Curve.

Basically all possible sorting can be done in this way:

enter image description here


(Blender 3.4+)


Examples

As you can see: Only the value that is passed as sort criterion is decisive.

Blender 4.0+

The concept of weighting has been used in some other nodes since version 4.0, and Points to Curve is available here with precisely this feature.

This converts points into curves and uses the weighting as a criterion for the order of the points to be used.

As a result, a curve is obtained whose points are arranged exactly according to the value of the weighting.

This curve can then be converted back into points if necessary.

quellenform
  • 35,177
  • 10
  • 50
  • 133
  • 1
    This is great news! I'll wait for the stable version as usual, and I would hope we would just get a general sorting field already… – Markus von Broady Oct 23 '22 at 22:06
  • In Blender 4.0.0 beta a new node Points to Curves is available by which the points can be sorted directly (if the original index is needed afterwards use Capture Attributecan be used). See also: link – Patter Nov 12 '23 at 12:01
  • @Patter Thanks for the tip! ...I noticed it almost at the same time and updated the answer ;-) – quellenform Nov 12 '23 at 12:04
8

Quadratic Sort (Pre B3.4)

This is one of outdated answers that would unnecessarily bury (currently) objectively the best answer by quellenform. You can still access this answer by reading the previous revision.

Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
  • 3
    I like this solution. It's compact and seems to be robust. – André Zmuda May 29 '22 at 15:11
  • @AndréZmuda I think it's 100% perfect. The only topology where it would fail, that I can think of, is one with over 16777216 vertices, when the float smallest increment goes over 1, and yet I position the vertices at +1 increments for each index. Of course there could be more failure points like inaccuracies of some nodes used (recently I got bitten by the color ramp reducing precision severly), but since I'm not moving vertices, only counting, it probably works for any practical geometry... – Markus von Broady May 29 '22 at 15:30
  • @MarkusvonBroady Could you please also give this ingenious node tree a proper name? I'll test a bit more, make a few comparisons, write an overview and then add that to the question. – quellenform May 29 '22 at 15:51
  • Sort Perfect 3000 – Markus von Broady May 29 '22 at 15:56
  • 2
    @MarkusvonBroady OK, ...then I will choose "Quadratic Sorting Technique" ...? ;-) – quellenform May 29 '22 at 15:58
  • @MarkusvonBroady ...I pulled back the Accepted Answer because I want to start a Bounty on it (which I can start in 5 hours). ...the answer is simply brilliant! – quellenform May 29 '22 at 18:03
  • 1
    I learned Accumulate Field from you and array technique from @AndréZmuda :D – Markus von Broady May 29 '22 at 19:52
  • MVB: This looks definitive, until loops or somesuch available? Curious: (n^2) time if serial.. but this looks susceptible to parallel processing? Are GN fields intended to be put through the GPU, eventually? – Robin Betts May 30 '22 at 07:23
  • 1
    @RobinBetts Loops are available, just not while loops - iterating over geometry is an equivalent of for v in geo.vertices, and what I'm doing here is for i in verts: for j in verts or rather x2=x*len(x); for v in x2. Parallel may improve the speed but not complexity... And I would expect that Blender eventually will introduce Sorted Field node... But then it would be nice to have some sequence bigger than Vector to sort by more than 3 criteria... – Markus von Broady May 30 '22 at 09:33
  • 1
    @MarkusvonBroady Only, that these for loops have some major limitations, that make life so hard: You can only change the item, that you hold in your hand; you can only see the state of your geometry before starting the loop, which makes the Accumulate Field node necessary. - Oh, I forgot - you may as well create geometry at the point, that you hold in your hand, when using the Instance on Point "loop". - We would already have much more possibilities, if we had not these limitations ;-) – André Zmuda May 30 '22 at 16:23
  • Thanks @quellenform ! – Markus von Broady Jun 06 '22 at 10:42
  • @MarkusvonBroady Have you actually tried using the original ID as the search criteria instead of the X-axis? I'm creating a new comparison and testing this right now, and it doesn't work well for me. Do you have any idea why or can you reproduce this behavior? – quellenform Jun 06 '22 at 13:20
  • @quellenform I sort by x coordinate, then index. Do you mean to sort by ID, then index? Wouldn't the original ID be the index? Oh and if you know your ID is unique, you can sort by a single criterion... – Markus von Broady Jun 06 '22 at 13:34
  • @MarkusvonBroady I am about to post the following test example: https://blend-exchange.com/b/MMpp7vv1/ ...Tests 2, 4, and 5 fail on Quadratic Sorting, and I just can't find the problem. Do you have an idea? – quellenform Jun 06 '22 at 16:06
  • @quellenform hmmm... What's the logic there with the accumulate field in tests 2 and 4? For example, if there's X coordinates: 1, 2, -100, 4, 5, the Leading values will be 1, 3, -97, -93, -88, and so the vertices will be sorted as 3, 4, 0, 1, 2. I just don't see a reason for combining Accumulate Field and sorting like that... As for ID, you're casting very large/very small integers to float32. See my first comment here about number 16777216- though in this case it's not a downside of the setup - you can duplicate the setup, where TA nodes use integers and it should work for an integer key. – Markus von Broady Jun 06 '22 at 17:10
  • @quellenform I haven't figured out the "what is supposed to happen" part, but I think the problem comes from the Accumulate node being recalculated - so you need to use a capture attribute after it, and before the sorting algorithm, to make sure it's evaluated once, and the sorting algorithm doesn't interfere with Accumulate Field... BTW, an impressive project, inspires me to make a big comparison like that too, though you butchered my pretty node tree :D – Markus von Broady Jun 06 '22 at 17:32
  • 1
    @MarkusvonBroady The node Accumulate Field was only meant as a test to get other values for the sorting, otherwise it has no deeper reason. The real question is: Why is the result so different compared to the other variants? However, your tip helped, the node Capture Attribute solved it (even if I currently can't quite figure out why). And hey, there's nothing "butchered" at all, just made them looking a w e s o m e! ;-) – quellenform Jun 06 '22 at 17:53
  • @MarkusvonBroady Could you find the error in the meantime? Is it my implementation or your nodes? – quellenform Jun 08 '22 at 18:34
5

Resolution based sorting (pre B3.4)

This is one of outdated answers that would unnecessarily bury (currently) objectively the best answer by quellenform. You can still access this answer by reading the previous revision.

Markus von Broady
  • 36,563
  • 3
  • 30
  • 99
  • Great, thanks (also to @jstkiko) for the contribution! A few more days and we have all the relevant variants of the sorting options together in one thread ;-) – quellenform May 31 '22 at 00:49
  • I think this could be possibly optimized: read the stack size as early as possible to remove the excess geometry first; or maybe create multiple mesh line geometries going from 0;0;0 to 0;0;0 and having vert counts of 1, 10, 100, 1000, 10000 or vert counts of 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, and then joining them as instances and instancing them on the "high res mesh line" taking log10/log2 of the stack size as instance index. The main sampling line now could have much less resolution, as verts would be added on demand... – Markus von Broady May 31 '22 at 08:45
  • 1
    @MarkusvonBroady thank you for summarizing and improving my solution. I was offline for a couple of days so I couldn't post it myself. – jst kiko May 31 '22 at 09:25
  • 1
    @MarkusvonBroady I have built a node, that calculates the minimum delta of an attribute. This way, you could know, which resolution you need. The only drawback is it's complexity: O(n²). Originally I intended to integrate it into the solution of JstKiko. But I didn't because of this drawback. Here you may take a look at it: min_delta_attribute.blend – André Zmuda May 31 '22 at 19:12
  • 1
    ... and it crashes on my computer with a high number of vertices due to the grid. Maybe because I currently only have onboard graphics. If you experience the same: This could be resolved by replacing the grid with instances of meshlines and adapting the solution to it. – André Zmuda May 31 '22 at 19:19
  • @AndréZmuda right, you can duplicate the geometry for each vertex, and for each such geometry remove the vertex for which you duplicated it - this way you can look at the nearest vertex that isn't self... Which is quadratic. But since my other solution has perfect accuracy with the same complexity, this defeats the purpose. Spawning the vertices on demand as I described in the above comment is also less than O(n²) but it still doesn't solve resolution problem. But I think I have an idea for a faster way to find smallest delta... – Markus von Broady May 31 '22 at 19:25
  • @MarkusvonBroady I had the idea to group the points into chunks and thus reducing from n² to m*k² with m<<n and k<<n. But I couldn't find a way to do this with GN till now. - To be more precise: I could build those chunks, but I didn't find a way to compute the deltas for these chunks, then. – André Zmuda May 31 '22 at 19:30
  • @AndréZmuda I figured if you spawn a mesh line setting the value to Z, and setting XY = 0, you can now instance triangles on these points and use ray casting down and up with a slight offset (shouldn't be too hard to calculate the smallest possible offset at a given float, basically reproducing nextafter() in nodes) and so you can find the next/previous point. Still doesn't help with sorting, though. I'm thinking, in case of snapping the values to integers, you could also capture attribute of how much you snapped and use that as 2nd sort criterion (index becoming the 3rd). – Markus von Broady Jun 01 '22 at 10:23
3

"Ramp Sorting Technique"

This is one of the outdated answers that would unnecessarily bury the (currently) objectively best answer from quellenform. You can still access this answer by reading the previous revision.

quellenform
  • 35,177
  • 10
  • 50
  • 133
  • Since you're comparing, here's a dare: test all 3 solutions on Suzanne with 4 subdivision levels. :D – Markus von Broady May 29 '22 at 14:44
  • I tested this without subdivision and got ~1.7 seconds, so I didn't dare to try subdivisions. The Convex Hull answer was blazing fast and was dealing even with 4 subdivision levels. Though when I decided to randomize on X a little by applying subdiv and using proportional editing - it crashed (probably even unrelated to your setup :D). My solution was very fast without subdivisions, scaled very well to one subdivision but at lvl 2 crashed :D. Though when I resign from edges (> to points > to verts) it survives lvl 2 and takes 3 seconds... – Markus von Broady May 29 '22 at 15:11