diff --git a/rhino/rhinopanel.py b/rhino/rhinopanel.py index ac994cd..0db9463 100644 --- a/rhino/rhinopanel.py +++ b/rhino/rhinopanel.py @@ -303,6 +303,10 @@ _PROJECT_SETTINGS_DEFAULTS = { "schnittDepthBack": 8.0, "schnittHeightMin": -1.0, "schnittHeightMax": 12.0, + # Arbeitseinheit. DOSSIER-Default ist Meter (Architektur-Standard). + # Beim Doc-Open prueft startup.py ob doc.ModelUnitSystem dem hier + # entspricht — sonst Dialog mit "Umstellen"-Option. + "unit": "meters", # "meters" | "millimeters" | "centimeters" }, "materials": [], "project": { @@ -319,6 +323,44 @@ _PROJECT_SETTINGS_DEFAULTS = { } +# Mapping Setting-String → Rhino.UnitSystem Enum (lazy import vermeidet +# Bootstrap-Problem wenn Rhino.dll noch nicht da ist) +_UNIT_STRING_TO_ENUM = None + + +def _unit_string_to_enum(): + global _UNIT_STRING_TO_ENUM + if _UNIT_STRING_TO_ENUM is not None: + return _UNIT_STRING_TO_ENUM + try: + import Rhino + _UNIT_STRING_TO_ENUM = { + "meters": Rhino.UnitSystem.Meters, + "millimeters": Rhino.UnitSystem.Millimeters, + "centimeters": Rhino.UnitSystem.Centimeters, + } + except Exception: + _UNIT_STRING_TO_ENUM = {} + return _UNIT_STRING_TO_ENUM + + +def get_project_unit(doc): + """Liefert die Arbeitseinheit aus den Project-Settings als String + ('meters'|'millimeters'|'centimeters'). Default 'meters'. + """ + ps = load_project_settings(doc) or {} + d = ps.get("defaults") or {} + u = (d.get("unit") or "meters").lower() + if u not in ("meters", "millimeters", "centimeters"): + u = "meters" + return u + + +def get_project_unit_enum(doc): + """Wie get_project_unit, aber als Rhino.UnitSystem Enum.""" + return _unit_string_to_enum().get(get_project_unit(doc)) + + def _normalize_project_meta(p): """Garantiert das project-Schema. Strings werden gestripped, mum als float (default 0.0 wenn nicht parsebar).""" diff --git a/rhino/startup.py b/rhino/startup.py index c6de017..44dd373 100644 --- a/rhino/startup.py +++ b/rhino/startup.py @@ -141,6 +141,74 @@ def _assign_default_display_modes(doc): print("[STARTUP] view-modes: {} Viewport(s) gesetzt".format(n_set)) +_DOC_FLAG_UNIT_CHECKED = "dossier_unit_checked" + + +def _check_doc_unit(doc): + """Prueft ob doc.ModelUnitSystem der DOSSIER-Project-Setting-Arbeitseinheit + entspricht. Bei Mismatch: Modal-Dialog mit "Umstellen" / "Spaeter"-Option. + + Idempotent pro Doc via doc.Strings-Flag — wird nur EINMAL pro Doc gefragt. + Wenn User "Spaeter" waehlt, fragt DOSSIER beim selben Doc nicht mehr (Flag + bleibt gesetzt). Fuer erneute Frage: doc.Strings-Key loeschen. + """ + if doc is None: return + try: + if doc.Strings.GetValue(_DOC_FLAG_UNIT_CHECKED) == "1": + return + except Exception: pass + try: + import rhinopanel + target_unit_str = rhinopanel.get_project_unit(doc) + target_unit_enum = rhinopanel.get_project_unit_enum(doc) + except Exception as ex: + print("[STARTUP] unit-check: project-setting lesen:", ex) + return + if target_unit_enum is None: return + try: + current = doc.ModelUnitSystem + except Exception: + return + if current == target_unit_enum: + # Schon passend → einmalig Flag setzen, beim naechsten Open kein Check + try: doc.Strings.SetString(_DOC_FLAG_UNIT_CHECKED, "1") + except Exception: pass + return + # Mismatch — Dialog zeigen + try: + import Eto.Forms as ef + msg = ("Dieses Doc ist in '{}'.\n" + "DOSSIER-Projekteinstellung: '{}'.\n\n" + "Doc auf '{}' umstellen?\n" + "(Bestehende Geometrie wird skaliert)").format( + str(current), target_unit_str, target_unit_str) + result = ef.MessageBox.Show( + msg, "DOSSIER — Arbeitseinheit", + ef.MessageBoxButtons.YesNo, + ef.MessageBoxType.Question) + try: + doc.Strings.SetString(_DOC_FLAG_UNIT_CHECKED, "1") + except Exception: pass + if str(result).lower().endswith("yes"): + # _-Units _ _Yes konvertiert Geometrie automatisch mit + unit_cmd = {"meters": "_Meters", + "millimeters": "_Millimeters", + "centimeters": "_Centimeters"}.get(target_unit_str) + if unit_cmd: + try: + Rhino.RhinoApp.RunScript( + "_-Units _Model {} _Yes _EnterEnd".format(unit_cmd), + False) + print("[STARTUP] Doc auf {} umgestellt (Geometrie skaliert)".format( + target_unit_str)) + except Exception as ex: + print("[STARTUP] unit-convert RunScript:", ex) + else: + print("[STARTUP] User hat Unit-Umstellung verweigert — Doc bleibt {}".format(current)) + except Exception as ex: + print("[STARTUP] unit-check dialog:", ex) + + def _on_doc_opened(sender, e): """Greift bei jedem geoeffneten Doc nach Rhino-Start. Migration ist idempotent (Flag in doc.Strings).""" @@ -149,6 +217,7 @@ def _on_doc_opened(sender, e): import panel_base panel_base.migrate_to_dossier(doc) _assign_default_display_modes(doc) + _check_doc_unit(doc) except Exception as ex: print("[STARTUP] _on_doc_opened:", ex) @@ -212,6 +281,12 @@ def _load_all(sender, e): _assign_default_display_modes(Rhino.RhinoDoc.ActiveDoc) except Exception as ex: print("[STARTUP] view-modes assign:", ex) + # Unit-Check fuer das beim Start aktive Doc — fragt einmal pro Doc + # wenn doc.ModelUnitSystem != Project-Setting + try: + _check_doc_unit(Rhino.RhinoDoc.ActiveDoc) + except Exception as ex: + print("[STARTUP] unit-check active doc:", ex) # DOSSIERUI Window-Layout — Hinweis fuer manuelles Laden _hint_dossier_ui() # Startup-Timing-Summary 3 Sekunden spaeter (nachdem alle async Idle- diff --git a/src/components/ProjectSettingsDialog.jsx b/src/components/ProjectSettingsDialog.jsx index faa69ab..483c3be 100644 --- a/src/components/ProjectSettingsDialog.jsx +++ b/src/components/ProjectSettingsDialog.jsx @@ -799,6 +799,32 @@ export default function ProjectSettingsDialog({ Voreinstellungen fuer neue Elemente. Pro-Element editierte Werte bleiben davon unberuehrt. + +
+ + Einheit + +
+ {[ + { code: 'meters', label: 'Meter (m)' }, + { code: 'centimeters', label: 'cm' }, + { code: 'millimeters', label: 'mm' }, + ].map(u => ( + setDefault('unit', u.code)} + title={`Doc-Unit auf ${u.label} einstellen`} /> + ))} +
+
+
+ Wenn ein geoeffnetes Doc nicht in dieser Einheit ist, fragt + DOSSIER ob umgestellt werden soll. Architektur-Standard: Meter. +
+