Skip to content

Commit e876381

Browse files
author
nuofan
committed
Fixed bugs and happy new year~
1 parent 1f01359 commit e876381

31 files changed

+1205
-836
lines changed
File renamed without changes.
File renamed without changes.
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# -*- coding: utf-8 -*-
2+
# Author: nuofan
3+
"""
4+
Export robot description format files from Fusion360 design
5+
"""
6+
7+
import adsk, adsk.core, adsk.fusion, traceback
8+
import os, sys
9+
from ...core.link import Link
10+
from ...core.joint import Joint
11+
from . import constants
12+
from ...core import write
13+
from ...core import utils
14+
15+
def get_link_joint_list(design: adsk.fusion.Design):
16+
"""
17+
Get the link list and joint list to export
18+
19+
Return:
20+
link_list: [Link]
21+
a list contains all the links that will be exported
22+
joint_list: [Joint]
23+
a list contains all the joint that will be exported
24+
"""
25+
root = design.rootComponent
26+
link_list = []
27+
joint_list = []
28+
occs: adsk.fusion.OccurrenceList = root.allOccurrences
29+
30+
# try to solve the nested components problem
31+
# but still not fully tested
32+
for occ in occs:
33+
# TODO: it seems use occ.joints.count will make it usable with occurrences? Test it
34+
if occ.component.joints.count > 0:
35+
# textPalette.writeText(str(occ.fullPathName))
36+
continue
37+
else:
38+
# Only occurrence contains zero joint and has zero childOccurrences
39+
# can be seen as a link
40+
if occ.childOccurrences.count > 0:
41+
# textPalette.writeText(str(occ.fullPathName))
42+
# textPalette.writeText(str(occ.childOccurrences.count))
43+
continue
44+
else:
45+
# textPalette.writeText(str(occ.fullPathName))
46+
# textPalette.writeText(str(occ.childOccurrences.count))
47+
if occ.isLightBulbOn:
48+
# only the occurrence light bulb on that the occurrence will be exported
49+
link_list.append(Link(occ)) # add link objects into link_list
50+
51+
for joint in root.allJoints:
52+
joint_list.append(Joint(joint)) # add joint objects into joint_list
53+
54+
return link_list, joint_list
55+
56+
def export_stl(design: adsk.fusion.Design, save_dir: str, links: list[Link]):
57+
"""
58+
export each component's stl file into "save_dir/mesh"
59+
60+
Parameters
61+
---------
62+
design: adsk.fusion.Design
63+
current active design
64+
save_dir: str
65+
the directory to store the export stl file
66+
"""
67+
# create a single exportManager instance
68+
export_manager = design.exportManager
69+
# set the directory for the mesh file
70+
try: os.mkdir(save_dir + "/meshes")
71+
except: pass
72+
mesh_dir = save_dir + "/meshes"
73+
74+
for link in links:
75+
visual_body: adsk.fusion.BRepBody = link.get_visual_body()
76+
col_body: adsk.fusion.BRepBody = link.get_collision_body()
77+
if (visual_body is None) and (col_body is None):
78+
# export the whole occurrence
79+
mesh_name = mesh_dir + "/" + link.get_name()
80+
occ = link.get_link_occ()
81+
stl_export_options = export_manager.createSTLExportOptions(occ, mesh_name)
82+
stl_export_options.sendToPrintUtility = False
83+
stl_export_options.isBinaryFormat = True
84+
stl_export_options.meshRefinement = adsk.fusion.MeshRefinementSettings.MeshRefinementLow
85+
export_manager.execute(stl_export_options)
86+
elif (visual_body is not None) and (col_body is not None):
87+
# export visual and collision geometry seperately
88+
visual_mesh_name = mesh_dir + "/" + link.get_name() + "_visual"
89+
visual_exp_options = export_manager.createSTLExportOptions(visual_body, visual_mesh_name)
90+
visual_exp_options.sendToPrintUtility = False
91+
visual_exp_options.isBinaryFormat = True
92+
visual_exp_options.meshRefinement = adsk.fusion.MeshRefinementSettings.MeshRefinementHigh
93+
export_manager.execute(visual_exp_options)
94+
95+
col_mesh_name = mesh_dir + "/" + link.get_name() + "_collision"
96+
col_exp_options = export_manager.createSTLExportOptions(col_body, col_mesh_name)
97+
col_exp_options.sendToPrintUtility = False
98+
col_exp_options.isBinaryFormat = True
99+
col_exp_options.meshRefinement = adsk.fusion.MeshRefinementSettings.MeshRefinementLow
100+
export_manager.execute(col_exp_options)
101+
102+
elif (visual_body is None) and (col_body is not None):
103+
error_message = "Please set two bodies, one for visual and one for collision. \n"
104+
error_message = error_message + "Body for visual missing."
105+
utils.error_box(error_message)
106+
utils.terminate_box()
107+
elif (visual_body is not None) and (col_body is None):
108+
error_message = "Please set two bodies, one for visual and one for collision. \n"
109+
error_message = error_message + "Body for collision missing."
110+
utils.error_box(error_message)
111+
utils.terminate_box()
112+
113+
114+
def run():
115+
# Initialization
116+
app = adsk.core.Application.get()
117+
ui = app.userInterface
118+
product = app.activeProduct
119+
design = adsk.fusion.Design.cast(product)
120+
121+
msg_box_title = "ACDC4Robot Message"
122+
123+
# open a text palette for debuging
124+
textPalette = ui.palettes.itemById("TextCommands")
125+
if not textPalette.isVisible:
126+
textPalette.isVisible = True
127+
constants.set_text_palette(textPalette)
128+
129+
try:
130+
# Set design type into do not capture design history
131+
design.designType = adsk.fusion.DesignTypes.DirectDesignType
132+
133+
# Check the length unit of Fusion360
134+
if design.unitsManager.defaultLengthUnits != "m":
135+
ui.messageBox("Please set length unit to 'm'!", msg_box_title)
136+
return 0 # exit run() function
137+
138+
root = design.rootComponent # get root component
139+
allComp = design.allComponents
140+
robot_name = root.name.split()[0]
141+
constants.set_robot_name(robot_name)
142+
143+
# Set the folder to store exported files
144+
folder_dialog = ui.createFolderDialog()
145+
folder_dialog.title = "Chose your folder to export"
146+
dialog_result = folder_dialog.showDialog() # show folder dialog
147+
save_folder = ""
148+
if dialog_result == adsk.core.DialogResults.DialogOK:
149+
save_folder = folder_dialog.folder
150+
else:
151+
ui.messageBox("ACDC4Robot was canceled", msg_box_title)
152+
return 0 # exit run() function
153+
154+
save_folder = save_folder + "/" + robot_name
155+
try: os.mkdir(save_folder)
156+
except: pass
157+
158+
ui.messageBox("Start ACDC4Robot Add-IN", msg_box_title)
159+
160+
# get all the link & joint elements to export
161+
link_list, joint_list = get_link_joint_list(design)
162+
163+
rdf = constants.get_rdf()
164+
simulator = constants.get_sim_env()
165+
166+
if rdf == None:
167+
ui.messageBox("Robot description format is None. \
168+
Please choose one robot description format", msg_box_title)
169+
elif rdf == "URDF":
170+
if simulator == "None":
171+
ui.messageBox("Simulation environment is None. \
172+
Please select a simulation environment.", msg_box_title)
173+
elif simulator == "Gazebo":
174+
# write to .urdf file
175+
write.write_urdf(link_list, joint_list, save_folder, robot_name)
176+
# export mesh files
177+
export_stl(design, save_folder, link_list)
178+
ui.messageBox("Finish exporting URDF for Gazebo.", msg_box_title)
179+
180+
elif simulator == "PyBullet":
181+
# write to .urdf file
182+
write.write_urdf(link_list, joint_list, save_folder, robot_name)
183+
# export mesh files
184+
export_stl(design, save_folder, link_list)
185+
# generate pybullet script
186+
write.write_hello_pybullet(rdf, robot_name, save_folder)
187+
ui.messageBox("Finish exporting URDF for PyBullet.", msg_box_title)
188+
189+
elif rdf == "SDFormat":
190+
if simulator == "None":
191+
ui.messageBox("Simulation environment is None. \
192+
Please select a simulation environment.", msg_box_title)
193+
elif simulator == "Gazebo":
194+
# write to .sdf file
195+
write.write_sdf(link_list, joint_list, save_folder, robot_name)
196+
# write a model cofig file
197+
author = constants.get_author_name()
198+
des = constants.get_model_description()
199+
write.write_sdf_config(save_folder, robot_name, author, des)
200+
# export stl files
201+
export_stl(design, save_folder, link_list)
202+
ui.messageBox("Finish exporting SDFormat for Gazebo.", msg_box_title)
203+
elif simulator == "PyBullet":
204+
# write to .sdf file
205+
write.write_sdf(link_list, joint_list, save_folder, robot_name)
206+
# export stl files
207+
export_stl(design, save_folder, link_list)
208+
# generate pybullet script
209+
write.write_hello_pybullet(rdf,robot_name, save_folder)
210+
ui.messageBox("Finish exporting SDFormat for PyBullet.", msg_box_title)
211+
212+
elif rdf == "MJCF":
213+
if simulator == "None":
214+
ui.messageBox("Simulation environment is None. \
215+
Please select a simulation environment.", msg_box_title)
216+
elif simulator == "Gazebo":
217+
ui.messageBox("Gazebo not support MJCF. \
218+
Please select MuJoCo for simulation.", msg_box_title)
219+
elif simulator == "PyBullet":
220+
ui.messageBox("PyBullet not support MJCF. \
221+
Please select MuJoCo for simulation.", msg_box_title)
222+
elif simulator == "MuJoCo":
223+
# write to .xml file
224+
write.write_mjcf(root, robot_name, save_folder)
225+
# export stl files
226+
export_stl(design, save_folder, link_list)
227+
ui.messageBox("Finish exporting MJCF for MuJoCo.", msg_box_title)
228+
229+
except:
230+
if ui:
231+
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Add-IN/Fusion2Robot/commands/Fusion2Robot/entry.py renamed to Add-IN/ACDC4Robot/commands/ACDC4Robot/entry.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
import os
33
from ...lib import fusion360utils as futil
44
from ... import config
5-
from . import fusion2Robot
5+
from . import acdc4robot
66
from . import constants
77
app = adsk.core.Application.get()
88
ui = app.userInterface
99

1010

1111
# TODO *** Specify the command identity information. ***
12-
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_Fusion2Robot'
12+
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_ACDC4Robot'
1313
CMD_NAME = 'Fusion to Robot'
1414
CMD_Description = 'Export Fusion360 design model to robot description format'
1515

@@ -86,17 +86,19 @@ def command_created(args: adsk.core.CommandCreatedEventArgs):
8686
rdf_items.add("None", True)
8787
rdf_items.add("URDF", False)
8888
rdf_items.add("SDFormat", False)
89+
rdf_items.add("MJCF", False)
8990

9091
# create a drop down command input to choose simulation environment
9192
sim_env_input = inputs.addDropDownCommandInput("simulation_env", "Simulation Environment", adsk.core.DropDownStyles.LabeledIconDropDownStyle)
9293
sim_env_items = sim_env_input.listItems
9394
sim_env_items.add("None", True)
9495
sim_env_items.add("Gazebo", False)
9596
sim_env_items.add("PyBullet", False)
97+
sim_env_items.add("MuJoCo", False)
9698
sim_env_input.isVisible = False
9799

98100
# create string value input for sdf info
99-
sdf_author_input = inputs.addStringValueInput("SDF_Author_name", "Author Name", "Fusion2Robot")
101+
sdf_author_input = inputs.addStringValueInput("SDF_Author_name", "Author Name", "ACDC4Robot")
100102
sdf_author_input.isVisible = False
101103
sdf_description_input = inputs.addTextBoxCommandInput("SDF_Description", "Description", "Description about the robot model", 3, False)
102104
sdf_description_input.isVisible = False
@@ -139,7 +141,7 @@ def command_execute(args: adsk.core.CommandEventArgs):
139141
constants.set_author_name(name_input.value)
140142
constants.set_model_description(text_input.text)
141143

142-
fusion2Robot.run()
144+
acdc4robot.run()
143145
# TODO ******************************** Your code here ********************************
144146

145147
# Get a reference to your command's inputs.
@@ -169,6 +171,7 @@ def command_input_changed(args: adsk.core.InputChangedEventArgs):
169171
inputs = command.commandInputs
170172
rdf: adsk.core.DropDownCommandInput = inputs.itemById("robot_description_format")
171173
sim_env: adsk.core.DropDownCommandInput = inputs.itemById("simulation_env")
174+
sim_env_items = sim_env.listItems
172175
sdf_author = inputs.itemById("SDF_Author_name")
173176
sdf_description = inputs.itemById("SDF_Description")
174177
# inputs = args.inputs
@@ -182,6 +185,9 @@ def command_input_changed(args: adsk.core.InputChangedEventArgs):
182185

183186
elif changed_input.selectedItem.name == "SDFormat":
184187
sim_env.isVisible = True
188+
189+
elif changed_input.selectedItem.name == "MJCF":
190+
sim_env.isVisible = True
185191

186192

187193
if (rdf.selectedItem.name == "SDFormat") and (sim_env.selectedItem.name == "Gazebo"):

Add-IN/Fusion2Robot/commands/__init__.py renamed to Add-IN/ACDC4Robot/commands/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# TODO Import the modules corresponding to the commands you created.
44
# If you want to add an additional command, duplicate one of the existing directories and import it here.
55
# You need to use aliases (import "entry" as "my_module") assuming you have the default module named "entry".
6-
from .Fusion2Robot import entry as RDF
6+
from .ACDC4Robot import entry as RDF
77
# from .ImportRobot import entry as Model
88

99
# TODO add your imported modules to this list.
@@ -25,4 +25,4 @@ def start():
2525
# The stop function will be run when the add-in is stopped.
2626
def stop():
2727
for command in commands:
28-
command.stop()
28+
command.stop()
File renamed without changes.

0 commit comments

Comments
 (0)