@@ -50,7 +50,6 @@ def create_body_and_coordinates(doc, body_obj, fc_objects):
5050 fc_objects ["Origin" ] = body_fc .Origin
5151 for child in body_fc .Origin .Group :
5252 role = getattr (child , "Role" , "" )
53- # Role is typically "X_Axis", "Y_Axis", "Z_Axis", "XY_Plane", etc.
5453 if role in {"X_Axis" , "Y_Axis" , "Z_Axis" , "XY_Plane" , "XZ_Plane" , "YZ_Plane" }:
5554 fc_objects [role ] = child
5655
@@ -90,19 +89,20 @@ def export_jcad_to_fcstd(jcad_dict: dict) -> "fc.Document":
9089 "Origin" , "X_Axis" , "Y_Axis" , "Z_Axis" , "XY_Plane" , "XZ_Plane" , "YZ_Plane"
9190 }
9291
93- # 1) Sanitize all JCAD object names and build a mapping
94- name_mapping = {
95- obj ["name" ]: sanitize_object_name (obj ["name" ])
96- for obj in jcad_dict .get ("objects" , [])
97- }
92+ # 1) Sanitize JCAD names in place and build a mapping
93+ name_mapping = {}
9894 for obj in jcad_dict .get ("objects" , []):
99- obj ["name" ] = name_mapping [obj ["name" ]]
95+ original = obj ["name" ]
96+ sanitized = sanitize_object_name (original )
97+ name_mapping [original ] = sanitized
98+ obj ["name" ] = sanitized
99+
100100 update_references (jcad_dict , name_mapping )
101101
102102 fc_objects = {}
103- guidata = {} # objectName → { "ShapeColor": (r,g,b), "Visibility": bool }
103+ guidata = {}
104104
105- # 2) Separate out any PartDesign::Body objects
105+ # 2) Separate PartDesign::Body entries from others
106106 body_objs = [
107107 o for o in jcad_dict .get ("objects" , [])
108108 if o ["shape" ] == "PartDesign::Body"
@@ -112,75 +112,68 @@ def export_jcad_to_fcstd(jcad_dict: dict) -> "fc.Document":
112112 if o not in body_objs and o ["name" ] not in coordinate_names
113113 ]
114114
115- # 3) Create bodies (so that coordinate system objects appear)
115+ # Helper: determine RGB tuple and visibility flag
116+ def _color_and_visibility (jcad_obj ):
117+ opts = jcad_dict .get ("options" , {}).get (jcad_obj ["name" ], {})
118+ hexcol = (
119+ jcad_obj .get ("parameters" , {}).get ("Color" )
120+ or opts .get ("color" , "#808080" )
121+ )
122+ rgb = _hex_to_rgb (hexcol )
123+ visible = opts .get ("visible" )
124+ if visible is None :
125+ visible = jcad_obj .get ("visible" , True )
126+ return rgb , visible
127+
128+ # 3) Create all PartDesign::Body objects
116129 for body_obj in body_objs :
117130 body_fc = create_body_and_coordinates (doc , body_obj , fc_objects )
118131 apply_object_properties (body_fc , body_obj , prop_handlers , doc )
119132
120- # Record body color
121- hexcol = body_obj .get ("parameters" , {}).get ("Color" ) or \
122- (jcad_dict .get ("options" , {}).get (body_obj ["name" ], {}).get ("color" ) or "#808080" )
123- rgb = _hex_to_rgb (hexcol )
124-
125- # Instead of just building color_map, build a complete guidata dictionary
126- body_opts = jcad_dict .get ("options" , {}).get (body_obj ["name" ], {})
127- # Check visibility: prioritize options, then object-level visible, default to True
128- visible = body_opts .get ("visible" )
129- if visible is None :
130- visible = body_obj .get ("visible" , True )
131-
133+ rgb , visible = _color_and_visibility (body_obj )
132134 guidata [body_obj ["name" ]] = {
133135 "ShapeColor" : {"type" : "App::PropertyColor" , "value" : rgb },
134136 "Visibility" : {"type" : "App::PropertyBool" , "value" : visible },
135137 }
136138
137- # Any coordinate‐system objects that now exist should inherit the same color
138- for coord_name in coordinate_names :
139- if coord_name in fc_objects :
140- guidata [coord_name ] = {
139+ # Coordinate children inherit the same color but remain hidden
140+ for coord in coordinate_names :
141+ if coord in fc_objects :
142+ guidata [coord ] = {
141143 "ShapeColor" : {"type" : "App::PropertyColor" , "value" : rgb },
142- "Visibility" : {"type" : "App::PropertyBool" , "value" : False }, # Usually coordinate objects are hidden
144+ "Visibility" : {"type" : "App::PropertyBool" , "value" : False },
143145 }
144146
145- # 4) Create all other (non-body, non-coordinate) objects
147+ # 4) Create all other objects
146148 for obj in other_objs :
147149 fc_obj = doc .addObject (obj ["shape" ], obj ["name" ])
148150 fc_objects [obj ["name" ]] = fc_obj
149151 apply_object_properties (fc_obj , obj , prop_handlers , doc )
150152
151- # Instead of just building color_map, build a complete guidata dictionary
152- obj_opts = jcad_dict .get ("options" , {}).get (obj ["name" ], {})
153- hexcol = obj .get ("parameters" , {}).get ("Color" ) or obj_opts .get ("color" , "#808080" )
154- # Check visibility: prioritize options, then object-level visible, default to True
155- visible = obj_opts .get ("visible" )
156- if visible is None :
157- visible = obj .get ("visible" , True )
158-
159- # Build guidata entry with both color and visibility
153+ rgb , visible = _color_and_visibility (obj )
154+ default_camera = (
155+ "OrthographicCamera {\n "
156+ " viewportMapping ADJUST_CAMERA\n "
157+ " position 5.0 0.0 10.0\n "
158+ " orientation 0.7 0.2 0.4 1.0\n "
159+ " nearDistance 0.2\n "
160+ " farDistance 20.0\n "
161+ " aspectRatio 1.0\n "
162+ " focalDistance 8.0\n "
163+ " height 16.0\n "
164+ "}" )
160165 guidata [obj ["name" ]] = {
161- "ShapeColor" : {"type" : "App::PropertyColor" , "value" : _hex_to_rgb ( hexcol ) },
166+ "ShapeColor" : {"type" : "App::PropertyColor" , "value" : rgb },
162167 "Visibility" : {"type" : "App::PropertyBool" , "value" : visible },
163168 }
169+ guidata ["GuiCameraSettings" ] = default_camera
164170
165- # 5) Recompute so that FreeCAD has generated any missing children
171+ # 5) Recompute so FreeCAD generates any missing children
166172 doc .recompute ()
167173
168- # 6 ) Save to a temp FCStd using guidata instead of colors
174+ # 7 ) Save with guidata so FreeCAD writes a full GuiDocument.xml
169175 with tempfile .NamedTemporaryFile (delete = False , suffix = ".FCStd" ) as tmp :
170- tmp_path = tmp .name
171-
172- # Default camera settings
173- default_camera = 'OrthographicCamera { viewportMapping ADJUST_CAMERA position 8.5470247 -1.1436439 9.9673195 orientation 0.86492187 0.23175442 0.44519675 1.0835806 nearDistance 0.19726367 farDistance 17.140171 aspectRatio 1 focalDistance 8.6602545 height 17.320509 }'
174-
175- # Add camera to guidata - try the direct string approach
176- guidata ["GuiCameraSettings" ] = default_camera
177-
178- # Use guidata to include both color, visibility, AND camera
179- OfflineRenderingUtils .save (
180- doc ,
181- filename = tmp_path ,
182- guidata = guidata
183- )
176+ path = tmp .name
177+ OfflineRenderingUtils .save (doc , filename = path , guidata = guidata )
184178
185- # 7) Finally, open that new FCStd in FreeCAD and return the Document handle
186- return fc .app .openDocument (tmp_path )
179+ return fc .app .openDocument (path )
0 commit comments