Skip to content

Commit d3373eb

Browse files
committed
Add support for unconventional names
1 parent a9e5170 commit d3373eb

File tree

1 file changed

+45
-10
lines changed

1 file changed

+45
-10
lines changed

jupytercad_freecad/freecad/jcad_converter.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,68 @@
22
import OfflineRenderingUtils # type: ignore[import]
33
import base64
44
import tempfile
5-
# from .loader import FCStd
65
import os
6+
import re
77

88
from .loader import _hex_to_rgb
99
from .props.base_prop import BaseProp
1010
from . import props as Props
1111

1212

13-
def export_jcad_to_fcstd(jcad_dict: dict) -> 'fc.Document':
13+
def sanitize_object_name(name: str) -> str:
14+
"""Convert object names to FreeCAD-compatible format."""
15+
# Replace spaces or hyphens with underscores, then drop any other non-word
16+
sanitized = name.replace(" ", "_").replace("-", "_")
17+
return re.sub(r"[^\w_]", "_", sanitized)
18+
19+
20+
def export_jcad_to_fcstd(jcad_dict: dict) -> "fc.Document":
1421
doc = fc.app.newDocument("__jcad_export__")
1522
doc.Meta = jcad_dict.get("metadata", {})
16-
23+
24+
# Build Prop handler lookup
1725
prop_handlers = {
1826
Cls.name(): Cls
1927
for Cls in Props.__dict__.values()
2028
if isinstance(Cls, type) and issubclass(Cls, BaseProp)
2129
}
2230

23-
# LOCAL variable for guidata (no function attribute)
24-
guidata = {}
31+
# 1) Build a simple original→sanitized name map
32+
name_mapping = {
33+
obj["name"]: sanitize_object_name(obj["name"])
34+
for obj in jcad_dict.get("objects", [])
35+
}
2536

37+
# 2) Rename each object in place, and fix any string or list references in parameters
38+
for obj in jcad_dict.get("objects", []):
39+
orig = obj["name"]
40+
obj["name"] = name_mapping[orig]
41+
42+
params = obj.get("parameters", {})
43+
for key, val in params.items():
44+
# If it’s a string reference to another object, rewrite it:
45+
if isinstance(val, str) and val in name_mapping:
46+
params[key] = name_mapping[val]
47+
48+
# If it’s a list of references, rewrite every entry that matches a key:
49+
elif isinstance(val, list):
50+
params[key] = [
51+
name_mapping[item] if (isinstance(item, str) and item in name_mapping) else item
52+
for item in val
53+
]
54+
55+
# 3) Replay sanitized objects into FreeCAD
56+
guidata = {}
2657
for jcad_obj in jcad_dict.get("objects", []):
2758
shape_type = jcad_obj["shape"]
28-
name = jcad_obj["name"]
29-
params = jcad_obj.get("parameters", {})
30-
opts = jcad_dict.get("options", {}).get(name, {})
59+
name = jcad_obj["name"]
60+
params = jcad_obj.get("parameters", {})
61+
opts = jcad_dict.get("options", {}).get(name, {})
3162

63+
# Add object to FreeCAD
3264
fc_obj = doc.addObject(shape_type, name)
3365

66+
# Apply all non-color props via handlers
3467
for prop, jval in params.items():
3568
if prop == "Color":
3669
continue
@@ -44,7 +77,7 @@ def export_jcad_to_fcstd(jcad_dict: dict) -> 'fc.Document':
4477
jcad_object=jcad_obj,
4578
fc_prop=getattr(fc_obj, prop),
4679
fc_object=fc_obj,
47-
fc_file=doc
80+
fc_file=doc,
4881
)
4982
if new_val is not None:
5083
setattr(fc_obj, prop, new_val)
@@ -56,13 +89,15 @@ def export_jcad_to_fcstd(jcad_dict: dict) -> 'fc.Document':
5689
rgb = _hex_to_rgb(hexcol)
5790
guidata[name] = {
5891
"ShapeColor": {"type": "App::PropertyColor", "value": rgb},
59-
"Visibility": {"type": "App::PropertyBool", "value": opts.get("visible", True)}
92+
"Visibility": {"type": "App::PropertyBool", "value": opts.get("visible", True)},
6093
}
6194

6295
doc.recompute()
6396

97+
# 4) Write out a temp FCStd, then apply colors/visibility
6498
with tempfile.NamedTemporaryFile(delete=False, suffix=".FCStd") as tmp:
6599
tmp_path = tmp.name
100+
doc.saveAs(tmp_path)
66101

67102
OfflineRenderingUtils.save(
68103
doc,

0 commit comments

Comments
 (0)