Polygon Islands Convenience Method?



  • Hi!

    Is there an available convenience method for getting all polygon islands in a polygon object? It seems like one exists internally for things like the Texture > View > Multi-Color Islands option.

    2c161963-9b33-4c89-8763-d017ddae1ece-image.png

    Specifically, I'm looking for polygon islands/shells, but an additional command/option for UV Islands would be great too.

    If one doesn't exist, I'd like to request that one be added to the Python SDK.

    Thank you,

    Donovan



  • Hi @dskeith,

    thank you for reaching out to us. There is unfortunately no solution out of the box with which you can achieve this. And it is also rather unlikely that we will add such a functionality. To implement it yourself, you could make use of either:

    • c4d.utils.SendModelingCommand with MCOMMAND_SELECTCONNECTED. This will work with Ngons, but will come with a lot of overhead due to SMC.
    • c4d.utils.Neighbor. You can also make this work for Ngons, but it will bee harder because you have to deal then with the translation maps. Algorithmically this will be "cleaner".

    You will find below an example for the first option.

    Cheers,
    Ferdinand

    edit: For uvs things would work in a similar fashion.

    """On how to select polygon islands.
    
    As discussed in:
        https://plugincafe.maxon.net/topic/13194
    
    Written for R23.
    """
    
    import c4d
    
    
    def GetPolygonIslandSelections(node):
        """Yields connected polygon island id lists for a polygon mesh.
        
        Args:
            node (c4d.PolygonObject): Description
        
        Yields:
            list[int]: An island of polygon ids.
        """
        if not isinstance(node, c4d.PolygonObject):
            raise TypeError(f"Expected PolygonObject for node, received: {node}")
    
        # The polygon selection of the node and some stuff.
        selection = node.GetPolygonS()
        count = node.GetPolygonCount()
        polygonIdSeeds = list(range(count))
    
        def selectConnected(node, index):
            """Selects the connected polygons for a given polygon index.
            """
            selection.DeselectAll()
            selection.Select(index)
    
            kwargs = {"command": c4d.MCOMMAND_SELECTCONNECTED,
                      "list": [node],
                      "mode": c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                      "bc": c4d.BaseContainer(),
                      "doc": doc}
    
            return c4d.utils.SendModelingCommand(**kwargs)
    
        # We could also write "while polygonIdSeeds:", but while loops give me
        # anxiety ^^
        for _ in range(count):
            # Select all polygons connected to the first polygon id in our seeds.
            if not selectConnected(node, polygonIdSeeds[0]):
                raise RuntimeError("Something went wrong.")
    
            # Evaluate the new selection state.
            island = [i for i, v in enumerate(selection.GetAll(count)) if v]
            # Yield the island.
            yield island
    
            # Intersect our remaining seed ids with that island.
            polygonIdSeeds = [pid for pid in polygonIdSeeds
                              if pid not in island]
            # Break out if we exhausted all of them.
            if not polygonIdSeeds:
                break
    
        # Clean up the selection.
        selection.DeselectAll()
    
    def CreatePolygonSelectionTag(node, selectionIds):
        """Generates a polygon selection tag for the given polygon ids.
        """
        if not isinstance(node, c4d.PolygonObject):
            raise TypeError(f"Expected PolygonObject for node, received: {node}")
    
        # The polygon selection.
        selection = node.GetPolygonS()
         # Clean up the selection.
        selection.DeselectAll()
    
        for index in selectionIds:
            selection.Select(index)
    
        # Create a selection tag fur the current polygon selection.
        kwargs = {"command": c4d.MCOMMAND_GENERATESELECTION,
                  "list": [node],
                  "mode": c4d.MODELINGCOMMANDMODE_POLYGONSELECTION,
                  "bc": c4d.BaseContainer(),
                  "doc": doc}
    
        return c4d.utils.SendModelingCommand(**kwargs)
    
    
    
    def main():
        """Entry point.
        """
        i = 0
        for island in GetPolygonIslandSelections(op):
            if not CreatePolygonSelectionTag(op, island):
                raise RuntimeError("Could not create polygon selection tag.")
            i += 1
        c4d.EventAdd()
        print (f"Added {i} selection tags.")
    
    if __name__ == '__main__':
        main()
    
    


  • And another good resource is the Calculate Group per Polygon from Cesar Vonc.
    Cheers,
    Maxime.



  • here is a script i use to color polygon groups. unfortunately a bit slow

    import c4d
    from c4d import gui
    import random
    
    def main():
        random.seed(666)
    
        c4d.CallCommand(12139) # point mode
        selection  = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)
        for s in selection:
    
            cnt = s.GetPointCount()
            tag = c4d.VariableTag(c4d.Tvertexcolor, cnt)
    
    
            data = tag.GetDataAddressW()
    
            bs = s.GetPointS()
    
            done = []
    
            for i in range(cnt):
    
                if i in done: continue
    
                r = random.random()
                g = random.random()
                b = random.random()
    
                c = c4d.Vector4d(r,g,b,1)
    
                bs.DeselectAll()
                bs.Select(i)
    
                c4d.CallCommand(12557)
    
                sel = bs.GetAll(cnt)
    
                for index, selected in enumerate(sel):
                    if not selected: continue
    
                    done.append(index)
                    c4d.VertexColorTag.SetColor(data, None, None, index, c)
    
                done = list(set(done))
    
            s.InsertTag(tag)
    
    # Execute main()
    if __name__=='__main__':
        main()
    


  • Wow! What an incredible set of answers. You've each addressed a different thing I intended to do with these polygon groups once I had access to them.

    Thank you @ferdinand @m_adam and @pyr!


Log in to reply