[BUG] - API & RENDER - Apply Appearance to linked components fails
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Also - Refer the attached post in the forums
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).
Design workspace - after API applies appearances.
All appearances applied as defined in the CSV
(there is one occurrence that doesnt have an appearance intentionally).
Batch render of API appearances
Note the 2 cubes don't have any appearances in the render (other than the appearance by physical material).
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()