GF/AGF-Outlines: eigene Layer + minimaler Stempel/UI

Geschossflaeche (GF) und Aussengeschossflaeche (AGF) sind reine
Flaechen-Ausweisungs-Outlines — keine Raeume mit Name/Nummer/Funktion.

Backend:
- Neue Layer-Helpers _layer_path_raum_gf (61_GF) und _layer_path_raum_agf
  (62_AGF) — eigene Sublayer pro Geschoss, eigene Default-Farben
- _layer_path_for_raum_sia(doc, gname, sia): routet sia=gf → GF-Layer,
  sia=agf → AGF-Layer, sonst RAEUME-Layer (HNF/NNF/VF/FF/leer)
- _regenerate_element_body raum_outline-Branch nutzt das Routing →
  Source-Outline migriert automatisch auf den richtigen Layer
- _update_wall raum-Branch: bei SIA-Wechsel (z.B. HNF → GF) auch
  Layer-Migration
- _make_raum_stamp_text: bei sia=gf/agf default-layout
  override auf [["sia", "area"]] → Stempel zeigt nur "GF 234.5 m²" /
  "AGF 18.0 m²" ohne Nummer/Name/Funktion

Frontend (RaumProperties):
- Conditional isFlaeche = sia in (gf, agf)
- Versteckt bei isFlaeche: Stil-Picker, Nummer-Input, Name-Input,
  Funktion-Input, StempelLayoutBuilder
- Bleibt sichtbar: Geschoss, SIA-Tag-Selector, Fuellung, Rundung,
  Skala-Modus, Flaeche/Umfang-Footer
- Info-Zeile zeigt bei isFlaeche: "Flaechen-Outline (GF) auf eigenem
  Layer · Stempel zeigt nur GF + Flaeche"
This commit is contained in:
2026-05-27 00:24:57 +02:00
parent 2386366566
commit ac7b2f2ee5
2 changed files with 130 additions and 63 deletions
+55 -6
View File
@@ -1079,6 +1079,36 @@ def _layer_path_raum(doc, geschoss_name):
return "{}::{}".format(geschoss_name, sub)
def _layer_path_raum_gf(doc, geschoss_name):
"""Geschossflaeche-Outline — Sublayer 'GF' (Code 61). Separater Layer
weil GF-Outlines die ganze Wand-Topologie umfassen und NICHT zusammen
mit Nutzflaechen-Raeumen visualisiert/exportiert werden sollen."""
sub = _find_ebene_sublayer_name(doc, ["gf", "geschossfl"],
"61", "GF",
default_color="#a0a0a0", default_lw=0.18)
return "{}::{}".format(geschoss_name, sub)
def _layer_path_raum_agf(doc, geschoss_name):
"""Aussengeschossflaeche-Outline (Balkone/Terrassen) — Sublayer 'AGF'
(Code 62). Eigener Layer."""
sub = _find_ebene_sublayer_name(doc, ["agf", "aussen"],
"62", "AGF",
default_color="#90b090", default_lw=0.13)
return "{}::{}".format(geschoss_name, sub)
def _layer_path_for_raum_sia(doc, geschoss_name, sia_tag):
"""Routet auf den richtigen Layer je nach SIA-Klassifikation.
gf 61_GF, agf 62_AGF, alles andere (HNF/NNF/VF/FF/leer) 60_RAEUME.
Damit kann der User pro Geschoss die Geschossflaechen separat sichtbar/
unsichtbar schalten oder als eigenes Layout exportieren."""
s = (sia_tag or "").lower()
if s == "gf": return _layer_path_raum_gf(doc, geschoss_name)
if s == "agf": return _layer_path_raum_agf(doc, geschoss_name)
return _layer_path_raum(doc, geschoss_name)
def _layer_path_symbole(doc, geschoss_name, variant):
"""Symbol-Layer (Library-Items). variant = '2d' oder '3d'.
Pfad: <geschoss>::40_SYMBOLE::SYMBOLE_2D bzw. SYMBOLE_3D.
@@ -4907,8 +4937,16 @@ def _make_raum_stamp_text(centroid, name, nummer, funktion, area, rundung,
return tag if (tag and tag != "") else None
return None
# Layout resolven: explizit gesetzt > show_*-Flags
if layout and isinstance(layout, list) and any(layout):
# Layout resolven: explizit gesetzt > show_*-Flags > GF/AGF-Default
# Sonderfall GF/AGF: das sind reine Flaechen-Outlines (Geschoss-
# flaeche / Aussengeschossflaeche) — kein Raum-Inhalt. Stempel
# zeigt nur 'GF 234.5 m²' bzw. 'AGF 18.0 m²' — keine Nummer/
# Name/Funktion.
_is_flaeche = (sia_code or "").lower() in ("gf", "agf")
if _is_flaeche and not (layout and isinstance(layout, list)
and any(layout)):
rows = [["sia", "area"]]
elif layout and isinstance(layout, list) and any(layout):
rows = layout
else:
# Aus show_*-Flags ein implizites Layout bauen
@@ -6328,9 +6366,15 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
# Raum: Source = geschlossene Outline; Volumes = TextEntity (Stempel)
# + optional Brep-Fuellung (SIA-Modus). Geht NICHT durch den
# Brep-Replace-Pfad — wird direkt hier emittiert.
layer = _ensure_layer(doc, _layer_path_raum(doc, geschoss_name))
# Layer-Routing nach SIA-Klassifikation: gf → 61_GF, agf → 62_AGF,
# sonst 60_RAEUME (HNF/NNF/VF/FF/leer). So sind GF-Outlines auf
# eigenem Layer einzeln aus-/einblendbar.
_raum_sia = (meta.get("raum_sia") or "").lower()
layer = _ensure_layer(doc,
_layer_path_for_raum_sia(doc, geschoss_name, _raum_sia))
src_layer = layer
# Source-Outline ggf. auf richtigen Layer migrieren
# Source-Outline ggf. auf richtigen Layer migrieren (z.B. nach
# SIA-Tag-Wechsel HNF → GF)
try:
if src_layer >= 0 and src_obj.Attributes.LayerIndex != src_layer:
new_attrs = src_obj.Attributes.Duplicate()
@@ -10659,10 +10703,15 @@ class ElementeBridge(panel_base.BaseBridge):
old_modus, r_modus, scale, r_th))
gstart = p.get("geschoss", old_meta["geschoss"])
attrs = axis_obj.Attributes
if gstart != old_meta["geschoss"]:
# Layer-Routing: bei Geschoss-Wechsel ODER SIA-Wechsel (gf/agf
# haben eigene Sublayer) den richtigen Layer setzen.
old_sia = (old_meta.get("raum_sia") or "").lower()
new_sia = (r_sia or "").lower()
if gstart != old_meta["geschoss"] or new_sia != old_sia:
gs = _geschoss_by_id(doc, gstart)
gn = gs.get("name", "EG") if gs else "EG"
attrs.LayerIndex = _ensure_layer(doc, _layer_path_raum(doc, gn))
attrs.LayerIndex = _ensure_layer(doc,
_layer_path_for_raum_sia(doc, gn, new_sia))
_attach_meta(attrs, wall_id, "raum_outline",
gstart, 0.0, "", "", "mid",
raum_name=r_name,