diff --git a/rhino/elemente.py b/rhino/elemente.py index ee81d55..b2ef3fe 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -2722,7 +2722,8 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over, stempel_show_nf=None, stempel_show_vf=None, stempel_show_ff=None, stempel_show_ngf=None, stempel_show_gf=None, stempel_show_agf=None, - stempel_show_count=None, stempel_show_seps=None, + stempel_show_count=None, stempel_show_personen=None, + stempel_show_seps=None, stempel_stil_id=None, wand_layered=None, wand_layers=None, wand_layer_idx=None, wand_chain_members=None, @@ -2962,6 +2963,7 @@ def _attach_meta(obj_attrs, wall_id, type_, geschoss, dicke, uk_over, ok_over, (_KEY_STEMPEL_SHOW_GF, stempel_show_gf), (_KEY_STEMPEL_SHOW_AGF, stempel_show_agf), (_KEY_STEMPEL_SHOW_COUNT, stempel_show_count), + (_KEY_STEMPEL_SHOW_PERS, stempel_show_personen), (_KEY_STEMPEL_SHOW_SEPS, stempel_show_seps), ): if _v is not None: @@ -3200,6 +3202,7 @@ def _read_meta(obj): st_show_gf = _sh_on(_KEY_STEMPEL_SHOW_GF, True) st_show_agf = _sh_on(_KEY_STEMPEL_SHOW_AGF, True) st_show_count = _sh_on(_KEY_STEMPEL_SHOW_COUNT, False) + st_show_pers = _sh_on(_KEY_STEMPEL_SHOW_PERS, False) st_show_seps = _sh_on(_KEY_STEMPEL_SHOW_SEPS, True) st_stil_id = a.GetUserString(_KEY_STEMPEL_STIL_ID) or "" # Field-Layout — parsed JSON list of rows @@ -3311,6 +3314,7 @@ def _read_meta(obj): "raum_align": r_align, "raum_sia": r_sia, "raum_fuellung": r_fuell, + "raum_personen": r_personen, "raum_txt_font": r_font, "raum_txt_bold": r_bold, "raum_txt_italic": r_ital, @@ -3340,6 +3344,7 @@ def _read_meta(obj): "stempel_show_gf": st_show_gf, "stempel_show_agf": st_show_agf, "stempel_show_count": st_show_count, + "stempel_show_personen": st_show_pers, "stempel_show_seps": st_show_seps, "stempel_stil_id": st_stil_id, "wand_layered": w_layered, @@ -3978,6 +3983,7 @@ _KEY_STEMPEL_SHOW_NGF = "dossier_stempel_show_ngf" # default "1" _KEY_STEMPEL_SHOW_GF = "dossier_stempel_show_gf" # default "1" _KEY_STEMPEL_SHOW_AGF = "dossier_stempel_show_agf" # default "1" _KEY_STEMPEL_SHOW_COUNT = "dossier_stempel_show_count" # default "0" +_KEY_STEMPEL_SHOW_PERS = "dossier_stempel_show_pers" # default "0" _KEY_STEMPEL_SHOW_SEPS = "dossier_stempel_show_seps" # default "1" _KEY_STEMPEL_STIL_ID = "dossier_stempel_stil_id" # aktiver Stil # Storage-Key fuer Stempel-Stile (Presets, per Doc) @@ -3985,7 +3991,8 @@ _KEY_STEMPEL_STILE = "dossier_stempel_stile" _STEMPEL_STIL_FIELDS = ( "header", "showScope", "showHnf", "showNnf", "showNf", "showVf", "showFf", - "showNgf", "showGf", "showAgf", "showCount", "showSeparators", + "showNgf", "showGf", "showAgf", "showCount", "showPersonen", + "showSeparators", "font", "bold", "italic", "txtH", ) @@ -5160,7 +5167,7 @@ def compute_sia_bilanz(doc, scope="total"): laut SIA 416). """ out = {"hnf": 0.0, "nnf": 0.0, "vf": 0.0, "ff": 0.0, - "gf": 0.0, "agf": 0.0, "count": 0, + "gf": 0.0, "agf": 0.0, "count": 0, "personen": 0, "scope": scope, "geschossName": ""} if doc is None: return out target_gid = None @@ -5181,6 +5188,8 @@ def compute_sia_bilanz(doc, scope="total"): if sia not in ("hnf", "nnf", "vf", "ff", "gf", "agf"): continue out[sia] += float(area) out["count"] += 1 + try: out["personen"] += int(m.get("raum_personen", 0) or 0) + except Exception: pass except Exception: pass out["nf"] = out["hnf"] + out["nnf"] out["ngf"] = out["nf"] + out["vf"] + out["ff"] @@ -5188,7 +5197,8 @@ def compute_sia_bilanz(doc, scope="total"): def _format_bilanz_lines(bilanz, rundung="0.1", visibility=None, - show_separators=True, show_count=False): + show_separators=True, show_count=False, + show_personen=False): """Baut die Stempel-Textzeilen aus einer Bilanz. Zeigt nur Kategorien mit Flaeche > 0 UND deren visibility-Flag True ist. @@ -5231,6 +5241,11 @@ def _format_bilanz_lines(bilanz, rundung="0.1", visibility=None, if show_count and bilanz.get("count", 0) > 0: if show_separators and lines: lines.append(sep) lines.append("{} Räume".format(bilanz["count"])) + # Personen-Total optional (Summe ueber alle Raeume im Scope) + if show_personen and bilanz.get("personen", 0) > 0: + if not (show_count and show_separators) and show_separators and lines: + lines.append(sep) + lines.append("{} Personen".format(bilanz["personen"])) return lines @@ -5238,6 +5253,7 @@ def _make_stempel_text(pos, scope, doc, text_height=0.20, z=0.0, font=None, bold=False, italic=False, header_text="Nutzflächen", show_scope=True, show_separators=True, show_count=False, + show_personen=False, visibility=None): """Baut die Stempel-TextEntity fuer einen Scope ("total" oder "geschoss:"). header_text + show_* steuern Layout-Customisation. @@ -5262,7 +5278,8 @@ def _make_stempel_text(pos, scope, doc, text_height=0.20, z=0.0, header = " · ".join(header_parts) if header_parts else "" body = _format_bilanz_lines(bilanz, visibility=visibility, show_separators=show_separators, - show_count=show_count) + show_count=show_count, + show_personen=show_personen) if not body: body = ["(keine klassifizierten Räume)"] lines = [] @@ -6771,6 +6788,7 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name show_scope=bool(meta.get("stempel_show_scope", True)), show_separators=bool(meta.get("stempel_show_seps", True)), show_count=bool(meta.get("stempel_show_count", False)), + show_personen=bool(meta.get("stempel_show_personen", False)), visibility=visibility) if new_te is None: return False # In-place replace @@ -7144,6 +7162,7 @@ class ElementeBridge(panel_base.BaseBridge): "layout": meta.get("raum_layout") or [], "txtModus": meta.get("raum_txt_modus", "fix"), "stilId": meta.get("raum_stil_id", ""), + "personen": int(meta.get("raum_personen", 0) or 0), "area": area, "areaFmt": _format_area(area, rnd_eff), "umfang": perim, @@ -7174,6 +7193,7 @@ class ElementeBridge(panel_base.BaseBridge): "showGf": bool(meta.get("stempel_show_gf", True)), "showAgf": bool(meta.get("stempel_show_agf", True)), "showCount": bool(meta.get("stempel_show_count", False)), + "showPersonen": bool(meta.get("stempel_show_personen", False)), "showSeparators": bool(meta.get("stempel_show_seps", True)), "stilId": meta.get("stempel_stil_id", ""), "bilanz": bilanz, @@ -11015,6 +11035,13 @@ class ElementeBridge(panel_base.BaseBridge): old_meta.get("raum_show_area", True))) r_show_sia = bool(p.get("showSia", old_meta.get("raum_show_sia", False))) + # Personen-Belegung (int, default 0) + try: + r_personen = int(p.get("personen", + old_meta.get("raum_personen", 0))) + except Exception: + r_personen = int(old_meta.get("raum_personen", 0) or 0) + if r_personen < 0: r_personen = 0 # Layout: Liste-of-Rows aus Frontend (JSON-serializable). Wenn # nicht im Patch → vom alten meta uebernehmen. r_layout = p.get("layout", old_meta.get("raum_layout", [])) @@ -11072,7 +11099,8 @@ class ElementeBridge(panel_base.BaseBridge): raum_show_area=r_show_are, raum_show_sia=r_show_sia, raum_layout=r_layout, - raum_txt_modus=r_modus) + raum_txt_modus=r_modus, + raum_personen=r_personen) axis_obj.Attributes = attrs axis_obj.CommitChanges() _save_last(raum_name_last=r_name, raum_rundung=r_rnd, @@ -11123,6 +11151,8 @@ class ElementeBridge(panel_base.BaseBridge): old_meta.get("stempel_show_agf", True))) st_show_count = bool(p.get("showCount", old_meta.get("stempel_show_count", False))) + st_show_pers = bool(p.get("showPersonen", + old_meta.get("stempel_show_personen", False))) st_show_seps = bool(p.get("showSeparators", old_meta.get("stempel_show_seps", True))) attrs = axis_obj.Attributes @@ -11142,6 +11172,7 @@ class ElementeBridge(panel_base.BaseBridge): stempel_show_gf=st_show_gf, stempel_show_agf=st_show_agf, stempel_show_count=st_show_count, + stempel_show_personen=st_show_pers, stempel_show_seps=st_show_seps) axis_obj.Attributes = attrs axis_obj.CommitChanges() diff --git a/src/ElementeApp.jsx b/src/ElementeApp.jsx index 9991548..6102817 100644 --- a/src/ElementeApp.jsx +++ b/src/ElementeApp.jsx @@ -937,6 +937,7 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo const [name, setName] = useState(raum.name || 'Raum') const [nummer, setNummer] = useState(raum.nummer || '') const [funktion, setFunktion] = useState(raum.funktion || '') + const [personenStr, setPersonenStr] = useState(String(raum.personen || 0)) // Texthoehe (raum.txtH) + Ausrichtung (raum.align) werden via Oberleiste // gesetzt — kein Local-State noetig, Stil-Speichern liest direkt aus raum. const txtModus = raum.txtModus || 'fix' @@ -948,7 +949,8 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo setName(raum.name || 'Raum') setNummer(raum.nummer || '') setFunktion(raum.funktion || '') - }, [raum.id, raum.name, raum.nummer, raum.funktion]) + setPersonenStr(String(raum.personen || 0)) + }, [raum.id, raum.name, raum.nummer, raum.funktion, raum.personen]) // Aktueller Wert von raum_fuellung: "" | "Solid" | "Hatch1" | … | "ByLayer" const fuell = raum.fuellung || '' @@ -1133,6 +1135,33 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo )} + {/* Personen-Belegung (SIA: Schulen, Bueros, Versammlungen) — + aggregiert im Bilanz-Stempel als "Personen"-Zeile wenn aktiv. */} + {!isFlaeche && ( +
+ Personen + setPersonenStr(e.target.value)} + onBlur={() => { + const n = parseInt(personenStr, 10) + const v = Number.isFinite(n) && n >= 0 ? n : 0 + if (v !== (raum.personen || 0)) onUpdate({ personen: v }) + setPersonenStr(String(v)) + }} + onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }} + placeholder="0" + title="Anzahl Personen — wird im Bilanz-Stempel summiert (m²/Person nach SIA)" + style={{ width: 64, fontSize: 11, textAlign: 'right', + fontFamily: 'DM Mono, monospace' }} /> + {raum.area > 0 && raum.personen > 0 && ( + + {(raum.area / raum.personen).toFixed(1)} m²/Person + + )} +
+ )} + {/* Stempel-Layout — Drag-and-Drop. Jede Row ist eine Textzeile, Felder innerhalb einer Row landen in derselben Zeile. Drag zwischen Rows um umzuordnen. Klick auf Field oben fügt es in @@ -1185,7 +1214,8 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet setHeaderDraft(stempel.header || 'Nutzflächen') }, [stempel.id, stempel.header]) - // Show-Flags vom Backend (default true ausser showCount) + // Show-Flags vom Backend — default true ausser folgende: + const _OFF_BY_DEFAULT = new Set(['showCount', 'showPersonen']) const _show = (k, def = true) => (stempel[k] !== undefined ? !!stempel[k] : def) @@ -1205,6 +1235,7 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet showGf: _show('showGf'), showAgf: _show('showAgf'), showCount: _show('showCount', false), + showPersonen: _show('showPersonen', false), showSeparators: _show('showSeparators'), font: stempel.font || '', bold: !!stempel.bold, @@ -1239,9 +1270,9 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet const ShowTog = ({ field, label, hint }) => ( onUpdate({ [field]: !_show(field, field === 'showCount' ? false : true) })} + icon={_show(field, _OFF_BY_DEFAULT.has(field) ? false : true) ? 'check_box' : 'check_box_outline_blank'} + active={_show(field, _OFF_BY_DEFAULT.has(field) ? false : true)} + onClick={() => onUpdate({ [field]: !_show(field, _OFF_BY_DEFAULT.has(field) ? false : true) })} title={hint || label} /> ) @@ -1327,6 +1358,7 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet + @@ -1357,6 +1389,16 @@ function StempelProperties({ stempel, geschosse, stempelStile, onUpdate, onDelet {r.val.toFixed(1)} m² ))} + {_show('showPersonen', false) && (bilanz.personen || 0) > 0 && ( +
+ Personen + {bilanz.personen} +
+ )}