# Using Geometry Nodes, how do I select the 'apex' vertices only

Got stuck on this for a while...

I have a mesh representing a roof. Now I need to pick those 'apex' points (circled in blue) for instancing specific roof tiles there.

My closest approach so far is: For each vertex, evaluate all its neighboring vertices and check their height differences (Z of this vertex - Z of peer vertex), so if the min value is less than 0, delete this vertex from the geometry. So the output geometry should only has those apex points in it.

I know mathmatically this is not the most accurate approach, just thought it might be easier to implement.

This is the GN group I came up with, but unfortunately it doesn't work. The Attribute Statistic node seems just go through the entire geometry and spit a constant value instead of one value per vertex.

How should I fix this issue, or is there a better way of achieving the end goal here?

Blend file:

• Could you edit the question and share a .blend file to make it a bit easier for those who might attempt to figure this out? blend-exchange.com Commented Jun 10, 2023 at 8:19
• It was super messy, haha. But yea, can do. Let me clear it a bit. Commented Jun 10, 2023 at 10:09
• Done, file uploaded :P Commented Jun 10, 2023 at 10:25

Here comes the solution.

The general idea is, to duplicate the mesh. Then, on the copy, we delete all vertices that are not an apex. Finally, we can turn the copy into points and instantiate objects on the apex.

# How does it work

The instantiation should be clear, so I will not go into that. Instead, let’s start with the Delete Geometry node.

As you already wrote, an apex is the highest point of himself and his neighbors. This means, we want to delete a point, if there is any neighbor, that has a higher z-coordinate. And to show a bit of goodwill, we add an Epsilon to it ;-). That is what is happening here: The Delete Geometry node iterates over all vertices of the mesh and the others check for each vertex, if there is a higher neighbor.

The difficult part is, to access the neighbors and compare their heights. This is done by the following set of nodes:

There is no direct way, to compare all neighboring vertices of a certain vertex. But the Edges of Vertex node allows us, to sort neighboring edges by a weight and the Edge Vertices node allows us, to access the positions of the vertices of an edge. Only, it is difficult to access the z-value of the highest vertex of the edge, that we receive from the Edges of Vertex node. To solve this, we calculate this value separately and store it at each edge in the attribute max height (I will explain this later). This allows us to read this value right here.

What do the nodes above do? Evaluate at Index reads the stored max height of the edge, that it receives from Edges of Vertex. Edges of Vertex looks at all edges connected to the current vertex, sorts them descending by the attribute max height and returns the index of the first edge in this order. This is the one, that contains the highest vertex of all these edges.

Now let’s look at the nodes, that calculate the max height attribute for all edges:

Store Named Attribute iterates over all edges of the given geometry. Edge Vertices provides the position of the vertices of every edge. We take the z-coordinate of these positions and store the maximum of them in the attribute max height of the corresponding edge.

# What is wrong with your node setup

First let’s take a look at these nodes:

Before, you should take a deep look at this chapter (Fields - Blender Manual) of the blender manual. Fields are a core concept of geometry nodes. You need to understand them thoroughly. Especially, you should understand the difference between circled and diamond-shaped sockets and you should understand the concept of field contexts.

What goes wrong with the above nodes is, that Attribute Statistic does not operate on a field, it operates on a value (circled output socket). This means, it does not give you different minima for every point but one single minimum for all points.

Now let’s take a look at the selection of your Attribute Statistic:

First you store the index of every point on the point itself. Then you use the Attribute Statistic node to iterate over all edges. And you use the attribute, that has been captured on the point in the context of the edge. This gives you the rounded interpolated value of the indices of the vertices of the corresponding edge. Let’s say, you have an edge with vertices with index 1 and 6. Than you will get 4 as the result ((1+6)/2 rounded).

Now you select those edges, where the index of one of its vertices is equal to this interpolated value. This is the case for all edges, that have vertices with subsequent indices (e.g. 1,2 or 5,6).

Finally let’s take a look at which values the Attribute Statistic node gets out of this selection:

If the index of vertex 2 is equal to the interpolated value (which is the same as: if vertex 2 has the higher index), then calculate (z2 – z1) else calculate (z1 – z2).

Overall you take the minimum value of this difference of all edges, that have vertices with subsequent indices and delete all points, if this value is less than -0.1.

• Oh wow! I'm truly amazed by the comprehensive and detailed deep dive you provided into the fundamental basics. Indeed I knew I must have missed some of those basics. Really appreciate for both your brilliant solution and the thorough explanation. Thank you! Commented Jun 11, 2023 at 16:58
• I guess the other thing I didn't form a correct understanding is 'context'. In my case with the use of Capture Attribute, my idea was because eventually all the outputs, including the one spit out from Attribute Statistic node, feed into the Delete Geometry node which is set to the domain of Point, so I thought, ok the main iteration is in a per vertex basis, as if that's the main loop in programming terms, and the rest of the nodes including Attribute Statistic node is a sub-loop that only goes through the edges sharing the same vertex. Well, clearly my ‘hypothesis’ is wrong there, lol. Commented Jun 11, 2023 at 17:23
• You are welcome :-) - context is indeed no simple topic. And you have to clearly understand the involved nodes, to know when a context change happens. Commented Jun 11, 2023 at 19:40