# Solved PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

PolygonObject's

``````SetSelectedEdges(self, e, pSel, ltype)
``````

..., method takes a pSel argument of type `BaseSelect`.

The help for this method further tells us that:

``````The edges are indexed uniquely by a Neighbor object, so each
edge has a single index.
``````

OK, makes sense so far. We use the edge index from the `Neighbor` object to identify an edge once, rather than N times where N is the number of polygons that share said edge.

But, in order to use a "logical" edge index from the Neighbor object, we need to be able to tell which edge and/or edges this logical edge maps to. Unfortunately, the `Neighbor` class offers no way to get the properties of the logical edge to physical edge mapping, that it stores internally, given a logical edge index.

There is only one method in the `Neighbor` class that has anything to do with the logical indexes that it stores, and that is `Neighbor.GetEdgeCount()`. This puts a cap on the number of logical edges, which are clearly indexed from `0` to `Neighbor.GetEdgeCount() - 1`.

But, how in the world does one get info about these logical edges (by logical edge index) in order to decide whether or not to use them with the `PolygonObject.SetSelectedEdges()` method, if the representation and properties of the logical edges are completely opaque to users of the `c4d.utils.Neighbor` class? There is not a single method in this class that gives the developer any information about which physical points, edges, or polygons a logical edge, as referred to by its edge index in the `Neighbor` class, represents.

Without any information and mapping to something physical and concrete, how can one refer to a "distinct logical edge" by its index within `Neighbor` and use that as the basis for forming the selection criteria inside a `BaseSelect` object as required by the `pSel` argument of the `SetSelectedEdges()` method of `PolygonObject`?

TL;DR: If I can't tell which actual edges the edge indices of a `Neighbor` object refer to, I can't make use of `Neighbor` edge indices for any operation that requires them.

Am I overlooking something, or is this API completely broken?

Hi @mikegold10, to adds some clarification.
A PolygonObject doesn't have a strong edge representation. An edge is simply defined by 2 points.
With that in mind, there is actually no direct way (like GetPolygonCount or GetPointsCount) to retrieves the count of an edge in a PolygonObject, but a BaseSelect to select all elements needs this count to be able to select all elements. But here the tricky part because there is also ngnon edge(aka hidden edges) that can't be selected directly.
So the faster way to know the count of all selectable edges is to create a neighbor object and use GetEdgeCount.

So now let's say I want via python to select edge from pt ID 80 to pt ID 81.
Here is how to do it with both methods:

``````import c4d

def GetEdgeFromCpolygon(polygonObject, ptIdA, ptIdB):
for polyId, cPoly in enumerate(polygonObject.GetAllPolygons()):
edgeIdAB = cPoly.FindEdge(ptIdA, ptIdB)
if edgeIdAB != c4d.NOTOK:
return polyId, edgeIdAB

raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))

def GetFromNeighboor(polygonObject, nbr, ptIdA, ptIdB):
polyIdListA = nbr.GetPointPolys(ptIdA)
polyIdListB = nbr.GetPointPolys(ptIdB)

commonPolygonIdList = list(set(polyIdListA) & set(polyIdListB))
for commonPolygonId in commonPolygonIdList:
nbrPolyInfo = nbr.GetPolyInfo(commonPolygonId)

neigboorPolyId = nbr.GetNeighbor(ptIdA, ptIdB, commonPolygonId)
if not neigboorPolyId in nbrPolyInfo["face"]:
continue

nbrPolyId = nbrPolyInfo["face"].index(neigboorPolyId)
return nbrPolyInfo["edge"][nbrPolyId]

raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))

# Main function
def main():
doc.StartUndo()

# Select from the Cpolygon by editing directly the Edge BaseSelect
polygonId, edgeId = GetEdgeFromCpolygon(op, 80, 81)

edges = op.GetEdgeS()
#edges.Select(polygonId * 4 + edgeId)

# Select from SetSelectedEdges
nbr = c4d.utils.Neighbor()
nbr.Init(op)

edges = c4d.BaseSelect()
edgeId = GetFromNeighboor(op, nbr, 80, 81)
edges.Select(edgeId)

op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION)

doc.EndUndo()

if __name__=='__main__':
main()
``````

Note that the SetSelectedEdges fail if one of the points targeted to be select have more than 1 open edge, since in GetFromNeighboor, neigboorPolyId will have an id of -1 and in the `face` list returned by GetPolyInfo 2 face will have an Id of -1, so there is no way to know which edge is the correct one.

However, looking at the implementation of SetSelectedEdges I would really recommend not using it except for full selection since it iterates each polygon and do.

``````for polyId in range(op.GetPolygonCount():
polyInfo = nbr.GetPolyInfo(polyId)

# {0, 1, 2, 3} mean there is one condition for each so understand it as ["edge"][0] then ["edge"][1], etc..
if passedEdgeSelectToSetSelectedEdges.IsSelected(polyInfo["edge"][{0, 1, 2, 3}]:
baseSelect.Select(polyId  * 4 + {0, 1, 2, 3})
``````

So using SetSelectedEdges is way more inefficient, can give false result than using the BaseSelect directly so I will really recommend using GetEdgeS, GetEdgeH or GetPhongBreak.

Cheers,
Maxime

MAXON SDK Specialist

Hi,

edges are globally indexed by `polygon_index * 4 + local edge_index` (see `PolygonObject.GetEdgeS`), where the local edge index lies in the interval `[0, 3]`. So the edge between the points `c` and `d` of the polygon with the id `10` would have the index `43`. You will have to pass edges in this form to `SetSelectEdges`, the function will then turn your partial edge selection into a full selection by selecting the corresponding global edge indices of polygons that share the selected edges.

You can also do the same by using the `Neighbor` class and do it manually by retrieving the polygons attached to an edge defined by two points.

Cheers,
zipit

MAXON SDK Specialist
developers.maxon.net

As discussed here going from unique edge index to global edge index (polygon * 4 + polygon side) is not possible. You are better of working with global edge indeces to start with ... I have learned that the hard way.

Hi,

edges are globally indexed by `polygon_index * 4 + local edge_index` (see `PolygonObject.GetEdgeS`), where the local edge index lies in the interval `[0, 3]`. So the edge between the points `c` and `d` of the polygon with the id `10` would have the index `43`. You will have to pass edges in this form to `SetSelectEdges`, the function will then turn your partial edge selection into a full selection by selecting the corresponding global edge indices of polygons that share the selected edges.

You can also do the same by using the `Neighbor` class and do it manually by retrieving the polygons attached to an edge defined by two points.

Cheers,
zipit

The whole point of SetSelectedEdges is not to do the `4*PolygonIdx+PolygonEdgeIdx` math and have to supply multiple physical edges where a single logical edge from `Neighbor` will suffice).

Please reread the below quoted portion of my post and especially the part I marked in bold:

``````SetSelectedEdges(self, e, pSel, ltype)
``````

..., method takes a pSel argument of type BaseSelect.

The help for this method further tells us that:

The edges are indexed uniquely by a Neighbor object, so each
edge has a single index.

This tells us that we should not use the `4*PolygonIdx+PolygonEdgeIdx` formula to specify the edge indices for the `BaseSelect`, but instead refer to the logical edge indices as determined and governed by the `Neighbor` object after it does its calculations. The problem and crux of the question is that we don't know what those edges are, since they don't seem to be accessible to code that is outside of the Neighbor object. This information seems to be encapsulated within the `Neighbor`'s guts and hidden from the users of the object, making it inadequate for its intended role in this case: The selection of a subset of the logical edge indices from the `Neighbor` as required by the `pSel` argument of `SetSelectedEdges()`.

My point with regard to which edge indexes to use is further driven home by the following sample code that appears in the help for `BaseSelect.SelectAll()` which shows the correct usage of the `BaseSelect` with the `SetSelectedEdges()` method and coincides completely with what I am asserting:

``````def main():
nbr = c4d.utils.Neighbor()
nbr.Init(op) # Initialize neighbor with a polygon object

edges = c4d.BaseSelect()

# Select all edges in the range [0, nbr.GetEdgeCount()-1]
edges.SelectAll(nbr.GetEdgeCount()-1)

# ### ============
# ### The preceding line clearly demonstrates that Neighbor based
# ### logical indices should be used with the BaseSelect since
# ### nbr.GetEdgeCount() returns the number of Neighbor deduced
# ### logical edges and not the total number of (potential)
# ### physical edges (i.e., which would be 4*num_polys), at least the
# ### the way I understand it.

# Select all edges from our edges selection
op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION)

if __name__ == '__main__':
main()
``````

Note: The Dev Comments portion in the above code was added by me to point out what can be deduced from the line of significance in the sample code from the help documentation.

Hi @mikegold10, to adds some clarification.
A PolygonObject doesn't have a strong edge representation. An edge is simply defined by 2 points.
With that in mind, there is actually no direct way (like GetPolygonCount or GetPointsCount) to retrieves the count of an edge in a PolygonObject, but a BaseSelect to select all elements needs this count to be able to select all elements. But here the tricky part because there is also ngnon edge(aka hidden edges) that can't be selected directly.
So the faster way to know the count of all selectable edges is to create a neighbor object and use GetEdgeCount.

So now let's say I want via python to select edge from pt ID 80 to pt ID 81.
Here is how to do it with both methods:

``````import c4d

def GetEdgeFromCpolygon(polygonObject, ptIdA, ptIdB):
for polyId, cPoly in enumerate(polygonObject.GetAllPolygons()):
edgeIdAB = cPoly.FindEdge(ptIdA, ptIdB)
if edgeIdAB != c4d.NOTOK:
return polyId, edgeIdAB

raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))

def GetFromNeighboor(polygonObject, nbr, ptIdA, ptIdB):
polyIdListA = nbr.GetPointPolys(ptIdA)
polyIdListB = nbr.GetPointPolys(ptIdB)

commonPolygonIdList = list(set(polyIdListA) & set(polyIdListB))
for commonPolygonId in commonPolygonIdList:
nbrPolyInfo = nbr.GetPolyInfo(commonPolygonId)

neigboorPolyId = nbr.GetNeighbor(ptIdA, ptIdB, commonPolygonId)
if not neigboorPolyId in nbrPolyInfo["face"]:
continue

nbrPolyId = nbrPolyInfo["face"].index(neigboorPolyId)
return nbrPolyInfo["edge"][nbrPolyId]

raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))

# Main function
def main():
doc.StartUndo()

# Select from the Cpolygon by editing directly the Edge BaseSelect
polygonId, edgeId = GetEdgeFromCpolygon(op, 80, 81)

edges = op.GetEdgeS()
#edges.Select(polygonId * 4 + edgeId)

# Select from SetSelectedEdges
nbr = c4d.utils.Neighbor()
nbr.Init(op)

edges = c4d.BaseSelect()
edgeId = GetFromNeighboor(op, nbr, 80, 81)
edges.Select(edgeId)

op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION)

doc.EndUndo()

if __name__=='__main__':
main()
``````

Note that the SetSelectedEdges fail if one of the points targeted to be select have more than 1 open edge, since in GetFromNeighboor, neigboorPolyId will have an id of -1 and in the `face` list returned by GetPolyInfo 2 face will have an Id of -1, so there is no way to know which edge is the correct one.

However, looking at the implementation of SetSelectedEdges I would really recommend not using it except for full selection since it iterates each polygon and do.

``````for polyId in range(op.GetPolygonCount():
polyInfo = nbr.GetPolyInfo(polyId)

# {0, 1, 2, 3} mean there is one condition for each so understand it as ["edge"][0] then ["edge"][1], etc..
if passedEdgeSelectToSetSelectedEdges.IsSelected(polyInfo["edge"][{0, 1, 2, 3}]:
baseSelect.Select(polyId  * 4 + {0, 1, 2, 3})
``````

So using SetSelectedEdges is way more inefficient, can give false result than using the BaseSelect directly so I will really recommend using GetEdgeS, GetEdgeH or GetPhongBreak.