Custom Graphics sample code errors - corrected

Custom Graphics sample code errors - corrected

pludikar
Collaborator Collaborator
305 Views
0 Replies
Message 1 of 1

Custom Graphics sample code errors - corrected

pludikar
Collaborator
Collaborator

I'm not sure how long the example code for custom graphics (see here) has been around, but I suspect it's probably close to 5-6 years.  Anyway, I think it was taken out of the oven too early, because there are a number of errors in it, which I'm sure won't help anyone new to the custom graphics concept.

 

I've corrected the most obvious errors (see below) - I would suggest that someone in the F360 team goes through my code, takes out the change comments I've included ("#<<<") - then republishes the example.  I've also brought the code up to date with f' strings and : annotations. 

 

I've also added a preSelect event placeholder - although that doesn't react to anything other than in mesh mode.  Selectability for everything else doesn't seem to kick in until after the code has been executed - if anyone knows how to get that to work during preview, I'd love to hear what I'm missing.

 

Also - the API reference for customGraphicsBrepBody (here) has bad links (404) under the edges and faces property values (CustomGraphicsBRepEdges ,  CustomGraphicsBRepFaces )

 

Regards

Peter

 

#Author-Autodesk Inc.
#Description-Demo custom graphics examples

import adsk.core, adsk.fusion, adsk.cam, traceback
import math

# Globals
_app:adsk.core.Application = None
_ui:adsk.core.UserInterface = None
_des:adsk.fusion.Design = None
_cgGroups:adsk.fusion.CustomGraphicsGroups = None
_numTeeth = 5
_handlers = []
appearancesMap = {}
coordNumber = 0
stripNumber = 0
_pointSetImage = './resources/16x16.png'
_thickness = 0.5 * 2.54
_anchorPt:adsk.core.Point3D = None
#_scaleFactor is used to limit the size of pixel-scaled model however large the actual graphics model.
_scaleFactor = 10

_commandId = 'CustomGraphicsSample_Python'
_colorEffect_solid_id = 'SolidColorEfect'
_colorEffect_basicMaterial_id = 'BasicMaterialColorEffect'
_colorEffect_appearance_id = 'AppearanceColorEffect'
_colorEffect_vertex_id = 'VertexColorEffect'

# Global Command inputs
_customGraphicsObj:adsk.core.DropDownCommandInput = None
_colorEffects:adsk.core.DropDownCommandInput = None
_red:adsk.core.IntegerSliderCommandInput = None
_green:adsk.core.IntegerSliderCommandInput = None
_blue:adsk.core.IntegerSliderCommandInput = None
_opacity:adsk.core.SliderCommandInput = None
_glossiness:adsk.core.SliderCommandInput = None
#_text:adsk.core.StringValueCommandInput = None
_selection:adsk.core.SelectionCommandInput = None
_transform:adsk.core.DistanceValueCommandInput = None
_materialLibList:adsk.core.DropDownCommandInput = None
_appearanceList:adsk.core.DropDownCommandInput = None
_appearanceFilter:adsk.core.StringValueCommandInput = None
_coordTable:adsk.core.TableCommandInput = None
_add:adsk.core.BoolValueCommandInput = None
_addStrip:adsk.core.BoolValueCommandInput = None
_delete:adsk.core.BoolValueCommandInput = None
_isLineStrip:adsk.core.BoolValueCommandInput = None
_lineStylePattern:adsk.core.DropDownCommandInput = None
_lineStyleWeight:adsk.core.IntegerSliderCommandInput = None
_lineStyleScale:adsk.core.IntegerSliderCommandInput = None

_viewPlacementGroup:adsk.core.GroupCommandInput = None
_viewCorner:adsk.core.ButtonRowCommandInput = None
_viewScaleGroup:adsk.core.GroupCommandInput = None
_pixelScale:adsk.core.FloatSliderCommandInput = None
_billboardingGroup:adsk.core.GroupCommandInput = None
_billboardingStyle:adsk.core.ButtonRowCommandInput = None

def run(context):
    try:
        global _app, _ui, _des, _cgGroups
        _app = adsk.core.Application.get()
        _ui  = _app.userInterface        
        doc = _app.activeDocument
        prods = doc.products
        _des = prods.itemByProductType('DesignProductType')
        if not _des:
            raise Exception('Failed to get fusion design.')

        # get the entry for custom graphics
        activeProd = _app.activeProduct
        cam:adsk.cam.CAM = activeProd
        if cam.productType == 'CAMProductType':              
                       
            _cgGroups = cam.customGraphicsGroups # was if cam: !!!!as provided in the sample - This is wrong.  It equates to an adsk.core.Design object when not in cam!!
        else:
            _cgGroups = _des.rootComponent.customGraphicsGroups

        cmdDef = _ui.commandDefinitions.itemById(_commandId)
        if not cmdDef:
            # Create a command definition.
            cmdDef = _ui.commandDefinitions.addButtonDefinition(_commandId, 'CustomGraphicsSample', 'Custom Graphics Sample') 
        
        # Connect to the command created event.
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)
        
        # Execute the command.
        cmdDef.execute()

        # prevent this module from being terminate when the script returns, because we are waiting for event handlers to fire
        adsk.autoTerminate(False)
    except:
        if _ui:
            _ui.messageBox(f'Failed:\n{traceback.format_exc()}')
            
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
#            eventArgs:adsk.core.CommandEventArgs = args

            # when the command is done, terminate the script
            # this will release all globals which will remove all event handlers
            adsk.terminate()
        except:
            if _ui:
                _ui.messageBox(f'Failed in MyCommandDestroyHandler:\n{traceback.format_exc()}')     

def addRow(tableInput):
    global coordNumber
    
    x = (coordNumber % 4)//2  #<<<<<<<<<<<<<< added, just to make the auto generated line more interesting
    y = (abs(coordNumber+1) % 4)//2  #<<<<<<<<<<<<<< added, just to make the auto generated line more interesting
    tableChildInputs = tableInput.commandInputs

    xValueInput = tableChildInputs.addValueInput(f'{_coordTable.id}_x{coordNumber}', 'Value', 'cm', adsk.core.ValueInput.createByReal(10*x))
    yValueInput = tableChildInputs.addValueInput(f'{_coordTable.id}_y{coordNumber}', 'Value', 'cm', adsk.core.ValueInput.createByReal(10*y))
    zValueInput = tableChildInputs.addValueInput(f'{_coordTable.id}_z{coordNumber}', 'Value', 'cm', adsk.core.ValueInput.createByReal(coordNumber))

    row = tableInput.rowCount
    tableInput.addCommandInput(xValueInput, row, 0)
    tableInput.addCommandInput(yValueInput, row, 1)
    tableInput.addCommandInput(zValueInput, row, 2)
    
    coordNumber = coordNumber + 1
    
def addLineStrip(tableInput):
    global stripNumber
    tableChildInputs = tableInput.commandInputs
    strInput = tableChildInputs.addStringValueInput(f'{_coordTable.id}_strip{stripNumber}', 'Line Strip', '-- Line Strip --')
    strInput.isReadOnly = True
    
    row = tableInput.rowCount
    tableInput.addCommandInput(strInput, row, 0, 0, 2)
    
    stripNumber = stripNumber + 1                
    
def replaceItems(cmdInput, newItems):
    try:
        cmdInput.listItems.clear()
        itemNone = cmdInput.listItems.add('None', True, '')
        itemNone.isSelected = True
        if len(newItems) > 0:
            for item in newItems:
                cmdInput.listItems.add(item, False, '')
            cmdInput.listItems[1].isSelected = True
            cmdInput.listItems[0].deleteMe()   
    except:
        if _ui:
            _ui.messageBox(f'Failed in replaceItems:\n{traceback.format_exc()}')        
        
def getAppearancesFromLib(libName, filterExp):
    try:
        global appearancesMap
        appearanceList = None
        if libName in appearancesMap:
            appearanceList = appearancesMap[libName]
        else:
            materialLib = _app.materialLibraries.itemByName(libName)
            appearances = materialLib.appearances
            appearanceNames = []
            for appearance in appearances:
                appearanceNames.append(appearance.name)
            appearancesMap[libName] = appearanceNames
            appearanceList = appearanceNames

        if filterExp and len(filterExp) > 0:
            filteredList = []
            for appearanceName in appearanceList:
                if appearanceName.lower().find(filterExp.lower()) >= 0:
                    filteredList.append(appearanceName)
            return filteredList
        else:
            return appearanceList
    except:
        if _ui:
            _ui.messageBox(f'Failed in getAppearancesFromLib:\n{traceback.format_exc()}')        

def hasAppearances(lib):
    if lib and lib.appearances.count > 0:
        return True
    return False
    
def getMaterialLibNames(libFilter):
    materialLibs = _app.materialLibraries
    libNames = []
    for materialLib in materialLibs:
        if (not libFilter) or libFilter(materialLib):
            libNames.append(materialLib.name)
    return libNames   
             
def getAppearance(libName, appearanceName):
    try:
        if not appearanceName or appearanceName == 'None':
            return
        appearance = _des.appearances.itemByName(appearanceName)
        if appearance:
            return appearance
        
        matLib = _app.materialLibraries.itemByName(libName)      
        if matLib:
            appearance = matLib.appearances.itemByName(appearanceName)

        return appearance            
    except:
        if _ui:
            _ui.messageBox(f'Failed in getAppearance:\n{traceback.format_exc()}')        
    
# Event handler for the commandCreated event.
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs:adsk.core.CommandCreatedEventArgs = args
            
            # Verify that a Fusion design is active.
            if not _des:
                _ui.messageBox('A Fusion design must be active when invoking this command.')
                return()

            cmd = eventArgs.command
            cmd.isExecutedWhenPreEmpted = False
            inputs = cmd.commandInputs            
            
            global _customGraphicsObj, _selection, _coordTable, _add, _delete
            global _colorEffects,_glossiness, _opacity, _transform, _isLineStrip, _addStrip, _lineStyleWeight, _lineStyleScale, _lineStylePattern
            global _red,_green,_blue, _appearanceList, _materialLibList, _appearanceFilter
            
            # menu for different kinds of custom graphics
            _customGraphicsObj = inputs.addDropDownCommandInput(_commandId + '_cgObj', 'Custom Graphics Object', adsk.core.DropDownStyles.TextListDropDownStyle)
            _customGraphicsObj.listItems.add('Mesh', True)     
            _customGraphicsObj.listItems.add('Lines', False) 
            _customGraphicsObj.listItems.add('PointSet', False)
            _customGraphicsObj.listItems.add('Curve', False)
            _customGraphicsObj.listItems.add('BRep', False)
            #_customGraphicsObj.listItems.add('Text', False)       
            _customGraphicsObj.listItems.add('Lines - Custom', False)
            _customGraphicsObj.listItems.add('PointSet - Custom', False) 
            
            # coordinates table used by 'Lines - Custom' and 'PointSet - Custom'
            _coordTable = inputs.addTableCommandInput(f'{_commandId}_table', 'Coordinates Table', 3, '1:1:1')    
            _coordTable.maximumVisibleRows = 10
            addRow(_coordTable)          
            _add = inputs.addBoolValueInput(_coordTable.id + '_add', 'Add', False, '', True)
            _coordTable.addToolbarCommandInput(_add)
            _addStrip = inputs.addBoolValueInput(_coordTable.id + '_addStrip', 'AddStrip', False, '', True)
            _coordTable.addToolbarCommandInput(_addStrip)
            _delete = inputs.addBoolValueInput(_coordTable.id + '_delete', 'Delete', False, '', True)
            _coordTable.addToolbarCommandInput(_delete)
            _coordTable.isVisible = False
            _add.isVisible = False
            _delete.isVisible = False
            _addStrip.isVisible = False
            
            # specific for 'Lines - Custom'
            _isLineStrip = inputs.addBoolValueInput(_commandId + '_isLineStrip', 'Use LineStrip', True, '', True)
            _isLineStrip.isVisible = False
            
            # color effects for custom graphics Mesh/BRep
            _colorEffects = inputs.addDropDownCommandInput(_commandId + '_colorEffects', 'Color Effect', adsk.core.DropDownStyles.TextListDropDownStyle)
            _colorEffects.listItems.add(_colorEffect_solid_id, True) 
            _colorEffects.listItems.add(_colorEffect_basicMaterial_id, False) 
            _colorEffects.listItems.add(_colorEffect_appearance_id, False)     
            _colorEffects.listItems.add(_colorEffect_vertex_id, False)   
            
            # RGB for solid colors
            _red = inputs.addIntegerSliderCommandInput(_commandId + '_red', 'Red', 0, 255, False)
            _red.valueOne = 255
            _green = inputs.addIntegerSliderCommandInput(_commandId + '_green', 'Green', 0, 255, False)
            _green.valueOne = 0
            _blue = inputs.addIntegerSliderCommandInput(_commandId + '_blue', 'Blue', 0, 255, False)   
            _blue.valueOne = 0
            
            # specific for basic material color effect
            _glossiness = inputs.addFloatSliderCommandInput(_commandId + '_glossiness', 'Glossiness', '', 0.0, 128.0, False)
            _glossiness.valueOne = 128.0
            _glossiness.isVisible = False            
            _opacity = inputs.addFloatSliderCommandInput(_commandId + '_opacity', 'Opacity', '', 0.0, 1.0, False)
            _opacity.valueOne = 1.0
            _opacity.isVisible = False

            # for appearance color effect
            _materialLibList = inputs.addDropDownCommandInput(_commandId + '_materialLib', 'Material Library', adsk.core.DropDownStyles.TextListDropDownStyle)
            listItems = _materialLibList.listItems
            materialLibNames = getMaterialLibNames(hasAppearances)
            for materialName in materialLibNames:
                listItems.add(materialName, False, '')
            listItems[0].isSelected = True
            _materialLibList.isVisible = False
            _appearanceList = inputs.addDropDownCommandInput(_commandId + '_appearanceList', 'Appearance', adsk.core.DropDownStyles.TextListDropDownStyle)
            appearances = getAppearancesFromLib(materialLibNames[0], '')
            listItems = _appearanceList.listItems
            for appearanceName in appearances:
                listItems.add(appearanceName, False, '')
            listItems[0].isSelected = True
            _appearanceList.isVisible = False
            _appearanceFilter = inputs.addStringValueInput(_commandId + '_appearanceFilter', 'Filter', '')
            _appearanceFilter.isVisible = False

            # selection input for custom graphics BRep/Curve
            _selection = inputs.addSelectionInput(_commandId + '_sel', 'Selection', '')
            _selection.setSelectionLimits(0, 1)
            _selection.isVisible = False
            _selection.isEnabled = False
            
            # for custom graphics text
            #_text = inputs.addStringValueInput(_commandId + '_text', 'Text', 'This is a text.')
            #_text.isVisible = False
            
            # transform for all custom graphics entity
            _transform = inputs.addDistanceValueCommandInput(_commandId + '_transform', 'Transform', adsk.core.ValueInput.createByReal(0))
            _transform.setManipulator( adsk.core.Point3D.create(0,0,0), adsk.core.Vector3D.create(1,0,0))
            
            # menu for different kinds of line sytles
            _lineStylePattern = inputs.addDropDownCommandInput(_commandId + '_LSPattern', 'Line Style Pattern', adsk.core.DropDownStyles.TextListDropDownStyle)
            _lineStylePattern.listItems.add('Solid Line', True)     
            _lineStylePattern.listItems.add('Center Line', False) 
            _lineStylePattern.listItems.add('Dashed Line', False)
            _lineStylePattern.listItems.add('Dot Line', False)
            _lineStylePattern.listItems.add('Phantom Line', False)
            _lineStylePattern.listItems.add('Tracks Line', False)       
            _lineStylePattern.listItems.add('ZigZag Line', False)
            _lineStylePattern.isVisible = False
            
            # for line sytle weight
            _lineStyleWeight = inputs.addIntegerSliderCommandInput(_commandId + '_LSWeight', 'Line Style Weight', 1, 20, False)
            _lineStyleWeight.valueOne = 1
            _lineStyleWeight.isVisible = False
            
            # for line style scale
            _lineStyleScale = inputs.addIntegerSliderCommandInput(_commandId + '_LSScale', 'Line Style Scale', 1, 100, False)
            _lineStyleScale.valueOne = 10
            _lineStyleScale.isVisible = False
            
            global _viewPlacementGroup, _viewCorner, _viewScaleGroup, _pixelScale, _billboardingGroup, _billboardingStyle            
            # for view placement attribute
            _viewPlacementGroup = inputs.addGroupCommandInput(_commandId + '_VPGroup', 'View Placement')
            _viewPlacementGroup.isEnabledCheckBoxDisplayed = True
            _viewPlacementGroup.isEnabledCheckBoxChecked = False
            _viewCorner = _viewPlacementGroup.children.addButtonRowCommandInput(_commandId + '_viewCorner', 'corner', False)
            _viewCorner.listItems.add('Upper Left', False, './resources/upperLeft')
            _viewCorner.listItems.add('Upper Right', False, './resources/upperRight')
            _viewCorner.listItems.add('Lower Left', False, './resources/lowerLeft')
            _viewCorner.listItems.add('Lower Right', False, './resources/lowerRight')     
            
            # for view scale attribute
            _viewScaleGroup = inputs.addGroupCommandInput(_commandId + '_VSGroup', 'View Scale')
            _viewScaleGroup.isEnabledCheckBoxDisplayed = True
            _viewScaleGroup.isEnabledCheckBoxChecked = False
            _pixelScale = _viewScaleGroup.children.addFloatSliderCommandInput(_commandId + '_pixelScale', 'pixel scale', '', 0.5, 5, False)
            _pixelScale.valueOne = 1
            _pixelScale.setText('Smaller', 'Larger')
            
            # for billboarding attribute
            _billboardingGroup = inputs.addGroupCommandInput(_commandId + '_BBGroup', 'Billboarding')
            _billboardingGroup.isEnabledCheckBoxDisplayed = True
            _billboardingGroup.isEnabledCheckBoxChecked = False        
            _billboardingStyle = _billboardingGroup.children.addButtonRowCommandInput(_commandId + '_billboardingStyle', 'style', False)
            _billboardingStyle.listItems.add('Screen', False, './resources/One')
            _billboardingStyle.listItems.add('Axis', False, './resources/Two')
            _billboardingStyle.listItems.add('Right Reading', False, './resources/Three')          
            
            # Connect to the command related events.
            onExecute = MyCommandExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)  

            onPreSelect = MyPreSelectHandler()
            cmd.preSelect.add(onPreSelect)
            _handlers.append(onPreSelect)  
            
            onExecutePreview = MyCommandExecuteHandler()
            cmd.executePreview.add(onExecutePreview)
            _handlers.append(onExecutePreview)  
            
            onInputChanged = MyCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)     

            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)
        except:
            if _ui:
                _ui.messageBox(f'Failed in MyCommandCreatedHandler:\n{traceback.format_exc()}')

def applyColorEffect(cgEnt):
    try:
        colorEffect = None
        if _colorEffects.selectedItem.name == _colorEffect_solid_id:
            colorEffect = adsk.fusion.CustomGraphicsSolidColorEffect.create(adsk.core.Color.create(int(_red.valueOne),int(_green.valueOne),int(_blue.valueOne),255))
        elif _colorEffects.selectedItem.name == _colorEffect_basicMaterial_id:
            diffuseColor = adsk.core.Color.create(0,255,0,255)                      
            ambientColor = adsk.core.Color.create(255,0,0,255)            
            specularColor = adsk.core.Color.create(0,0,255,255)  
            emissiveColor = adsk.core.Color.create(0,0,0,255) 
            colorEffect = adsk.fusion.CustomGraphicsBasicMaterialColorEffect.create(diffuseColor, ambientColor, specularColor, emissiveColor, float(_glossiness.valueOne), float(_opacity.valueOne))
        elif _colorEffects.selectedItem.name == _colorEffect_appearance_id:
            appearance = getAppearance(_materialLibList.selectedItem.name, _appearanceList.selectedItem.name)
            if appearance:
                if not _des.appearances.itemByName(appearance.name):
                    appearance = _des.appearances.addByCopy(appearance, appearance.name)
                colorEffect = adsk.fusion.CustomGraphicsAppearanceColorEffect.create(appearance)
        elif _colorEffects.selectedItem.name == _colorEffect_vertex_id:
            colorEffect = adsk.fusion.CustomGraphicsVertexColorEffect.create()
        if colorEffect:
            cgEnt.color = colorEffect
    except:
        if _ui:
            _ui.messageBox(f'Failed in applyColorEffect:\n{traceback.format_exc()}')

def getCoordinatesFromTable(tableInput):
    try:
        vecCoord = []
        vecStripLen = []
        stripLen = 0
        if _coordTable:
            for i in range(0, _coordTable.rowCount):
                xValueInput:adsk.core.ValueCommandInput = _coordTable.getInputAtPosition(i,0)
                if not( '_strip' in xValueInput.id):
                    stripLen = stripLen + 1
                    yValueInput:adsk.core.ValueCommandInput = _coordTable.getInputAtPosition(i,1)
                    zValueInput:adsk.core.ValueCommandInput = _coordTable.getInputAtPosition(i,2)
                    vecCoord.extend([xValueInput.value, yValueInput.value, zValueInput.value])    
                else:
                    vecStripLen.append(stripLen)
                    stripLen = 0
            vecStripLen.append(stripLen)
        return vecCoord, vecStripLen
    except:
        if _ui:
            _ui.messageBox(f'Failed in getCoordinatesFromTable:\n{traceback.format_exc()}')    
				
# Event handler for the execute event.
class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            # get selection entity first since it's fragile and any creation/edit operations will clear the selection.
            selEntity = None
            if _selection.selectionCount > 0:
                selEntity = _selection.selection(0).entity
                                    
            if _customGraphicsObj:
                cgGroup:adsk.fusion.CustomGraphicsGroup = _cgGroups.add()     
                
                global _anchorPt, _scaleFactor
                if not _anchorPt:
                    _anchorPt = adsk.core.Point3D.create(0,0,0)
                cgEnt = None
                if _customGraphicsObj.selectedItem.name == 'Mesh':
                    cgEnt = drawMesh(cgGroup) 
                    _anchorPt.setWithArray([0,0,_thickness/2])
                elif _customGraphicsObj.selectedItem.name == 'Lines':
                    cgEnt = drawLines(cgGroup)                
                    _anchorPt.setWithArray([0,0,_thickness/2])
                elif _customGraphicsObj.selectedItem.name == 'PointSet':
                    cgEnt = drawPointSet(cgGroup)
                elif _customGraphicsObj.selectedItem.name == 'BRep':                    
                    if selEntity:
                        body:adsk.fusion.BRepBody = selEntity
                        cgEnt = cgGroup.addBRepBody(body)
                elif _customGraphicsObj.selectedItem.name == 'Curve':
                    if selEntity:
                        skCurve:adsk.fusion.SketchCurve = selEntity
                        sk = skCurve.parentSketch
                        curve = skCurve.geometry
                        curve.transformBy(sk.transform)
                        cgEnt = cgGroup.addCurve(curve)
                        # cgEnt.weight = float(_lineStyleWeight.valueOne)     #<<<<<<<<<<<< moved to below                   
                #elif _customGraphicsObj.selectedItem.name == 'Text':
                #    if _text.value:
                #        cgEnt = cgGroup.addText(_text.value, 'None', 10, adsk.core.Point3D.create(0,0,0))
                elif _customGraphicsObj.selectedItem.name == 'PointSet - Custom':
                    vecCoords, vecStripLen = getCoordinatesFromTable(_coordTable)
                    if len(vecCoords) < 4:  #<<<<<<<<<<<<<<<<<<<<<<<  added
                        return
                    coords = adsk.fusion.CustomGraphicsCoordinates.create(vecCoords)
                    cgEnt = cgGroup.addPointSet(coords, [], adsk.fusion.CustomGraphicsPointTypes.UserDefinedCustomGraphicsPointType, _pointSetImage)
                elif _customGraphicsObj.selectedItem.name == 'Lines - Custom':
                    vecCoords, vecStripLength = getCoordinatesFromTable(_coordTable)
                    if len(vecCoords) < 4:  #<<<<<<<<<<<<<<<<<<<<<<<  added
                        return
                    coords = adsk.fusion.CustomGraphicsCoordinates.create(vecCoords)
                    isLineStrip = _isLineStrip.value
                    if coords.coordinateCount < 1:
                        return
                    cgEnt = cgGroup.addLines(coords, [], isLineStrip, vecStripLength)

                cgEnt.lineStylePattern = getLinePattern()               #<<<<<<<<<<<<<<<<<<<< added
                cgEnt.weight = float(_lineStyleWeight.valueOne)         #<<<<<<<<<<<<<<<<<<<< added
                cgEnt.lineStyleScale = float(_lineStyleScale.valueOne)  #<<<<<<<<<<<<<<<<<<<< added
                
                # add attributes to the custom graphics entity
                if cgEnt:
                    # transform
                    transMat = adsk.core.Matrix3D.create()
                    origin = adsk.core.Point3D.create(float(_transform.value),0,0)
                    transMat.setWithCoordinateSystem(origin, adsk.core.Vector3D.create(1,0,0), adsk.core.Vector3D.create(0,1,0), adsk.core.Vector3D.create(0,0,1))
                    cgEnt.transform = transMat   
                    # color effect
                    if not adsk.fusion.CustomGraphicsPointSet.cast(cgEnt):
                        applyColorEffect(cgEnt)                    
                    # calculate _scaleFactor and _anchorPt for viewPlacement, viewScale and billboarding attributes based on the bounding box of custom graphics entity
                    maxPt = cgEnt.boundingBox.maxPoint
                    minPt = cgEnt.boundingBox.minPoint       
                    _scaleFactor = 100 / minPt.distanceTo(maxPt)
                    _anchorPt.setWithArray([(minPt.x + maxPt.x) / 2, (minPt.y + maxPt.y) / 2, (minPt.z + maxPt.z) / 2])
                    # view placement
                    if _viewPlacementGroup and _viewPlacementGroup.isVisible and _viewPlacementGroup.isEnabledCheckBoxChecked and _viewCorner and _viewCorner.selectedItem:                       
                        viewPt = adsk.core.Point2D.create(100,100)
                        # upper left by default
                        corner = adsk.fusion.ViewCorners.upperLeftViewCorner                          
                        if _viewCorner.selectedItem.name == 'Upper Right':
                            corner = adsk.fusion.ViewCorners.upperRightViewCorner
                        elif _viewCorner.selectedItem.name == 'Lower Left':
                            corner = adsk.fusion.ViewCorners.lowerLeftViewCorner
                        elif _viewCorner.selectedItem.name == 'Lower Right':
                            corner = adsk.fusion.ViewCorners.lowerRightViewCorner
                        attr = adsk.fusion.CustomGraphicsViewPlacement.create(_anchorPt, corner, viewPt)
                        cgEnt.viewPlacement = attr
                    # view scale
                    if _viewScaleGroup and _viewScaleGroup.isVisible and _viewScaleGroup.isEnabledCheckBoxChecked and _pixelScale:
                        attr = adsk.fusion.CustomGraphicsViewScale.create(_scaleFactor * _pixelScale.valueOne, _anchorPt)
                        cgEnt.viewScale = attr
                    # billboarding
                    if _billboardingGroup and _billboardingGroup.isVisible and _billboardingGroup.isEnabledCheckBoxChecked and _billboardingStyle and _billboardingStyle.selectedItem:
                        # screen style by default
                        bbStyle = adsk.fusion.CustomGraphicsBillBoardStyles.ScreenBillBoardStyle
                        if _billboardingStyle.selectedItem.name == 'Axis':
                            bbStyle = adsk.fusion.CustomGraphicsBillBoardStyles.AxialBillBoardStyle
                        elif _billboardingStyle.selectedItem.name == 'Right Reading':
                            bbStyle = adsk.fusion.CustomGraphicsBillBoardStyles.RightReadingBillBoardStyle
                        attr = adsk.fusion.CustomGraphicsBillBoard.create(_anchorPt)
                        attr.axis = adsk.core.Vector3D.create(0,1,0)
                        attr.billBoardStyle = bbStyle
                        cgEnt.billBoarding = attr
                            
        except:
            if _ui:
                _ui.messageBox(f'Failed in MyCommandExecuteHandler:\n{traceback.format_exc()}')

class MyPreSelectHandler(adsk.core.SelectionEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            pass                            
        except:
            if _ui:
                _ui.messageBox(f'Failed in MyPreSelectHandler:\n{traceback.format_exc()}')

def changeCGObjVisibility(strObjName):
    try:       
        _colorEffects.listItems.clear()
        _colorEffects.listItems.add(_colorEffect_solid_id, True) 
        _colorEffects.listItems.add(_colorEffect_basicMaterial_id, False) 
        _colorEffects.listItems.add(_colorEffect_appearance_id, False)   
        _colorEffects.isVisible = False
        _selection.clearSelection()
        _selection.clearSelectionFilter()
        _selection.setSelectionLimits(0, 0)
        _selection.isVisible = False
        _selection.isEnabled = False        
        #_text.isVisible = False
        _coordTable.isVisible = False
        _isLineStrip.isVisible = False
        _lineStylePattern.isVisible = False
        _lineStyleWeight.isVisible = False
        _lineStyleScale.isVisible = False
        changeColorEffectVisibility(None)
        
        _viewPlacementGroup.isVisible = False
        _viewScaleGroup.isVisible = False
        _billboardingGroup.isVisible = False
        
        if strObjName == 'Mesh':
            _colorEffects.isVisible = True
            _colorEffects.listItems.add(_colorEffect_vertex_id, False) 
            changeColorEffectVisibility(_colorEffect_solid_id)
            _viewPlacementGroup.isVisible = True
            _viewScaleGroup.isVisible = True
            _billboardingGroup.isVisible = True
        elif strObjName == 'Lines':    		         
            changeColorEffectVisibility(_colorEffect_solid_id)
            _lineStylePattern.isVisible = True
            _lineStyleWeight.isVisible = True
            if _lineStylePattern.selectedItem.name != 'Solid Line':
                _lineStyleScale.isVisible = True
            _viewPlacementGroup.isVisible = True
            _viewScaleGroup.isVisible = True
            _billboardingGroup.isVisible = True
        elif strObjName == 'Curve':
            _selection.isVisible = True
            _selection.isEnabled = True
            _selection.tooltip = 'select a curve'
            _selection.commandPrompt = 'select a curve'
            _selection.addSelectionFilter('SketchCurves')
            _selection.setSelectionLimits(1,1)
            changeColorEffectVisibility(_colorEffect_solid_id)
            _lineStyleWeight.isVisible = True
            _viewPlacementGroup.isVisible = True
            _viewScaleGroup.isVisible = True
            _billboardingGroup.isVisible = True
        elif strObjName == 'BRep':
            _selection.isVisible = True
            _selection.isEnabled = True
            _selection.tooltip = 'select a body'
            _selection.commandPrompt = 'select a body'
            _selection.addSelectionFilter('Bodies')  
            _selection.setSelectionLimits(1,1)
            _colorEffects.isVisible = True
            changeColorEffectVisibility(_colorEffect_solid_id)
            _viewPlacementGroup.isVisible = True
            _viewScaleGroup.isVisible = True
            _billboardingGroup.isVisible = True
        #elif strObjName == 'Text':
        #    _text.isVisible = True
        #    changeColorEffectVisibility(_colorEffect_solid_id)   
        elif strObjName == 'PointSet - Custom':   
            _coordTable.isVisible = True 
            _addStrip.isEnabled = False
        elif strObjName == 'Lines - Custom': 
            _coordTable.isVisible = True
            _isLineStrip.isVisible = True
            _addStrip.isEnabled = True
            changeColorEffectVisibility(_colorEffect_solid_id)
            _lineStylePattern.isVisible = True
            _lineStyleWeight.isVisible = True
            if _lineStylePattern.selectedItem.name != 'Solid Line':
                _lineStyleScale.isVisible = True
    except:
        if _ui:
            _ui.messageBox(f'Failed in changeCGObjVisibility:\n{traceback.format_exc()}')
                 
def changeColorEffectVisibility(strColorEffectName):
    try:
        _red.isVisible = False
        _green.isVisible = False
        _blue.isVisible = False
        _opacity.isVisible = False
        _glossiness.isVisible = False
        _appearanceList.isVisible = False
        _materialLibList.isVisible = False
        _appearanceFilter.isVisible = False
        
        if strColorEffectName == _colorEffect_solid_id:	
            _red.isVisible = True
            _green.isVisible = True
            _blue.isVisible = True	    		    
        elif strColorEffectName == _colorEffect_basicMaterial_id:		    		
            _opacity.isVisible = True
            _glossiness.isVisible = True
        elif strColorEffectName == _colorEffect_appearance_id:
            _appearanceList.isVisible = True
            _materialLibList.isVisible = True
            _appearanceFilter.isVisible = True
            
    except:
        if _ui:
            _ui.messageBox(f'Failed in changeColorEffectVisibility:\n{traceback.format_exc()}')

def changeLineStyleInputsVisibility(patternName):
    try:
        if patternName == 'Solid Line':
            _lineStyleScale.isVisible = False
        else:
            _lineStyleScale.isVisible = True
        
    except:
        if _ui:
            _ui.messageBox(f'Failed in changeLineStyleInputsVisibility:\n{traceback.format_exc()}')
    
# Event handler for the inputChanged event.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs:adsk.core.InputChangedEventArgs = args
            changedInput = eventArgs.input
            if changedInput.id == _commandId + '_cgObj':
                changeCGObjVisibility(_customGraphicsObj.selectedItem.name)
            elif changedInput.id == _commandId + '_colorEffects':
                changeColorEffectVisibility(_colorEffects.selectedItem.name)  
            elif changedInput.id == _commandId + '_appearanceFilter' or changedInput.id == _commandId + '_materialLib':
                appearances = getAppearancesFromLib(_materialLibList.selectedItem.name, _appearanceFilter.value)
                replaceItems(_appearanceList, appearances)
            elif changedInput.id == _coordTable.id + '_add':
                addRow(_coordTable)
            elif changedInput.id == _coordTable.id + '_addStrip':
                addLineStrip(_coordTable)
            elif changedInput.id == _coordTable.id + '_delete':
               if _coordTable.selectedRow == -1:
                   _ui.messageBox('Select one row to delete')
               else:
                   _coordTable.deleteRow(_coordTable.selectedRow)
            elif changedInput.id == _commandId + '_LSPattern':
                changeLineStyleInputsVisibility(_lineStylePattern.selectedItem.name)
        except:
            if _ui:
                _ui.messageBox(f'Failed in MyCommandInputChangedHandler:\n{traceback.format_exc()}')
def rotate2D(rad, vec):
    try:
        x = vec[0]
        y = vec[1]
        return (x*math.cos(rad)-y*math.sin(rad), x*math.sin(rad)+y*math.cos(rad))
    except:
        if _ui:
            _ui.messageBox(f'Failed in rotate2D:\n{traceback.format_exc()}')    

def calculateCoordinates(numTeeth):
    try:         
        # holeDia < rootDia < pitchDia < outsideDia     
        holeDia = 0.5 * 2.54
        diametralPitch = 2 / 2.54
        pitchDia = numTeeth / diametralPitch
        if (diametralPitch < (20 *(math.pi/180))-0.000001):
            dedendum = 1.157 / diametralPitch
        else:
            circularPitch = math.pi / diametralPitch
            if circularPitch >= 20:
                dedendum = 1.25 / diametralPitch
            else:
                dedendum = (1.2 / diametralPitch) + (.002 * 2.54)                
        rootDia = pitchDia - (2 * dedendum)        
        outsideDia = (numTeeth + 2) / diametralPitch
        
        rPts0 = []    # 2 * numTeeth for root
        hPts0 = []    # 2 * numTeeth for hole
        pPts0 = []    # 2 * numTeeth for pitch
        oPts0 = []    # 1 * numTeeth for outside
        rPts1 = []    # 2 * numTeeth for root with thickness
        hPts1 = []    # 2 * numTeeth for hole with thickness
        pPts1 = []    # 2 * numTeeth for pitch with thickness
        oPts1 = []    # 1 * numTeeth for outside with thickness
        
        vecRootRadi = [rootDia/2.0, 0]
        vecHoleRadi = [holeDia/2.0, 0]
        vecPitchRadi = [pitchDia/2.0, 0]
        vecOutRadi = [outsideDia/2.0, 0]
        unitRadian = math.pi / numTeeth
        
        vecCoords = []    
        vecColors = []
        for i in range(0, 2 * numTeeth):            
            x, y = rotate2D(unitRadian * (i - 0.5), vecRootRadi)
            rPts0.append(int(len(vecCoords) / 3))
            rPts1.append(int(len(vecCoords) / 3) + 1)
            vecCoords.extend([x, y, 0, x, y, _thickness])
            vecColors.extend([255,0,255,128, 255,0,255,128])
    
        for i in range(0, 2 * numTeeth):            
            x, y = rotate2D(unitRadian * (i - 0.5), vecHoleRadi)
            hPts0.append(int(len(vecCoords) / 3))
            hPts1.append(int(len(vecCoords) / 3) + 1)   
            vecCoords.extend([x, y, 0, x, y, _thickness])
            vecColors.extend([255,0,0,128,  255,0,0,128])
            
        for i in range(0, 2 * numTeeth):            
            x, y = rotate2D(unitRadian * (i - 0.5), vecPitchRadi)
            pPts0.append(int(len(vecCoords) / 3))
            pPts1.append(int(len(vecCoords) / 3) + 1)   
            vecCoords.extend([x, y, 0, x, y, _thickness])
            vecColors.extend([0,0,255,128, 0,0,255,128])
    
        for i in range(0, numTeeth):            
            x, y = rotate2D(unitRadian * i * 2 , vecOutRadi)
            oPts0.append(int(len(vecCoords) / 3))
            oPts1.append(int(len(vecCoords) / 3) + 1)   
            vecCoords.extend([x, y, 0, x, y, _thickness])
            vecColors.extend([0,255,255,128, 0,255,255,128])
    
        return (rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1, vecCoords, vecColors)
    except:
        if _ui:
            _ui.messageBox(f'Failed in calculateCoordinates:\n{traceback.format_exc()}')

def calculateStripLen(numTeeth):
    try:
        vecStripLen = []
        for i in range(0, numTeeth):
            vecStripLen.append(6)
        for i in range(0, 2 * numTeeth):
            vecStripLen.append(21)  
        for i in range(0, numTeeth):
            vecStripLen.append(24)
        for i in range(0, 2 * numTeeth):
            vecStripLen.append(6)            
        return vecStripLen
    except Exception as error:
        _ui.messageBox("calculateTriangles Failed : " + str(error)) 
            
def calculateTriangles(numTeeth, rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1):
    try: 
        vertexIndexList = []    
        # triangles between teeth
        for i in range(0, numTeeth):
            idx0 = (2*i+1) % (2*numTeeth)
            idx1 = (2*i+2) % (2*numTeeth)
            rPtA0 = rPts0[idx0]
            rPtB0 = rPts0[idx1]         
            rPtA1 = rPts1[idx0]
            rPtB1 = rPts1[idx1]  
            vertexIndexList.extend([rPtA0,rPtB0,rPtB1, rPtB1,rPtA1,rPtA0])
        # triangles on surface0
        for i in range(0, numTeeth):
            rPtA = rPts0[i*2]
            rPtB = rPts0[i*2 + 1]
            rPtC = rPts0[(i*2 + 2)%(2*numTeeth)]        
            hPtA = hPts0[i*2]
            hPtB = hPts0[i*2 + 1]  
            hPtC = hPts0[(i*2 + 2)%(2*numTeeth)]        
            pPtA = pPts0[i*2]
            pPtB = pPts0[i*2 + 1]               
            oPt = oPts0[i]                                  
            vertexIndexList.extend([hPtB,hPtC,rPtC, rPtC,rPtB,hPtB])
            vertexIndexList.extend([rPtA,rPtB,pPtB, pPtB,pPtA,rPtA])  
            vertexIndexList.extend([hPtA,hPtB,rPtB, rPtB,rPtA,hPtA])           
            vertexIndexList.extend([pPtA,pPtB,oPt])              
        # triangles on surface1
        for i in range(0, numTeeth):
            rPtA = rPts1[i*2]
            rPtB = rPts1[i*2 + 1]
            rPtC = rPts1[(i*2 + 2)%(2*numTeeth)]
            hPtA = hPts1[i*2]
            hPtB = hPts1[i*2 + 1]  
            hPtC = hPts1[(i*2 + 2)%(2*numTeeth)]
            pPtA = pPts1[i*2]
            pPtB = pPts1[i*2 + 1]         
            oPt = oPts1[i]             
            vertexIndexList.extend([hPtC,hPtB,rPtB, rPtB,rPtC,hPtC])
            vertexIndexList.extend([rPtB,rPtA,pPtA, pPtA,pPtB,rPtB]) 
            vertexIndexList.extend([hPtB,hPtA,rPtA, rPtA,rPtB,hPtB])          
            vertexIndexList.extend([pPtB,pPtA,oPt])         
        # triangles on teeth
        for i in range(0, numTeeth):
            rPtA0 = rPts0[i*2]
            rPtB0 = rPts0[i*2 + 1]  
            pPtA0 = pPts0[i*2]
            pPtB0 = pPts0[i*2 + 1]    
            rPtA1 = rPts1[i*2]
            rPtB1 = rPts1[i*2 + 1]  
            pPtA1 = pPts1[i*2]
            pPtB1 = pPts1[i*2 + 1] 
            oPt0 = oPts0[i]
            oPt1 = oPts1[i]        
            # triangles on one tooth
            vertexIndexList.extend([rPtA1, rPtA0, pPtA0, pPtA0, pPtA1, rPtA1])
            vertexIndexList.extend([pPtA1, pPtA0, oPt0, oPt0, oPt1, pPtA1])
            vertexIndexList.extend([rPtB0, rPtB1, pPtB1, pPtB1, pPtB0, rPtB0])    
            vertexIndexList.extend([pPtB0, pPtB1, oPt1, oPt1, oPt0, pPtB0])         
        # triangles on inner face
        for i in range(0, 2*numTeeth):
            hPtA0 = hPts0[i]
            hPtB0 = hPts0[(i + 1)%(2*numTeeth)] 
            hPtA1 = hPts1[i]
            hPtB1 = hPts1[(i + 1)%(2*numTeeth)] 
            vertexIndexList.extend([hPtA1,hPtB1,hPtB0, hPtB0,hPtA0,hPtA1])                
            
        return vertexIndexList
    except Exception as error:
        _ui.messageBox("calculateTriangles Failed : " + str(error)) 
        return None
# Builds a custom graphics mesh.
def drawMesh(cgGroup):
    try:        
        # Calculate mesh coordinates
        rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1, vecCoords, vecColors = calculateCoordinates(_numTeeth)        
        coordinates = adsk.fusion.CustomGraphicsCoordinates.create(vecCoords)
        coordinates.colors = vecColors

        # Calculate mesh triangles
        vertexIndexList = calculateTriangles(_numTeeth, rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1)        
        # Add Custom Graphics mesh
        normalVectors = []
        normalIndexList = []
        cgMesh = cgGroup.addMesh(coordinates, vertexIndexList, normalVectors, normalIndexList)        
        return cgMesh
        
    except Exception as error:
        _ui.messageBox("drawMesh Failed : " + str(error)) 
        return None
        
def drawLines(cgGroup):
    try:
        rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1, vecCoords, vecColors = calculateCoordinates(_numTeeth)        
        coordinates = adsk.fusion.CustomGraphicsCoordinates.create(vecCoords)       
        vertexIndexList = calculateTriangles(_numTeeth, rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1)        
            
        stripLen = calculateStripLen(_numTeeth)
        cgLines = cgGroup.addLines(coordinates, vertexIndexList, True, stripLen)
        cgLines.lineStylePattern = getLinePattern()  #<<<<<<<<<<<<<<<<<<< replaced if-elif-elif... with new function 
        cgLines.weight = float(_lineStyleWeight.valueOne)
        cgLines.lineStyleScale = float(_lineStyleScale.valueOne)
        
        return cgLines
    except Exception as error:
        _ui.messageBox("drawLines Failed : " + str(error)) 
        return None   
    
def getLinePattern(): # <<<<<<<<<<<<<<<<<<<  added new function, because the same mechanism is used in two separate places
    pattern = None
    if _lineStylePattern.selectedItem.name == 'Solid Line':
        pattern = adsk.fusion.LineStylePatterns.continuousLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'Center Line':
        pattern = adsk.fusion.LineStylePatterns.centerLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'Dashed Line':
        pattern = adsk.fusion.LineStylePatterns.dashedLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'Dot Line':
        pattern = adsk.fusion.LineStylePatterns.dotLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'Phantom Line':
        pattern = adsk.fusion.LineStylePatterns.phantomLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'Tracks Line':
        pattern = adsk.fusion.LineStylePatterns.tracksLineStylePattern
    elif _lineStylePattern.selectedItem.name == 'ZigZag Line':
        pattern = adsk.fusion.LineStylePatterns.zigzagLineStylePattern
    return pattern
        
def drawPointSet(cgGroup):
    try:
        rPts0, hPts0, pPts0, oPts0, rPts1, hPts1, pPts1, oPts1, vecCoords, vecColors = calculateCoordinates(_numTeeth)        
        coordinates = adsk.fusion.CustomGraphicsCoordinates.create(vecCoords)       
               
        cgPoints = cgGroup.addPointSet(coordinates, [], adsk.fusion.CustomGraphicsPointTypes.UserDefinedCustomGraphicsPointType, _pointSetImage)

        return cgPoints
    except Exception as error:
        _ui.messageBox("drawPointSet Failed : " + str(error)) 
        return None

 

I'm not an expert, but I know enough to be very, very dangerous.

Life long R&D Engineer (retired after 30+ years in Military Communications, Aerospace Robotics and Transport Automation).
306 Views
0 Replies
Replies (0)