Get all ngons [SOLVED]

On 23/11/2016 at 03:27, xxxxxxxx wrote:

Is there a way for getting each group of ngon?

Here my first attempt who is working 90% time but that mean the obj need to be inserted into the document and is not optimized at all on huge mesh.

def deconnexion(obj) :
    doc = c4d.documents.GetActiveDocument()   
    
    settings = c4d.BaseContainer()
    settings[c4d.MDATA_DISCONNECT_PRESERVEGROUPS] = False
    test = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DISCONNECT,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                bc = settings,
                                doc = doc)
  
def get_ngnon(obj) :
    save_before = obj.GetClone()
    mode = doc.GetMode()
    doc.SetMode(c4d.Mpolygons)
    
    deconnexion(obj)
    selection = obj.GetPolygonS()
    selection.DeselectAll()
    
    buffer_all_ngons = []
    poly_to_check = range(0,obj.GetPolygonCount())
  
    for polygon_index in xrange(obj.GetPolygonCount()) :
        selection.DeselectAll()
        #Si notre l'index est dans le poly to check
        if polygon_index in poly_to_check:
            #On select notre poly
            selection.Select(polygon_index)
            
            # On selectionne les élements connecté
            c4d.CallCommand(12557)
            
            #Si il y a plus de 1 poly c'est que c'est un ngno
            if selection.GetCount() >= 2:
                count = op.GetPolygonCount()
                buffer = []
                for i in xrange(count) :
                    if selection.IsSelected(i) :
                        if i in poly_to_check: poly_to_check.remove(i)
                        buffer.append(i)
                buffer_all_ngons.append(buffer)
                
    doc.SetMode(mode)
    save_before.CopyTo(obj,0)
            
    return buffer_all_ngons

Here is another attempt here you can see my scene http://img4.hostingpics.net/pics/432338ngon.jpg
poly id 0 = quad 
all other are ngon

In this attempt I only try to have all ngons.
But finaly I would like to have the same output as my first attempt.

def get_ngon_v2(obj) :
    edge_ngon = list(set(obj.GetNgonEdgesCompact()))
    all_ngons = []
    
    nbr = c4d.utils.Neighbor()
    nbr.Init(obj)
    for i in xrange(op.GetPolygonCount()) :
      pli = nbr.GetPolyInfo(i)["edge"]
      for edge in pli:
          if edge in edge_ngon:
              all_ngons.append(i)
              
    return list(set(all_ngons))

Thanks in advance 🙂

On 25/11/2016 at 09:54, xxxxxxxx wrote:

Hi,

Are you trying to get all polygons inside one N-gon?

You're first solution works with my tests but the second that uses PolygonObject.GetNgonEdgesCompact() can't work (the function doesn't return edge indices).

N-gon support is limited in Python. C++ API PolygonObject::GetAndBuildNgon() gives information for all N-gons of an object.
In Python it's only possible to get the number of N-gons via PolygonObject.GetNgonCount().

On 25/11/2016 at 11:04, xxxxxxxx wrote:

Not sure if this helps.
But here are a couple of scripts I've used to get n-gons.

Example #1

#This script retrieves a list that contains n-gon information for each selected polygon  
  
import c4d  
def main() :  
    
  obj = doc.GetActiveObject()  
  if obj is None: return False  
    
  ngons = []  
        
  EdgeS = obj.GetPolygonS()   
  polyCount = obj.GetPolygonCount()     
  allEdges = obj.GetNgonEdgesCompact()  
    
  for poly in xrange(polyCount) :  
      if EdgeS.IsSelected(poly) :   
          selPoly = poly  
          if allEdges[selPoly] !=0:  
              #print "index: ",selPoly, "poly is an ngon"  
              ngons.append(selPoly)  
    
  print ngons  
    
  c4d.EventAdd()  
  
if __name__=='__main__':  
  main()

Example #2

#This script returns all of the polygons in an object that are n-gons using the mark option  
#Not fully tested!! Might not work in every situation!!  
  
import c4d  
def main() :  
  
  obj = doc.GetActiveObject()  
  polys = obj.GetAllPolygons()  
  polyCount = obj.GetPolygonCount()  
    
  nbr = c4d.utils.Neighbor()  
  nbr.Init(obj)  
    
  for poly in xrange(polyCount) :          
      pli = nbr.GetPolyInfo(poly)  
      #print pli["mark"]  
    
      m = pli["mark"]  
      if m[0]==False and m[1]==True and m[2] == True and m[3]==True:  
          print "polygonID: ", poly, "is an ngon"  
        
      else: print "Not ngon"      
            
  c4d.EventAdd()  
  
if __name__=='__main__':  
  main()

-ScottA

On 27/11/2016 at 08:37, xxxxxxxx wrote:

Yes I'm trying to get all polygons inside one N-Gon.
I thinked about getting all ngon and then with GepolyInfo if polygon is in the all_ngon_list than do something but the porblem is if all ngon are connected that will never tell me wich polygon belong to wich ngon.

Yes my first function work but not in all case. Anyway I will use it and optimize it using GetPolyInfo instead of selection which is not a great things ^^

Yeah maybe I will port my code to C++ but I'm not an expert in C++ ^^'.
Hooo so what are the value returned by GetNgonEdgesCompact()?.
Anyway thanks for your highlight.

Thanks scotta for yours scripts but both of them are not usefull in my case. Since my script change uv randomly by polygon. I need to know which polygon belong to wich ngon(like I do in my first script).
Then I can random a whole ngon with the same seed.

So I think I will stick to my first try and jsut optimize the selection function 🙂

On 28/11/2016 at 01:49, xxxxxxxx wrote:

For people who want to try here is the optimized function. Maybe to much speedy for float in python since the first method( get_ngonv2) return me an execution time of 0.0. But I guess it's a float error.

import c4d
import time
  
def deconnexion(obj) :
    doc = c4d.documents.GetActiveDocument()   
    
    settings = c4d.BaseContainer()
    settings[c4d.MDATA_DISCONNECT_PRESERVEGROUPS] = False
    test = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_DISCONNECT,
                                list = [obj],
                                mode = c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                                bc = settings,
                                doc = doc)
                                
def get_ngon(obj) :
    save_before = obj.GetClone()
    mode = doc.GetMode()
    doc.SetMode(c4d.Mpolygons)
    
    deconnexion(obj)
    selection = obj.GetPolygonS()
    selection.DeselectAll()
    
    buffer_all_ngons = []
    poly_to_check = range(0,obj.GetPolygonCount())
  
    for polygon_index in xrange(obj.GetPolygonCount()) :
        selection.DeselectAll()
        if polygon_index in poly_to_check:
            selection.Select(polygon_index)
            
            c4d.CallCommand(12557)
  
            if selection.GetCount() >= 2:
                count = obj.GetPolygonCount()
                buffer = []
                for i in xrange(count) :
                    if selection.IsSelected(i) :
                        if i in poly_to_check: poly_to_check.remove(i)
                        buffer.append(i)
                buffer_all_ngons.append(buffer)
                
    doc.SetMode(mode)
    save_before.CopyTo(obj,0)
            
    return buffer_all_ngons
    
def get_poly_info(nbr,id_poly) :
    poly_connected = [] 
    pli = nbr.GetPolyInfo(id_poly)["face"]   
    for face in pli:
        if face != c4d.NOTOK:
            poly_connected.append(face)
            
    return list(set(poly_connected))
    
def get_ngonv3(obj) :
    save_before = obj.GetClone()
  
    deconnexion(obj)
    
    all_poly_done = []
    all_ngons = []
    
    nbr = c4d.utils.Neighbor()
    nbr.Init(obj)
    for i in xrange(obj.GetPolygonCount()) :
        poly_to_do = []
        poly_ngon  = []
        poly_to_do.append(i)
        
        for id_poly in poly_to_do:
            if not id_poly in all_poly_done:
                buffer_poly_info = get_poly_info(nbr,id_poly)
                
                poly_to_do += buffer_poly_info
                
                poly_ngon.append(id_poly)
                
                all_poly_done.append(id_poly)
                
        if len(poly_ngon) :
            all_ngons.append(poly_ngon)
                
    save_before.CopyTo(obj,0)
    return all_ngons
  
  
def main() :
    t = time.time()    
    doc = c4d.documents.GetActiveDocument()
    obj = doc.GetActiveObject()
    print get_ngonv3(obj)
    print time.time() - t
    
    c4d.EventAdd()
  
    t = time.time()    
    doc = c4d.documents.GetActiveDocument()
    obj = doc.GetActiveObject()
    print get_ngon(obj)
    print time.time() - t
  
  
if __name__=='__main__':
    main()

As said before in some case it fail. Then be carefull using it !

But looks like no one get a better idea so I will  deal with it and mark it as solved.

On 30/11/2016 at 03:31, xxxxxxxx wrote:

Hi!

I've investigated the return values of PolygonObject.GetNgonEdgesCompact(). These give information on Ngon edges for each polygon so this function can be used to retrieve all Ngons from a polygon object.

The function returns a value (bit masked) for each polygon telling which of its edge(s) are actually from an Ngon. If the value is 0 the polygon isn't inside an Ngon.
Note the statement in the docs about the hidden Ngon edges is wrong. It only gives information on Ngon edges.
To check if a polygon edge is an Ngon edge use the condition:

edgeValue & (1 << edgeIndex) == 0

Where edgeValue is the value for a polygon returned by GetNgonEdgesCompact() and edgeIndex the polygon edge index between 0 and 3.

The following script selects all Ngon edges of a polygon object.
The function GetNgonEdges() is the most important part where the conversion from GetNgonEdgesCompact() 'local' edges values are converted to the actual polygon object indices.

import c4d
from c4d import utils
  
def GetNgonEdges(op, nbr) :
   # Edge selection for the Ngon edges
    edgeSel = c4d.BaseSelect()
    
    # Retrieve Ngon edges 'local' values for each polygon
    edgesInfo = op.GetNgonEdgesCompact()
    
    # Process each polygon
    polyCount = op.GetPolygonCount()
    for polyIdx in xrange(polyCount) :
        edgeValue = edgesInfo[polyIdx]
        if edgeValue == 0:
            continue
        
        # Retrieve polygon and polygon information
        poly = op.GetPolygon(polyIdx)
        polyInfo = nbr.GetPolyInfo(polyIdx)
        
        # For each edge of polygon
        for edgeIdx in xrange(4) :
            # Check if edge has been already processed
            if polyInfo["mark"][edgeIdx]:
                continue
            
            # Check if polygon edge is an Ngon edge
            if edgeValue & (1 << edgeIdx) == 0:
                # Select polygon object Ngon edge
                # polyInfo['edge'][edgeIdx] gives the polygon object edge index for the polygon edge at edgeIdx
                edgeSel.Select(polyInfo['edge'][edgeIdx])
    
    return edgeSel
  
def main() :
    if op is None or op.GetType() != c4d.Opolygon:
        return
    
    if op.GetNgonCount() == 0:
        return
    
    # Set Edges mode
    doc.SetMode(c4d.Medges)
    
    # Create and initialize a Neigbor instance for the object
    nbr = utils.Neighbor()
    nbr.Init(op)
    
    # Process polygon object and obtain Ngon edges
    edgeSel = GetNgonEdges(op, nbr)
  
    # Select Ngon edges
    op.SetSelectedEdges(nbr, edgeSel, c4d.EDGESELECTIONTYPE_SELECTION)
    c4d.EventAdd()
  
if __name__=='__main__':
    main()

Finally, GetNgonEdgesCompact() gives the info needed on Ngons and using it plus processing its data is fast.