Unit-aware: m → Doc-Units im Regen-Pfad
DOSSIER-UI nimmt immer Meter entgegen — bisher wurden die Werte 1:1 als Doc-Units verwendet, was bei Doc=Millimeter winzige Geometrie ergab (0.25m getippt → 0.25mm Wand). Storage-Konvention bleibt METER (UI-friendly). Konvertierung passiert beim Geometrie-Bau: - Neue Helpers _m2u / _u2m via Rhino.RhinoMath.UnitScale - _regenerate_element_body normalisiert ALLE m-typischen Meta-Felder am Eingang via _m2u — Geometrie-Code darunter bleibt unveraendert und arbeitet in Doc-Units (funktioniert in jedem Unit-System) - Lokaler _gs() Wrapper konvertiert Geschoss-OKFF + Hoehe → Doc-Units - _resolve_uk_ok / _resolve_decke_z / _resolve_dach_base konvertieren Geschoss-Heights aus JSON (m) → Doc-Units - _resolve_raum_text_height_m masstab-Pfad: m-Result → Doc-Units - _sync_raum_stamps_to_source: Stempel-TextHeight + Position-Delta sind in Doc-Units, werden vor Storage via _u2m → m konvertiert Effekt: - Doc in Metern: kein sichtbarer Unterschied (UnitScale=1, no-op) - Doc in Millimeter: 0.25 (m) wird zu 250 (mm) Wand → richtig dick - State-Emit zum Frontend bleibt in m → UI konsistent
This commit is contained in:
+135
-28
@@ -535,6 +535,42 @@ _OEFF_PIECE_DEFS = {
|
|||||||
# Doc-Wechsel, aber NICHT Rhino-Restart — was passt: "ich hab gerade 0.30
|
# Doc-Wechsel, aber NICHT Rhino-Restart — was passt: "ich hab gerade 0.30
|
||||||
# fuer eine Wand benutzt, neue Wand soll auch 0.30 sein".
|
# fuer eine Wand benutzt, neue Wand soll auch 0.30 sein".
|
||||||
|
|
||||||
|
def _m2u(value, doc=None):
|
||||||
|
"""Konvertiert einen Wert in Metern in Doc-Units. Nutzt RhinoMath.UnitScale.
|
||||||
|
Bei doc=meter ist scale=1 → no-op. Bei doc=mm ist scale=1000.
|
||||||
|
DOSSIER-Konvention: UI typt in Metern, Geometrie wird in Doc-Units gebaut.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
v = float(value)
|
||||||
|
except Exception:
|
||||||
|
return value
|
||||||
|
if doc is None: doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return v
|
||||||
|
try:
|
||||||
|
scale = Rhino.RhinoMath.UnitScale(
|
||||||
|
Rhino.UnitSystem.Meters, doc.ModelUnitSystem)
|
||||||
|
return v * float(scale)
|
||||||
|
except Exception:
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def _u2m(value, doc=None):
|
||||||
|
"""Inverse von _m2u: Doc-Units → Meter. Fuer State-Emit zum Frontend."""
|
||||||
|
try:
|
||||||
|
v = float(value)
|
||||||
|
except Exception:
|
||||||
|
return value
|
||||||
|
if doc is None: doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return v
|
||||||
|
try:
|
||||||
|
scale = Rhino.RhinoMath.UnitScale(
|
||||||
|
Rhino.UnitSystem.Meters, doc.ModelUnitSystem)
|
||||||
|
if scale == 0: return v
|
||||||
|
return v / float(scale)
|
||||||
|
except Exception:
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
def _last(key, default):
|
def _last(key, default):
|
||||||
# `_reset_panels.py` cleart sticky via `= None` (statt del), daher kann
|
# `_reset_panels.py` cleart sticky via `= None` (statt del), daher kann
|
||||||
# sc.sticky.get() den default ueberlesen und ein None zurueckgeben.
|
# sc.sticky.get() den default ueberlesen und ein None zurueckgeben.
|
||||||
@@ -594,14 +630,17 @@ def _active_geschoss_name(doc):
|
|||||||
|
|
||||||
|
|
||||||
def _resolve_uk_ok(doc, gid, uk_over, ok_over):
|
def _resolve_uk_ok(doc, gid, uk_over, ok_over):
|
||||||
"""Wand: UK = OKFF, OK = OKFF + Hoehe (Standard fuer Geschoss-volle Wand)."""
|
"""Wand: UK = OKFF, OK = OKFF + Hoehe (Standard fuer Geschoss-volle Wand).
|
||||||
|
Geschoss-Heights JSON ist in METERN — hier auf Doc-Units konvertieren,
|
||||||
|
damit Geometrie-Code direkt arbeiten kann. uk_over/ok_over kommen
|
||||||
|
bereits in Doc-Units von _regenerate_element_body's Meta-Normalisierung."""
|
||||||
g = _geschoss_by_id(doc, gid)
|
g = _geschoss_by_id(doc, gid)
|
||||||
if g is None:
|
if g is None:
|
||||||
uk = float(uk_over) if uk_over not in (None, "") else 0.0
|
uk = float(uk_over) if uk_over not in (None, "") else 0.0
|
||||||
ok = float(ok_over) if ok_over not in (None, "") else 3.0
|
ok = float(ok_over) if ok_over not in (None, "") else _m2u(3.0, doc)
|
||||||
return uk, ok
|
return uk, ok
|
||||||
okff = float(g.get("okff", 0.0))
|
okff = _m2u(g.get("okff", 0.0), doc)
|
||||||
hoehe = float(g.get("hoehe", 3.0))
|
hoehe = _m2u(g.get("hoehe", 3.0), doc)
|
||||||
auto_uk = okff
|
auto_uk = okff
|
||||||
auto_ok = okff + hoehe
|
auto_ok = okff + hoehe
|
||||||
uk = float(uk_over) if uk_over not in (None, "") else auto_uk
|
uk = float(uk_over) if uk_over not in (None, "") else auto_uk
|
||||||
@@ -617,9 +656,11 @@ def _resolve_decke_z(doc, gid, dicke, uk_over, ok_over):
|
|||||||
Override-Logik:
|
Override-Logik:
|
||||||
- Nur OK_override gesetzt → OK = override, UK = OK - dicke
|
- Nur OK_override gesetzt → OK = override, UK = OK - dicke
|
||||||
- Nur UK_override gesetzt → UK = override, OK = UK + dicke
|
- Nur UK_override gesetzt → UK = override, OK = UK + dicke
|
||||||
- Beide gesetzt → beide literal"""
|
- Beide gesetzt → beide literal
|
||||||
|
|
||||||
|
Alle Werte hier in Doc-Units (Geschoss-JSON wird via _m2u konvertiert)."""
|
||||||
g = _geschoss_by_id(doc, gid)
|
g = _geschoss_by_id(doc, gid)
|
||||||
okff = float(g.get("okff", 0.0)) if g else 0.0
|
okff = _m2u(g.get("okff", 0.0), doc) if g else 0.0
|
||||||
auto_ok = okff
|
auto_ok = okff
|
||||||
has_ok = ok_over not in (None, "")
|
has_ok = ok_over not in (None, "")
|
||||||
has_uk = uk_over not in (None, "")
|
has_uk = uk_over not in (None, "")
|
||||||
@@ -3773,12 +3814,13 @@ def _find_target_volume(doc, element_id):
|
|||||||
|
|
||||||
def _resolve_dach_base(doc, gid, uk_over):
|
def _resolve_dach_base(doc, gid, uk_over):
|
||||||
"""Basis-Hoehe des Dachs an der Traufe (= Eave) = OKFF + Hoehe des
|
"""Basis-Hoehe des Dachs an der Traufe (= Eave) = OKFF + Hoehe des
|
||||||
Geschosses (Oberkante der Waende). uk_override kann das ueberschreiben."""
|
Geschosses (Oberkante der Waende). uk_override kann das ueberschreiben.
|
||||||
|
Geschoss-Heights JSON ist in METERN → hier auf Doc-Units konvertieren."""
|
||||||
g = _geschoss_by_id(doc, gid)
|
g = _geschoss_by_id(doc, gid)
|
||||||
if g is None:
|
if g is None:
|
||||||
return float(uk_over) if uk_over not in (None, "") else 3.0
|
return float(uk_over) if uk_over not in (None, "") else _m2u(3.0, doc)
|
||||||
okff = float(g.get("okff", 0.0))
|
okff = _m2u(g.get("okff", 0.0), doc)
|
||||||
hoehe = float(g.get("hoehe", 3.0))
|
hoehe = _m2u(g.get("hoehe", 3.0), doc)
|
||||||
auto = okff + hoehe
|
auto = okff + hoehe
|
||||||
return float(uk_over) if uk_over not in (None, "") else auto
|
return float(uk_over) if uk_over not in (None, "") else auto
|
||||||
|
|
||||||
@@ -4735,16 +4777,23 @@ def _format_area(area, rundung):
|
|||||||
return "{:.2f}".format(a) # exakt
|
return "{:.2f}".format(a) # exakt
|
||||||
|
|
||||||
|
|
||||||
def _resolve_raum_text_height_m(value, modus):
|
def _resolve_raum_text_height_m(value, modus, doc=None):
|
||||||
"""Konvertiert raum_txt_h (User-Wert) in Modell-Meter, je nach Modus:
|
"""Konvertiert raum_txt_h (User-Wert) in MODELL-DOC-UNITS, je nach Modus:
|
||||||
modus=fix → value ist bereits Meter
|
modus=fix → value ist Meter, Modell-Doc-Units = m * UnitScale
|
||||||
modus=masstab → value ist Paper-mm, Modell-Meter = value*scale/1000
|
(Doc=Meter → no-op; Doc=mm → ×1000)
|
||||||
(scale aus aktivem Viewport via massstab.get_applied_scale_ratio)
|
modus=masstab → value ist Paper-mm, Modell-Doc-Units =
|
||||||
|
(value * scale / 1000) * UnitScale
|
||||||
|
(scale aus aktivem Viewport via massstab)
|
||||||
Fallback bei fehlendem Massstab: 100 (1:100).
|
Fallback bei fehlendem Massstab: 100 (1:100).
|
||||||
|
Hinweis: im Aufruf-Pfad in _regenerate_element_body wurde raum_txt_h
|
||||||
|
bereits durch die Meta-Normalisierung in Doc-Units konvertiert FUER
|
||||||
|
fix-Modus. Hier behandeln wir den masstab-Pfad sauber und lassen den
|
||||||
|
fix-Pfad als no-op.
|
||||||
"""
|
"""
|
||||||
try: v = float(value)
|
try: v = float(value)
|
||||||
except Exception: v = 0.20
|
except Exception: v = 0.20
|
||||||
if (modus or "fix") != "masstab":
|
if (modus or "fix") != "masstab":
|
||||||
|
# value ist schon in Doc-Units (vor-konvertiert in _regenerate_element_body)
|
||||||
return v
|
return v
|
||||||
try:
|
try:
|
||||||
import massstab as _ms
|
import massstab as _ms
|
||||||
@@ -4753,7 +4802,9 @@ def _resolve_raum_text_height_m(value, modus):
|
|||||||
sc_ratio = None
|
sc_ratio = None
|
||||||
if not sc_ratio or sc_ratio <= 0:
|
if not sc_ratio or sc_ratio <= 0:
|
||||||
sc_ratio = 100.0
|
sc_ratio = 100.0
|
||||||
return float(v) * float(sc_ratio) / 1000.0
|
# masstab: Paper-mm → Modell-Meter → Modell-Doc-Units
|
||||||
|
m_value = float(v) * float(sc_ratio) / 1000.0
|
||||||
|
return _m2u(m_value, doc)
|
||||||
|
|
||||||
|
|
||||||
def _make_raum_stamp_text(centroid, name, nummer, funktion, area, rundung,
|
def _make_raum_stamp_text(centroid, name, nummer, funktion, area, rundung,
|
||||||
@@ -5516,7 +5567,60 @@ def _regenerate_element(doc, element_id):
|
|||||||
|
|
||||||
def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name):
|
def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name):
|
||||||
"""Eigentliche Implementierung des Regen — der aeussere Wrapper
|
"""Eigentliche Implementierung des Regen — der aeussere Wrapper
|
||||||
`_regenerate_element` setzt _REGEN_BUSY und dispatcht oeffnung_point."""
|
`_regenerate_element` setzt _REGEN_BUSY und dispatcht oeffnung_point.
|
||||||
|
|
||||||
|
UNIT-NORMALISIERUNG: DOSSIER-Storage ist immer in METERN (UI-Konvention).
|
||||||
|
Hier am Eingang konvertieren wir ALLE m-Werte → Doc-Units. So bleibt der
|
||||||
|
Geometrie-Code unten unveraendert und funktioniert in jedem Doc-Unit-
|
||||||
|
System (Meter, Millimeter, …).
|
||||||
|
"""
|
||||||
|
# Meta-Werte normalisieren: Liste m-typischer Felder → _m2u
|
||||||
|
_M_FIELDS = ("dicke", "uk_override", "ok_override",
|
||||||
|
"oeff_breite", "oeff_hoehe", "oeff_brueest",
|
||||||
|
"oeff_rahmen_b", "oeff_rahmen_tiefe", "oeff_rahmen_offset",
|
||||||
|
"treppe_breite", "treppe_lauf_d", "treppe_h_over",
|
||||||
|
"trag_b", "trag_h", "trag_d", "trag_t", "trag_z_over",
|
||||||
|
"raum_stamp_dx", "raum_stamp_dy")
|
||||||
|
for _k in _M_FIELDS:
|
||||||
|
if _k in meta:
|
||||||
|
_v = meta[_k]
|
||||||
|
if _v == "" or _v is None: continue
|
||||||
|
try: meta[_k] = _m2u(_v, doc)
|
||||||
|
except Exception: pass
|
||||||
|
# raum_txt_h: nur im fix-Modus in m (sonst paper-mm) → entsprechend
|
||||||
|
# konvertieren. Im masstab-Modus uebernimmt _resolve_raum_text_height_m
|
||||||
|
# die Berechnung (selber doc-unit-aware).
|
||||||
|
if meta.get("type") == "raum_outline" and meta.get("raum_txt_modus") != "masstab":
|
||||||
|
try: meta["raum_txt_h"] = _m2u(meta.get("raum_txt_h", 0.20), doc)
|
||||||
|
except Exception: pass
|
||||||
|
# Wand-Schichten: jede Schicht hat eine eigene dicke
|
||||||
|
if isinstance(meta.get("wand_layers"), list):
|
||||||
|
for _ly in meta["wand_layers"]:
|
||||||
|
if isinstance(_ly, dict) and "dicke" in _ly:
|
||||||
|
try: _ly["dicke"] = _m2u(_ly["dicke"], doc)
|
||||||
|
except Exception: pass
|
||||||
|
# Treppe Soll-Werte: dict mit s/a/sa Range-Listen
|
||||||
|
if isinstance(meta.get("treppe_soll"), dict):
|
||||||
|
for _k in ("s", "a", "sa"):
|
||||||
|
_arr = meta["treppe_soll"].get(_k)
|
||||||
|
if isinstance(_arr, list) and len(_arr) >= 2:
|
||||||
|
try:
|
||||||
|
_arr[0] = _m2u(_arr[0], doc)
|
||||||
|
_arr[1] = _m2u(_arr[1], doc)
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
|
# Geschoss-Lookup-Wrapper: konvertiert hoehe + okff → Doc-Units.
|
||||||
|
# ALLE Geometrie-Aufrufe unten benutzen _gs() statt _geschoss_by_id().
|
||||||
|
def _gs(gid):
|
||||||
|
g = _geschoss_by_id(doc, gid)
|
||||||
|
if g is None: return None
|
||||||
|
g = dict(g)
|
||||||
|
try: g["hoehe"] = _m2u(g.get("hoehe", 3.0), doc)
|
||||||
|
except Exception: pass
|
||||||
|
try: g["okff"] = _m2u(g.get("okff", 0.0), doc)
|
||||||
|
except Exception: pass
|
||||||
|
return g
|
||||||
|
|
||||||
if meta["type"] == "wand_axis":
|
if meta["type"] == "wand_axis":
|
||||||
# Chain-Detection: wenn diese Wand mit Nachbarn (gleiche Geometrie/
|
# Chain-Detection: wenn diese Wand mit Nachbarn (gleiche Geometrie/
|
||||||
# Material/Hoehe, 2-Wall-Joints) zu einem Polyline-Chain gehoert,
|
# Material/Hoehe, 2-Wall-Joints) zu einem Polyline-Chain gehoert,
|
||||||
@@ -5971,8 +6075,8 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
elif meta["type"] == "treppe_axis":
|
elif meta["type"] == "treppe_axis":
|
||||||
# Start- und Zielgeschoss → uk/ok aus OKFF-Differenz.
|
# Start- und Zielgeschoss → uk/ok aus OKFF-Differenz.
|
||||||
# H-Override hat Vorrang vor Zielgeschoss.
|
# H-Override hat Vorrang vor Zielgeschoss.
|
||||||
g_start = _geschoss_by_id(doc, meta["geschoss"])
|
g_start = _gs(meta["geschoss"])
|
||||||
g_end = _geschoss_by_id(doc, meta.get("geschoss_end", ""))
|
g_end = _gs(meta.get("geschoss_end", ""))
|
||||||
if g_start is None:
|
if g_start is None:
|
||||||
return False
|
return False
|
||||||
uk = float(g_start.get("okff", 0.0))
|
uk = float(g_start.get("okff", 0.0))
|
||||||
@@ -6015,7 +6119,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
pt = geom.Location
|
pt = geom.Location
|
||||||
else:
|
else:
|
||||||
pt = geom # Point3d
|
pt = geom # Point3d
|
||||||
g_start = _geschoss_by_id(doc, meta["geschoss"])
|
g_start = _gs(meta["geschoss"])
|
||||||
uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
||||||
z_over = meta.get("trag_z_over", "")
|
z_over = meta.get("trag_z_over", "")
|
||||||
if z_over:
|
if z_over:
|
||||||
@@ -6037,7 +6141,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
src_layer = layer
|
src_layer = layer
|
||||||
elif meta["type"] == "traeger_axis":
|
elif meta["type"] == "traeger_axis":
|
||||||
# Achse + z_top aus Geschoss + Override
|
# Achse + z_top aus Geschoss + Override
|
||||||
g_start = _geschoss_by_id(doc, meta["geschoss"])
|
g_start = _gs(meta["geschoss"])
|
||||||
uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
||||||
h = float(g_start.get("hoehe", 3.0)) if g_start else 3.0
|
h = float(g_start.get("hoehe", 3.0)) if g_start else 3.0
|
||||||
z_over = meta.get("trag_z_over", "")
|
z_over = meta.get("trag_z_over", "")
|
||||||
@@ -6075,8 +6179,8 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
doc.Objects.ModifyAttributes(src_obj, new_attrs, True)
|
doc.Objects.ModifyAttributes(src_obj, new_attrs, True)
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
area, perim, ctr = _raum_amp(geom)
|
area, perim, ctr = _raum_amp(geom)
|
||||||
# Z-Lage: auf Geschoss-OKFF
|
# Z-Lage: auf Geschoss-OKFF (Doc-Units via _gs)
|
||||||
g_start = _geschoss_by_id(doc, meta["geschoss"])
|
g_start = _gs(meta["geschoss"])
|
||||||
z_uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
z_uk = float(g_start.get("okff", 0.0)) if g_start else 0.0
|
||||||
|
|
||||||
# Alte Stempel + Fills loeschen
|
# Alte Stempel + Fills loeschen
|
||||||
@@ -6210,7 +6314,8 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
# Texthoehe: bei modus=masstab aus Paper-mm + aktuellem Scale aufloesen
|
# Texthoehe: bei modus=masstab aus Paper-mm + aktuellem Scale aufloesen
|
||||||
eff_h = _resolve_raum_text_height_m(
|
eff_h = _resolve_raum_text_height_m(
|
||||||
meta.get("raum_txt_h", 0.20),
|
meta.get("raum_txt_h", 0.20),
|
||||||
meta.get("raum_txt_modus", "fix"))
|
meta.get("raum_txt_modus", "fix"),
|
||||||
|
doc=doc)
|
||||||
te = _make_raum_stamp_text(
|
te = _make_raum_stamp_text(
|
||||||
stamp_pt,
|
stamp_pt,
|
||||||
meta.get("raum_name", "Raum"),
|
meta.get("raum_name", "Raum"),
|
||||||
@@ -12192,10 +12297,12 @@ def _sync_raum_stamps_to_source(doc):
|
|||||||
if not isinstance(src_geom, rg.Curve): continue
|
if not isinstance(src_geom, rg.Curve): continue
|
||||||
_, _, ctr = _raum_amp(src_geom)
|
_, _, ctr = _raum_amp(src_geom)
|
||||||
if ctr is None: continue
|
if ctr is None: continue
|
||||||
new_dx = pos.X - ctr.X
|
# Stempel-Position ist in Doc-Units → fuer Storage (Meter) konvertieren
|
||||||
new_dy = pos.Y - ctr.Y
|
new_dx = _u2m(pos.X - ctr.X, doc)
|
||||||
# Font/Style/Size aus der TextEntity lesen
|
new_dy = _u2m(pos.Y - ctr.Y, doc)
|
||||||
try: new_h = float(te.TextHeight)
|
# Font/Style/Size aus der TextEntity lesen.
|
||||||
|
# TextHeight ist in Doc-Units → fuer Storage (Meter) konvertieren
|
||||||
|
try: new_h = _u2m(float(te.TextHeight), doc)
|
||||||
except Exception: new_h = float(meta.get("raum_txt_h", 0.20))
|
except Exception: new_h = float(meta.get("raum_txt_h", 0.20))
|
||||||
cur_font = None
|
cur_font = None
|
||||||
try: cur_font = te.Font
|
try: cur_font = te.Font
|
||||||
|
|||||||
Reference in New Issue
Block a user