Welcome to od|forum

Register now to gain access to all of our features. Once registered and logged in, you will be able to contribute to this site by submitting your own content or replying to existing content. You'll be able to customize your profile, receive reputation points as a reward for submitting content, while also communicating with other members via your own private inbox, plus much more! This message will be removed once you have signed in.

magneto

Is there a way to get primitives using a point?

29 posts in this topic

I am wondering if this is possible to do in a python SOP? Basically something that would return the primitives that uses a particular point.

I looked at the help and in the forums but couldn't find anything.

If there is an hscript expression that would work too.

Thanks :)

Share this post


Link to post
Share on other sites

Basically something that would return the primitives that uses a particular point.

You could build a tree for the bounding boxes of the primitives and find the prims that intersect the location of the point.

Might be faster than iterating over all primitives to see if they use a point.

Or use hou.Geometry.nearestPrim if you only need one prim.

Share this post


Link to post
Share on other sites

Thanks rdg. How do you build a tree for the bounding boxes of primitives? Actually I also don't know how to get the bounding box of a primitive :)

Is there an expression for that? My geometry is a single connected polygon mesh, not sure if that matters.

Share this post


Link to post
Share on other sites

Thanks rdg. How do you build a tree for the bounding boxes of primitives? Actually I also don't know how to get the bounding box of a primitive :)

Is there an expression for that? My geometry is a single connected polygon mesh, not sure if that matters.

Good question, don't know. Probably would end up using hou.Geometry.nearestPrim, if I had to use python.

Or put the point into a group. Put $PR onto all prims, convert the point group into a prim group and look at the stored attribute?

That sounds pretty Houdini.

1 person likes this

Share this post


Link to post
Share on other sites

Thanks, that's a cool trick. I guess I could use a Group SOP to convert each point to prims and then read the group contents in python. I was trying to keep everything in python.

Interestingly there is a way to get the points of a primitive in python, but not the other way around :)

Share this post


Link to post
Share on other sites

Sorry. My CTRL+C/CTRL+V wasn't working as it should.

Interestingly there is a way to get the points of a primitive in python, but not the other way around :)

Yes, there is:

# This code is called when instances of this SOP cook.
node = hou.pwd()
geo = node.geometry()

# Add code to modify the contents of geo.
pointnumber = node.evalParm('val')

def GetAllPoints():
    """ use it to get points of each primitive """
    dict = {}
    for prim in geo.prims():
        points = []
        for verticle in prim.vertices():
            points.append(verticle.point().number())
        dict[prim.number()] = points
    return dict


def GetPointPrimitives(dict, pointnumber):
    """ use it to get primitives that uses this point """
    prims = []
    for k, v in dict.items():
        if pointnumber in v:
            prims.append(k)
    return prims

# MAIN()        
print(GetPointPrimitives(GetAllPoints(), pointnumber))

Edited by mantragora
1 person likes this

Share this post


Link to post
Share on other sites

Thanks mantragora, that's the method I was talking about. But looking up the prims from the points would be slow. You could make another dictionary from yours where point numbers would be the keys but that would be even slower to construct :)

These are the kinds of solutions I don't like implementing because they are not scalable. If I had 10 points in a mesh with 100 points, and it takes 1 ms for cooking my SOP, having the same 10 points in a mesh with 1 million points will be 10000 slower, which would be 10 seconds (just an example), but it shouldn't be. I shouldn't pay that price because I am not modifying the whole mesh.

Reminds me Edit Poly limitations in Max (not Editable Poly), where even setting the position of a vertex/point would be an epic undertaking :)

Share this post


Link to post
Share on other sites

Thanks mantragora, that's the method I was talking about. But looking up the prims from the points would be slow. You could make another dictionary from yours where point numbers would be the keys but that would be even slower to construct :)

These are the kinds of solutions I don't like implementing because they are not scalable. If I had 10 points in a mesh with 100 points, and it takes 1 ms for cooking my SOP, having the same 10 points in a mesh with 1 million points will be 10000 slower, which would be 10 seconds (just an example), but it shouldn't be. I shouldn't pay that price because I am not modifying the whole mesh.

Reminds me Edit Poly limitations in Max (not Editable Poly), where even setting the position of a vertex/point would be an epic undertaking :)

Use InlineCPP.

Share this post


Link to post
Share on other sites

Use InlineCPP.

I want to, but don't want to take a huge detour from the task at hand :)

Do you know which functions would be of interest for this? Although if I use the HDK, I would want to solve this using edges. Not sure if they exist there but this primitive thing I am trying is some sort of a dirty way for me to construct edge loops :D

Share this post


Link to post
Share on other sites

There is an old documentation example in inlineCpp section called "point_ref_utils" that looks like it may guide you a little:

The following example shows how to return an array of array of structures, where each structure contains two integers named prim_id and vertex_id. It creates a function that receives a hou.Geometry object and, for each point in it, returns the vertices (as primitive and vertex ids) that reference that point.

but it uses deprecated GB library so it doesn't work in H12. And it's not for edges. Just messing with vertex, point, primitive.

I haven't poked new GA stuff yet so can't help with it.

Edited by mantragora
1 person likes this

Share this post


Link to post
Share on other sites

Given the realities of production and available technology, quite often there is no way to go about things other than doing them the 'slow' way. In this case, doing what you want using inlinecpp is quite trivial, if you have access to it. However, if you can't use that then you are forced to go with a pure Python way which can indeed be slow for large meshes. Depending on the situation and how you need the information, the slow method might not be so bad. For example, to do this with just Python I'd use something like the following:

def buildPointPrimRefMap(geo):
    """ Build a dictionary whose keys are hou.Point objects and values
        are a list of hou.Primitive objects that reference the point.
    """
    point_map = {}

    for prim in geo.prims():
        for vert in prim.vertices():
            pt = vert.point()

            if not pt in point_map:
                point_map[pt] = []

            point_map[pt].append(prim)

    return point_map

This results in a dictionary where I can use a hou.Point to get any prims that reference it.

Now if the geometry has a huge number of points and/or primitives, it would definitely be slow to calculate. However, if I could create my system so that it only ever had to calculate once per use then perhaps it wouldn't be so bad. I could do this if I had a Python SOP that only needed to cook once in my session. If the node needed to be time dependent due to deforming but not changing topology then perhaps on its first cook it could stash the resulting structure into the cached user data and reuse it. If the topology was going to be constantly changing, or I needed this as a one off query then there's really not a lot you can actually do other than to suffer through the slowness of processing huge amounts of geometry data with Python.

Using inlinecpp:

cpp_geo_methods = inlinecpp.createLibrary("cpp_geo_methods",
includes="""#include <GU/GU_Detail.h>""",
structs=[("IntArray", "*i"),],
function_sources=[
"""
IntArray connectedPrims(const GU_Detail *gdp, int idx)
{
    std::vector<int>    ids;

    GA_Offset           ptOff, primOff;
    GA_OffsetArray      prims;

    GA_OffsetArray::const_iterator prims_it;

    ptOff = gdp->pointOffset(idx);

    gdp->getPrimitivesReferencingPoint(prims, ptOff);

    for (prims_it = prims.begin(); !prims_it.atEnd(); ++prims_it)
    {
        ids.push_back(gdp->primitiveIndex(*prims_it));
    }

    return ids;
}
""",])

def connectedPrims(point):
    """ Returns a tuple of primitives connected to the point. """
    geo = point.geometry()

    result = cpp_geo_methods.connectedPrims(geo, point.number())

    return geo.globPrims(' '.join([str(i) for i in result]))

Edited by graham
2 people like this

Share this post


Link to post
Share on other sites

having the same 10 points in a mesh with 1 million points will be 10000 slower, which would be 10 seconds (just an example), but it shouldn't be. I shouldn't pay that price because I am not modifying the whole mesh.

That's why I meant you should build a proper tree. Searches likes this always are expensive, that's why people invented search trees :)

Having said that, I guess they're not as exposed in Houdini as Houdini's strength is the 'procedural node based workflow'.

So converting a point selection in to a primitive groups is done with a node network rather than writing code.

Pyhton's overrates anyway. It's a nice rapid prototyping tool, but you shouldn't use it to undermine Houdini's core strengths.

There are plenty of applications that don't work at all if you don't use Python, if you're really want to write code.

Share this post


Link to post
Share on other sites

So I made small performance test with code in Python that you can find in my previous post here and the same code just rewrited to inlineCPP. Just if anyone was curious is it worth it to rewrite.

On attached picture you can see timings from:

testpythonsop1 - this is clear Python version

inlinecpptests1 - this is Python with InlineCpp version

Check amount of polygons in tests to compare how it scales. I used gridSOP->subdivideSOP(set to 2)->triangulateSOP->and here is Python operator. Starting point was 5 divisions in grid on both axes. Between each test there is 5 divisions difference. Last test is for 30 divisions.

Perfomance is really nice with InlineCpp but writing code in it is the worst experience you can get. Debugging = Freddy "The" Kruger "The" Nightmare ;).

Below is my InlineCpp code. Sorry if it is not optimized as it should be. I can't write anything better without access to VisualStudio and Visual Assist.

# This code is called when instances of this SOP cook.
node = hou.pwd()
geo = node.geometry()

# Add code to modify the contents of geo.
pointnumber = node.evalParm('val')

import inlinecpp

geofuncs = inlinecpp.createLibrary( 
"example", 
acquire_hom_lock=True,
structs=( ("IntArray", "*i"), ),
includes=""" 
#include <GU/GU_Detail.h> 
#include <algorithm>
""",
function_sources=["""

IntArray GetAllPrims(GU_Detail *gdp, int pointnumber)
{
    std::vector<int> polys;
    std::vector<int> points;
    std::vector<std::vector<int>> pointlist;

    GEO_PrimList prims = gdp->primitives();
    for(int i = 0; i < prims.entries(); i++)
    {
        polys.push_back(i);
        for(int j = 0; j < prims[i]->getVertexCount(); j++)
        {
            int pointnum = prims[i]->getVertexElement(j).getPointIndex();
            points.push_back(pointnum);
        }
        pointlist.push_back(points);   
        points.clear(); 
    }

    std::vector<int> polylist;
    for(int j = 0; j < polys.size(); j++)
    {
        if(std::find(pointlist[j].begin(), pointlist[j].end(), pointnumber) != pointlist[j].end())
        { polylist.push_back(j); }   
    }
    return polylist;
}

"""])

#MAIN()
result = geofuncs.GetAllPrims(geo, pointnumber)
list = []
for r in result: 
    list.append(r)
print(list)

post-7494-134058582625_thumb.jpg

Edited by mantragora
1 person likes this

Share this post


Link to post
Share on other sites

Thanks alot guys, I will have to try these out. The inlinecpp examples you wrote looks pretty fast.

@rdg: I know, but there is a price you pay when you construct accelerated data structures, and you pay it upfront, which is what I want to avoid for my trivial task. Not the mention the speed trade off will also be paid in memory.

Share this post


Link to post
Share on other sites

I think Graham's inlinecpp wrapper for getPrimitivesReferencingPoint() should be faster.

1 person likes this

Share this post


Link to post
Share on other sites

I think Graham's inlinecpp wrapper for getPrimitivesReferencingPoint() should be faster.

Yeah, 3 times with 30 division grid :). It's 1110 vs 33 vs 11 ms. Is it because of this new GA stuff ?

Edited by mantragora
1 person likes this

Share this post


Link to post
Share on other sites

There's really no point in trying to do anything fancy here since this is a simple case where all the information you need is easily available through the HDK already.

Edward is right in that the code I posted is faster. You probably can't get much faster than that simple block of code. In a comparison using the code mantragora posted above and my previous code, on a piece of geometry with ~1 million points and polygons, to return the connected prims his code took ~360ms while mine took ~0.59ms. The reason for this is that his code needs to painstakingly build a data structure by iterating over all the prims and vertices several times. In large scale geometry such as this test, while technically still quite fast, it is doing way more work than it needs to. Houdini already knows about this data so it's totally overkill to try and build it yourself. Using ~14000 points like the more complex test above yields his code down to ~9ms while mine maintains about the same as before.

Also, a tip for testing performance in a read only cases like this: It is often better to determine efficiency by not using a Python SOP that processes actual geometry. If your SOP needs to cook and copy geometry from its input you will always incur some slowdown. It is more useful to reference geometry from another SOP where you can more easily get the geometry as read only and not have to incur any additional overhead. All my tests above were run this way.

Edited by graham
1 person likes this

Share this post


Link to post
Share on other sites

Is it because of this new GA stuff ?

Yes, getPrimitivesReferencingPoint() is new with GA.

1 person likes this

Share this post


Link to post
Share on other sites

Thanks guys for valuable opinions.

Btw Graham, how do you do what you mentioned in your last paragraph? You are saying a Python Geometry SOP, even if it just reads some data, will copy the whole geometry, right? How do you prevent this? Is this a workflow trick? :)

Edit:

@Edward, if getPrimitivesReferencingPoint is new, how did you guys do it before? :)

Btw do you also have functions like:

getEdgesReferencingPoint

getEdgeInGeometry

I saw an Edge class with Point a, b just didn't see any method for these after a quick glance.

Edited by magneto

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now