Python PhongSelectionTool [SOLVED]

On 08/10/2014 at 06:38, xxxxxxxx wrote:

Is it possible that PhongSelectionTool to use with Python. I want to set a Phongangle and it is me then output the corresponding edges as selection.

On 19/10/2016 at 23:24, xxxxxxxx wrote:

If anyone has got an example of this, I'd love to see it. I've got a sense of how to call the command, but not how to call the "Select All" button once the tool is active.

On 20/10/2016 at 01:58, xxxxxxxx wrote:

Hi there,

I´m not quite sure if I understand your questions right...maybe this helps:
a simple call button example from the docs (actually with a tool).call button
And the header file of the tool toolphongselection.h


import c4d  
from c4d import plugins  
def main() :  
  if doc.GetAction != 1019730:  
      c4d.CallCommand(1019730, 1019730)  
  tool = plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)  
  if tool is not None:  
      c4d.CallButton(tool, c4d.PWS_BTN_SELECT_ALL)  
  data = doc.GetActiveToolData()  
if __name__=='__main__':  

best wishes

On 20/10/2016 at 02:02, xxxxxxxx wrote:


the plugin ID of a tool and most other elements can be obtained from the "Customize Commands" dialog. With that ID it is possible to enable a tool using SetAction() or CallCommand(). The settings of a tool are stored in a BaseContainer. This BaseContainer can be obtained with GetToolData(). To press a button on a tool one has to get the "plugin" representing that tool. This "plugin" is obtained with FindPlugin(). Then one can press a button with CallButton().

For handling tools see also the C++ documentation: Active Tool.

# 1019730 is the tool ID  
# or  
# getting the tool settigns  
toolData = c4d.plugins.GetToolData(doc, 1019730)  
toolData[c4d.MDATA_PWS_OVERRIDE_PHONG] = True  
toolData[c4d.MDATA_PWS_ANGLE_THRESHOLD] = 0.25  
# getting the tool 'plugin'  
tool = c4d.plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)  
if tool is not None:  
  # press buttons  
  c4d.CallButton(tool, c4d.PWS_BTN_SELECT_ALL)  
  c4d.CallButton(tool, c4d.PWS_TAG_ADD_SEL)  

best wishes,

On 20/10/2016 at 14:15, xxxxxxxx wrote:

Hi Sebastian & monkeytack,

That's quite helpful, thank you! I'm trying to use CallButton within a Python Generator to create a new spline based on phong-break angles.

My current strategy is to create a temporary document, clone my object and stuff it into the temp_doc, and then run the PhongSelection tool's "Select All" button. But I'm getting a thread error when I do this.

The documentation offers this warning for CallButton:
"Warning Only calls from the main thread!"

Which I think means I can't call this command from a PythonGenerator. Is that correct?

If it is, I think I'll need to write my own phong angle edge selection routine.

On 21/10/2016 at 00:49, xxxxxxxx wrote:


this tool (like every other tool) works on the active document (ToolData::Message()) and in this case on the selected (active) polygon objects. So the functionality of a tool can only be applied to the elements of the active document, not to any other (internal) BaseDocument.

CallButton() emulates user-interaction so it should only be used like user-interaction. And within the generation of of virtual elements like in GetVirtualObjects() or inside the Python Generator obviously no user interaction can happen.

best wishes,

On 21/10/2016 at 13:25, xxxxxxxx wrote:

Okay, I've written an expression that auto-selects based on phong angle. For the latest version of the code, please see this Github Gist.

"""Select Phong Edges Expression   
Selects any edges w/ broken phong shading.   
Last Modified: 2016-10-21   
Usage Instructions   
1. Add a Python Expression tag to a polygon object.   
2. Paste in this code.   
By Donovan Keith for MAXON USA   
Feel free to re-use this code in any personal or commercial projects.   
import c4d   
import math   
def YieldEdges(op, nbr) :   
    """Generates a list of a,b points in all edges.   
    vadr = op.GetAllPolygons()   
    for i in xrange(op.GetPolygonCount()) :   
      pli = nbr.GetPolyInfo(i)   
      for side in xrange(4) : # # test all 4 sides of a polygon   
        # Only proceed if edge has not already been processed   
        # and edge really exists (for triangles side 2 from c..d does not exist as c==d)   
        if pli["mark"][side] or side==2 and vadr[i].c==vadr[i].d: continue   
        # One can also skip the side==2 && vadr[i].c==vadr[i].d test as pli["mark"][2] is always True for triangles   
        if side==0:   
          a=vadr[i].a; b=vadr[i].b   
        elif side==1:   
          a=vadr[i].b; b=vadr[i].c   
        elif side==2:   
          a=vadr[i].c; b=vadr[i].d   
        elif side==3:   
          a=vadr[i].d; b=vadr[i].a   
        yield a, b   
def GetEdgeAngle(poly1, poly2, points) :   
    """Returns the angle between two polygons.   
    Limitation: only takes the first triangle into account.   
    poly1, poly2: CPolygons   
    Return: angle in Radians   
    Source Reference:   
    a1,b1,c1 = points[poly1.a],points[poly1.b],points[poly1.c]   
    n1 = (b1 - a1).Cross(c1 - a1).GetNormalized()   
    a2,b2,c2 = points[poly2.a],points[poly2.b],points[poly2.c]   
    n2 = (b2 - a2).Cross(c2 - a2).GetNormalized()   
    angle_rad = c4d.utils.GetAngle(n1,n2)   
    return angle_rad   
def GetPhongTag(obj) :   
    """Returns the first PhongTag on object."""   
    if not obj:   
    tags = obj.GetTags()   
    for tag in tags:   
        if tag.GetType() == c4d.Tphong:   
            return tag   
def GetPhongAngle(obj) :   
    """Returns phong limit angle of obj"""   
    if not obj:   
    phong_tag = GetPhongTag(obj)   
    if not phong_tag:   
        return 0.0   
    angle_limit = phong_tag[c4d.PHONGTAG_PHONG_ANGLELIMIT]   
    if angle_limit:   
        return phong_tag[c4d.PHONGTAG_PHONG_ANGLE]   
        return c4d.utils.Rad(180.0)   
def PhongSelectEdges(obj, phong_deg=None) :   
    """Selects the edges whose adjoining polys have a > phong_deg angle   
    between them. Also selects broken phong edges. If phong_deg is not   
    specified, the function will calculate based on the Phong tag on the   
    Note: no Undo support or Update calls are made.   
    points = obj.GetAllPoints()   
    polys = obj.GetAllPolygons()   
    # Calculate the phong threshold using the specified angle, or if None the Phong tag on the object   
    phong_angle = None   
    if phong_deg is not None:   
        phong_angle = c4d.utils.Rad(phong_deg)   
        phong_angle = GetPhongAngle(obj)   
        if phong_angle is None:   
            phong_angle = 0   
    # Select Manually Broken Edges   
    neighbor = c4d.utils.Neighbor()   
    edge_count = neighbor.GetEdgeCount()   
    phong_break_edges = obj.GetSelectedEdges(neighbor, c4d.EDGESELECTIONTYPE_PHONG)   
    # Go through every edge   
    for edge_index, edge in enumerate(YieldEdges(obj, neighbor)) :   
        point_a, point_b = edge   
        # Get the neighbor polygons   
        poly1_index, poly2_index = neighbor.GetEdgePolys(point_a, point_b)   
        poly1 = polys[poly1_index]   
        poly2 = polys[poly2_index]   
        # Calculate the difference between them   
        angle = GetEdgeAngle(poly1, poly2, points)   
        # If difference is over a threshold, select the edge   
        if angle >= phong_angle:   
    # Select the Broken Phong Edges + Auto-Broken Edges   
    obj.SetSelectedEdges(neighbor, phong_break_edges, c4d.EDGESELECTIONTYPE_SELECTION)   
def main() :   
    if not op:   
    # Retrieve the polygon object   
    obj = op.GetObject()   
    if not obj or not obj.IsInstanceOf(c4d.Opolygon) :   

PS: Sorry for the ugly formatting, for some reason this forum always adds extra spaces to the code I paste.