SOLVED Get Point Positions of Selected N-Gon or Polygon

What's the simplest way to get all point positions of a selected Polygon (including N-Gons)?
Cheers

Hi,

you forgot to tag your question, but from your history I assume this a Python question. There is PolygonObject. GetPolygonTranslationMap and PolygonObject.GetNGonTranslationMap which will allow you to translate between Ngon and Quads/Tri indices. Which in turn will you allow to infer the point indices.

But note that there is no Ngon type in Python. Which means that interacting with them is quite limited. One workaround is to use SendModellingCommand to let Cinema's core do whatever you are trying to do with the Ngons.

Cheers,
zipit

Speaking of PolygonObject.GetNGonTranslationMap, the Python description seems to be wrong (copied from the C++ docs?). The doc says

ngoncnt (int) – The number of N-gons. Pass the length of the list returned by GetPolygonTranslationMap(), not the value given by GetNgonCount().

but the number needed here is not the length of the list (second return value), but the first return value of GetPolygonTranslationMap.

For the return value, the doc explains

The number of polygons in N-gon i is stored in list[i][0].
Then the polygon indices are stored in list[i][j] where j goes from 1 to list[i][0].

but the number of polygons is not stored at all. The sublists are simply the polygon indices, and the number must be inferred with the len() function.

(Newest online docs, tested with R21)

hello,

you have a old post explaining what are ngons here

I've assumed the selected polygon or ngon are done by the user. If so, every polygon that compose a N-gon will be selected.
Ngon doesn't exist in c4d, there's only tri and quads. Ngon are a group of tri or quads.

In the object itself, we manage quads and tri.

So you have to retrieve the selected polygon, convert that selection to points selection. Retrieves the coordinates of the selected points.

This is the code without error check but it make it shorter and easier to understand.

import c4d
from c4d import gui
# Welcome to the world of Python


def ConvertFromPolyToPoint(obj):
    #Retrieves the selected polygons
    bs = obj.GetPolygonS()
    #Gets All Points and point count
    points = obj.GetAllPoints()
    pcnt = obj.GetPointCount()
    
    # Gets the Boolean array for every polygons
    selectedPolygon = bs.GetAll(pcnt)
    # Creates a BaseSelect for points (convert polygon selection to point selection)
    bsPoint = c4d.BaseSelect()
    
    # Checks all polygon to see if they are selected
    for i, selected in enumerate(selectedPolygon):
        if selected:
            # select all the points that belong to that polygon
            cPoly = op.GetPolygon(i)
            bsPoint.Select( cPoly.a)
            bsPoint.Select( cPoly.b)
            bsPoint.Select( cPoly.c)
            bsPoint.Select( cPoly.d)
    # Retrieves the selected point as an array
    selectedPoint = bsPoint.GetAll(pcnt)
    return selectedPoint
   

def GetSelectedPointPosition (obj):
    # Converts the selected polygon to an array of selected points
    selectedPoint = ConvertFromPolyToPoint(obj)
    # Retrieves all the points
    points = obj.GetAllPoints()
    
    pointsPosition = []
    for point, selected in zip(points, selectedPoint):
        if selected:
            pointsPosition.append(point)
    
    return pointsPosition
# Main function
def main():
    pointsPosition = GetSelectedPointPosition(op)
    print (len (pointsPosition), pointsPosition)
   
   
    # Execute main()
if __name__=='__main__':
    main()

Of course, if you want to retrieve the position of all the ngons of an object than you have to retrieves the ngon list first.

@Cairyn
you are absolutely right, i will update the documentation right now.

Cheers,
Manuel

Thank you everyone, and I'll make sure to tag my posts next time.

HI:
I found a way to get the global coordinates and indexes of all the points on N_gons.The Python script can select all N_gons faces, select points on all N_gons faces, and get the global coordinates and index of the points.

Two Suggestions by the way:

1.GetAllPoints() is used to get all the coordinates, but these are local coordinates, which need to be converted to global coordinates, and the results converted to global coordinates are usually correct.However, it is difficult to obtain the global coordinates of all points in the cache. Can you provide global coordinate parameters for all points?

  1. After the polygon object is baked as. ABC file, if the number of polygons is 0, the objects in the cache will change from polygons to blank objects.Can the cache still be a polygon object if the number of points is zero?

The script code is as follows:

import c4d
from c4d import utils

def main():
    
    Polygons = op.GetAllPolygons()

    Selected_Polygon = op.GetPolygonS()
    
    #Determines whether n-gons edges exist on all polygon faces of the current object.
    N_gons = op.GetNgonEdgesCompact()

    Polygons_index  = [i for i,o in enumerate(Polygons)]
    
    #N-gons edge Numbers on all N-Gons faces.
    N_gons_Edge_number = [h for h,z in enumerate(N_gons) if z > 0]

    #select N-gons face.
    Selected_Polygon.DeselectAll()
    sele = [ Selected_Polygon.Select(ty) for ty in N_gons_Edge_number
            if len(Polygons_index) == len(N_gons) ]
    
    #Toggle select mode.
    bc = c4d.BaseContainer()
    bc.SetData(c4d.MDATA_CONVERTSELECTION_LEFT, 2)        #ConvertSelection's Left "Polygons" option in memory
    bc.SetData(c4d.MDATA_CONVERTSELECTION_RIGHT, 0)       #ConvertSelection's Right "Edges" option in memory
    #bc.SetData(c4d.MDATA_CONVERTSELECTION_TOLERANT, True) #Select the selection->ConvertSelection's "Tolerant" option in memory
    utils.SendModelingCommand(c4d.MCOMMAND_CONVERTSELECTION, 
                                list = [op], 
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION, 
                                bc=bc, 
                                doc = doc)

    #Gets the global coordinates and index of the points on the N-gons plane.
    All_Point = op.GetAllPoints()
    PointS = op.GetPointS()
    Pointe_Array = PointS.GetAll(op.GetPointCount())

    All_posits = [(Arra,index) 
                    for Arra,index in enumerate(All_Point)]
                    
    posit = [(Array,indexe) 
            for Array,indexe in enumerate(All_Point) 
            if Pointe_Array[Array] == 1]

    #The global matrix of the current object.
    op_matrix = op.GetMg()

    matrix = []
    for orientation in posit:
        mt = c4d.Matrix()
        mt.off = orientation[1]
        m_mt = op_matrix * mt
        matrix.append((orientation[0],m_mt))
    
    #Global coordinates and index of points on the N-GONS plane.
    point_position = [(t[0],t[1].off) for t in matrix]
    print point_position
    
    #Select all points on the N-gons side.
    PointS.DeselectAll()
    sele_point = [ PointS.Select(ty[0]) 
                for ty in point_position]
    
    c4d.EventAdd()

if __name__=='__main__':
    main()

@x_nerve Thanks!

@x_nerve said in Get Point Positions of Selected N-Gon or Polygon:

1.GetAllPoints() is used to get all the coordinates, but these are local coordinates, which need to be converted to global coordinates, and the results converted to global coordinates are usually correct.However, it is difficult to obtain the global coordinates of all points in the cache. Can you provide global coordinate parameters for all points?

2.After the polygon object is baked as. ABC file, if the number of polygons is 0, the objects in the cache will change from polygons to blank objects.Can the cache still be a polygon object if the number of points is zero?

  1. yes i forgot it was local coordinate but global coordinates are just the matrix of the object * the local coordinates:
    points = op.GetAllPoints()
    mg = op.GetMg()
    points = [mg * i for i in points]
    print points

If you retrieve the cache, you will retrieve a polygon object. So i don't understand why it will be difficult to retrieve the points's position. Retrieving an updated cache yes, it can sometimes be a kind of headache. Can you be a bit more clear about that ?

2- I didn't checked the result of the export alembic file (nor our code) but what do you mean "as blank objects" a null object ?
If you have a good argument for that, i will be happy to add a suggestion in our database and ping our devs to change that. Unless they have a really good reason to return a blank object they will be also happy to update it.

Cheers,
Manuel

HI:
I found that if a polygon object has zero points on frame 0, using Alembic baking, it will only bake into blank objects.But as long as the initial number of points is not zero, the baking is normal.

The Python Tag script code is as follows, really just baking into blank objects.


import c4d

def main():

    obj = op.GetObject()
    frame = doc.GetTime().GetFrame(doc.GetFps())

    if frame >= 25:
        obj.ResizeObject(4,1)
        points = [c4d.Vector(0, 0, 0),c4d.Vector(-100, 0, 0),
                c4d.Vector(-100, 100, 0),c4d.Vector(0, 100, 0)]

        obj.SetAllPoints(points)

        cpolygon = c4d.CPolygon(0,1,2,3)

        obj.SetPolygon(0,cpolygon)

        obj.Message(c4d.MSG_UPDATE)
        c4d.EventAdd()

    else:
        obj.ResizeObject(0, 0)
        obj.Message(c4d.MSG_UPDATE)
        c4d.EventAdd()

    if frame >= 65:
        obj.ResizeObject(0, 0)
        obj.Message(c4d.MSG_UPDATE)
        c4d.EventAdd()

if __name__=='__main__':
    main()

@x_nerve said in Get Point Positions of Selected N-Gon or Polygon:

I found that if a polygon object has zero points on frame 0, using Alembic baking, it will only bake into blank objects.But as long as the initial number of points is not zero, the baking is normal.

Thanks a lot for the feedback, I could reproduce that on S22 but not on R23, so it's seems to be already fixed.

If you found some other bugs, feel free to report them to our support team -> https://support.maxon.net/index.php?lang=en_US
This one in particular isn't related to our API so there's no need to report to us.

Cheers,
Manuel

Hi:

This is a bit of a hassle, but can still be scripted, who CARES about an extra polygon.

import c4d

def main():

    polygons = op.GetAllPolygons()
    points = op.GetAllPoints()
    point = [c4d.Vector(10000, 10000, 10000),c4d.Vector(9999, 10000,10000),
            c4d.Vector(9999, 10000, 10001),c4d.Vector(10000, 10000, 10001)]

    point_indexe = [[len(points) + i ,point[i]]
                    for i in range(len(point))]

    op.ResizeObject(len(points) + len(point),len(polygons) + 1)

    set_point = [op.SetPoint(k[0], k[1])
                    for k in point_indexe]

    op.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()

if __name__=='__main__':
    main()