Announcements
Autodesk Community will be read-only between April 26 and April 27 as we complete essential maintenance. We will remove this banner once completed. Thanks for your understanding

[BUG] - API & RENDER - Apply Appearance to linked components fails

gvisca44
Advocate Advocate
515 Views
8 Replies
Message 1 of 9

[BUG] - API & RENDER - Apply Appearance to linked components fails

gvisca44
Advocate
Advocate

Also - Refer the attached post in the forums

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/occurrence-appearance-batch-render-fails/t...

 

The application of an appearance through the API results in different behaviours than the UI.

 

I have developed a script that will read a CSV file that lists parts and/or assemblies with a corresponding appearance. The script will then:
1. Copy the selected appearance to the design.
2. Apply the appearance to the appropriate occurrence object.

 

However, I have found inconsistent results where appearances are applied through the API to linked objects in two areas; (1) the apparent linkage of objects and (2) when rendering, the linked objects DO NOT receive the appearance.

 

I have set up the attached test model where there are a combination of components within the design, components linked from the same folder and components linked from a different folder.

 

[EDIT] - I'm not sure the test model will repeat the results - as the archive creation process flattens the folder structure (assuming my guess as to cause is anywhere near correct).  But ... it is attached anyway.

 

When using the UI, all operations seem to operate successfully - as one would expect.  However, when using the API, they don't.

 

Batch render of UI appearances

All appearances applied to mimic the definitions in the CSV

(there is one occurrence that doesnt have an appearance intentionally).

Appearance-UI-Render.png

 

Design workspace - after API applies appearances.

All appearances applied as defined in the CSV

(there is one occurrence that doesnt have an appearance intentionally).

Script Appearances.png

 

Batch render of API appearances

Note the 2 cubes don't have any appearances in the render (other than the appearance by physical material).

 Appearance-API-Render.png

 

Refer the following table for a comparison of operations/results. I won't suggest this is an exhaustive comparison - simply enough to represent the issue.

 

The table is added as PDF attachment.  NOTE - There are 4 pages in the attachment!

 

This is written as an add-in - so theres a whole lot of code i've removed.  Anyone debugging will have to add back the appropriate entry lines. 

 

 

 

 

 

### Code entry
        filename = getAppearanceFile()
        if filename != None:
            msg = f'Appearance file: "{filename}"'
            futil.log(msg)
            processAppearanceFile(filename)
            listAppearances()
        else:
            #do nothing
            return
### End code entry

def processAppearanceFile(filename):
    occs = []
    root = design.rootComponent
    occs = root.allOccurrences

    # Open the appearances file 
    with open(filename) as f:

        reader = csv.reader(f)
            
        for line in reader:

            componentName = line[0]

            if componentName[0] == '#':
                continue                        # if component name in file is commented - ignore and move to next line.

            if line[1] == 'None':
                componentAppearance = None      # if the specified appearance is None - this sets up to remove any appearances from the Occurrence.
            else:
                componentAppearance: adsk.core.Appearance = getAppearance(line[1])

                if not componentAppearance:     # if we dont get an appearances from the CSV file - message and exit.
                    msg = f'Cancelled - "Appearance" in CSV file cannot be obtained: \n Component: \n \t{line[0]} \n Appearance: \n \t{line[1]} \n Script terminating' 
                    ui.messageBox(msg, CMD_NAME)
                    return
                
            occ:adsk.fusion.Occurrence
            for occ in occs:
                occComponentName = futil.filterFusionCompNameInserts(occ.component.name)
                #Utility to remove extraneous information previously inserted by Fusion from a component name
                
                #msg = f'Examining Occ Name: "{occ.name}" \t Occ Component Name: "{occComponentName}" '
                #futil.log(msg)

                if occComponentName == componentName:      
                    msg = f'\nMatch with model.  \t Input: "{componentName}" \t Model: "{occComponentName}" '
                    futil.log(msg)

                    # If an appearance is specified in the input file - apply it.
                    # if none is specified in the input file - remove it.
                    if componentAppearance:

                        appToOccurrence(occ, componentAppearance, True)
                        adsk.doEvents()

                    else:
                        appToOccurrence(occ, componentAppearance, False)
                        adsk.doEvents()

def getAppearance(appearanceName):
    materialLibs = app.materialLibraries
    appearance = None
    designAppearance = None

    for appearance in design.appearances:

        if design.appearances.count == 0:
            msg = f'No local appearances'
            futil.log(msg)
            break

        if appearance.name == appearanceName:
            designAppearance = appearance
            msg = f'Appearance found in Design \t "{designAppearance.name}" \t & will be used.'
            futil.log(msg)
            return designAppearance

    for materialLib in materialLibs:
        
        for appearance in materialLib.appearances:

            if appearance.name == appearanceName:
                designAppearance = design.appearances.addByCopy(appearance, appearance.name)
                msg = f'Appearance found in MatLib \t "{designAppearance.name}" \t & copied to Design'
                futil.log(msg)
                return designAppearance

def listAppearances():
    designAppearances:adsk.core.Appearances = design.appearances

    msg = f'Design contains "{designAppearances.count}" appearances'
    futil.log(msg)

    for appearance in designAppearances:
        
        msg = f'Appearance \t"{appearance.name}" isUsed \t"{appearance.isUsed}" '
        futil.log(msg)

        for object in appearance.usedBy:
    
            msg = f'Appearance \t"{appearance.name}" use by \t"{object.name}" '
            futil.log(msg)
            #app.log(msg)

def appToOccurrence(occ:adsk.fusion.Occurrence, app:adsk.core.Appearance, applyAppearance: boolean):
    
    if applyAppearance:
        occ.appearance = app  
        adsk.doEvents()
    else:
        occ.appearance = None
        adsk.doEvents()

 

 

 

 

 

 

 

 

1 Like
516 Views
8 Replies
Replies (8)
Message 2 of 9

gvisca44
Advocate
Advocate

Support Case 21538622 

0 Likes
Message 3 of 9

adam.nagy
Autodesk Support
Autodesk Support

I uploaded your model, opened Design1 and ran the below code - I could see the appearances applied, then went to the Render workspace and there as well all seemed good:

adamnagy_0-1699470641867.png

 

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

app = None
ui = None
design = None

def applyAppearences():
	appearance = getAppearance('Brass - Matte')
	root = design.rootComponent
	occs = root.allOccurrences
	for occ in occs:
		occ.appearance = appearance
		print(occ.name)  

def getAppearance(appearanceName):
	materialLibs = app.materialLibraries
	
	for appearance in design.appearances:
		if appearance.name == appearanceName:
			designAppearance = appearance
			return designAppearance

	for materialLib in materialLibs:
		for appearance in materialLib.appearances:
			if appearance.name == appearanceName:
				designAppearance = design.appearances.addByCopy(appearance, appearance.name)
				return designAppearance

def run(context):
    global app, ui, design
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        ui.messageBox('Hello script')
        design = app.activeProduct
        applyAppearences()

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

"archive creation process flattens the folder structure"

I also tried moving the external components into a subfolder and try again, but that worked too.

 



Adam Nagy
Autodesk Platform Services
0 Likes
Message 4 of 9

gvisca44
Advocate
Advocate

Thanks @adam.nagy 

 

Wondering if you might do a quick test for me.

 

After running your script - 

 

In the design workspace, press A to open the Appearances window.

Right click the Brass - Matte appearance, and hit Unassign & Delete.

For me - the appearance is only removed from the LocalComp

 

Same for you ?

 

 

0 Likes
Message 5 of 9

gvisca44
Advocate
Advocate

To further represent my point.

 

I added the following listAppearances function:

def listAppearances():
    designAppearances:adsk.core.Appearances = design.appearances

    msg = f'Design contains "{designAppearances.count}" appearances'
    print(msg)

    for appearance in designAppearances:
        
        msg = f'Appearance \t"{appearance.name}" isUsed \t"{appearance.isUsed}" '
        print(msg)

        for object in appearance.usedBy:
    
            msg = f'Appearance \t"{appearance.name}" use by \t"{object.name}" '
            print(msg)
            #app.log(msg)

 

Then added an appearance to each component occurrence (save 1) through the UI.

 

Result:

Design contains "2" appearances
Appearance 	"Brass - Matte" isUsed 	"True" 
Appearance 	"Brass - Matte" use by 	"LocalComp1:1" 
Appearance 	"Brass - Matte" use by 	"LocalLinkedComp1 v2:1" 
Appearance 	"Brass - Matte" use by 	"LinkedComp1 v3:1" 
Appearance 	"Brass - Matte" use by 	"LA1-Comp1:1" 
Appearance 	"Steel - Satin" isUsed 	"True" 
Appearance 	"Steel - Satin" use by 	"Body1" 

 

I then applied the appearance via your script, followed by the listAppearances function

 

with this result:

Design contains "2" appearances
Appearance 	"Steel - Satin" isUsed 	"True" 
Appearance 	"Steel - Satin" use by 	"Body1" 
Appearance 	"Brass - Matte" isUsed 	"True" 
Appearance 	"Brass - Matte" use by 	"LocalComp1:1" 

 

 

 

0 Likes
Message 6 of 9

gvisca44
Advocate
Advocate

@adam.nagy 

 

Sorry for the three posts.  I've been trying to work out why your script "worked" ... notwithstanding what I think are errors represented in the two prior posts.  

 

I decided to go back and check my original script and working environment ... lo and behold everything is working!

 

Between the time I made the original posts and now, two things have changed (that I can think of):

1. A new release (well ... 2) of F360

2. I moved all my data from single user to teams.

 

However - I still think there is an discrepency between UI and API as represented in the posts above.

0 Likes
Message 7 of 9

adam.nagy
Autodesk Support
Autodesk Support

Yes, I get the same 🤔

 

Design contains "2" appearances
Appearance 	"Steel - Satin" isUsed 	"True" 
Appearance 	"Steel - Satin" use by 	"Body1" 
Appearance 	"Brass - Matte" isUsed 	"True" 
Appearance 	"Brass - Matte" use by 	"LocalAssy1:1" 

 

I'll check with others. 



Adam Nagy
Autodesk Platform Services
0 Likes
Message 8 of 9

BrianEkins
Mentor
Mentor

I'm able to reproduce the problem and there is a bug filed. Hopefully, it can be fixed soon.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
1 Like
Message 9 of 9

gvisca44
Advocate
Advocate

It would seem this got fixed in Fusion 2.0.18220 x86_64

0 Likes