Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Polygon Islands Convenience Method?

    Cinema 4D SDK
    r23 python
    5
    7
    1.4k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • dskeithD
      dskeith
      last edited by

      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

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        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://developers.maxon.net/forum/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()
        
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 1
        • M
          m_adam
          last edited by

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

          MAXON SDK Specialist

          Development Blog, MAXON Registered Developer

          1 Reply Last reply Reply Quote 0
          • P
            pyr
            last edited by pyr

            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()
            
            1 Reply Last reply Reply Quote 0
            • dskeithD
              dskeith
              last edited by

              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!

              1 Reply Last reply Reply Quote 0
              • Z zauhar referenced this topic on
              • P
                pyr
                last edited by

                this script breaks an poly object into island, colorized it by random, offset to center of bounding box and some noise. after that i connects all islands again.

                its super fast compared to my older "solution"

                import c4d
                from c4d import gui
                import random
                
                def AddVertexColor(s,center,rad):
                
                    cnt = s.GetPointCount()
                    tag = c4d.VariableTag(c4d.Tvertexcolor, cnt)
                
                
                    data = tag.GetDataAddressW()
                
                    bs = s.GetPointS()
                    bs.SelectAll(cnt)
                    done = []
                
                    points = s.GetAllPoints()
                
                
                    r = random.random()
                
                
                
                    for i in range(cnt):
                
                        g = (points[0]-center).GetLength()    / rad
                        b = c4d.utils.noise.Noise(points[0]*0.01,rad)
                        c = c4d.Vector(r,g,b)
                
                        c4d.VertexColorTag.SetColor(data, None, None, i, c)
                
                
                    s.InsertTag(tag)
                
                
                def main():
                    random.seed(666)
                
                
                    selection  = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)
                    for s in selection:
                        center = s.GetMp()
                        rad    = s.GetRad().GetLength()
                        c4d.CallCommand(12298)
                        c4d.CallCommand(17891)
                        mesh = s.GetDown()
                
                        while mesh:
                
                            AddVertexColor(mesh,center,rad)
                            mesh = mesh.GetNext()
                
                        c4d.CallCommand(100004768)
                        c4d.CallCommand(16768)
                # Execute main()
                if __name__=='__main__':
                    main()
                
                1 Reply Last reply Reply Quote 1
                • E
                  ELJeffery
                  last edited by ELJeffery

                  Here is something I recently used to count UV Islands as well as get some other info

                  import c4d
                  from collections import defaultdict
                  
                  def get_uv_islands(obj, tol=1e-5):
                      """
                      Compute UV islands for the given polygon object using its UVW tag.
                      This version retrieves the UV dictionary for each polygon using uvTag.GetSlow(polyIndex),
                      then groups connected polygons (sharing at least one UV coordinate) into islands
                      using an iterative flood-fill algorithm.
                      """
                      uvw_tag = obj.GetTag(c4d.Tuvw)
                      if uvw_tag is None:
                          c4d.gui.MessageDialog("The object does not have a UVW tag.")
                          return None
                  
                      poly_count = obj.GetPolygonCount()
                  
                      # Build maps:
                      # - face_to_uvs: mapping from polygon index to its set of rounded UV keys.
                      # - uv_to_faces: reverse mapping from each UV key to the set of polygon indices using that UV.
                      face_to_uvs = defaultdict(set)
                      uv_to_faces = defaultdict(set)
                  
                      def uv_key(vec):
                          # Round UV vector components to mitigate floating-point precision issues.
                          return (round(vec.x, 5), round(vec.y, 5))
                  
                      # Build connectivity maps based on each polygon's UV data.
                      for poly_index in range(poly_count):
                          poly = obj.GetPolygon(poly_index)
                          uv_data = obj.GetTag(c4d.Tuvw).GetSlow(poly_index)
                          # If the polygon is a triangle, remove the extraneous 'd' key.
                          if poly.IsTriangle() and 'd' in uv_data:
                              del uv_data['d']
                          for key in uv_data:
                              uv_vec = uv_data[key]
                              key_tuple = uv_key(uv_vec)
                              face_to_uvs[poly_index].add(key_tuple)
                              uv_to_faces[key_tuple].add(poly_index)
                  
                      # Use an iterative flood-fill to group connected faces.
                      islands = []
                      faces_left = set(range(poly_count))
                  
                      while faces_left:
                          island = []
                          start_face = next(iter(faces_left))  # Pick an arbitrary face from unvisited ones.
                          stack = [start_face]
                          while stack:
                              face_idx = stack.pop()
                              if face_idx not in faces_left:
                                  continue
                              faces_left.remove(face_idx)
                              island.append(face_idx)
                              # For every UV key in this face, add all neighboring faces.
                              for uv_val in face_to_uvs[face_idx]:
                                  for neighbor in uv_to_faces[uv_val]:
                                      if neighbor in faces_left:
                                          stack.append(neighbor)
                          islands.append(island)
                  
                      return islands
                  
                  def get_island_uv_center(obj, uvw_tag, island):
                      """
                      Given a polygon object, its UVW tag, and an island (list of polygon indices),
                      compute the UV bounding box and center.
                      """
                      # Initialize min/max with infinities.
                      min_u = float('inf')
                      max_u = -float('inf')
                      min_v = float('inf')
                      max_v = -float('inf')
                      # Gather unique UV coordinates from all faces in the island.
                      unique_uvs = {}
                      for face_idx in island:
                          poly = obj.GetPolygon(face_idx)
                          uv_data = uvw_tag.GetSlow(face_idx)
                          if poly.IsTriangle() and 'd' in uv_data:
                              del uv_data['d']
                          for key in uv_data:
                              uv = uv_data[key]
                              key_tuple = (round(uv.x, 6), round(uv.y, 6))
                              unique_uvs[key_tuple] = uv
                  
                      # Compute the bounding box.
                      for uv in unique_uvs.values():
                          if uv.x < min_u:
                              min_u = uv.x
                          if uv.x > max_u:
                              max_u = uv.x
                          if uv.y < min_v:
                              min_v = uv.y
                          if uv.y > max_v:
                              max_v = uv.y
                  
                      center_u = (min_u + max_u) / 2.0
                      # Invert V axis so the center is correct in UV space.
                      center_v = 1.0 - ((min_v + max_v) / 2.0)
                  
                      return center_u, center_v, min_u, max_u, min_v, max_v
                  
                  def main():
                      doc = c4d.documents.GetActiveDocument()
                      obj = doc.GetActiveObject()
                      if obj is None:
                          c4d.gui.MessageDialog("Please select a polygon object.")
                          return
                  
                      uvw_tag = obj.GetTag(c4d.Tuvw)
                      if uvw_tag is None:
                          c4d.gui.MessageDialog("The object does not have a UVW tag!")
                          return
                  
                      islands = get_uv_islands(obj)
                      if islands is None:
                          return
                  
                      num_islands = len(islands)
                      print("Number of UV islands:", num_islands)
                      for idx, island in enumerate(islands):
                          center_u, center_v, min_u, max_u, min_v, max_v = get_island_uv_center(obj, uvw_tag, island)
                          print("Island", idx, "has faces:", island)
                          print("Island Num",idx,"    Bounding Box: U [{:.6f}, {:.6f}], V [{:.6f}, {:.6f}]".format(min_u, max_u, min_v, max_v))
                          print("                 Center: U = {:.6f}, V = {:.6f}".format(center_u, center_v))
                  
                  if __name__=='__main__':
                      main()
                  

                  Output looks something like

                  Number of UV islands: 2
                  Island 0 has faces: [0, 5, 3, 4]
                  Island Num 0     Bounding Box: U [0.004902, 0.495098], V [0.004902, 0.740196]
                                  Center: U = 0.250000, V = 0.627451
                  Island 1 has faces: [1, 2]
                  Island Num 1     Bounding Box: U [0.004902, 0.495098], V [0.750000, 0.995098]
                                  Center: U = 0.250000, V = 0.127451
                  

                  Hope it is useful!

                  1 Reply Last reply Reply Quote 2
                  • First post
                    Last post