From a2f99d356d7a8a91de62211961895f406db10cd7 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Tue, 8 Jul 2025 16:16:52 -0700 Subject: [PATCH 01/23] feat: adding design check tab to exporter --- .../src/UI/ConfigCommand.py | 7 ++++ .../src/UI/DesignCheckTab.py | 33 +++++++++++++++++++ .../src/UI/GeneralConfigTab.py | 2 ++ 3 files changed, 42 insertions(+) create mode 100644 exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 242773d227..dbe265c8b4 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -15,6 +15,7 @@ import src.UI.GamepieceConfigTab as GamepieceConfigTab import src.UI.GeneralConfigTab as GeneralConfigTab import src.UI.JointConfigTab as JointConfigTab +import src.UI.DesignCheckTab as DesignCheckTab from src import APP_WEBSITE_URL, gm from src.APS.APS import getAuth, getUserInfo from src.Logging import getLogger, logFailure @@ -26,6 +27,7 @@ generalConfigTab: GeneralConfigTab.GeneralConfigTab jointConfigTab: JointConfigTab.JointConfigTab gamepieceConfigTab: GamepieceConfigTab.GamepieceConfigTab +designCheckTab: DesignCheckTab.DesignCheckTab logger = getLogger() @@ -37,6 +39,7 @@ def reload() -> None: importlib.reload(GeneralConfigTab) importlib.reload(GamepieceConfigTab) importlib.reload(JointConfigTab) + importlib.reload(DesignCheckTab) importlib.reload(Parser) logger.info("UI modules reloaded successfully.") @@ -92,6 +95,10 @@ def notify(self, args: adsk.core.CommandCreatedEventArgs) -> None: jointConfigTab = JointConfigTab.JointConfigTab(args) generalConfigTab.jointConfigTab = jointConfigTab + global designCheckTab + designCheckTab = DesignCheckTab.DesignCheckTab(args) + generalConfigTab.designCheckTab = designCheckTab + if not exporterOptions.exportMode == ExportMode.FIELD: gamepieceConfigTab.isVisible = False diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py new file mode 100644 index 0000000000..95cd965a88 --- /dev/null +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -0,0 +1,33 @@ +import adsk.core +import adsk.fusion + +from src.Logging import getLogger, logFailure + +logger = getLogger # TODO: Remove + + +class DesignCheckTab: + designCheckTab: adsk.core.TabCommandInput + + @logFailure + def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: + self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Check") + designCheckTabInputs = self.designCheckTab.children + + @property + def isVisible(self) -> bool: + return self.designCheckTab.isVisible or False + + @isVisible.setter + def isVisible(self, value: bool) -> None: + self.designCheckTab.isVisible = value + + @property + def isActive(self) -> bool: + return self.designCheckTab.isActive or False + + @logFailure + def handleInputChanged( + self, args: adsk.core.InputChangedEventArgs, globalCommandInputs: adsk.core.CommandInputs + ) -> None: + commandInput = args.input diff --git a/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py b/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py index f734113975..1338712f48 100644 --- a/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py @@ -7,6 +7,7 @@ from src.UI.CreateCommandInputsHelper import createBooleanInput from src.UI.GamepieceConfigTab import GamepieceConfigTab from src.UI.JointConfigTab import JointConfigTab +from src.UI.DesignCheckTab import DesignCheckTab from src.Util import ( convertMassUnitsFrom, convertMassUnitsTo, @@ -23,6 +24,7 @@ class GeneralConfigTab: previousSelectedModeDropdownIndex: int jointConfigTab: JointConfigTab gamepieceConfigTab: GamepieceConfigTab + designConfigTab: DesignCheckTab @logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs, exporterOptions: ExporterOptions) -> None: From 066e38cd0aa6706a0763ccf67f9a1308df891fb8 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Mon, 14 Jul 2025 13:02:37 -0700 Subject: [PATCH 02/23] feat: adding height and perimeter rule checks --- .../src/UI/DesignCheckTab.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 95cd965a88..48c4d70f31 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,9 +1,11 @@ import adsk.core import adsk.fusion +from adsk.fusion import Design +from src import gm from src.Logging import getLogger, logFailure -logger = getLogger # TODO: Remove +logger = getLogger() # TODO: Remove class DesignCheckTab: @@ -14,6 +16,12 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Check") designCheckTabInputs = self.designCheckTab.children + # add a height thing (maximum 106 cm) + logger.info(f"{self.fusion_design_height}") + + # get the robot perimeter + logger.info(f"{self.fusion_design_perimeter}") + @property def isVisible(self) -> bool: return self.designCheckTab.isVisible or False @@ -31,3 +39,19 @@ def handleInputChanged( self, args: adsk.core.InputChangedEventArgs, globalCommandInputs: adsk.core.CommandInputs ) -> None: commandInput = args.input + + @property + def fusion_design_height(self) -> float: + design = Design.cast(gm.app.activeProduct) + if design: + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return overall_bounding_box.height + return 0.0 + + @property + def fusion_design_perimeter(self) -> float: + design = Design.cast(gm.app.activeProduct) + if design: + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return 2 * (overall_bounding_box.width + overall_bounding_box.length) + return 0.0 From 4fa43c862b70e13d6982944ca851e821c068e794 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Mon, 14 Jul 2025 13:07:01 -0700 Subject: [PATCH 03/23] feat: adding DRC UI --- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 48c4d70f31..82917c27e0 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -13,13 +13,19 @@ class DesignCheckTab: @logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: - self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Check") + self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check") designCheckTabInputs = self.designCheckTab.children # add a height thing (maximum 106 cm) - logger.info(f"{self.fusion_design_height}") + self.designCheckTab.children.addTextBoxCommandInput( + "designHeightText", "Design Height", f"{self.fusion_design_height:.2f} cm", 1, True + ) # get the robot perimeter + self.designCheckTab.children.addTextBoxCommandInput( + "designPerimeterText", "Design Perimeter", f"{self.fusion_design_perimeter:.2f} cm", 1, True + ) + logger.info(f"{self.fusion_design_perimeter}") @property From 5021b5b9248e0dd4cc369fb719bd0fb4af528f8d Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Fri, 18 Jul 2025 10:59:22 -0700 Subject: [PATCH 04/23] fix: formatting and mypy --- .../src/UI/DesignCheckTab.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 82917c27e0..c24b822505 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,17 +1,15 @@ import adsk.core import adsk.fusion -from adsk.fusion import Design -from src import gm -from src.Logging import getLogger, logFailure +from src import gm, Logging -logger = getLogger() # TODO: Remove +logger = Logging.getLogger() class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput - @logFailure + @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check") designCheckTabInputs = self.designCheckTab.children @@ -23,7 +21,7 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: # get the robot perimeter self.designCheckTab.children.addTextBoxCommandInput( - "designPerimeterText", "Design Perimeter", f"{self.fusion_design_perimeter:.2f} cm", 1, True + "designPerimeterText", "Design Perimeter", f"{self.fusion_design_perimeter:.2f} cm", 1, True ) logger.info(f"{self.fusion_design_perimeter}") @@ -40,24 +38,24 @@ def isVisible(self, value: bool) -> None: def isActive(self) -> bool: return self.designCheckTab.isActive or False - @logFailure - def handleInputChanged( - self, args: adsk.core.InputChangedEventArgs, globalCommandInputs: adsk.core.CommandInputs - ) -> None: - commandInput = args.input + # @Logging.logFailure + # def handleInputChanged( + # self, args: adsk.core.InputChangedEventArgs, globalCommandInputs: adsk.core.CommandInputs + # ) -> None: + # commandInput = args.input @property def fusion_design_height(self) -> float: - design = Design.cast(gm.app.activeProduct) + design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return overall_bounding_box.height + return float(overall_bounding_box.height) return 0.0 @property def fusion_design_perimeter(self) -> float: - design = Design.cast(gm.app.activeProduct) + design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return 2 * (overall_bounding_box.width + overall_bounding_box.length) + return float(2 * (overall_bounding_box.width + overall_bounding_box.length)) return 0.0 From cecb5d9d84639f55bfc23bb0511c7c1cac7ad104 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Fri, 18 Jul 2025 10:59:55 -0700 Subject: [PATCH 05/23] formatting --- exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py | 2 +- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 2 +- exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index a77642b532..1b43354690 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -12,10 +12,10 @@ import adsk.fusion import src.Parser.SynthesisParser.Parser as Parser +import src.UI.DesignCheckTab as DesignCheckTab import src.UI.GamepieceConfigTab as GamepieceConfigTab import src.UI.GeneralConfigTab as GeneralConfigTab import src.UI.JointConfigTab as JointConfigTab -import src.UI.DesignCheckTab as DesignCheckTab import src.UI.TaggingConfigTab as TaggingConfigTab from src import APP_WEBSITE_URL, Logging, gm from src.APS.APS import getAuth, getUserInfo diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index c24b822505..1ce3b2e1d0 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,7 +1,7 @@ import adsk.core import adsk.fusion -from src import gm, Logging +from src import Logging, gm logger = Logging.getLogger() diff --git a/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py b/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py index ecca5efa2b..109f23add1 100644 --- a/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py @@ -5,9 +5,9 @@ from src.Parser.ExporterOptions import ExporterOptions from src.Types import KG, ExportLocation, ExportMode, UnitSystem from src.UI.CreateCommandInputsHelper import createBooleanInput +from src.UI.DesignCheckTab import DesignCheckTab from src.UI.GamepieceConfigTab import GamepieceConfigTab from src.UI.JointConfigTab import JointConfigTab -from src.UI.DesignCheckTab import DesignCheckTab from src.UI.TaggingConfigTab import TaggingConfigTab from src.Util import ( convertMassUnitsFrom, From b0df31b217cccb101c70e12aa96bd9f9876e9b88 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Fri, 18 Jul 2025 14:55:48 -0700 Subject: [PATCH 06/23] feat: adding table with icons showing validity --- .github/workflows/FissionUnitTest.yml | 30 ++- .../DesignCheckIcons/valid-preview148x26.png | Bin 0 -> 1711 bytes .../src/UI/DesignCheckTab.py | 60 +++++- .../SynthesisFusionAddin/src/UI/IconPaths.py | 5 + fission/.gitattributes | 1 + fission/bun.lock | 1 + fission/package.json | 3 +- fission/public/.gitignore | 3 - fission/public/assetpack.zip | 3 + fission/src/test/TestSetup.server.ts | 57 ++++++ fission/vite.config.ts | 177 ++++++++++-------- 11 files changed, 233 insertions(+), 107 deletions(-) create mode 100644 exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview148x26.png create mode 100644 fission/.gitattributes delete mode 100644 fission/public/.gitignore create mode 100644 fission/public/assetpack.zip create mode 100644 fission/src/test/TestSetup.server.ts diff --git a/.github/workflows/FissionUnitTest.yml b/.github/workflows/FissionUnitTest.yml index 2d16f317ed..db6a5c4ae6 100644 --- a/.github/workflows/FissionUnitTest.yml +++ b/.github/workflows/FissionUnitTest.yml @@ -26,31 +26,25 @@ jobs: node-version: 20 - - name: Cache Synthesis assets + - name: Cache Unzipped Synthesis Assets id: cache-assets uses: actions/cache@v3 with: - path: fission/public/ - key: ${{ runner.os }}-assets-v1 - restore-keys: | - ${{ runner.os }}-assets- + path: fission/public/Downloadables + key: ${{ runner.os }}-assets-${{hashFiles('fission/public/assetpack.zip')}} - - name: Download assets if not cached + - name: Download Synthesis assetpack if not cached if: steps.cache-assets.outputs.cache-hit != 'true' run: | + cd .. apt update - apt install -y unzip - curl -o public/assetpack.zip https://synthesis.autodesk.com/downloadables/assetpack.zip && unzip -o public/assetpack.zip -d public/ - # Transition GH-1205: - # Avoiding the cache step for now as it was causing issues. - # Should revisit this later as this is a major speedup that we lost. - # - name: Cache downloaded browsers - # id: cache-browsers - # uses: actions/cache@v3 - # with: - # path: | - # ~/.cache/ms-playwright/ - # key: ${{ runner.os }}-assets-playwright-${{ env.PLAYWRIGHT_VERSION }}-v3 + apt install git-lfs unzip + git config --global --add safe.directory `pwd` + git lfs pull + + cd fission + unzip -o public/assetpack.zip -d public/ || echo + - name: Cache Node Dependencies uses: actions/cache@v3 diff --git a/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview148x26.png b/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview148x26.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6f07c1947b3079a8244cc4061fd98b453001b3 GIT binary patch literal 1711 zcmcgtdoCy_`@ zFHd)0bx4i1sEbCduZOEfSF>$`8qlIWSH_Y^ItHIZgLF8XLn4uN`QH9Z z)J+op(4c4oQ~;_@i-EZ;Rshjm4z&Q=&M&4VDzEy z6!0AudH}CNiyIZ2*HZltm#b9qEp6)Pr6j3UiTb2*XX-l!BBx)H%Rm4(7C= zWfY_PvA+vlG|+m`a~#Lp&^CmjF^p+I!wqiZFQQeC+iFzvz+-`xfP@&t+`=srn3SX37rr0yF#{QX z@H>Zd4QPl!L_YG>hfPCT6{g^!BDLIoWHg;jbCF7{tN#P@U!~Bd#{s7lpS8~JpZ`tkWh|Hthbw{NqHiN zcfy$E@0u;5kkJLC5((vCLQQX^?1W>&0ns>9J7JAqtCvgd5UoYFAO+K8PbGWor2Z-- z#$+ufvIwd?ykTB+Nk2=*?wh6=IBJ^R{K@sTPKsrmq&r@2OKf8)1WK0qg)3p1)SjJ0 z?pwWv@fCY+o_#&RlWN~BRdwhMgy>dFf+8%q)<1psQHx@d8X8(9>upv{7cWqLuu$!r z6|_S$Pxymxzjclqr}awLNc80U0$cuFS(={c68#a42p`@xG&yyopssc+yX3)BC)sT$ z#`w~YEwx*;uHO8ltUW(5_*8q?WgX(~pk-oh)W!D<*Dx`+{UTwm*Kk1h)QiNTvBY)( zgV8N-I^!z{$EC(EI_-0$y%eG?hAA_mZ<4&Ru?svH zFfb>%u_B%0VekH8oL|&6UcQP#JiN?kKIT$lW_Pkre-?MrW>XuZOHB9pnHEX`Llx+f z|B6i18CLI>)E$qRkse96j+1ga$<3)t21Sy`siL4JA=5%}(xA_pGA%lFskhc-Q@&_l zw$v)Cj?QpUEZn}V?(V~>+459TleYHe7n*t8dOr1=whzyG_S9%p|8ghMHo%FMxnihN z870v-%o7rgf+w*qR(%G4a_fm91KZ&SwlIgLwT)=@Zz1cC9Wv%b8c8dhw7s^nokuw7 z4pPtH%o^%^WgCNC=9(*?&EZrTT6)Eq`gjPaGYyG-$_+{M&W&M%8?1UyC+QH?+xG|2 z4yK;qlm&>JMt3t9qHhD!t{$0^Q7D z75ZD*`HmvHC%cR)Vy^`i4_N8WuhDGSwh&1g1|&Cn!9XBizC^HwnO7iwc6x2hdL?C; zH*{SltRNNayW(rUJXF3ky41fZb7R$7(|5CEj_z#@0>SMBp>af%c~N8A=9Lpu>c6if z+wOJNKccv$U7UP-u5Y8{8Ox#kE%`dmZQ*h_E613XGj0=P(!)`Eo9p*3@BI8%yq5U5 Jm$(K={|1Jzr*Z%Q literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 1ce3b2e1d0..747558c3ec 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -2,29 +2,75 @@ import adsk.fusion from src import Logging, gm +from src.UI import IconPaths + +import importlib logger = Logging.getLogger() class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput + designCheckTable: adsk.core.TableCommandInput + + MAX_HEIGHT = 106.0 # cm @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: + # TODO: Remove + importlib.reload(IconPaths) + self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check") designCheckTabInputs = self.designCheckTab.children - # add a height thing (maximum 106 cm) - self.designCheckTab.children.addTextBoxCommandInput( - "designHeightText", "Design Height", f"{self.fusion_design_height:.2f} cm", 1, True + # Create the table for design checks + self.designCheckTable = designCheckTabInputs.addTableCommandInput( + "designCheckTable", "Design Checks", 3, "3:2:2" ) + self.designCheckTable.tablePresentationStyle = adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle + + # Row 1: Design Height + height = self.fusion_design_height + is_height_valid = height <= self.MAX_HEIGHT - # get the robot perimeter - self.designCheckTab.children.addTextBoxCommandInput( - "designPerimeterText", "Design Perimeter", f"{self.fusion_design_perimeter:.2f} cm", 1, True + height_name_input = designCheckTabInputs.addTextBoxCommandInput( + "designHeightText", "Design Height", "Design Height", 1, True ) + height_value_input = designCheckTabInputs.addTextBoxCommandInput( + "designHeightValue", "Value", f"{height:.2f} cm", 1, True + ) + height_icon_input = designCheckTabInputs.addImageCommandInput( + "heightStatusIcon", + "", + IconPaths.designCheckIcons["valid"] if is_height_valid else IconPaths.designCheckIcons["invalid"], + ) + + self.designCheckTable.addCommandInput(height_name_input, 0, 0) + self.designCheckTable.addCommandInput(height_value_input, 0, 1) + self.designCheckTable.addCommandInput(height_icon_input, 0, 2) + + # Row 2: Design Perimeter + perimeter = self.fusion_design_perimeter + # No rule for perimeter, so it's always valid for now + is_perimeter_valid = True + + perimeter_name_input = designCheckTabInputs.addTextBoxCommandInput( + "designPerimeterText", "Design Perimeter", "Design Perimeter", 1, True + ) + perimeter_value_input = designCheckTabInputs.addTextBoxCommandInput( + "designPerimeterValue", "Value", f"{perimeter:.2f} cm", 1, True + ) + perimeter_icon_input = designCheckTabInputs.addImageCommandInput( + "perimeterStatusIcon", + "", + IconPaths.designCheckIcons["valid"] if is_perimeter_valid else IconPaths.designCheckIcons["invalid"], + ) + + self.designCheckTable.addCommandInput(perimeter_name_input, 1, 0) + self.designCheckTable.addCommandInput(perimeter_value_input, 1, 1) + self.designCheckTable.addCommandInput(perimeter_icon_input, 1, 2) - logger.info(f"{self.fusion_design_perimeter}") + logger.info(f"Design Perimeter: {self.fusion_design_perimeter}") @property def isVisible(self) -> bool: diff --git a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py index 0a92adf355..bef69efdb8 100644 --- a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py +++ b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py @@ -43,6 +43,11 @@ "friction_override-enabled": resources + os.path.join("FrictionOverride_icon"), # resource folder } +designCheckIcons = { + "valid": resources + os.path.join("DesignCheckIcons", "valid-preview148x26.png"), + "invalid": resources + os.path.join("DesignCheckIcons", "valid-preview148x26.png"), +} + tagIcons = { "blank": resources + "blank-preview16x16.png", } diff --git a/fission/.gitattributes b/fission/.gitattributes new file mode 100644 index 0000000000..0bfa0baf18 --- /dev/null +++ b/fission/.gitattributes @@ -0,0 +1 @@ +public/assetpack.zip filter=lfs diff=lfs merge=lfs -text diff --git a/fission/bun.lock b/fission/bun.lock index 7e8ec62f3e..c008d1de6e 100644 --- a/fission/bun.lock +++ b/fission/bun.lock @@ -59,6 +59,7 @@ "protobufjs": "^7.5.3", "protobufjs-cli": "^1.1.3", "rollup": "^4.44.0", + "sirv": "^3.0.1", "tailwindcss": "^3.4.17", "tsconfig-paths": "^4.2.0", "typescript": "^5.8.3", diff --git a/fission/package.json b/fission/package.json index 63f4f5afd6..c7328b3d9a 100644 --- a/fission/package.json +++ b/fission/package.json @@ -17,7 +17,7 @@ "prettier": "bun x prettier src --check || npx prettier src --check", "prettier:fix": "bun x prettier src --write || npx prettier src --write", "format": "bun run prettier:fix && bun run lint:fix || npm run prettier:fix && npm run lint:fix", - "assetpack": "curl -o public/assetpack.zip https://synthesis.autodesk.com/downloadables/assetpack.zip && tar -xf public/assetpack.zip -C public/", + "assetpack": "git lfs pull && tar -xf public/assetpack.zip -C public/", "playwright:install": "bun x playwright install || npx playwright install" }, "dependencies": { @@ -76,6 +76,7 @@ "protobufjs": "^7.5.3", "protobufjs-cli": "^1.1.3", "rollup": "^4.44.0", + "sirv": "^3.0.1", "tailwindcss": "^3.4.17", "tsconfig-paths": "^4.2.0", "typescript": "^5.8.3", diff --git a/fission/public/.gitignore b/fission/public/.gitignore deleted file mode 100644 index e7c1e91286..0000000000 --- a/fission/public/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -assetpack.zip -Downloadables/ - diff --git a/fission/public/assetpack.zip b/fission/public/assetpack.zip new file mode 100644 index 0000000000..744f3da0ef --- /dev/null +++ b/fission/public/assetpack.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:985ef0918d3b8200fc076cc31d9af0199b08586e45fda4ecc716555a01660fee +size 193149510 diff --git a/fission/src/test/TestSetup.server.ts b/fission/src/test/TestSetup.server.ts new file mode 100644 index 0000000000..566d128283 --- /dev/null +++ b/fission/src/test/TestSetup.server.ts @@ -0,0 +1,57 @@ +import sirv from "sirv" +import http from "node:http" +import path from "node:path" + +let server: http.Server | undefined +const PORT = 3001 +const serveDirectory = path.join(process.cwd(), "public") +export async function setup() { + if (server) { + return + } + + console.log("Starting static file server...") + + const assets = sirv(serveDirectory) + + server = http.createServer((req, res) => { + res.setHeader("Access-Control-Allow-Origin", "*") + res.setHeader("Access-Control-Allow-Methods", "GET") + assets(req, res) + }) + + await new Promise((resolve, reject) => { + if (!server) { + console.warn("no server") + return + } + server.listen(PORT, "127.0.0.1", () => { + console.log(`Serving files from ${serveDirectory} on port ${PORT} `) + resolve() + }) + + server.once("error", err => { + console.error("Failed to start static file server:", err) + server = undefined + reject(err) + }) + }) +} + +export async function teardown() { + if (server) { + await new Promise((resolve, reject) => { + server!.close(err => { + if (err) { + console.error("Error stopping static file server:", err) + reject(err) + return + } + + console.log("Static file server stopped.") + server = undefined + resolve() + }) + }) + } +} diff --git a/fission/vite.config.ts b/fission/vite.config.ts index f959356c05..fbf5f9e106 100644 --- a/fission/vite.config.ts +++ b/fission/vite.config.ts @@ -1,30 +1,36 @@ -import { defineConfig } from 'vitest/config' -import * as path from 'path' -import react from '@vitejs/plugin-react-swc' -import basicSsl from '@vitejs/plugin-basic-ssl' -import glsl from 'vite-plugin-glsl' +import { defineConfig } from "vitest/config" +import * as path from "path" +import react from "@vitejs/plugin-react-swc" +import basicSsl from "@vitejs/plugin-basic-ssl" +import glsl from "vite-plugin-glsl" +import { loadEnv, ProxyOptions } from "vite" const basePath = "/fission/" const serverPort = 3000 const dockerServerPort = 80 -const useLocal = false +const useLocalAPS = false const useSsl = false const plugins = [ - react(), glsl({ - include: [ // Glob pattern, or array of glob patterns to import - '**/*.glsl', '**/*.wgsl', - '**/*.vert', '**/*.frag', - '**/*.vs', '**/*.fs' + react(), + glsl({ + include: [ + // Glob pattern, or array of glob patterns to import + "**/*.glsl", + "**/*.wgsl", + "**/*.vert", + "**/*.frag", + "**/*.vs", + "**/*.fs", ], - exclude: undefined, // Glob pattern, or array of glob patterns to ignore + exclude: undefined, // Glob pattern, or array of glob patterns to ignore warnDuplicatedImports: true, // Warn if the same chunk was imported multiple times - defaultExtension: 'glsl', // Shader suffix when no extension is specified - minify: false, // Minify/optimize output shader code - watch: true, // Recompile shader on change - root: '/' // Directory for root imports - }) + defaultExtension: "glsl", // Shader suffix when no extension is specified + minify: false, // Minify/optimize output shader code + watch: true, // Recompile shader on change + root: "/", // Directory for root imports + }), ] if (useSsl) { @@ -32,67 +38,82 @@ if (useSsl) { } // https://vitejs.dev/config/ -export default defineConfig({ - plugins: plugins, - resolve: { - alias: [ - { find: "@/components", replacement: path.resolve(__dirname, "src", "ui", "components") }, - { find: "@/modals", replacement: path.resolve(__dirname, "src", "ui", "modals") }, - { find: "@/panels", replacement: path.resolve(__dirname, "src", "ui", "panels") }, - { find: "@", replacement: path.resolve(__dirname, "src") }, - ], - }, - test: { - testTimeout: 10000, - globals: true, - environment: "jsdom", - browser: { - enabled: true, - provider: "playwright", - instances: [ - { - name: "chromium", - browser: "chromium", - headless: true, - }, - { - name: "firefox", - browser: "firefox", - headless: true, - }, +export default defineConfig(({ mode, }) => { + process.env = {...process.env, ...loadEnv(mode, process.cwd())}; + const useLocalAssets = mode === "test" || process.env.DEV + const proxies: Record = {} + proxies["/api/mira"] = useLocalAssets + ? { + target: `http://localhost:${mode === "test" ? 3001 : serverPort}`, + changeOrigin: true, + secure: false, + rewrite: path => path.replace(/^\/api\/mira/, "/Downloadables/Mira").replace("robots", "Robots").replace("fields", "Fields"), + } + : { + target: `https://synthesis.autodesk.com/`, + changeOrigin: true, + secure: true, + } + proxies["/api/aps"] = useLocalAPS + ? { + target: `http://localhost:${dockerServerPort}/`, + changeOrigin: true, + secure: false, + } + : { + target: `https://synthesis.autodesk.com/`, + changeOrigin: true, + secure: true, + } + + return { + plugins: plugins, + publicDir: "./public", + resolve: { + alias: [ + { find: "@/components", replacement: path.resolve(__dirname, "src", "ui", "components") }, + { find: "@/modals", replacement: path.resolve(__dirname, "src", "ui", "modals") }, + { find: "@/panels", replacement: path.resolve(__dirname, "src", "ui", "panels") }, + { find: "@", replacement: path.resolve(__dirname, "src") }, ], }, - }, - server: { - // this ensures that the browser opens upon server start - // open: true, - // this sets a default port to 3000 - port: serverPort, - cors: false, - proxy: useLocal - ? { - "/api/mira": { - target: `http://localhost:${serverPort}${basePath}`, - changeOrigin: true, - secure: false, - rewrite: path => path.replace(/^\/api\/mira/, "/Downloadables/Mira"), - }, - "/api/aps": { - target: `http://localhost:${dockerServerPort}/`, - changeOrigin: true, - secure: false, - }, - } - : { - "/api": { - target: `https://synthesis.autodesk.com/`, - changeOrigin: true, - secure: true, - }, - }, - }, - build: { - target: "esnext", - }, - base: basePath, + test: { + globalSetup: ["src/test/TestSetup.server.ts"], + testTimeout: 10000, + globals: true, + environment: "jsdom", + browser: { + enabled: true, + provider: "playwright", + instances: [ + { + name: "chromium", + browser: "chromium", + headless: true, + }, + { + name: "firefox", + browser: "firefox", + headless: true, + }, + ], + }, + }, + build: { + target: "esnext" + }, + server: { + + // this ensures that the browser opens upon server start + // open: true, + // this sets a default port to 3000 + port: serverPort, + cors: false, + proxy: proxies, + build: { + target: "esnext", + }, + base: basePath, + }, + } }) From 6d2e608b3194cb2b7eb1d0a5dd00a10e0778a0b6 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Fri, 18 Jul 2025 15:11:36 -0700 Subject: [PATCH 07/23] feat: adding invalid and proper perimeter validation --- .../invalid-preview112x18.png | Bin 0 -> 1456 bytes .../DesignCheckIcons/valid-preview148x26.png | Bin 1711 -> 0 bytes .../DesignCheckIcons/valid-preview93x18.png | Bin 0 -> 1323 bytes .../src/UI/DesignCheckTab.py | 20 ++---------------- .../SynthesisFusionAddin/src/UI/IconPaths.py | 4 ++-- 5 files changed, 4 insertions(+), 20 deletions(-) create mode 100644 exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/invalid-preview112x18.png delete mode 100644 exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview148x26.png create mode 100644 exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview93x18.png diff --git a/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/invalid-preview112x18.png b/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/invalid-preview112x18.png new file mode 100644 index 0000000000000000000000000000000000000000..22ff6ad2e2f7bd5ebc8940393639b08acb24670f GIT binary patch literal 1456 zcmah|doUDu9N!0GJ@(`(vms>aa7+V zO-YJaiY$wgSIDcolt-TBbGuW2+)dNmcfODLJm&lR<2%227bm(bg+?I|2(orNZCr&j zL|CgO$--VvD)>P-h)h?y6|uYN$J0U}6|&QVNgzloh>A$K%2pH_hFu)pb_hlMZ<0vR z(Sg4|)~xwr9vOi!GBdHbh=Bp9s$%_m#Kj>s6>@T*({bPcva^wqfkTJD<)X0>O-&dc zMqwdrZNDs;n1F=^R#u>?iPlzVXnf(Nq)<=*B_&Kv!PFFHW+0Q%(Sg&aVQ-JoQBWvY zwF>?HsH?+?6L|R&Z{MQ69(sC+j>h~vWMo9El$Eit07plB{0JHi?d@=PM{F$S=CF4! zCMVI*fRq$OMTyKkJ@E5GLIQGf;OPl%ZM=I26BAfkqP7;5l~7PXb2Dz;0+EQ876b+& zFApphIy-Ui9;&KfZVny~0Rc!$!?9yfQ9)Q3Hf@5jF>Gv5UJf=J^75FOK~@%ed%<9c z-mR&@)2GnVLVP@^RE&?~;X|mY!PggthLDiJl`AMKgT6kdr@`k#Obi?j+}zOB1%Uuh zp5W6b(H>`KEG;2C9Eyq}_^*@~my|}B3a|r~}T|1kf+#=Z3Y0-`?zTfj|=#~3x zi#1yg?@H>s*^_8%v?ew%<7RBF%;nIctqy_drdj0*lG+z8Usdcl5I{_85ESqh$Enp$&=NF0yk14ts5_+TS0wK=tV6^zz4V+EJ>1wm$B||GUtijgO zH`iRYT9EgxBc`0&$Wj_S&f9#-yFV%E$R%z%!S~#GEv{|MxZI3Hl;rs4OrAvVBH@MG z7Rna(xb9K;9a?<;lJxt(x;i)}M6g~yba=Cxx}gcN5~tK4((X?|$=i0P>fCp<6_htl%Z;CvrMGrO<@si-FqdnX zp~`t1Z`+Sp8{|+g53!;dK1K5C25Fp%=R}7A)fk6{nKHhsGxgwk#a&(I&Gxij^R!Y6 z@!Qn0L%)zi_4Cy_`@ zFHd)0bx4i1sEbCduZOEfSF>$`8qlIWSH_Y^ItHIZgLF8XLn4uN`QH9Z z)J+op(4c4oQ~;_@i-EZ;Rshjm4z&Q=&M&4VDzEy z6!0AudH}CNiyIZ2*HZltm#b9qEp6)Pr6j3UiTb2*XX-l!BBx)H%Rm4(7C= zWfY_PvA+vlG|+m`a~#Lp&^CmjF^p+I!wqiZFQQeC+iFzvz+-`xfP@&t+`=srn3SX37rr0yF#{QX z@H>Zd4QPl!L_YG>hfPCT6{g^!BDLIoWHg;jbCF7{tN#P@U!~Bd#{s7lpS8~JpZ`tkWh|Hthbw{NqHiN zcfy$E@0u;5kkJLC5((vCLQQX^?1W>&0ns>9J7JAqtCvgd5UoYFAO+K8PbGWor2Z-- z#$+ufvIwd?ykTB+Nk2=*?wh6=IBJ^R{K@sTPKsrmq&r@2OKf8)1WK0qg)3p1)SjJ0 z?pwWv@fCY+o_#&RlWN~BRdwhMgy>dFf+8%q)<1psQHx@d8X8(9>upv{7cWqLuu$!r z6|_S$Pxymxzjclqr}awLNc80U0$cuFS(={c68#a42p`@xG&yyopssc+yX3)BC)sT$ z#`w~YEwx*;uHO8ltUW(5_*8q?WgX(~pk-oh)W!D<*Dx`+{UTwm*Kk1h)QiNTvBY)( zgV8N-I^!z{$EC(EI_-0$y%eG?hAA_mZ<4&Ru?svH zFfb>%u_B%0VekH8oL|&6UcQP#JiN?kKIT$lW_Pkre-?MrW>XuZOHB9pnHEX`Llx+f z|B6i18CLI>)E$qRkse96j+1ga$<3)t21Sy`siL4JA=5%}(xA_pGA%lFskhc-Q@&_l zw$v)Cj?QpUEZn}V?(V~>+459TleYHe7n*t8dOr1=whzyG_S9%p|8ghMHo%FMxnihN z870v-%o7rgf+w*qR(%G4a_fm91KZ&SwlIgLwT)=@Zz1cC9Wv%b8c8dhw7s^nokuw7 z4pPtH%o^%^WgCNC=9(*?&EZrTT6)Eq`gjPaGYyG-$_+{M&W&M%8?1UyC+QH?+xG|2 z4yK;qlm&>JMt3t9qHhD!t{$0^Q7D z75ZD*`HmvHC%cR)Vy^`i4_N8WuhDGSwh&1g1|&Cn!9XBizC^HwnO7iwc6x2hdL?C; zH*{SltRNNayW(rUJXF3ky41fZb7R$7(|5CEj_z#@0>SMBp>af%c~N8A=9Lpu>c6if z+wOJNKccv$U7UP-u5Y8{8Ox#kE%`dmZQ*h_E613XGj0=P(!)`Eo9p*3@BI8%yq5U5 Jm$(K={|1Jzr*Z%Q diff --git a/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview93x18.png b/exporter/SynthesisFusionAddin/src/Resources/DesignCheckIcons/valid-preview93x18.png new file mode 100644 index 0000000000000000000000000000000000000000..aa887d1dc514dbad8b2498eaf012c492a877ad17 GIT binary patch literal 1323 zcmaJ>2}~1a6mBW^DfgwY0>Xj_$XyAiU@3=kw3SW@^+0Z!7E#f0i}66@5ERhKQ9*Dl zDBv7gFifygrUGJ+re+aA98*9h$co=;NM^FQy}a{#NB;aTc_%`*P}P);lnDfa8ZE#t zTx!`;(NZKywR(+NfYcDV;Z$GZsK6#m8Yq1mz~T}JD%zifNT{qfkQUqs4W|1`lLY)H z8^X{V-ulp|fZ_^QH@IaXs}{9#kkf#MIn38!jT+QeCd45QKop`TF)4zG4Duot_hFwW zJYx}Ch(bN+r6H{w-E6RBA-mEQjNk<$^a)HTLs<)2^O)y=a{(7vV5K0X5sfA=Ig7K- za8`$U5sKQ-MgnOV!+FRnK}j>3b)Ykg*<~!hzzYXBEMchzHDxHfi@PpxX+g^_?7D(0 zxybc`mptT|U{;|@2}(&wQhiW&sL5;-ekx z>QR3m_bp+$6xz3*W;N4*4DXUMt&CI^k+T7=v98lc4+!)oSG>}NYyo{!d8&hxjKZ*Luc zNa^BcEojvy{9?LCvsd1UpI!g^)WQ5wQCnuw<-WsrBNR<fBW^$d#cP0{W|Sy^x7uB9!x8tx|B&Bx?dJ6Nz=zklh*cw9K|NPp*6 zt9~_H?66e55cTW(*DR)EJ85cIt#AWBq*rLL`@<{e3PX}oOpah#JNiCP==siy^G&MR z0b@SNv6FK$c3daJraZ3QG2@N7Vb!vFK%L%K72@tkR|8pQCBiPNxWhIVpDs+wxrSGk z6Y^JXw!WE&G1&gc*L?N*j)IyB3CAOHEW2&^#$an+fqVVY&K9Cv!-u096osC_1vb&; zlImdpY9C2X|Mk(hVF`!bC>*fb{3JSEkf=fRnx7@7+0G7~V(T8KvE))|VeOC3IrB^_ z!z68TW0n^cI-F9nAn0ob(MVDzS(0sA=j3ja@sAV?&Oc2XXcb8ULK?ECmt~LDsJC% p^7Iq$lrcF#N?Ly0nNt^^MYxqWq;32|cJ8N}M)Tj|*W$~`{TsRd*9ias literal 0 HcmV?d00001 diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 747558c3ec..85c3b75249 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -4,22 +4,15 @@ from src import Logging, gm from src.UI import IconPaths -import importlib - -logger = Logging.getLogger() - - class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput MAX_HEIGHT = 106.0 # cm + MAX_PERIMETER = 304.0 # cm @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: - # TODO: Remove - importlib.reload(IconPaths) - self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check") designCheckTabInputs = self.designCheckTab.children @@ -51,8 +44,7 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: # Row 2: Design Perimeter perimeter = self.fusion_design_perimeter - # No rule for perimeter, so it's always valid for now - is_perimeter_valid = True + is_perimeter_valid = perimeter <= self.MAX_PERIMETER perimeter_name_input = designCheckTabInputs.addTextBoxCommandInput( "designPerimeterText", "Design Perimeter", "Design Perimeter", 1, True @@ -70,8 +62,6 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: self.designCheckTable.addCommandInput(perimeter_value_input, 1, 1) self.designCheckTable.addCommandInput(perimeter_icon_input, 1, 2) - logger.info(f"Design Perimeter: {self.fusion_design_perimeter}") - @property def isVisible(self) -> bool: return self.designCheckTab.isVisible or False @@ -84,12 +74,6 @@ def isVisible(self, value: bool) -> None: def isActive(self) -> bool: return self.designCheckTab.isActive or False - # @Logging.logFailure - # def handleInputChanged( - # self, args: adsk.core.InputChangedEventArgs, globalCommandInputs: adsk.core.CommandInputs - # ) -> None: - # commandInput = args.input - @property def fusion_design_height(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) diff --git a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py index bef69efdb8..a2877c7bdf 100644 --- a/exporter/SynthesisFusionAddin/src/UI/IconPaths.py +++ b/exporter/SynthesisFusionAddin/src/UI/IconPaths.py @@ -44,8 +44,8 @@ } designCheckIcons = { - "valid": resources + os.path.join("DesignCheckIcons", "valid-preview148x26.png"), - "invalid": resources + os.path.join("DesignCheckIcons", "valid-preview148x26.png"), + "valid": resources + os.path.join("DesignCheckIcons", "valid-preview93x18.png"), + "invalid": resources + os.path.join("DesignCheckIcons", "invalid-preview112x18.png"), } tagIcons = { From 140645b91bcdfa5aa246006e402163908ee651b1 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Fri, 18 Jul 2025 15:12:11 -0700 Subject: [PATCH 08/23] formatting --- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 85c3b75249..2c6b447950 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -4,6 +4,7 @@ from src import Logging, gm from src.UI import IconPaths + class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput @@ -20,7 +21,9 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: self.designCheckTable = designCheckTabInputs.addTableCommandInput( "designCheckTable", "Design Checks", 3, "3:2:2" ) - self.designCheckTable.tablePresentationStyle = adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle + self.designCheckTable.tablePresentationStyle = ( + adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle + ) # Row 1: Design Height height = self.fusion_design_height From 5e46d91971d3fcc9d9045055bf702487d011f76f Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 23 Jul 2025 15:44:41 -0700 Subject: [PATCH 09/23] fix: removing orientedboundingbox --- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 2c6b447950..a08943373a 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -81,14 +81,16 @@ def isActive(self) -> bool: def fusion_design_height(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: - overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return float(overall_bounding_box.height) + overall_bounding_box = design.rootComponent.boundingBox + return float(overall_bounding_box.maxPoint.z - overall_bounding_box.minPoint.z) return 0.0 @property def fusion_design_perimeter(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: - overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return float(2 * (overall_bounding_box.width + overall_bounding_box.length)) + overall_bounding_box = design.rootComponent.boundingBox + width = overall_bounding_box.maxPoint.x - overall_bounding_box.minPoint.x + length = overall_bounding_box.maxPoint.y - overall_bounding_box.minPoint.y + return float(2 * (width + length)) return 0.0 From 9130b8ab8a58acd8bb0bd4b42aee9bcfaa9d0ff6 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 23 Jul 2025 15:51:29 -0700 Subject: [PATCH 10/23] feat: adding extensibility --- .../src/UI/DesignCheckTab.py | 78 +++++++++---------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index a08943373a..6be7116777 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,5 +1,6 @@ import adsk.core import adsk.fusion +from typing import Any, Dict from src import Logging, gm from src.UI import IconPaths @@ -9,9 +10,6 @@ class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput - MAX_HEIGHT = 106.0 # cm - MAX_PERIMETER = 304.0 # cm - @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check") @@ -25,45 +23,41 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle ) - # Row 1: Design Height - height = self.fusion_design_height - is_height_valid = height <= self.MAX_HEIGHT - - height_name_input = designCheckTabInputs.addTextBoxCommandInput( - "designHeightText", "Design Height", "Design Height", 1, True - ) - height_value_input = designCheckTabInputs.addTextBoxCommandInput( - "designHeightValue", "Value", f"{height:.2f} cm", 1, True - ) - height_icon_input = designCheckTabInputs.addImageCommandInput( - "heightStatusIcon", - "", - IconPaths.designCheckIcons["valid"] if is_height_valid else IconPaths.designCheckIcons["invalid"], - ) - - self.designCheckTable.addCommandInput(height_name_input, 0, 0) - self.designCheckTable.addCommandInput(height_value_input, 0, 1) - self.designCheckTable.addCommandInput(height_icon_input, 0, 2) - - # Row 2: Design Perimeter - perimeter = self.fusion_design_perimeter - is_perimeter_valid = perimeter <= self.MAX_PERIMETER - - perimeter_name_input = designCheckTabInputs.addTextBoxCommandInput( - "designPerimeterText", "Design Perimeter", "Design Perimeter", 1, True - ) - perimeter_value_input = designCheckTabInputs.addTextBoxCommandInput( - "designPerimeterValue", "Value", f"{perimeter:.2f} cm", 1, True - ) - perimeter_icon_input = designCheckTabInputs.addImageCommandInput( - "perimeterStatusIcon", - "", - IconPaths.designCheckIcons["valid"] if is_perimeter_valid else IconPaths.designCheckIcons["invalid"], - ) - - self.designCheckTable.addCommandInput(perimeter_name_input, 1, 0) - self.designCheckTable.addCommandInput(perimeter_value_input, 1, 1) - self.designCheckTable.addCommandInput(perimeter_icon_input, 1, 2) + # Define and add design rules to the table + design_rules = [ + { + "name": "Design Height", + "calculation": self.fusion_design_height, + "max_value": 106.0, # cm + }, + { + "name": "Design Perimeter", + "calculation": self.fusion_design_perimeter, + "max_value": 304.0, # cm + }, + ] + + for i, rule in enumerate(design_rules): + value = rule["calculation"] + is_valid = value <= rule["max_value"] + rule_name = rule["name"] + rule_id = rule_name.replace(" ", "") + + name_input = designCheckTabInputs.addTextBoxCommandInput( + f"{rule_id}Name", rule_name, rule_name, 1, True + ) + value_input = designCheckTabInputs.addTextBoxCommandInput( + f"{rule_id}Value", "Value", f"{value:.2f} cm", 1, True + ) + icon_input = designCheckTabInputs.addImageCommandInput( + f"{rule_id}StatusIcon", + "", + IconPaths.designCheckIcons["valid"] if is_valid else IconPaths.designCheckIcons["invalid"], + ) + + self.designCheckTable.addCommandInput(name_input, i, 0) + self.designCheckTable.addCommandInput(value_input, i, 1) + self.designCheckTable.addCommandInput(icon_input, i, 2) @property def isVisible(self) -> bool: From 1a5eb20917029d7e4f32e76a37f8a899e2bda649 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 23 Jul 2025 15:51:58 -0700 Subject: [PATCH 11/23] fix: formatting --- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 6be7116777..e4efe370bd 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,6 +1,7 @@ +from typing import Any, Dict + import adsk.core import adsk.fusion -from typing import Any, Dict from src import Logging, gm from src.UI import IconPaths @@ -43,9 +44,7 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: rule_name = rule["name"] rule_id = rule_name.replace(" ", "") - name_input = designCheckTabInputs.addTextBoxCommandInput( - f"{rule_id}Name", rule_name, rule_name, 1, True - ) + name_input = designCheckTabInputs.addTextBoxCommandInput(f"{rule_id}Name", rule_name, rule_name, 1, True) value_input = designCheckTabInputs.addTextBoxCommandInput( f"{rule_id}Value", "Value", f"{value:.2f} cm", 1, True ) From 5ad15c1aa6761667b7f28de85028811f276f9e4d Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 23 Jul 2025 16:08:32 -0700 Subject: [PATCH 12/23] fix: mypy --- .../src/UI/DesignCheckTab.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index e4efe370bd..69bd752375 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -10,6 +10,7 @@ class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput + designRules: Dict[str, Any] = {} @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: @@ -25,7 +26,7 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: ) # Define and add design rules to the table - design_rules = [ + self.design_rules = [ { "name": "Design Height", "calculation": self.fusion_design_height, @@ -38,11 +39,11 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: }, ] - for i, rule in enumerate(design_rules): - value = rule["calculation"] - is_valid = value <= rule["max_value"] - rule_name = rule["name"] - rule_id = rule_name.replace(" ", "") + for i, rule in enumerate(self.design_rules): + value: float = rule["calculation"]() + is_valid: bool = value <= rule["max_value"] + rule_name: str = str(rule["name"]) + rule_id: str = rule_name.replace(" ", "") name_input = designCheckTabInputs.addTextBoxCommandInput(f"{rule_id}Name", rule_name, rule_name, 1, True) value_input = designCheckTabInputs.addTextBoxCommandInput( @@ -70,7 +71,7 @@ def isVisible(self, value: bool) -> None: def isActive(self) -> bool: return self.designCheckTab.isActive or False - @property + @Logging.logFailure def fusion_design_height(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: @@ -78,7 +79,7 @@ def fusion_design_height(self) -> float: return float(overall_bounding_box.maxPoint.z - overall_bounding_box.minPoint.z) return 0.0 - @property + @Logging.logFailure def fusion_design_perimeter(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: From 1cb1f5cdc77fa056c71ac7d4cfdb1f1ff56e945d Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Thu, 24 Jul 2025 10:26:46 -0700 Subject: [PATCH 13/23] feat: typing and resolving mypy --- .../SynthesisFusionAddin/src/UI/DesignCheckTab.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 69bd752375..4ecd60aad6 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Callable, Dict, List, TypedDict, cast import adsk.core import adsk.fusion @@ -7,10 +7,17 @@ from src.UI import IconPaths +class DesignRule(TypedDict): + name: str + calculation: Callable[[], float] + max_value: float + + class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput designRules: Dict[str, Any] = {} + design_rules: List[DesignRule] @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: @@ -40,8 +47,10 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: ] for i, rule in enumerate(self.design_rules): - value: float = rule["calculation"]() - is_valid: bool = value <= rule["max_value"] + calculation = rule["calculation"] + max_value: float = rule["max_value"] + value: float = calculation() + is_valid: bool = value <= max_value rule_name: str = str(rule["name"]) rule_id: str = rule_name.replace(" ", "") From 6aee78b3f1daa4900073bf42ef41192360d56c70 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Thu, 24 Jul 2025 10:55:08 -0700 Subject: [PATCH 14/23] feat: changing calculations using different orientations of the bounding box --- .../SynthesisFusionAddin/src/UI/DesignCheckTab.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 4ecd60aad6..b1aab10555 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -6,6 +6,7 @@ from src import Logging, gm from src.UI import IconPaths +logger = Logging.getLogger() class DesignRule(TypedDict): name: str @@ -84,16 +85,14 @@ def isActive(self) -> bool: def fusion_design_height(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: - overall_bounding_box = design.rootComponent.boundingBox - return float(overall_bounding_box.maxPoint.z - overall_bounding_box.minPoint.z) + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return float(overall_bounding_box.width) return 0.0 @Logging.logFailure def fusion_design_perimeter(self) -> float: design = adsk.fusion.Design.cast(gm.app.activeProduct) if design: - overall_bounding_box = design.rootComponent.boundingBox - width = overall_bounding_box.maxPoint.x - overall_bounding_box.minPoint.x - length = overall_bounding_box.maxPoint.y - overall_bounding_box.minPoint.y - return float(2 * (width + length)) + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return float(2 * (overall_bounding_box.height + overall_bounding_box.length)) return 0.0 From e9e3f4944293b6517701b8e9f562e83777e62055 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 30 Jul 2025 14:27:15 -0700 Subject: [PATCH 15/23] feat: adding web tab --- .../web/src/ui/DesignCheckTab.tsx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx new file mode 100644 index 0000000000..012f7866b6 --- /dev/null +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -0,0 +1,39 @@ +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, +} from "@mui/material" + +interface DesignCheckTabProps { } + +function DesignCheckTab({}: DesignCheckTabProps) { + return ( + + + + + + Component + + + Calculation + + + Is Valid + + + + + + + +
+
+ ) +} + +export default DesignCheckTab From fe2327f9e9ecfb0c85020c0fd1c73ca6263254ba Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Thu, 31 Jul 2025 17:25:13 -0700 Subject: [PATCH 16/23] feat: sending designRuleChecks to web-based UI --- .../src/DesignRuleChecks.py | 49 ++++++++++++++ .../src/UI/ConfigCommand.py | 4 ++ .../src/UI/DesignCheckTab.py | 44 ++---------- exporter/SynthesisFusionAddin/web/src/App.tsx | 7 +- .../SynthesisFusionAddin/web/src/lib/index.ts | 26 +++++++ .../web/src/ui/DesignCheckTab.tsx | 67 +++++++++++++------ 6 files changed, 135 insertions(+), 62 deletions(-) create mode 100644 exporter/SynthesisFusionAddin/src/DesignRuleChecks.py diff --git a/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py b/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py new file mode 100644 index 0000000000..d2ca492f65 --- /dev/null +++ b/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py @@ -0,0 +1,49 @@ +from typing import List, TypedDict, Callable + +import adsk.core +import adsk.fusion + +from src import Logging, gm + +class DesignRule(TypedDict): + name: str + calculation: Callable[[], float] + max_value: float + +class DesignRuleChecks: + designRules: List[DesignRule] + + @Logging.logFailure + def __init__(self) -> None: + self.designRules = [ + { + "name": "Design Height", + "calculation": self.fusion_design_height, + "max_value": 106.0, # cm + }, + { + "name": "Design Perimeter", + "calculation": self.fusion_design_perimeter, + "max_value": 304.0, # cm + }, + ] + + + def getDesignRules(self) -> List[DesignRule]: + return self.designRules + + @Logging.logFailure + def fusion_design_height(self) -> float: + design = adsk.fusion.Design.cast(gm.app.activeProduct) + if design: + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return float(overall_bounding_box.width) + return 0.0 + + @Logging.logFailure + def fusion_design_perimeter(self) -> float: + design = adsk.fusion.Design.cast(gm.app.activeProduct) + if design: + overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox + return float(2 * (overall_bounding_box.height + overall_bounding_box.length)) + return 0.0 diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 50f68c3618..c1e6ce3c6c 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -28,6 +28,7 @@ from src.Types import SELECTABLE_JOINT_TYPES, ExportLocation, ExportMode from src.UI import FileDialogConfig from src.UI.Handlers import PersistentEventHandler +from src.DesignRuleChecks import DesignRuleChecks from src.Util import convertMassUnitsTo, designMassCalculation generalConfigTab: GeneralConfigTab.GeneralConfigTab @@ -299,6 +300,9 @@ def notify(self, html_args: adsk.core.HTMLEventArgs) -> None: elif html_args.action == "cancelSelection": gm.ui.terminateActiveCommand() html_args.returnData = "{}" + + elif html_args.action == "designRules": + html_args.returnData = DesignRuleChecks().getDesignRules else: gm.ui.messageBox(f"Event {html_args.action} arrived{json.dumps(data, indent=2)}") diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 4b822c0675..e2aec98d03 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -3,23 +3,17 @@ import adsk.core import adsk.fusion -from src import Logging, gm +from src import Logging from src.UI import IconPaths +from src.DesignRuleChecks import DesignRuleChecks logger = Logging.getLogger() -class DesignRule(TypedDict): - name: str - calculation: Callable[[], float] - max_value: float - - class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput - designRules: Dict[str, Any] = {} - design_rules: List[DesignRule] + designRuleChecks: DesignRuleChecks @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: @@ -34,21 +28,9 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle ) - # Define and add design rules to the table - self.design_rules = [ - { - "name": "Design Height", - "calculation": self.fusion_design_height, - "max_value": 106.0, # cm - }, - { - "name": "Design Perimeter", - "calculation": self.fusion_design_perimeter, - "max_value": 304.0, # cm - }, - ] + self.designRuleChecks = DesignRuleChecks() - for i, rule in enumerate(self.design_rules): + for i, rule in enumerate(self.designRuleChecks.getDesignRules()): calculation = rule["calculation"] max_value: float = rule["max_value"] value: float = calculation() @@ -81,19 +63,3 @@ def isVisible(self, value: bool) -> None: @property def isActive(self) -> bool: return self.designCheckTab.isActive or False - - @Logging.logFailure - def fusion_design_height(self) -> float: - design = adsk.fusion.Design.cast(gm.app.activeProduct) - if design: - overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return float(overall_bounding_box.width) - return 0.0 - - @Logging.logFailure - def fusion_design_perimeter(self) -> float: - design = adsk.fusion.Design.cast(gm.app.activeProduct) - if design: - overall_bounding_box = design.rootComponent.orientedMinimumBoundingBox - return float(2 * (overall_bounding_box.height + overall_bounding_box.length)) - return 0.0 diff --git a/exporter/SynthesisFusionAddin/web/src/App.tsx b/exporter/SynthesisFusionAddin/web/src/App.tsx index c77a825651..267ad84409 100644 --- a/exporter/SynthesisFusionAddin/web/src/App.tsx +++ b/exporter/SynthesisFusionAddin/web/src/App.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from "react" import "./App.css" -import { RestartAlt, Settings, SportsFootball, Texture } from "@mui/icons-material" +import { RestartAlt, Settings, SportsFootball, Texture, CheckBox } from "@mui/icons-material" import DownloadIcon from "@mui/icons-material/Download" import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing" import SaveIcon from "@mui/icons-material/Save" @@ -37,6 +37,7 @@ import GeneralConfigTab from "./ui/GeneralConfigTab.tsx" import GlobalAlert from "./ui/GlobalAlert.tsx" import JointsConfigTab from "./ui/JointsConfigTab.tsx" import MaterialTaggingTab, { type TaggedBody } from "./ui/MaterialTaggingTab.tsx" +import DesignCheckTab from "./ui/DesignCheckTab.tsx" function TabPanel(props: { children?: React.ReactNode; value: number; index: number }) { const { children, value, index, ...other } = props @@ -228,6 +229,7 @@ function App() { /> } iconPosition={"start"} label="Materials" /> + } iconPosition={"start"} label="Design Check" /> {/**/} @@ -259,6 +261,9 @@ function App() { selection={{ isSelecting, setIsSelecting }} /> + + + { + if (import.meta.env.DEV && typeof window.adsk === "undefined") { + return new Promise(resolve => { + setTimeout(() => { + const token = Math.random().toString(36).substring(2, 15) + resolve([ + { + name: token, + calculation: parseFloat(token), + max_value: parseFloat(token) + } + ]) + }, 2000) + }) + } + + return await sendData("designRules", {}) +} + diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx index 012f7866b6..f02ff20456 100644 --- a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -7,32 +7,55 @@ import { TableHead, TableRow, } from "@mui/material" +import { useEffect, useState } from "react" +import { type DesignRule, getDesignRules } from "../lib" -interface DesignCheckTabProps { } +interface DesignCheckTabProps {} function DesignCheckTab({}: DesignCheckTabProps) { - return ( - - - - - - Component - - - Calculation - - - Is Valid - - - - - + const [rules, setRules] = useState([]) + + useEffect(() => { + getDesignRules().then(data => { + if (data) { + setRules(data) + } + }) + }, []) - -
-
+ return ( + <> + + + + + + Component + + + Calculation + + + Is Valid + + + + + + {rules.map(rule => ( + + {rule.name} + {rule.calculation} + + {rule.calculation <= rule.max_value ? "Valid" : "Invalid"} + + + + ))} + +
+
+ ) } From efcfde1bb57faababf965a8a34c62bc9d51e1d5a Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Mon, 4 Aug 2025 13:50:21 -0700 Subject: [PATCH 17/23] feat: adding json dumps and fixing callable functions --- .../SynthesisFusionAddin/src/DesignRuleChecks.py | 4 ++-- .../SynthesisFusionAddin/src/UI/ConfigCommand.py | 2 +- .../SynthesisFusionAddin/src/UI/DesignCheckTab.py | 4 +--- .../SynthesisFusionAddin/web/src/lib/index.ts | 15 --------------- 4 files changed, 4 insertions(+), 21 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py b/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py index d2ca492f65..5a848fe135 100644 --- a/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py +++ b/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py @@ -18,12 +18,12 @@ def __init__(self) -> None: self.designRules = [ { "name": "Design Height", - "calculation": self.fusion_design_height, + "calculation": self.fusion_design_height(), "max_value": 106.0, # cm }, { "name": "Design Perimeter", - "calculation": self.fusion_design_perimeter, + "calculation": self.fusion_design_perimeter(), "max_value": 304.0, # cm }, ] diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index c1e6ce3c6c..7f89c72420 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -302,7 +302,7 @@ def notify(self, html_args: adsk.core.HTMLEventArgs) -> None: html_args.returnData = "{}" elif html_args.action == "designRules": - html_args.returnData = DesignRuleChecks().getDesignRules + html_args.returnData = json.dumps(DesignRuleChecks().getDesignRules) else: gm.ui.messageBox(f"Event {html_args.action} arrived{json.dumps(data, indent=2)}") diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index e2aec98d03..45e7105fd3 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -13,7 +13,6 @@ class DesignCheckTab: designCheckTab: adsk.core.TabCommandInput designCheckTable: adsk.core.TableCommandInput - designRuleChecks: DesignRuleChecks @Logging.logFailure def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: @@ -28,9 +27,8 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle ) - self.designRuleChecks = DesignRuleChecks() - for i, rule in enumerate(self.designRuleChecks.getDesignRules()): + for i, rule in enumerate(self.designRuleChecks().getDesignRules()): calculation = rule["calculation"] max_value: float = rule["max_value"] value: float = calculation() diff --git a/exporter/SynthesisFusionAddin/web/src/lib/index.ts b/exporter/SynthesisFusionAddin/web/src/lib/index.ts index 9b5f82a656..fb5b987f2d 100644 --- a/exporter/SynthesisFusionAddin/web/src/lib/index.ts +++ b/exporter/SynthesisFusionAddin/web/src/lib/index.ts @@ -154,21 +154,6 @@ export interface DesignRule { max_value: number } export async function getDesignRules(): Promise { - if (import.meta.env.DEV && typeof window.adsk === "undefined") { - return new Promise(resolve => { - setTimeout(() => { - const token = Math.random().toString(36).substring(2, 15) - resolve([ - { - name: token, - calculation: parseFloat(token), - max_value: parseFloat(token) - } - ]) - }, 2000) - }) - } - return await sendData("designRules", {}) } From b05fd8e2b7888eddc54a8b50e5df5541f90ec00a Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 6 Aug 2025 15:30:44 -0700 Subject: [PATCH 18/23] fix: conflicts --- exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py | 6 +++--- .../SynthesisFusionAddin/src/{ => lib}/DesignRuleChecks.py | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename exporter/SynthesisFusionAddin/src/{ => lib}/DesignRuleChecks.py (100%) diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index 45e7105fd3..af04bea6f9 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -4,8 +4,8 @@ import adsk.fusion from src import Logging -from src.UI import IconPaths -from src.DesignRuleChecks import DesignRuleChecks +from src.lib import IconPaths +from src.lib.DesignRuleChecks import DesignRuleChecks logger = Logging.getLogger() @@ -28,7 +28,7 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: ) - for i, rule in enumerate(self.designRuleChecks().getDesignRules()): + for i, rule in enumerate(DesignRuleChecks().getDesignRules()): calculation = rule["calculation"] max_value: float = rule["max_value"] value: float = calculation() diff --git a/exporter/SynthesisFusionAddin/src/DesignRuleChecks.py b/exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py similarity index 100% rename from exporter/SynthesisFusionAddin/src/DesignRuleChecks.py rename to exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py From 77f852b98bd360297f6922e3659a71e0ee87d851 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 6 Aug 2025 19:43:17 -0700 Subject: [PATCH 19/23] fix: table not displaying on tab --- exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index c43a9b1ed0..46463b2bf6 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -29,7 +29,7 @@ from src.Parser.SynthesisParser.Utilities import guid_occurrence from src.Types import SELECTABLE_JOINT_TYPES, ExportLocation, ExportMode from src.UI import FileDialogConfig -from src.DesignRuleChecks import DesignRuleChecks +from src.lib.DesignRuleChecks import DesignRuleChecks generalConfigTab: GeneralConfigTab.GeneralConfigTab jointConfigTab: JointConfigTab.JointConfigTab @@ -302,7 +302,7 @@ def notify(self, html_args: adsk.core.HTMLEventArgs) -> None: html_args.returnData = "{}" elif html_args.action == "designRules": - html_args.returnData = json.dumps(DesignRuleChecks().getDesignRules) + html_args.returnData = json.dumps(DesignRuleChecks().getDesignRules()) else: gm.ui.messageBox(f"Event {html_args.action} arrived{json.dumps(data, indent=2)}") From af430a895c5fd9d4f6ed734181558368d8edcdeb Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 6 Aug 2025 19:48:54 -0700 Subject: [PATCH 20/23] feat: adding validation header --- exporter/SynthesisFusionAddin/web/src/App.tsx | 2 +- .../SynthesisFusionAddin/web/src/lib/index.ts | 11 ++++++++++ .../web/src/ui/DesignCheckTab.tsx | 20 +++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/exporter/SynthesisFusionAddin/web/src/App.tsx b/exporter/SynthesisFusionAddin/web/src/App.tsx index 267ad84409..8a743bfa59 100644 --- a/exporter/SynthesisFusionAddin/web/src/App.tsx +++ b/exporter/SynthesisFusionAddin/web/src/App.tsx @@ -261,7 +261,7 @@ function App() { selection={{ isSelecting, setIsSelecting }} /> - + { + if (import.meta.env.DEV && typeof window.adsk === "undefined") { + return new Promise(resolve => { + setTimeout(() => { + resolve([ + { name: "Rule 1", calculation: 10, max_value: 20 }, + { name: "Rule 2", calculation: 25, max_value: 20 }, + { name: "Rule 3", calculation: 5, max_value: 15 }, + ]) + }, 1000) + }) + } return await sendData("designRules", {}) } diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx index f02ff20456..a540119f30 100644 --- a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -16,15 +16,31 @@ function DesignCheckTab({}: DesignCheckTabProps) { const [rules, setRules] = useState([]) useEffect(() => { - getDesignRules().then(data => { + const fetchRules = async () => { + const data = await getDesignRules() if (data) { setRules(data) } - }) + } + + fetchRules() }, []) + function isDesignValid(): string { + rules.forEach(rule => { + if (rule.calculation > rule.max_value) { + return "Invalid" + } + }) + + return "Valid" + } + return ( <> +

+ Checks Passing: {isDesignValid()} +

From 0fe4a9869e1a111dde8e51e451d2d859ea515d5c Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 6 Aug 2025 19:51:07 -0700 Subject: [PATCH 21/23] fix: biome errors and formatting --- .../src/UI/ConfigCommand.py | 2 +- .../src/UI/DesignCheckTab.py | 1 - .../src/lib/DesignRuleChecks.py | 5 +++-- exporter/SynthesisFusionAddin/web/src/App.tsx | 4 ++-- .../SynthesisFusionAddin/web/src/lib/index.ts | 5 ++--- .../web/src/ui/DesignCheckTab.tsx | 18 +++--------------- 6 files changed, 11 insertions(+), 24 deletions(-) diff --git a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py index 46463b2bf6..954fbfc512 100644 --- a/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py +++ b/exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py @@ -22,6 +22,7 @@ import src.UI.TaggingConfigTab as TaggingConfigTab from src import APP_WEBSITE_URL, Logging, gm from src.APS.APS import getAuth, getUserInfo +from src.lib.DesignRuleChecks import DesignRuleChecks from src.lib.Handlers import PersistentEventHandler from src.lib.Util import convertMassUnitsTo, designMassCalculation from src.Logging import getLogger, logFailure @@ -29,7 +30,6 @@ from src.Parser.SynthesisParser.Utilities import guid_occurrence from src.Types import SELECTABLE_JOINT_TYPES, ExportLocation, ExportMode from src.UI import FileDialogConfig -from src.lib.DesignRuleChecks import DesignRuleChecks generalConfigTab: GeneralConfigTab.GeneralConfigTab jointConfigTab: JointConfigTab.JointConfigTab diff --git a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py index af04bea6f9..3b513b91e1 100644 --- a/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py +++ b/exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py @@ -27,7 +27,6 @@ def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None: adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle ) - for i, rule in enumerate(DesignRuleChecks().getDesignRules()): calculation = rule["calculation"] max_value: float = rule["max_value"] diff --git a/exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py b/exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py index 5a848fe135..048437bdf5 100644 --- a/exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py +++ b/exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py @@ -1,15 +1,17 @@ -from typing import List, TypedDict, Callable +from typing import Callable, List, TypedDict import adsk.core import adsk.fusion from src import Logging, gm + class DesignRule(TypedDict): name: str calculation: Callable[[], float] max_value: float + class DesignRuleChecks: designRules: List[DesignRule] @@ -28,7 +30,6 @@ def __init__(self) -> None: }, ] - def getDesignRules(self) -> List[DesignRule]: return self.designRules diff --git a/exporter/SynthesisFusionAddin/web/src/App.tsx b/exporter/SynthesisFusionAddin/web/src/App.tsx index 8a743bfa59..6ea703b08c 100644 --- a/exporter/SynthesisFusionAddin/web/src/App.tsx +++ b/exporter/SynthesisFusionAddin/web/src/App.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from "react" import "./App.css" -import { RestartAlt, Settings, SportsFootball, Texture, CheckBox } from "@mui/icons-material" +import { CheckBox, RestartAlt, Settings, SportsFootball, Texture } from "@mui/icons-material" import DownloadIcon from "@mui/icons-material/Download" import PrecisionManufacturingIcon from "@mui/icons-material/PrecisionManufacturing" import SaveIcon from "@mui/icons-material/Save" @@ -32,12 +32,12 @@ import { type Joint, WheelType, } from "./lib/types.ts" +import DesignCheckTab from "./ui/DesignCheckTab.tsx" import GamepiecesConfigTab from "./ui/GamepiecesConfigTab.tsx" import GeneralConfigTab from "./ui/GeneralConfigTab.tsx" import GlobalAlert from "./ui/GlobalAlert.tsx" import JointsConfigTab from "./ui/JointsConfigTab.tsx" import MaterialTaggingTab, { type TaggedBody } from "./ui/MaterialTaggingTab.tsx" -import DesignCheckTab from "./ui/DesignCheckTab.tsx" function TabPanel(props: { children?: React.ReactNode; value: number; index: number }) { const { children, value, index, ...other } = props diff --git a/exporter/SynthesisFusionAddin/web/src/lib/index.ts b/exporter/SynthesisFusionAddin/web/src/lib/index.ts index 9e14c5d0e4..fa973886ba 100644 --- a/exporter/SynthesisFusionAddin/web/src/lib/index.ts +++ b/exporter/SynthesisFusionAddin/web/src/lib/index.ts @@ -149,8 +149,8 @@ window.fusionJavaScriptHandler = { } export interface DesignRule { - name: string, - calculation: number, + name: string + calculation: number max_value: number } export async function getDesignRules(): Promise { @@ -167,4 +167,3 @@ export async function getDesignRules(): Promise { } return await sendData("designRules", {}) } - diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx index a540119f30..58ba095b5c 100644 --- a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -1,18 +1,8 @@ -import { - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, -} from "@mui/material" +import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material" import { useEffect, useState } from "react" import { type DesignRule, getDesignRules } from "../lib" -interface DesignCheckTabProps {} - -function DesignCheckTab({}: DesignCheckTabProps) { +function DesignCheckTab() { const [rules, setRules] = useState([]) useEffect(() => { @@ -38,9 +28,7 @@ function DesignCheckTab({}: DesignCheckTabProps) { return ( <> -

- Checks Passing: {isDesignValid()} -

+

Checks Passing: {isDesignValid()}

From 01abb79e7cfede344c1c530b7ff147764d04950d Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Tue, 19 Aug 2025 11:52:44 -0700 Subject: [PATCH 22/23] fix: sendData run when window is undefined --- .../SynthesisFusionAddin/web/src/lib/index.ts | 26 +++++-------------- .../web/src/ui/DesignCheckTab.tsx | 22 +++++++++++++--- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/exporter/SynthesisFusionAddin/web/src/lib/index.ts b/exporter/SynthesisFusionAddin/web/src/lib/index.ts index fa973886ba..a8ecf063df 100644 --- a/exporter/SynthesisFusionAddin/web/src/lib/index.ts +++ b/exporter/SynthesisFusionAddin/web/src/lib/index.ts @@ -24,6 +24,12 @@ interface InitResponse { tagData: FusionBody[] } +export interface DesignRule { + name: string + calculation: number + max_value: number +} + interface Messages { selectJoint: [Empty, FusionJoint] selectGamepiece: [Empty, FusionGamepiece[]] @@ -147,23 +153,3 @@ window.fusionJavaScriptHandler = { return "OK" }, } - -export interface DesignRule { - name: string - calculation: number - max_value: number -} -export async function getDesignRules(): Promise { - if (import.meta.env.DEV && typeof window.adsk === "undefined") { - return new Promise(resolve => { - setTimeout(() => { - resolve([ - { name: "Rule 1", calculation: 10, max_value: 20 }, - { name: "Rule 2", calculation: 25, max_value: 20 }, - { name: "Rule 3", calculation: 5, max_value: 15 }, - ]) - }, 1000) - }) - } - return await sendData("designRules", {}) -} diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx index 58ba095b5c..22cbce5896 100644 --- a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -1,21 +1,37 @@ import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material" import { useEffect, useState } from "react" -import { type DesignRule, getDesignRules } from "../lib" +import { type DesignRule, sendData } from "../lib" function DesignCheckTab() { + const [isWindowLoaded, setIsWindowLoaded] = useState() const [rules, setRules] = useState([]) useEffect(() => { + if (!isWindowLoaded) return + const fetchRules = async () => { - const data = await getDesignRules() + const data = await sendData("designRules", {}) if (data) { setRules(data) + console.log(data[0].calculation) } } fetchRules() + }, [isWindowLoaded]) + + // biome-ignore lint/correctness/useExhaustiveDependencies: onWindowLoad is stable + useEffect(() => { + if (typeof window.adsk === "undefined") { + requestAnimationFrame(onWindowLoad) + return + } }, []) + function onWindowLoad(): void { + setIsWindowLoaded(true) + } + function isDesignValid(): string { rules.forEach(rule => { if (rule.calculation > rule.max_value) { @@ -49,7 +65,7 @@ function DesignCheckTab() { {rules.map(rule => ( {rule.name} - {rule.calculation} + {Math.round(rule.calculation * 100) / 100} cm {rule.calculation <= rule.max_value ? "Valid" : "Invalid"} From 9fd087d30a58718be24a99bb1b49bb1efd9c3321 Mon Sep 17 00:00:00 2001 From: Dhruv Arora Date: Wed, 20 Aug 2025 01:37:04 -0700 Subject: [PATCH 23/23] fix: removing redundant state implementation --- .../web/src/ui/DesignCheckTab.tsx | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx index 22cbce5896..fbfb82345e 100644 --- a/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx +++ b/exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx @@ -3,12 +3,9 @@ import { useEffect, useState } from "react" import { type DesignRule, sendData } from "../lib" function DesignCheckTab() { - const [isWindowLoaded, setIsWindowLoaded] = useState() const [rules, setRules] = useState([]) useEffect(() => { - if (!isWindowLoaded) return - const fetchRules = async () => { const data = await sendData("designRules", {}) if (data) { @@ -17,21 +14,12 @@ function DesignCheckTab() { } } - fetchRules() - }, [isWindowLoaded]) - - // biome-ignore lint/correctness/useExhaustiveDependencies: onWindowLoad is stable - useEffect(() => { if (typeof window.adsk === "undefined") { - requestAnimationFrame(onWindowLoad) + requestAnimationFrame(fetchRules) return } }, []) - function onWindowLoad(): void { - setIsWindowLoaded(true) - } - function isDesignValid(): string { rules.forEach(rule => { if (rule.calculation > rule.max_value) {