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:
+55
-6
@@ -1079,6 +1079,36 @@ def _layer_path_raum(doc, geschoss_name):
|
|||||||
return "{}::{}".format(geschoss_name, sub)
|
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):
|
def _layer_path_symbole(doc, geschoss_name, variant):
|
||||||
"""Symbol-Layer (Library-Items). variant = '2d' oder '3d'.
|
"""Symbol-Layer (Library-Items). variant = '2d' oder '3d'.
|
||||||
Pfad: <geschoss>::40_SYMBOLE::SYMBOLE_2D bzw. SYMBOLE_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 tag if (tag and tag != "—") else None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Layout resolven: explizit gesetzt > show_*-Flags
|
# Layout resolven: explizit gesetzt > show_*-Flags > GF/AGF-Default
|
||||||
if layout and isinstance(layout, list) and any(layout):
|
# 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
|
rows = layout
|
||||||
else:
|
else:
|
||||||
# Aus show_*-Flags ein implizites Layout bauen
|
# 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)
|
# Raum: Source = geschlossene Outline; Volumes = TextEntity (Stempel)
|
||||||
# + optional Brep-Fuellung (SIA-Modus). Geht NICHT durch den
|
# + optional Brep-Fuellung (SIA-Modus). Geht NICHT durch den
|
||||||
# Brep-Replace-Pfad — wird direkt hier emittiert.
|
# 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
|
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:
|
try:
|
||||||
if src_layer >= 0 and src_obj.Attributes.LayerIndex != src_layer:
|
if src_layer >= 0 and src_obj.Attributes.LayerIndex != src_layer:
|
||||||
new_attrs = src_obj.Attributes.Duplicate()
|
new_attrs = src_obj.Attributes.Duplicate()
|
||||||
@@ -10659,10 +10703,15 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
old_modus, r_modus, scale, r_th))
|
old_modus, r_modus, scale, r_th))
|
||||||
gstart = p.get("geschoss", old_meta["geschoss"])
|
gstart = p.get("geschoss", old_meta["geschoss"])
|
||||||
attrs = axis_obj.Attributes
|
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)
|
gs = _geschoss_by_id(doc, gstart)
|
||||||
gn = gs.get("name", "EG") if gs else "EG"
|
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",
|
_attach_meta(attrs, wall_id, "raum_outline",
|
||||||
gstart, 0.0, "", "", "mid",
|
gstart, 0.0, "", "", "mid",
|
||||||
raum_name=r_name,
|
raum_name=r_name,
|
||||||
|
|||||||
+75
-57
@@ -937,6 +937,10 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
|||||||
// Texthoehe (raum.txtH) + Ausrichtung (raum.align) werden via Oberleiste
|
// Texthoehe (raum.txtH) + Ausrichtung (raum.align) werden via Oberleiste
|
||||||
// gesetzt — kein Local-State noetig, Stil-Speichern liest direkt aus raum.
|
// gesetzt — kein Local-State noetig, Stil-Speichern liest direkt aus raum.
|
||||||
const txtModus = raum.txtModus || 'fix'
|
const txtModus = raum.txtModus || 'fix'
|
||||||
|
// Sonderfall: GF/AGF sind reine Flaechen-Outlines (keine Raum-Inhalte
|
||||||
|
// wie Name/Nummer/Funktion). UI wird darunter minimiert auf SIA-Tag +
|
||||||
|
// Skala + Fluessigkeits-Anzeige.
|
||||||
|
const isFlaeche = (raum.sia === 'gf' || raum.sia === 'agf')
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setName(raum.name || 'Raum')
|
setName(raum.name || 'Raum')
|
||||||
setNummer(raum.nummer || '')
|
setNummer(raum.nummer || '')
|
||||||
@@ -988,21 +992,23 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
|||||||
{/* Stempel-Stil Preset-Picker — apply einer gespeicherten Visual-Vorlage
|
{/* Stempel-Stil Preset-Picker — apply einer gespeicherten Visual-Vorlage
|
||||||
auf den Raum. "+ Aktuell speichern" frischt die Liste mit den
|
auf den Raum. "+ Aktuell speichern" frischt die Liste mit den
|
||||||
jetzigen Settings als neuen Stil. Stile sind per-Doc. */}
|
jetzigen Settings als neuen Stil. Stile sind per-Doc. */}
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
{!isFlaeche && (
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Stil</span>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<select value={activeStilId}
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Stil</span>
|
||||||
onChange={(e) => handleStilChange(e.target.value)}
|
<select value={activeStilId}
|
||||||
style={{ flex: 1, fontSize: 11 }}
|
onChange={(e) => handleStilChange(e.target.value)}
|
||||||
title="Gespeicherter Stempel-Stil — Klick wendet die Vorlage an">
|
style={{ flex: 1, fontSize: 11 }}
|
||||||
<option value="">— Stil wählen —</option>
|
title="Gespeicherter Stempel-Stil — Klick wendet die Vorlage an">
|
||||||
{stilList.map(s => (
|
<option value="">— Stil wählen —</option>
|
||||||
<option key={s.id} value={s.id}>{s.name}</option>
|
{stilList.map(s => (
|
||||||
))}
|
<option key={s.id} value={s.id}>{s.name}</option>
|
||||||
<option disabled>──────────</option>
|
))}
|
||||||
<option value="__save__">+ Aktuelle Settings als Stil speichern…</option>
|
<option disabled>──────────</option>
|
||||||
{activeStilId && <option value="__delete__">🗑 Aktiven Stil löschen</option>}
|
<option value="__save__">+ Aktuelle Settings als Stil speichern…</option>
|
||||||
</select>
|
{activeStilId && <option value="__delete__">🗑 Aktiven Stil löschen</option>}
|
||||||
</div>
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Geschoss</span>
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Geschoss</span>
|
||||||
@@ -1013,30 +1019,35 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
{/* Nummer + Name nur bei normalen Raeumen (nicht GF/AGF-Flaechen) */}
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Nummer</span>
|
{!isFlaeche && (
|
||||||
<input type="text" value={nummer}
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
onChange={(e) => setNummer(e.target.value)}
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Nummer</span>
|
||||||
onBlur={() => {
|
<input type="text" value={nummer}
|
||||||
if (nummer !== (raum.nummer || '')) onUpdate({ nummer })
|
onChange={(e) => setNummer(e.target.value)}
|
||||||
}}
|
onBlur={() => {
|
||||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
if (nummer !== (raum.nummer || '')) onUpdate({ nummer })
|
||||||
style={{ flex: 1, fontSize: 11,
|
}}
|
||||||
fontFamily: 'DM Mono, monospace' }} />
|
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||||
</div>
|
style={{ flex: 1, fontSize: 11,
|
||||||
|
fontFamily: 'DM Mono, monospace' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
{!isFlaeche && (
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Name</span>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<input type="text" value={name}
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Name</span>
|
||||||
onChange={(e) => setName(e.target.value)}
|
<input type="text" value={name}
|
||||||
onBlur={() => {
|
onChange={(e) => setName(e.target.value)}
|
||||||
const v = (name || 'Raum').trim()
|
onBlur={() => {
|
||||||
if (v !== raum.name) onUpdate({ name: v })
|
const v = (name || 'Raum').trim()
|
||||||
else setName(v)
|
if (v !== raum.name) onUpdate({ name: v })
|
||||||
}}
|
else setName(v)
|
||||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
}}
|
||||||
style={{ flex: 1, fontSize: 11 }} />
|
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||||
</div>
|
style={{ flex: 1, fontSize: 11 }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Typ</span>
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Typ</span>
|
||||||
@@ -1100,27 +1111,33 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
{!isFlaeche && (
|
||||||
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Funktion</span>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
<input type="text" value={funktion}
|
<span style={{ fontSize: 10, color: 'var(--text-secondary)', width: 60 }}>Funktion</span>
|
||||||
onChange={(e) => setFunktion(e.target.value)}
|
<input type="text" value={funktion}
|
||||||
onBlur={() => {
|
onChange={(e) => setFunktion(e.target.value)}
|
||||||
const v = (funktion || '').trim()
|
onBlur={() => {
|
||||||
if (v !== (raum.funktion || '')) onUpdate({ funktion: v })
|
const v = (funktion || '').trim()
|
||||||
}}
|
if (v !== (raum.funktion || '')) onUpdate({ funktion: v })
|
||||||
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
}}
|
||||||
placeholder="z.B. Wohnen, Bad, Büro …"
|
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur() }}
|
||||||
style={{ flex: 1, fontSize: 11 }} />
|
placeholder="z.B. Wohnen, Bad, Büro …"
|
||||||
</div>
|
style={{ flex: 1, fontSize: 11 }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Stempel-Layout — Drag-and-Drop. Jede Row ist eine Textzeile,
|
{/* Stempel-Layout — Drag-and-Drop. Jede Row ist eine Textzeile,
|
||||||
Felder innerhalb einer Row landen in derselben Zeile. Drag
|
Felder innerhalb einer Row landen in derselben Zeile. Drag
|
||||||
zwischen Rows um umzuordnen. Klick auf Field oben fügt es in
|
zwischen Rows um umzuordnen. Klick auf Field oben fügt es in
|
||||||
eine eigene neue Row hinzu. */}
|
eine eigene neue Row hinzu.
|
||||||
<StempelLayoutBuilder
|
Bei GF/AGF-Flaechen wird das ausgeblendet — Stempel zeigt da
|
||||||
layout={layout}
|
automatisch nur den SIA-Tag + Flaeche (z.B. "GF · 234.5 m²"). */}
|
||||||
availableFields={availableFields}
|
{!isFlaeche && (
|
||||||
onChange={(newLayout) => onUpdate({ layout: newLayout })} />
|
<StempelLayoutBuilder
|
||||||
|
layout={layout}
|
||||||
|
availableFields={availableFields}
|
||||||
|
onChange={(newLayout) => onUpdate({ layout: newLayout })} />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Hinweis: Typografie (Font/Stil/Höhe) wird in der OBERLEISTE
|
{/* Hinweis: Typografie (Font/Stil/Höhe) wird in der OBERLEISTE
|
||||||
gesetzt — den Stempel im Viewport anklicken. Aenderungen werden
|
gesetzt — den Stempel im Viewport anklicken. Aenderungen werden
|
||||||
@@ -1132,8 +1149,9 @@ function RaumProperties({ raum, geschosse, onUpdate, onDelete, hatchPatterns, fo
|
|||||||
display: 'flex', alignItems: 'center', gap: 4,
|
display: 'flex', alignItems: 'center', gap: 4,
|
||||||
}}>
|
}}>
|
||||||
<Icon name="info" size={11} style={{ color: 'var(--text-muted)' }} />
|
<Icon name="info" size={11} style={{ color: 'var(--text-muted)' }} />
|
||||||
Typografie (Font/Stil/Höhe): Stempel im Viewport selektieren →
|
{isFlaeche
|
||||||
Oberleiste.
|
? `Flächen-Outline (${(raum.sia || '').toUpperCase()}) auf eigenem Layer · Stempel zeigt nur ${(raum.sia || '').toUpperCase()} + Fläche`
|
||||||
|
: 'Typografie (Font/Stil/Höhe): Stempel im Viewport selektieren → Oberleiste.'}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
|
|||||||
Reference in New Issue
Block a user