diff --git a/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py b/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py index 342b35f229..ba9f064e0b 100644 --- a/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py +++ b/exporter/SynthesisFusionAddin/src/Parser/ExporterOptions.py @@ -7,8 +7,6 @@ import os import platform from dataclasses import dataclass, field, fields -from enum import Enum, EnumType -from typing import get_origin import adsk.core from adsk.fusion import CalculationAccuracy, TriangleMeshQualityOptions @@ -25,6 +23,8 @@ PhysicalDepth, PreferredUnits, Wheel, + encodeNestedObjects, + makeObjectFromJson, ) @@ -58,71 +58,22 @@ class ExporterOptions: physicalDepth: PhysicalDepth = field(default=PhysicalDepth.AllOccurrence) physicalCalculationLevel: CalculationAccuracy = field(default=CalculationAccuracy.LowCalculationAccuracy) + @logFailure @timed def readFromDesign(self) -> "ExporterOptions": - try: - designAttributes = adsk.core.Application.get().activeProduct.attributes - for field in fields(self): - attribute = designAttributes.itemByName(INTERNAL_ID, field.name) - if attribute: - setattr( - self, - field.name, - self._makeObjectFromJson(field.type, json.loads(attribute.value)), - ) + designAttributes = adsk.core.Application.get().activeProduct.attributes + for field in fields(self): + attribute = designAttributes.itemByName(INTERNAL_ID, field.name) + if attribute: + attrJsonData = makeObjectFromJson(field.type, json.loads(attribute.value)) + setattr(self, field.name, attrJsonData) - return self - except: - return ExporterOptions() + return self @logFailure @timed def writeToDesign(self) -> None: designAttributes = adsk.core.Application.get().activeProduct.attributes for field in fields(self): - data = json.dumps( - getattr(self, field.name), - default=lambda obj: ( - obj.value - if isinstance(obj, Enum) - else ( - { - key: (lambda value: (value if not isinstance(value, Enum) else value.value))(value) - for key, value in obj.__dict__.items() - } - if hasattr(obj, "__dict__") - else obj - ) - ), - indent=4, - ) + data = json.dumps(getattr(self, field.name), default=encodeNestedObjects, indent=4) designAttributes.add(INTERNAL_ID, field.name, data) - - # There should be a way to clean this up - Brandon - def _makeObjectFromJson(self, objectType: type, data: any) -> any: - primitives = (bool, str, int, float, type(None)) - if isinstance(objectType, EnumType): - return objectType(data) - elif ( - objectType in primitives or type(data) in primitives - ): # Required to catch `fusion.TriangleMeshQualityOptions` - return data - elif get_origin(objectType) is list: - return [self._makeObjectFromJson(objectType.__args__[0], item) for item in data] - - newObject = objectType() - attrs = [x for x in dir(newObject) if not x.startswith("__") and not callable(getattr(newObject, x))] - for attr in attrs: - currType = objectType.__annotations__.get(attr, None) - if get_origin(currType) is list: - setattr( - newObject, - attr, - [self._makeObjectFromJson(currType.__args__[0], item) for item in data[attr]], - ) - elif currType in primitives: - setattr(newObject, attr, data[attr]) - elif isinstance(currType, object): - setattr(newObject, attr, self._makeObjectFromJson(currType, data[attr])) - - return newObject diff --git a/exporter/SynthesisFusionAddin/src/Types.py b/exporter/SynthesisFusionAddin/src/Types.py index 892fe6211b..1aca3a5162 100644 --- a/exporter/SynthesisFusionAddin/src/Types.py +++ b/exporter/SynthesisFusionAddin/src/Types.py @@ -1,9 +1,9 @@ import os import pathlib import platform -from dataclasses import dataclass, field -from enum import Enum -from typing import Union +from dataclasses import dataclass, field, fields, is_dataclass +from enum import Enum, EnumType +from typing import Union, get_origin # Not 100% sure what this is for - Brandon JointParentType = Enum("JointParentType", ["ROOT", "END"]) @@ -88,6 +88,38 @@ def toKg(pounds: float) -> KG: return KG(round(pounds / 2.2062, 2)) +PRIMITIVES = (bool, str, int, float, type(None)) + + +def encodeNestedObjects(obj: any) -> any: + if isinstance(obj, Enum): + return obj.value + elif hasattr(obj, "__dict__"): + return {key: encodeNestedObjects(value) for key, value in obj.__dict__.items()} + else: + assert isinstance(obj, PRIMITIVES) + return obj + + +def makeObjectFromJson(objType: type, data: any) -> any: + if isinstance(objType, EnumType): + return objType(data) + elif isinstance(objType, PRIMITIVES) or isinstance(data, PRIMITIVES): + return data + elif get_origin(objType) is list: + return [makeObjectFromJson(objType.__args__[0], item) for item in data] + + obj = objType() + assert is_dataclass(obj) and isinstance(data, dict), "Found unsupported type to decode." + for field in fields(obj): + if field.name in data: + setattr(obj, field.name, makeObjectFromJson(field.type, data[field.name])) + else: + setattr(obj, field.name, field.default) + + return obj + + class OString: def __init__(self, path: object, fileName: str): """Generate a string for the operating system that matches fusion requirements