Help Needed: Filtering Selected Edges by Length
-
(Using: Cinema 4D 2025.1.1)
Hi there,
My Issue: I'm developing a Python script in Cinema 4D 2025.1.1 to filter the current edge selection based on a minimum length threshold. The goal is to keep only edges that are equal to or longer than the specified length.
Problem: While using the c4d.utils.Neighbor
class, several essential methods required for accurate edge mapping are missing in my environment, resulting in AttributeError. Specifically, the following methods are unavailable:- GetEdgeID(polyIndex, localEdgeIndex)
- GetEdgeInfo(edgeID, pointsArray, polysArray)
- GetEdgePolygonPoints(polyIndex, localEdgeIndex)
- GetEdgePoints(edgeID)
Only GetPolyInfo(polyIndex) and GetEdgePolys(a, b) are accessible, leading to duplicate edge references and inaccurate filtering.
Holy Grail: I need the following Neighbor class methods to achieve precise edge filtering:
- GetEdgeID(polyIndex, localEdgeIndex)
- Purpose: Maps a polygon's local edge to Cinema 4D's global edge ID.
- GetEdgePolygonPoints(polyIndex, localEdgeIndex)
- Purpose: Retrieves the two point indices defining the edge.
These methods are crucial for eliminating indexing mismatches and ensuring each selected edge is measured accurately in global space.
Diagnostic Script: Below is a script that tests all kinds of Neighbor methods to identify which ones are available in my Cinema 4D 2025.1.1 environment.import c4d from c4d import gui, utils def TestNeighborMethods(): doc = c4d.documents.GetActiveDocument() obj = doc.GetActiveObject() if not obj or not obj.IsInstanceOf(c4d.Opolygon): gui.MessageDialog("Please select a Polygon Object first.") return neighbor = utils.Neighbor() if not neighbor.Init(obj): gui.MessageDialog("Failed to initialize Neighbor.") return polyCount = obj.GetPolygonCount() print("========================================================") print("Testing Neighbor methods on the selected object...") print(f"Object: {obj.GetName() or 'Unnamed'}, Polygons: {polyCount}") print("--------------------------------------------------------") # List of Neighbor methods to test methods = [ ("GetPolyInfo", lambda: neighbor.GetPolyInfo(0)), ("GetEdgeID", lambda: neighbor.GetEdgeID(0, 0)), ("GetEdgeInfo", lambda: neighbor.GetEdgeInfo(0, [-1, -1], [-1, -1])), ("GetEdgePolygonPoints", lambda: neighbor.GetEdgePolygonPoints(0, 0)), ("GetEdgePoints", lambda: neighbor.GetEdgePoints(0)), ("GetEdgePolys", lambda: neighbor.GetEdgePolys(0, 1)) ] for name, func in methods: try: result = func() print(f"{name}: Succeeded. Returned: {result}") except AttributeError as e: print(f"{name}: [FAILED] AttributeError: {e}") except Exception as ex: print(f"{name}: [FAILED] {ex}") print("========================================================\n") def main(): TestNeighborMethods() if __name__ == "__main__": main()
Request: Given that only GetPolyInfo() and GetEdgePolys(a, b) are available, how can I accurately map global edge IDs to their corresponding point indices to achieve precise edge filtering? Are there alternative methods or workarounds to obtain the necessary mappings without the missing Neighbor methods?
Thank You! Any insights or solutions from the community on accessing the necessary Neighbor methods or alternative approaches to achieve accurate edge filtering would be greatly appreciated!
-
Hello @myosis,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
About your First Question
All the methods you list simply do not exist (neither in C++ nor in Python), see c4d.utils.Neighbor for the type overview.
I therefore must assume that you are using an AI, like, for example, ChatGPT, which hallucinated these methods. Please note that we reserve the right to refuse support when confronted with undisclosed AI gibberish, especially for beginner content. Always state when you used an AI to generate code.
Something such as an edge does not exist concretely in our API and many other APIs, i.e., other than for points and polygons, there is no explicit data type for edges which would be stored. Edges are defined implicitly by
CPolygon
. To filter a selection for edges of a certain length, you would have to convert edge indices to polygon and point indices and then measure the distance between the relevant points.Cheers,
FerdinandResult
Code
"""Deselects all edges in the edge selection of the active object whose edge length exceeds MAX_EDGE_LENGTH. Must be run as a Script Manager script with an editable polygon object selected. """ import c4d doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. MAX_EDGE_LENGTH: float = 25.0 # The maximum length of an edge before to be considered too long. MAX_EDGE_LENGTH_SQUARED: float = MAX_EDGE_LENGTH ** 2 # The square of `MAX_EDGE_LENGTH`. def main() -> None: """Called by Cinema 4D when the script is being executed. """ if not op or not op.IsInstanceOf(c4d.Opolygon): raise ValueError("The selected object is not a polygon object.") # Get the edge selection of the object and turn it into a list of selected edges indices. Also, # get the points and polygons of the object. selection: c4d.BaseSelect = op.GetEdgeS() selectedEdges: list[int] = [i for i in range(op.GetEdgeCount()) if selection.IsSelected(i)] points: list[c4d.Vector] = op.GetAllPoints() polygons: list[c4d.CPolygon] = op.GetAllPolygons() def getPointByIndex(poly: c4d.CPolygon, index: int) -> c4d.Vector: """Returns the point of the polygon at the given index. CPolygon has no index access, so we fix that here. """ if index == 0: return points[poly.a] elif index == 1: return points[poly.b] elif index == 2: return points[poly.c] elif index == 3: return points[poly.d] # Iterate over the edges and find the one's that are longer than MAX_EDGE_LENGTH. An edge index # is defined as: # # "The edges are indexed by 4 * polygon + edge where polygon is the polygon index and edge is # the edge index between 0 and 3." # # So, we must revert that here, then measure the edge length, and collect all too long edges. tooLongEdges: list[int] = [] for edgeIndex in selectedEdges: polygonIndex: int = edgeIndex // 4 edgeInPolygonIndex: int = edgeIndex % 4 poly: c4d.CPolygon = polygons[polygonIndex] pointA: c4d.Vector = getPointByIndex(poly, edgeInPolygonIndex) pointB: c4d.Vector = getPointByIndex(poly, (edgeInPolygonIndex + 1) % 4) # Getting the length of a vector is quite expensive, so we compare the squared lengths. edgeLengthSq: float = (pointA - pointB).GetLengthSquared() if edgeLengthSq > MAX_EDGE_LENGTH_SQUARED: tooLongEdges.append(edgeIndex) # Print the indices of the edges that are too long. print("The following edges are too long:", tooLongEdges) # Deselect all edges in the object's edge selection that are too long. for edgeIndex in tooLongEdges: selection.Deselect(edgeIndex) # Push an update event to Cinema 4D to redraw the object. c4d.EventAdd() if __name__ == '__main__': main()