Troubleshooting Additive Manufacturing Implementation in Fusion 360 using C++
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi,
I'm exploring additive Manufacturing in fusion 360 using C++. In the fusion's documentation I got a sample program in Python as below -
import adsk.core, adsk.fusion, adsk.cam, traceback, tempfile, time
from . import commands
from .lib import fusion360utils as futil
app = adsk.core.Application.get()
ui = app.userInterface
def showMessage(message):
app.log(message)
# Create an additive setup.
def createAdditiveSetup(models: list[adsk.fusion.Occurrence], cam: adsk.cam.CAM):
setups = cam.setups
input = setups.createInput(adsk.cam.OperationTypes.AdditiveOperation)
input.models = models
input.name = 'AdditiveSetup'
camManager = adsk.cam.CAMManager.get()
libraryManager = camManager.libraryManager
printSettingLibrary = libraryManager.printSettingLibrary
machineLibrary = libraryManager.machineLibrary
printSetting = None
machine = None
if True:
# URL-structure browsing
printSettingUrl = printSettingLibrary.urlByLocation(adsk.cam.LibraryLocations.Fusion360LibraryLocation) ## .Fusion360LibraryLocation vs .LocalLibraryLocation etc.
printSettings = printSettingLibrary.childPrintSettings(printSettingUrl)
machineUrl = machineLibrary.urlByLocation(adsk.cam.LibraryLocations.Fusion360LibraryLocation) ## .Fusion360LibraryLocation vs .LocalLibraryLocation etc.
machines = machineLibrary.childMachines(machineUrl)
for ps in printSettings:
if ps.name == "PLA (Direct Drive)": #print setting name from fusions library
printSetting = ps
break
for machine in machines: #model name from fusions library -- Example: "Generic FFF Machine"
if machine.model == "i3 MK3S+":
machine = machine
break
input.machine = machine
input.printSetting= printSetting
setup = setups.add(input)
return setup
# Given an occurrence, this finds all child occurrences that contain either a
# B-Rep or Mesh body. It is recursive, so it will find all occurrences at all levels.
def getValidOccurrences(occurrence: adsk.fusion.Occurrence) -> list[adsk.fusion.Occurrence]:
result = []
for childOcc in occurrence.childOccurrences:
if (childOcc.bRepBodies.count + childOcc.component.meshBodies.count > 0):
result.append(childOcc)
result.extend(getValidOccurrences(childOcc))
return result
def run(context):
try:
# This will run the start function in each of your commands as defined in commands/__init__.py
#commands.start()
# Make sure the TEXT COMMAND palette is visible.
textPalette = ui.palettes.itemById('TextCommands')
if not textPalette.isVisible:
textPalette.isVisible = True
adsk.doEvents()
doc = app.activeDocument
products = doc.products
# Make
camWS = ui.workspaces.itemById('CAMEnvironment')
camWS.activate()
cam = adsk.cam.CAM.cast(products.itemByProductType("CAMProductType"))
# Design creation
designWS = ui.workspaces.itemById('FusionSolidEnvironment')
designWS.activate()
design = adsk.fusion.Design.cast(products.itemByProductType("DesignProductType"))
camWS.activate()
showMessage('=============================================')
showMessage('Creating Manufacturing Model...')
manufacturingModels = cam.manufacturingModels
mmInput = manufacturingModels.createInput()
mmInput.name = "My Manufacturing Model - FFF"
manufacturingModel = manufacturingModels.add(mmInput)
showMessage('Getting occurrences...')
occs = getValidOccurrences(manufacturingModel.occurrence)
if len(occs) == 0:
ui.messageBox('No component has been added to the scene.')
return
showMessage('Creating arrange operation...')
setup = createAdditiveSetup(occs, cam)
# Define and create the arrange operation.
operationInput = setup.operations.createInput('additive_arrange')
arrange = setup.operations.add(operationInput)
parameter: adsk.cam.StringParameterValue = arrange.parameters.itemByName("arrange_arrangement_type").value
parameter.value = 'Pack2D'
# Specify the values to control the arrangement. All length units are centimeters.
parameter: adsk.cam.FloatParameterValue = arrange.parameters.itemByName("arrange_platform_clearance").value
parameter.value = 0
parameter: adsk.cam.FloatParameterValue = arrange.parameters.itemByName("arrange_frame_width").value
parameter.value = 0.5
parameter: adsk.cam.FloatParameterValue = arrange.parameters.itemByName("arrange_ceiling_clearance").value
parameter.value = 0.5
#
parameter: adsk.cam.FloatParameterValue = arrange.parameters.itemByName("arrange_object_spacing").value
parameter.value = 1
future = cam.generateToolpath(arrange)
while (future.isGenerationCompleted == False):
time.sleep(0.5)
# Create the automatic orientation operations for each occurrence.
for occ in occs:
showMessage(f'Defining orientation for occurrence "{occ.name}" ...')
operationInput = setup.operations.createInput('automatic_orientation')
#operationInput.isAutoCalculating = False
orientationTarget = operationInput.parameters.itemByName('optimizeOrientationTarget')
orientationTarget.value.value = [occ]
operationInput.displayName = 'Automatic Orientation: ' + occ.name
#global orientation
orientation = setup.operations.add(operationInput)
parameter: adsk.cam.FloatParameterValue = orientation.parameters.itemByName("optimizeOrientationSmallestRotation").value
parameter.value = 180 #angle units are always degrees
parameter: adsk.cam.BooleanParameterValue = orientation.parameters.itemByName("optimizeOrientationUsePreciseCalculation").value
parameter.value = True #capitilize
parameter: adsk.cam.FloatParameterValue = orientation.parameters.itemByName("optimizeOrientationCriticalAngle").value
parameter.value = 45 #angle units are always degrees
parameter: adsk.cam.FloatParameterValue = orientation.parameters.itemByName("optimizeOrientationDistanceToPlatform").value
parameter.value = 0 #units are always cm
parameter: adsk.cam.BooleanParameterValue = orientation.parameters.itemByName("optimizeOrientationMoveToCenter").value
parameter.value = True #capitilize
parameter: adsk.cam.FloatParameterValue = orientation.parameters.itemByName("optimizeOrientationFrameWidth").value
parameter.value = 0.5 #units are always cm
parameter: adsk.cam.FloatParameterValue = orientation.parameters.itemByName("optimizeOrientationCeilingClearance").value
parameter.value = 0.5 #units are always cm
parameter: adsk.cam.ChoiceParameterValue = orientation.parameters.itemByName("optimizeOrientationRankingSupportVolume").value
parameter.value = '10' #take the number from dialog with its quotes
parameter: adsk.cam.ChoiceParameterValue = orientation.parameters.itemByName("optimizeOrientationRankingSupportArea").value
parameter.value = '0' #take the number from dialog with its quotes
parameter: adsk.cam.ChoiceParameterValue = orientation.parameters.itemByName("optimizeOrientationRankingBoundingBoxVolume").value
parameter.value = '2' #take the number from dialog with its quotes
parameter: adsk.cam.ChoiceParameterValue = orientation.parameters.itemByName("optimizeOrientationRankingPartHeight").value
parameter.value = '6' #take the number from dialog with its quotes
parameter: adsk.cam.ChoiceParameterValue = orientation.parameters.itemByName("optimizeOrientationRankingCOGHeight").value
parameter.value = '6' #take the number from dialog with its quotes
showMessage('Generating orientation...')
future = cam.generateToolpath(orientation)
while (future.isGenerationCompleted == False):
time.sleep(0.5)
generatedResults = orientation.generatedDataCollection
castPref = None
firstResult = None
primary = generatedResults.itemByIdentifier(adsk.cam.GeneratedDataType.OptimizedOrientationGeneratedDataType)
if isinstance(primary, adsk.cam.OptimizedOrientationResults):
castPref: adsk.cam.OptimizedOrientationResults = primary
firstResult = castPref.item(0)
castPref.currentOrientationResult = firstResult
showMessage('Generating arrange...')
future = cam.generateToolpath(arrange)
while (future.isGenerationCompleted == False):
time.sleep(0.5)
showMessage('Generating supports...')
supportInput = setup.operations.createInput('solid_volume_support')
volumeSupport = setup.operations.add(supportInput)
supportParam = volumeSupport.parameters.itemByName('supportTarget')
supportParam.value.value = occs
future = cam.generateToolpath(volumeSupport)
while (future.isGenerationCompleted == False):
time.sleep(0.5)
if volumeSupport.hasError:
volumeSupport.deleteMe()
showMessage('Generating toolpath...')
toolpath = None
i = 0
for i in range(setup.operations.count):
op = setup.operations.item(i)
if (op.strategy == 'additive_buildstyle'):
toolpath = op
break
if (toolpath == None):
return
future = cam.generateToolpath(toolpath)
while (future.isGenerationCompleted == False):
time.sleep(1.0)
app.activeViewport.fit()
# Start the toolpath simulation command.
# app.executeTextCommand('NaNeuCAMUI.AdditiveSimulateCmd')
showMessage('Finished.')
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
adsk.terminate()
futil.handle_error('run')
# end
def stop(context):
try:
# Remove all of the event handlers your app has created
futil.clear_handlers()
# This will run the start function in each of your commands as defined in commands/__init__.py
commands.stop()
except:
futil.handle_error('stop')
I changed these code in c++ as below -
#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <Cam/CamAll.h>
#include <vector>
#include <iostream>
#include <string>
#include <thread>
#define M_PI 3.14
using namespace adsk::core;
using namespace adsk::fusion;
using namespace adsk::cam;
Ptr<Application> app;
Ptr<UserInterface> ui;
/* Create Additive setup*/
Ptr<Setup> createAdditiveSetup(std::vector<Ptr<Occurrence>> models, Ptr<CAM>& cam) {
Ptr<Setups> setups = cam->setups();
Ptr<SetupInput> setupInput = setups->createInput(OperationTypes::AdditiveOperation);
std::vector<Ptr<Base>> _models;
for (size_t i = 0; i < models.size(); ++i) {
Ptr<Occurrence> item = models[i];
Ptr<Base> _occur = item->cast<Base>();
if (!_occur) {
ui->messageBox("occurrence is not type cast into Base !");
return false;
}
_models.push_back(_occur);
}
bool modelsFlag = setupInput->models(_models);
if (modelsFlag == false)
ui->messageBox("Models are not setup into setupInput");
setupInput->name("AdditiveSetup");
Ptr<CAMManager> camManager = CAMManager::get();
Ptr<CAMLibraryManager> libraryManager = camManager->libraryManager();
Ptr<PrintSettingLibrary> printSettingLibrary = libraryManager->printSettingLibrary();
Ptr<MachineLibrary> machineLibrary = libraryManager->machineLibrary();
Ptr<PrintSetting> printSetting = nullptr;
Ptr<Machine> machine = nullptr;
/* finding the desired print setting and machine */
Ptr<URL> printSettingUrl = printSettingLibrary->urlByLocation(LibraryLocations::Fusion360LibraryLocation);
std::vector<Ptr<PrintSetting>> printSettings = printSettingLibrary->childPrintSettings(printSettingUrl);
Ptr<URL> machineUrl = machineLibrary->urlByLocation(LibraryLocations::Fusion360LibraryLocation);
std::vector<Ptr<Machine>> machines = machineLibrary->childMachines(machineUrl);
for (auto ps : printSettings) {
if (ps->name() == "PLA (Direct Drive)") {
printSetting = ps;
break;
}
}
for (auto mach : machines) {
if (mach->model() == "i3 MK3S+") {
machine = mach;
break;
}
}
setupInput->machine(machine);
setupInput->printSetting(printSetting);
Ptr<Setup> setup = setups->add(setupInput);
return setup;
}
std::vector<Ptr<Occurrence>> getValidOccurrences(Ptr<Occurrence> occurrence) {
std::vector<Ptr<Occurrence>> result;
// Iterate through child occurrences
Ptr<OccurrenceList> childOccurrences = occurrence->childOccurrences();
for (size_t i = 0; i < childOccurrences->count(); ++i) {
Ptr<Occurrence> childOccurrence = childOccurrences->item(i);
// Check if the child occurrence has valid bodies
if (childOccurrence->bRepBodies()->count() + childOccurrence->component()->meshBodies()->count() > 0) {
result.push_back(childOccurrence);
}
// Recursively call the function for nested occurrences
std::vector<Ptr<Occurrence>> nestedOccurrences = getValidOccurrences(childOccurrence);
result.insert(result.end(), nestedOccurrences.begin(), nestedOccurrences.end());
}
return result;
}
extern "C" XI_EXPORT bool run(const char* context)
{
app = Application::get();
if (!app)
return false;
ui = app->userInterface();
if (!ui)
return false;
// get current document
Ptr<Document> doc = app->activeDocument();
Ptr<Products> products = doc->products();
// switch to the manufacture space
Ptr<Workspace> camWS = ui->workspaces()->itemById("CAMEnvironment");
camWS->activate();
// get the CAM product
Ptr<CAM> cam = products->itemByProductType("CAMProductType")->cast<CAM>();
// Design creation
Ptr<Workspace> designWS = ui->workspaces()->itemById("FusionSolidEnvironment");
designWS->activate();
Ptr<Design> design = products->itemByProductType("DesignProductType")->cast<Design>();
camWS->activate();
// creating Manufacturing Model...
Ptr<ManufacturingModels> manufacturingModels = cam->manufacturingModels();
Ptr< ManufacturingModelInput> mmInput = manufacturingModels->createInput();
mmInput->name("My Manufacturing Model - FFF");
Ptr<ManufacturingModel> manufacturingModel = manufacturingModels->add(mmInput);
/* getting occurence */
std::vector<Ptr<Occurrence>> occs = getValidOccurrences(manufacturingModel->occurrence());
if (occs.size() == 0) {
ui->messageBox("No component has been added to the scene");
return true;
}
/* Creating arrange operation....*/
Ptr<Setup> setup = createAdditiveSetup(occs, cam);
/* define and create the arrange operation*/
Ptr<OperationInput> operationInput = setup->operations()->createInput("additive_arrange");
Ptr<Operation> arrange = setup->operations()->add(operationInput);
Ptr<ChoiceParameterValue> stringparameter = arrange->parameters()->itemByName("arrange_arrangement_type")->value()->cast<ChoiceParameterValue>();
if(stringparameter != nullptr)
if (!stringparameter->value("Pack2D")) {
ui->messageBox("arrange_arrangement_type failed");
return true;
}
Ptr<FloatParameterValue> floatParameter = arrange->parameters()->itemByName("arrange_platform_clearance")->value()->cast<FloatParameterValue>();
if(floatParameter != nullptr)
if (!floatParameter->value(0)) {
ui->messageBox("arrange_platform_clearance failed !");
return true;
}
Ptr<FloatParameterValue> floatParameter2 = arrange->parameters()->itemByName("arrange_frame_width")->value()->cast<FloatParameterValue>();
if(floatParameter2 != nullptr)
if (!floatParameter2->value(0.5)) {
ui->messageBox("arrange_frame_width failed !");
return true;
}
Ptr<FloatParameterValue> floatParameter3 = arrange->parameters()->itemByName("arrange_ceiling_clearance")->value()->cast<FloatParameterValue>();
if (floatParameter3 != nullptr)
if (!floatParameter->value(0.5)) {
ui->messageBox("arrange_ceiling_clearance failed !");
return true;
}
Ptr<FloatParameterValue> floatParameter4 = arrange->parameters()->itemByName("arrange_object_spacing")->value()->cast<FloatParameterValue>();
if(floatParameter4 != nullptr)
if (!floatParameter->value(1)) {
ui->messageBox("arrange_object_spacing failed !");
return true;
}
Ptr<GenerateToolpathFuture> future = cam->generateToolpath(arrange);
while (future->isGenerationCompleted() == false)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
/*create the automatic orientation operations for each occurrence*/
for (Ptr<Occurrence> occ : occs) {
operationInput = setup->operations()->createInput("automatic_orientation");
//operationInput->isAutoCalculating(false);
Ptr<CAMParameter> orientationTarget = operationInput->parameters()->itemByName("optimizeOrientationTarget");
Ptr<CadObjectParameterValue> cadObjectParameterValue = orientationTarget->value()->cast<CadObjectParameterValue>();
if (!cadObjectParameterValue) {
ui->messageBox("optimizeOrientationTarget failed !");
return true;
}
//orientationTarget.value.value = [occ]
std::vector<Ptr<Base>> occurrence;
Ptr<Occurrence> oc = occ;
Ptr<Base> item = oc->cast<Base>();
occurrence.push_back(item);
if (!cadObjectParameterValue->value(occurrence)) {
ui->messageBox("Occurrence is not inserted for orientation");
return true;
}
operationInput->displayName("Automatic Orientation: " + occ->name());
/*global orientation*/
Ptr<Operation> orientation = setup->operations()->add(operationInput);
if (orientation != nullptr) {
Ptr<FloatParameterValue> parameter = orientation->parameters()->itemByName("optimizeOrientationSmallestRotation")->value();
if (!parameter->value(180)) { // angle units are always degrees
ui->messageBox("failed for optmize orientation smallest rotation");
}
Ptr<BooleanParameterValue> booleanparameter = orientation->parameters()->itemByName("optimizeOrientationUsePreciseCalculation")->value()->cast<BooleanParameterValue>();
if(booleanparameter != nullptr)
if (!booleanparameter->value(true)) {
ui->messageBox("optimizeOrientationUsePreciseCalculation failed !");
return true;
}
parameter = orientation->parameters()->itemByName("optimizeOrientationCriticalAngle")->value()->cast<FloatParameterValue>();
if (parameter != nullptr)
if (!parameter->value(45)) {
ui->messageBox("optimizeOrientationCriticalAngle failed!");
return true;
}
parameter = orientation->parameters()->itemByName("optimizeOrientationDistanceToPlatform")->value()->cast<FloatParameterValue>();
if (parameter != nullptr)
if (!parameter->value(0)) {
ui->messageBox("optimizeOrientationDistanceToPlatform failed !");
return true;
}
booleanparameter = orientation->parameters()->itemByName("optimizeOrientationMoveToCenter")->value()->cast<BooleanParameterValue>();
if (booleanparameter != nullptr)
if (!booleanparameter->value(true)) {
ui->messageBox("optimizeOrientationMoveToCenter failed !");
return true;
}
parameter = orientation->parameters()->itemByName("optimizeOrientationFrameWidth")->value()->cast<FloatParameterValue>();
if (parameter != nullptr)
if (!parameter->value(0.5)) {
ui->messageBox("optimizeOrientationFrameWidth failed !");
return true;
}
parameter = orientation->parameters()->itemByName("optimizeOrientationCeilingClearance")->value()->cast<FloatParameterValue>();
if (parameter != nullptr)
if (!parameter->value(0.5)) {
ui->messageBox("optimizeOrientationCeilingClearance failed!");
return true;
}
Ptr<ChoiceParameterValue> choiceParameter = orientation->parameters()->itemByName("optimizeOrientationRankingSupportVolume")->value()->cast<ChoiceParameterValue>();
if (choiceParameter != nullptr)
if (!choiceParameter->value("10")) {
ui->messageBox("optimizeOrientationRankingSupportVolume failed !");
return true;
}
choiceParameter = orientation->parameters()->itemByName("optimizeOrientationRankingSupportArea")->value()->cast<ChoiceParameterValue>();
if (choiceParameter != nullptr)
if (!choiceParameter->value("0")) {
ui->messageBox("optimizeOrientationRankingSupportArea failed!");
return true;
}
choiceParameter = orientation->parameters()->itemByName("optimizeOrientationRankingBoundingBoxVolume")->value()->cast<ChoiceParameterValue>();
if (choiceParameter != nullptr)
if (!choiceParameter->value("2")) {
ui->messageBox("optimizeOrientationRankingBoundingBoxVolume failed !");
return true;
}
choiceParameter = orientation->parameters()->itemByName("optimizeOrientationRankingPartHeight")->value()->cast<ChoiceParameterValue>();
if (choiceParameter != nullptr)
if (!choiceParameter->value("6")) {
ui->messageBox("optimizeOrientationRankingPartHeight failed !");
return true;
}
choiceParameter = orientation->parameters()->itemByName("optimizeOrientationRankingCOGHeight")->value()->cast<ChoiceParameterValue>();
if (choiceParameter != nullptr)
if (!choiceParameter->value("6")) {
ui->messageBox("optimizeOrientationRankingCOGHeight failed !");
return true;
}
Ptr<GenerateToolpathFuture> future = cam->generateToolpath(orientation);
if(future != nullptr)
while (future->isGenerationCompleted() == false)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
Ptr<OptimizedOrientationResult> firstResult = nullptr;
Ptr<GeneratedDataCollection> generatedResults = orientation->generatedDataCollection();
Ptr<GeneratedData> primary = generatedResults->itemByIdentifier(GeneratedDataType::OptimizedOrientationGeneratedDataType);
if (!primary) {
ui->messageBox("failed !");
return false;
}
if (Ptr<OptimizedOrientationResults> castPref = primary->cast<OptimizedOrientationResults>()) {
firstResult = castPref->item(0);
if (!castPref->currentOrientationResult(firstResult)) {
ui->messageBox("Optimized Orientation Result failed !");
return true;
}
}
future = cam->generateToolpath(arrange);
while (future->isGenerationCompleted() == false)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// generating support
Ptr<OperationInput> supportInput = setup->operations()->createInput("solid_volume_support");
Ptr<Operation> volumeSupport = setup->operations()->add(supportInput);
Ptr<CAMParameter> supportParameter = volumeSupport->parameters()->itemByName("supportTarget");
//supportParameter.value.value = occs;
Ptr<CadObjectParameterValue> _supportParameter = supportParameter->value()->cast<CadObjectParameterValue>();
std::vector<Ptr<Base>> _occs;
Ptr<Base> item = occs[0]->cast<Base>();
if (!item) {
ui->messageBox("occurrences is not typcast into Base class for support Target !");
return false;
}
if (!item->isValid()) {
ui->messageBox("occurrence is not valid for supportTarget !");
return false;
}
_occs.push_back(item);
if (!_supportParameter->value(_occs)) {
ui->messageBox("Inserting value for support parameter is failed !");
return true;
}
future = cam->generateToolpath(volumeSupport);
while (future->isGenerationCompleted() == false)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (volumeSupport->hasError())
volumeSupport->deleteMe();
}
// Generating toolPath
Ptr<Operation> toolpath = nullptr;
for (size_t i = 0; i < setup->operations()->count(); ++i) {
Ptr<Operation> op = setup->operations()->item(i);
if (op->strategy() == "additive_buildstyle") {
toolpath = op;
break;
}
}
if (toolpath == nullptr)
return false;
future = cam->generateToolpath(toolpath);
while (future->isGenerationCompleted() == false) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
app->activeViewport()->fit();
// start the toolpath simulation command
/*app->executeTextCommand("NaNeuCAMUI.AdditiveSimulateCmd");*/
}
/// end
//ui->messageBox("Hello addin");
return true;
}
extern "C" XI_EXPORT bool stop(const char* context)
{
if (ui)
{
ui->messageBox("Stop addin");
ui = nullptr;
}
return true;
}
#ifdef XI_WIN
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#endif // XI_WIN
In these c++ code, instead of textPalette activation, ui->messageBox() is used.
After the executing these c++ code facing some issue as shown in the below image -
at line no. 333, value(_occs) method returns false. I don't know, why?