11import freecad as fc
2+ import OfflineRenderingUtils # type: ignore[import]
23import base64
34import tempfile
4- from .loader import FCStd
5+ # from .loader import FCStd
56import os
67
8+ from .loader import _hex_to_rgb
9+ from .props .base_prop import BaseProp
10+ from . import props as Props
711
8- def convert_jcad_to_fcstd (jcad_dict : dict ) -> 'fc.Document' :
9- # 1) Spin up a brand‑new FreeCAD doc and write it out to a temp .FCStd
10- blank = fc .app .newDocument ("__jcad_blank__" )
11- blank .recompute () # ensure it's fully initialized
12- with tempfile .NamedTemporaryFile (delete = False , suffix = ".FCStd" ) as tmp_blank :
13- blank .saveAs (tmp_blank .name )
14- tmp_blank .flush ()
15- fc .app .closeDocument (blank .Name )
1612
17- # 2) Read its bytes, encode as base64, give to FCStd loader
18- with open (tmp_blank .name , "rb" ) as f :
19- b64_blank = base64 .b64encode (f .read ()).decode ("utf-8" )
20- os .remove (tmp_blank .name )
13+ def export_jcad_to_fcstd (jcad_dict : dict ) -> 'fc.Document' :
14+ doc = fc .app .newDocument ("__jcad_export__" )
15+ doc .Meta = jcad_dict .get ("metadata" , {})
16+
17+ prop_handlers = {
18+ Cls .name (): Cls
19+ for Cls in Props .__dict__ .values ()
20+ if isinstance (Cls , type ) and issubclass (Cls , BaseProp )
21+ }
2122
22- fcstd = FCStd ( )
23- fcstd . _sources = b64_blank
23+ # LOCAL variable for guidata (no function attribute )
24+ guidata = {}
2425
25- # 3) Replay your JCAD model into it
26- objs = jcad_dict . get ( "objects" , [])
27- opts = jcad_dict . get ( "options" , {})
28- meta = jcad_dict .get ("metadata " , {})
29- fcstd . save ( objs , opts , meta )
26+ for jcad_obj in jcad_dict . get ( "objects" , []):
27+ shape_type = jcad_obj [ "shape" ]
28+ name = jcad_obj [ "name" ]
29+ params = jcad_obj .get ("parameters " , {})
30+ opts = jcad_dict . get ( "options" , {}). get ( name , {} )
3031
31- # 4) Dump the new .FCStd and re‑open it
32- with tempfile .NamedTemporaryFile (delete = False , suffix = ".FCStd" ) as tmp_out :
33- tmp_out .write (base64 .b64decode (fcstd .sources ))
34- tmp_out .flush ()
32+ fc_obj = doc .addObject (shape_type , name )
3533
36- doc = fc .app .openDocument (tmp_out .name )
37- return doc
34+ for prop , jval in params .items ():
35+ if prop == "Color" :
36+ continue
37+ if hasattr (fc_obj , prop ):
38+ t = fc_obj .getTypeIdOfProperty (prop )
39+ Handler = prop_handlers .get (t )
40+ if Handler :
41+ try :
42+ new_val = Handler .jcad_to_fc (
43+ jval ,
44+ jcad_object = jcad_obj ,
45+ fc_prop = getattr (fc_obj , prop ),
46+ fc_object = fc_obj ,
47+ fc_file = doc
48+ )
49+ if new_val is not None :
50+ setattr (fc_obj , prop , new_val )
51+ except Exception as e :
52+ print (f"Error setting { prop } on { name } : { e } " )
3853
54+ # Build guidata entry
55+ hexcol = params .get ("Color" ) or opts .get ("color" ) or "#808080"
56+ rgb = _hex_to_rgb (hexcol )
57+ guidata [name ] = {
58+ "ShapeColor" : {"type" : "App::PropertyColor" , "value" : rgb },
59+ "Visibility" : {"type" : "App::PropertyBool" , "value" : opts .get ("visible" , True )}
60+ }
61+
62+ doc .recompute ()
63+
64+ with tempfile .NamedTemporaryFile (delete = False , suffix = ".FCStd" ) as tmp :
65+ tmp_path = tmp .name
66+
67+ OfflineRenderingUtils .save (
68+ doc ,
69+ filename = tmp_path ,
70+ guidata = guidata
71+ )
72+
73+ return fc .app .openDocument (tmp_path )
0 commit comments