This sample script demonstrates how to use Machine/Avoid/Gouge/Fixture functionality.
The script starts by opening a sample model from the CAM Samples folder via its URN. The model comprises a curved surface with a through slot, a countersunk hole and a raised, circular and filleted upstand from the surface. The model is supported by two rectangular blocks, themselves mounted on a fixture plate. A setup is included with a single operation running a 3-axis diagonal raster over the model, supports and fixture. The operation machines the fixture, the supporting blocks, the upper surface of the upstand and the area within the slot and hole, something we would like to avoid.
The script duplicates the original operation and then proceeds to create a series of MachineAvoidGroups. These are labelled as either Machine in the case of 2 cap surfaces for the slot and hole, Fixture for the fixture plate, Gouge for the supporting blocks and Avoid for the top face of the upstand. Additionally, both AxialOffset and RadialOffset can be specified for the Machine, Avoid and Fixture passes.
import adsk.core, adsk.fusion, adsk.cam, traceback dupName = 'Avoid/Machine using API' information = f""" This sample script demonstrates Machine/Avoid/Gouge/Fixture functionality. Move this dialog to one side and notice the following about the default operation: The existing operation machines the top face (yellow) and within the slot and hole. The supporting blocks (red) and the fixture plate (purple) are needlessly machined. We want it to avoid machining the top face, the fixture and within the slot and hole. Cap surfaces are provided for the slot and hole (colored green when set to visible). The mounting blocks are designated sacrificial so we can machine vertical walls fully. To achieve this, we duplicate the first operation and assign labels to surface groups. Both radial and axial “stock-to-leave” values can also be applied. Acknowledge this dialog and select the operation named "{dupName}" to see the new improved tool path. """ #################### Global Variables #################### _app = adsk.core.Application.get() _ui = _app.userInterface _machineAvoidGroups: adsk.cam.MachineAvoidGroups = None _cam: adsk.cam.CAM = None _design: adsk.fusion.Design = None # This URN should point to the project beneath the CAM Samples folder. PROJECT_URN = 'urn:adsk.wipprod:fs.file:vf.rxjQ68iyTN2ui31aDQULDw?version=1' # MachineAvoidAfter. # Map the strategies to surface colors for a clearer indication. modes = ['Avoid', 'Machine', 'Gouge', 'Fixture'] modeColor: dict[str, adsk.core.Color] = { modes[adsk.cam.MachiningMode.Avoid_MachiningMode]: adsk.core.Color.create(255, 255, 113, 30), # Yellow modes[adsk.cam.MachiningMode.Machine_MachiningMode]: adsk.core.Color.create(104, 255, 0, 30), # Green modes[adsk.cam.MachiningMode.Gouge_MachiningMode]: adsk.core.Color.create(204, 51, 51, 30), # Red modes[adsk.cam.MachiningMode.Fixture_MachiningMode]: adsk.core.Color.create(180, 120, 255, 30), # Purple } # The main script function. def run(context): global _cam, _design, _machineAvoidGroups try: # Load by URN a specific sample project that demonstrates machine/avoid/ignore/gouge. doc = loadProjectFromURN(PROJECT_URN) if doc is None: return products = doc.products # Switch to the manufacturing workspace (CAM environment). camWS = _app.userInterface.workspaces.itemById('CAMEnvironment') camWS.activate() _cam = adsk.cam.CAM.cast(products.itemByProductType("CAMProductType")) # Get the CAD product. _design = products.itemByProductType('DesignProductType') setups = _cam.setups # Ensure the document contains a Setup and then use the 1st setup in the document. if setups.count > 0: setup = setups.item(0) else: userMessageBox('This script requires that there is a Setup in the Fusion Document', True) return operations = setup.operations # Remove all but the first operation as this script is not using the intended sample model for opNum in range(1,operations.count): operations.item(opNum).deleteMe() # Duplicate the first operation in the setup. if operations.count > 0: opOriginal = operations.item(0) else: userMessageBox('This script requires that there is an existing operation in the Setup.', True) return opOriginal.isLightBulbOn = True opOriginal.duplicate() opDuplicate = operations.item(1) opDuplicate.name = dupName _app.log(f'The toolpath "{opOriginal.name}" has been duplicated and then renamed as "{opDuplicate.name}"') # Get the "checkSurfaceSelectionSets" parameter from the operation, which is a CadFaceGroups cad object. surfaceGroupsParam: adsk.cam.CadMachineAvoidGroupsParameterValue = opDuplicate.parameters.itemByName('checkSurfaceSelectionSets').value # Get the MachineAvoidGroups object from the CadFaceGroups cad object. This object manages the list of mutually exclusive groups. _machineAvoidGroups = surfaceGroupsParam.getMachineAvoidGroups() # Create a Machine group comprising the two green coloured caps over the curved surface. Not visible by default. if not createMachineAvoidGroup('Selection Set3', adsk.cam.MachiningMode.Machine_MachiningMode, 0.2, 0.1): return # Create an Avoid group comprising the yellow coloured uppermost horizontal face. if not createMachineAvoidGroup('Selection Set1', adsk.cam.MachiningMode.Avoid_MachiningMode, 0.05, 0.02): return # Create a Fixture group comprising the purple coloured base block. if not createMachineAvoidGroup('fixture_base', adsk.cam.MachiningMode.Fixture_MachiningMode, 5.0, 0.6): return # Create an Ignore/Gouge group comprising the 1st red colored sacrificial support block. if not createMachineAvoidGroup('sacrificial_block1', adsk.cam.MachiningMode.Gouge_MachiningMode): return # Create an Ignore/Gouge group comprising the 2nd red colored sacrificial support block. if not createMachineAvoidGroup('sacrificial_block2', adsk.cam.MachiningMode.Gouge_MachiningMode): return # Add the groups back to the parameter. surfaceGroupsParam.applyMachineAvoidGroups(_machineAvoidGroups) # Explain what has happened and how to see the result userMessageBox(information) # Generate the operation. _cam.generateToolpath(opDuplicate) # Display the new toolpath - this is ineffective - user needs to select the path themselves. opOriginal.isLightBulbOn = False opDuplicate.isLightBulbOn = True opDuplicate.isToolpathVisible = True # Give control back to Fusion so it can update the graphics. adsk.doEvents() return except Exception as e: userMessageBox(f'Failed:: {e}\n{traceback.format_exc()}', True) #################### Functions #################### def createMachineAvoidGroup(name: str, mode: adsk.cam.MachiningMode, axialOffset: float = None, radialOffset: float = None): '''Create a Machine/Avoid group of the specified mode and name using the stated offsets if applicable''' machineAvoidGroup: adsk.cam.MachineAvoidDirectSelection = _machineAvoidGroups.createNewMachineAvoidDirectSelectionGroup() machineAvoidGroup.machineMode = mode # Gouge groups require no offsets to be specified. if mode != adsk.cam.MachiningMode.Machine_MachiningMode: if radialOffset is not None: machineAvoidGroup.radialOffset = radialOffset if axialOffset is not None: machineAvoidGroup.axialOffset = axialOffset if mode == adsk.cam.MachiningMode.Machine_MachiningMode or mode == adsk.cam.MachiningMode.Avoid_MachiningMode: faceSelSet = _design.selectionSets.itemByName(name) if faceSelSet: machineAvoidGroup.inputGeometry = faceSelSet.entities else: userMessageBox(f'The Selection Set "{name}" does not exist in the current Fusion document', True) return False else: # Add the face/body/component to the exclusiveGroup selection. bodySelect = _cam.designRootOccurrence.bRepBodies.itemByName(name) if bodySelect: machineAvoidGroup.inputGeometry = getFacesAll(bodySelect) else: userMessageBox(f'The Body "{name}" does not exist in the current Fusion document', True) return False colorFaces(machineAvoidGroup.inputGeometry, mode) return True def getFacesAll(body: adsk.fusion.BRepBody): ''' Adds all the faces in the selected body to a list ''' allFaces: list[adsk.fusion.BRepFace] = [] for face in body.faces: allFaces.append(face) return allFaces def userMessageBox(messageText: str, isWarning: bool = False): ''' Keep messageBox calls informative but succinct''' iconType = adsk.core.MessageBoxIconTypes.WarningIconType if isWarning else adsk.core.MessageBoxIconTypes.InformationIconType _ui.messageBox(messageText, 'Fusion\t\t\t\t\t\t', adsk.core.MessageBoxButtonTypes.OKButtonType, iconType) # Title tabs widen messageBox. def colorFaces(faces: list[adsk.fusion.BRepFace], mode: adsk.cam.MachiningMode): ''' Color specified BRepFaces ''' modeName = modes[mode] for face in faces: # Check if "colorAppearance" already exists, if not, create it. appearances = _design.appearances colorAppearance = appearances.itemByName(modeName) if not colorAppearance: # Create a new appearance. appearanceLib: adsk.core.MaterialLibrary = _app.materialLibraries.itemById('BA5EE55E-9982-449B-9D66-9F036540E140') # Get the appearance using itemById as unlike itemByName it is not locale dependent. genericAppearance: adsk.core.Appearance = appearanceLib.appearances.itemById('Prism-129') # Clone the generic appearance. colorAppearance: adsk.core.Appearance = appearances.addByCopy(genericAppearance, modeName) # Get the 'Color' property using itemById as unlike itemByName it is not locale dependent. colorProperty: adsk.core.Property = colorAppearance.appearanceProperties.itemById('opaque_albedo') # Set the color of the appearance. colorProperty.value = modeColor[modeName] # Apply the appearance to the body. face.appearance = colorAppearance # Give control back to Fusion so it can update the graphics. adsk.doEvents() def loadProjectFromURN(urn:str = None) -> adsk.core.Document: ''' Minimal self-contained function to load and return a document via URN or return None safely ''' doc: adsk.core.Document = None app = adsk.core.Application.get() if urn is not None: try: # File not found causes an exception project: adsk.core.DataFile = app.data.findFileById(urn) if project: doc = app.documents.open(project, True) else: app.userInterface.messageBox(f'File not found for URN: {urn}!') except Exception as e: if str(e)[0:38] == '3 : Design is located in another team.': # Although the document has been loaded, variable 'doc' may not be populated if doc is None: doc: adsk.core.Document = adsk.core.Application.get().activeDocument elif str(e)[0:20] == '3 : file not found': app.userInterface.messageBox(f'File not found for URN: {urn}!') else: # Abandon for unhandled errors, displaying the error message. app.userInterface.messageBox(f'Failed:{str(e)}\n{traceback.format_exc()}') return doc