[VEX] Using "nearpoint" function WITHOUT including itself?

   584   4   2
User Avatar
Member
5 posts
Joined: Sept. 2024
Online
Hi all, I am currently trying to write some VEX code that measures the length of the closest point to itself. I want this attribute so that when I apply a sweep to a curve made from these points, I could make the tubes not intersect when increasing their radius...

Here is what I have so far:

int near = nearpoint(0, @P);
i@near = near;

Right now, each point is outputting the their own point number, which makes sense since technically the "closest" point is the point itself, but I want to check what is the closest "neighbour" to any given point. I would use the neighbour()function, but these points are not connected to one another, but are the result of a scatter node.

Attachments:
Screenshot from 2026-08-24 15-45-08.png (447.8 KB)

User Avatar
Member
38 posts
Joined: Jan. 2016
Offline
Something like this should work:
int near = nearpoints(0, @P, 2)[1]; // We limit max points to 2 since we only need the second closest anyway
i@near = near;
User Avatar
Member
5319 posts
Joined: Feb. 2012
Offline
Hi,

You can just remove the current point from the returned array using the removevalue() function, then take the first remaining point:

int pts[] = nearpoints ( 0, @P, 10 );
removevalue ( pts, @ptnum );

int nearpt = pts [ 0 ];

It is better to explicitly remove @ptnum rather than assume the first returned point is always the point itself. With point cloud functions like nearpoints(), pcfind(), pcopen(), etc., that is not guaranteed with coincident points or equal-distance cases.
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com] https://lnk.bio/animatrix [lnk.bio]
User Avatar
Member
26 posts
Joined: Aug. 2021
Offline
Hello. Also it's possible to get it done using neighbour and xyzdist functions. I appended a hip file so you can follow. Before running any code I had to prepare the scene. I created a sphere, then pointsfromvolume, then I ran POP net with random forces to create random point movement in space, then ran trail to create a point trail, and connected them with Add SOP, then froze at 100th frame with timeshift.

Now that we've got the lines that curve in random directions we can move onto deciding what method to use for calculating thickness so that once we run Sweep, we don't get intersecting geo. I want to address the method you mentioned, neighbour function. For this to work, I must highlight that you have to have only 1 primitive per curve, not split up with something like convertline SOP. Make it one continous curve primitive with polypath SOP. Anyway, this method is possible with a workaround. You should first drop "connect adjacent pieces" SOP with "Adjacent Points" connection type. That's going to create necessary connections between points.

Then you run this code:
/*MUST HAVE 1 PRIMITIVE FOR EACH CURVE (RUN POLYPATH SOP)!!!
OTHERWISE YOU CAN'T FIND "current_trail"*/
int current_prims[] = pointprims(0, @ptnum);
int current_trail = current_prims[0];

int all_neighbours[] = neighbours(0, @ptnum);
float min_external_dist = 10000;

foreach (int nb_pt; all_neighbours) {
    int target_prims[] = pointprims(0, nb_pt);
    int target_trail = target_prims[0];
    
    if (target_trail == current_trail) continue;

    vector nb_pos = point(0, "P", nb_pt);
    float d = distance(@P, nb_pos);
    
    if (d < min_external_dist) {
        min_external_dist = d;
        @min_ext_dist = min_external_dist;
    }
}


float max_allowed_radius = chf("max_allowed_radius");
float thickness_bonus = chf("thickness_bonus");

if (min_external_dist == 10000.0) {
    f@pscale = max_allowed_radius + thickness_bonus;
} else {
    float safe_radius = min_external_dist * 0.5;
    f@pscale = min(safe_radius, max_allowed_radius) + thickness_bonus;
}

This code searches for prim ID of the current pt and compares it against all connected neighbours. If a neighbour belongs to the same curve, we will skip following calculation. If a valid connection to external/neighbouring curve is found, it calculates a distance radius so the tubes don't intersect. If the point is isolated and doesn't find external trails within Connect Adjacent Pieces search radius, it defaults to max allowed radius slider parameter. There is a "thickness bonus" control parameter as a global scale modifier.

After this we Attribute Copy f@pscale attrib onto curve geo (the one before connect adjacent pieces SOP).
Sweep will recognize pscale attribute btw.

Unlike neighbour() method, the next one based on xyzdist function is a lot faster (more than 20 times) to compute. I prefer this one as you don't need to fiddle around with connectadjacentpieces SOP nor attribute Copy.

Basically you're just going to drop point wrangle and paste this code
/*MUST HAVE 1 PRIMITIVE FOR EACH CURVE (RUN POLYPATH SOP)!!!
OTHERWISE YOU CAN'T FIND "current_trail"*/
int current_trail_prims[] = pointprims(0, @ptnum);
int current_trail = current_trail_prims[0];

float max_search_radius = 5; 

// string mask to tell xyzdist to IGNORE our own trail prim
string ignore_self_mask = "!" + itoa(current_trail);

int closest_prim;
vector closest_uv;

float min_midair_dist = xyzdist(0, ignore_self_mask, @P, closest_prim, closest_uv, max_search_radius);

float max_allowed_pscale = chf("max_pscale_limit"); 

if (min_midair_dist >= max_search_radius) {
    f@pscale = max_allowed_pscale; 
} else {
    float safe_radius = min_midair_dist * 0.5;
    
    f@pscale = min(safe_radius, max_allowed_pscale); 
}

A prerequisite is to have 1 unique prim for each curve. We are looking up the prim ID of the current curve/point trail and we create a mask string (for example !0, !1, etc). We will use that mask in xyzdist function which will ignore pt's own curve when searching for closest position on target geo. This approach uses function's all available arguments.
float xyzdist(<geometry>geometry, string primgroup, vector origin, int &prim, vector &uv, float maxdist)
So now we have closest point on any other curve segment in 3D space. If a neighbouring curve is close we calculate a distance radius so tubes don't intersect once we drop Sweep node. It will default to max pscale slider if it doesn't find anything within max search radius.

I've attached a hip file and I encourage you to take a look. Both these methods yield essentially the same result but xyzdist method is a bit more accurate and computes way faster compared to neighbour one.
Edited by Italimpex Productions - Aug. 25, 2026 07:25:45

Attachments:
NEIGHBOUR_VS_XYZDIST.hip (415.8 KB)

Italimpex Productions
CGI Studio specializing in directing and producing advertisements & films
User Avatar
Member
5 posts
Joined: Sept. 2024
Online
Thank you all for the thorough answers! I certainly appreciate the really in-depth answers, though I found that something as simple as
int near = nearpoints(0, @P, 2)[1];
i@near = near;

is all I needed, haha.

Cheers.
  • Quick Links