Skip to content

Exporter DRC UI #1214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a2f99d3
feat: adding design check tab to exporter
Dhruv-0-Arora Jul 8, 2025
066e38c
feat: adding height and perimeter rule checks
Dhruv-0-Arora Jul 14, 2025
4fa43c8
feat: adding DRC UI
Dhruv-0-Arora Jul 14, 2025
a968146
Merging Dev
Dhruv-0-Arora Jul 18, 2025
5021b5b
fix: formatting and mypy
Dhruv-0-Arora Jul 18, 2025
cecb5d9
formatting
Dhruv-0-Arora Jul 18, 2025
fbd2574
Merge branch 'dev' into dhruv/1882/drc-ui-support
Dhruv-0-Arora Jul 18, 2025
b0df31b
feat: adding table with icons showing validity
Dhruv-0-Arora Jul 18, 2025
fe3e116
Merging Local
Dhruv-0-Arora Jul 18, 2025
6d2e608
feat: adding invalid and proper perimeter validation
Dhruv-0-Arora Jul 18, 2025
140645b
formatting
Dhruv-0-Arora Jul 18, 2025
c8d5c60
Merge branch 'dev' into dhruv/1882/drc-ui-support
PepperLola Jul 19, 2025
5e46d91
fix: removing orientedboundingbox
Dhruv-0-Arora Jul 23, 2025
00b1242
Merging Dev
Dhruv-0-Arora Jul 23, 2025
9130b8a
feat: adding extensibility
Dhruv-0-Arora Jul 23, 2025
1a5eb20
fix: formatting
Dhruv-0-Arora Jul 23, 2025
5ad15c1
fix: mypy
Dhruv-0-Arora Jul 23, 2025
1cb1f5c
feat: typing and resolving mypy
Dhruv-0-Arora Jul 24, 2025
6aee78b
feat: changing calculations using different orientations of the bound…
Dhruv-0-Arora Jul 24, 2025
134b42f
Merging Dev and Formatting
Dhruv-0-Arora Jul 25, 2025
e031d0b
Merging Dev
Dhruv-0-Arora Jul 30, 2025
e9e3f49
feat: adding web tab
Dhruv-0-Arora Jul 30, 2025
fe2327f
feat: sending designRuleChecks to web-based UI
Dhruv-0-Arora Aug 1, 2025
efcfde1
feat: adding json dumps and fixing callable functions
Dhruv-0-Arora Aug 4, 2025
f43d4e5
merging dev
Dhruv-0-Arora Aug 6, 2025
b05fd8e
fix: conflicts
Dhruv-0-Arora Aug 6, 2025
77f852b
fix: table not displaying on tab
Dhruv-0-Arora Aug 7, 2025
af430a8
feat: adding validation header
Dhruv-0-Arora Aug 7, 2025
0fe4a98
fix: biome errors and formatting
Dhruv-0-Arora Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions exporter/SynthesisFusionAddin/src/UI/ConfigCommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@

import src.Parser.ExporterOptions as moduleExporterOptions
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.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
Expand All @@ -32,6 +34,7 @@
generalConfigTab: GeneralConfigTab.GeneralConfigTab
jointConfigTab: JointConfigTab.JointConfigTab
gamepieceConfigTab: GamepieceConfigTab.GamepieceConfigTab
designCheckTab: DesignCheckTab.DesignCheckTab
taggingConfigTab: TaggingConfigTab.TaggingConfigTab

exporterPalette: Palette
Expand All @@ -50,6 +53,7 @@ def reload() -> None:
importlib.reload(GeneralConfigTab)
importlib.reload(GamepieceConfigTab)
importlib.reload(JointConfigTab)
importlib.reload(DesignCheckTab)
importlib.reload(TaggingConfigTab)

importlib.reload(moduleExporterOptions)
Expand Down Expand Up @@ -113,6 +117,10 @@ def notify(self, args: adsk.core.CommandCreatedEventArgs) -> None:
taggingConfigTab = TaggingConfigTab.TaggingConfigTab(args)
generalConfigTab.taggingConfigTab = taggingConfigTab

global designCheckTab
designCheckTab = DesignCheckTab.DesignCheckTab(args)
generalConfigTab.designCheckTab = designCheckTab

if not exporterOptions.exportMode == ExportMode.FIELD:
gamepieceConfigTab.isVisible = False

Expand Down Expand Up @@ -292,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 = json.dumps(DesignRuleChecks().getDesignRules())
else:
gm.ui.messageBox(f"Event {html_args.action} arrived<span>{json.dumps(data, indent=2)}</span>")

Expand Down
62 changes: 62 additions & 0 deletions exporter/SynthesisFusionAddin/src/UI/DesignCheckTab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from typing import Any, Callable, Dict, List, TypedDict, cast

import adsk.core
import adsk.fusion

from src import Logging
from src.lib import IconPaths
from src.lib.DesignRuleChecks import DesignRuleChecks

logger = Logging.getLogger()


class DesignCheckTab:
designCheckTab: adsk.core.TabCommandInput
designCheckTable: adsk.core.TableCommandInput

@Logging.logFailure
def __init__(self, args: adsk.core.CommandCreatedEventArgs) -> None:
self.designCheckTab = args.command.commandInputs.addTabCommandInput("designCheckTab", "Design Rule Check")
designCheckTabInputs = self.designCheckTab.children

# Create the table for design checks
self.designCheckTable = designCheckTabInputs.addTableCommandInput(
"designCheckTable", "Design Checks", 3, "3:2:2"
)
self.designCheckTable.tablePresentationStyle = (
adsk.core.TablePresentationStyles.itemBorderTablePresentationStyle
)

for i, rule in enumerate(DesignRuleChecks().getDesignRules()):
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(" ", "")

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:
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
2 changes: 2 additions & 0 deletions exporter/SynthesisFusionAddin/src/UI/GeneralConfigTab.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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.TaggingConfigTab import TaggingConfigTab
Expand All @@ -24,6 +25,7 @@ class GeneralConfigTab:
previousSelectedModeDropdownIndex: int
jointConfigTab: JointConfigTab
gamepieceConfigTab: GamepieceConfigTab
designCheckTab: DesignCheckTab
taggingConfigTab: TaggingConfigTab

@logFailure
Expand Down
50 changes: 50 additions & 0 deletions exporter/SynthesisFusionAddin/src/lib/DesignRuleChecks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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]

@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
5 changes: 5 additions & 0 deletions exporter/SynthesisFusionAddin/src/lib/IconPaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
"friction_override-enabled": resources + os.path.join("FrictionOverride_icon"), # resource folder
}

designCheckIcons = {
"valid": resources + os.path.join("DesignCheckIcons", "valid-preview93x18.png"),
"invalid": resources + os.path.join("DesignCheckIcons", "invalid-preview112x18.png"),
}

tagIcons = {
"blank": resources + "blank-preview16x16.png",
}
7 changes: 6 additions & 1 deletion exporter/SynthesisFusionAddin/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useState } from "react"

import "./App.css"
import { RestartAlt, Settings, SportsFootball, Texture } 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"
Expand Down Expand Up @@ -32,6 +32,7 @@ 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"
Expand Down Expand Up @@ -228,6 +229,7 @@ function App() {
/>

<Tab icon={<Texture />} iconPosition={"start"} label="Materials" />
<Tab icon={<CheckBox />} iconPosition={"start"} label="Design Check" />

{/*<Tab label="APS" />*/}
</Tabs>
Expand Down Expand Up @@ -259,6 +261,9 @@ function App() {
selection={{ isSelecting, setIsSelecting }}
/>
</TabPanel>
<TabPanel value={activeTab} index={4}>
<DesignCheckTab />
</TabPanel>
<Container
sx={{
position: "sticky",
Expand Down
21 changes: 21 additions & 0 deletions exporter/SynthesisFusionAddin/web/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface Messages {
selectJoint: [Empty, FusionJoint]
selectGamepiece: [Empty, FusionGamepiece[]]
selectBody: [Empty, FusionBody]
designRules: [Empty, DesignRule[]]
export: [ExporterConfig, Empty]
save: [ExporterConfig, Empty]
init: [Empty, InitResponse]
Expand Down Expand Up @@ -146,3 +147,23 @@ window.fusionJavaScriptHandler = {
return "OK"
},
}

export interface DesignRule {
name: string
calculation: number
max_value: number
}
export async function getDesignRules(): Promise<DesignRule[] | undefined> {
if (import.meta.env.DEV && typeof window.adsk === "undefined") {
return new Promise<DesignRule[]>(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", {})
}
66 changes: 66 additions & 0 deletions exporter/SynthesisFusionAddin/web/src/ui/DesignCheckTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"
import { useEffect, useState } from "react"
import { type DesignRule, getDesignRules } from "../lib"

function DesignCheckTab() {
const [rules, setRules] = useState<DesignRule[]>([])

useEffect(() => {
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 (
<>
<h4>Checks Passing: {isDesignValid()}</h4>
<TableContainer component={Paper} elevation={6}>
<Table sx={{ minWidth: 650 }} aria-label="simple table" size={"small"}>
<TableHead>
<TableRow>
<TableCell sx={{ width: "28%" }} align="center">
Component
</TableCell>
<TableCell sx={{ width: "28%" }} align="center">
Calculation
</TableCell>
<TableCell sx={{ width: "40%" }} align="center">
Is Valid
</TableCell>
<TableCell sx={{ width: "4%" }}></TableCell>
</TableRow>
</TableHead>
<TableBody>
{rules.map(rule => (
<TableRow key={rule.name} sx={{ "&:last-child td, &:last-child th": { border: 0 } }}>
<TableCell align="center">{rule.name}</TableCell>
<TableCell align="center">{rule.calculation}</TableCell>
<TableCell align="center">
{rule.calculation <= rule.max_value ? "Valid" : "Invalid"}
</TableCell>
<TableCell />
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}

export default DesignCheckTab
Loading