Material/Ebene-Separation: Hatch raus aus Material (Refactor a)
Material ist jetzt rein 3D — Section-Hatch (2D-Schnitt) wird nicht mehr
am Material definiert, sondern am Layer (via Rhino-Layer-Dialog oder
spaeter via Ebenen-Settings + neuer Hatch-Tab im Project-Settings).
Schema-Aenderungen:
- _normalize_material: hatch + scale entfernt
- _MATERIAL_LIBRARY (elemente.py): hatch + scale aus allen Builtin-Mats
- _get_all_materials: ohne hatch
- _send_state materials payload: nur {name, color}
- Library import_material: PBR + Texturen werden weitergegeben
Backend:
- _ensure_material_sublayer: erstellt Sublayer mit Color, RESETTET aber
alten SectionHatchIndex auf -1 (= "kein eigener Hatch") damit
Inheritance/User-Override greift. Vorher wurden alte Material-Hatch-
Werte da haengen geblieben.
Frontend:
- MaterialDetail: Schraffur-Section entfernt
- hatchPatterns-Prop entfernt
Konsequenz: existierende Waende verlieren ihren Section-Hatch beim
naechsten Regen. User definiert Section-Hatches jetzt auf Layer-Ebene.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+26
-20
@@ -2195,26 +2195,25 @@ def _make_volume_geometry(axis_curve, dicke, uk, ok, referenz="mid",
|
|||||||
# konfiguriert — sobald der User eine Clipping Plane setzt, zeigt Rhino
|
# konfiguriert — sobald der User eine Clipping Plane setzt, zeigt Rhino
|
||||||
# automatisch die korrekte Schnitt-Symbolik pro Schicht.
|
# automatisch die korrekte Schnitt-Symbolik pro Schicht.
|
||||||
_MATERIAL_LIBRARY = {
|
_MATERIAL_LIBRARY = {
|
||||||
"Beton": {"color": "#9a9a9a", "hatch": "Hatch3", "scale": 1.0},
|
"Beton": {"color": "#9a9a9a"},
|
||||||
"Stahlbeton": {"color": "#888888", "hatch": "Hatch3", "scale": 0.5},
|
"Stahlbeton": {"color": "#888888"},
|
||||||
"Mauerwerk": {"color": "#b67860", "hatch": "Hatch1", "scale": 1.0},
|
"Mauerwerk": {"color": "#b67860"},
|
||||||
"Dämmung": {"color": "#f4e4a0", "hatch": "Hatch2", "scale": 0.5},
|
"Dämmung": {"color": "#f4e4a0"},
|
||||||
"Holz": {"color": "#c89a5a", "hatch": "HatchDash", "scale": 1.0},
|
"Holz": {"color": "#c89a5a"},
|
||||||
"Stahl": {"color": "#7a7a7a", "hatch": "Solid", "scale": 1.0},
|
"Stahl": {"color": "#7a7a7a"},
|
||||||
"Putz": {"color": "#ede4d6", "hatch": "Solid", "scale": 1.0},
|
"Putz": {"color": "#ede4d6"},
|
||||||
"Glas": {"color": "#bcd4e0", "hatch": "Solid", "scale": 1.0},
|
"Glas": {"color": "#bcd4e0", "transparency": 0.7, "iorN": 1.5},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _get_all_materials(doc):
|
def _get_all_materials(doc):
|
||||||
"""Builtin _MATERIAL_LIBRARY + Projekt-Settings-Materialien gemerged.
|
"""Builtin _MATERIAL_LIBRARY + Projekt-Settings-Materialien gemerged.
|
||||||
Returns dict[name] -> full material dict (color/hatch/scale + PBR +
|
Returns dict[name] -> full material dict (color + PBR + textures +
|
||||||
textures + uvScaleM). Projekt-Settings ueberschreibt builtin bei
|
uvScaleM). Section-Hatch (2D-Schnitt) ist NICHT mehr im Material —
|
||||||
Namensgleichheit. Builtin-Materialien bekommen leere PBR-Defaults."""
|
der wird per Ebenen-Settings am Layer konfiguriert."""
|
||||||
merged = {}
|
merged = {}
|
||||||
for n, m in _MATERIAL_LIBRARY.items():
|
for n, m in _MATERIAL_LIBRARY.items():
|
||||||
merged[n] = dict(m)
|
merged[n] = dict(m)
|
||||||
# Builtin: PBR-Defaults wenn nicht gesetzt
|
|
||||||
merged[n].setdefault("roughness", 0.7)
|
merged[n].setdefault("roughness", 0.7)
|
||||||
merged[n].setdefault("reflection", 0.1)
|
merged[n].setdefault("reflection", 0.1)
|
||||||
merged[n].setdefault("transparency", 0.0)
|
merged[n].setdefault("transparency", 0.0)
|
||||||
@@ -2228,8 +2227,6 @@ def _get_all_materials(doc):
|
|||||||
for m in ps.get("materials", []):
|
for m in ps.get("materials", []):
|
||||||
n = m.get("name")
|
n = m.get("name")
|
||||||
if not n: continue
|
if not n: continue
|
||||||
# Komplettes dict uebernehmen — _normalize_material hat
|
|
||||||
# bereits alle Felder validiert.
|
|
||||||
merged[n] = dict(m)
|
merged[n] = dict(m)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] _get_all_materials:", ex)
|
print("[ELEMENTE] _get_all_materials:", ex)
|
||||||
@@ -2268,7 +2265,11 @@ def _set_layer_section_hatch(doc, layer_idx, hatch_name, scale=1.0,
|
|||||||
|
|
||||||
def _ensure_material_sublayer(doc, geschoss_name, material_name):
|
def _ensure_material_sublayer(doc, geschoss_name, material_name):
|
||||||
"""Stellt sicher dass `<geschoss>::20_WAENDE::<material>` existiert,
|
"""Stellt sicher dass `<geschoss>::20_WAENDE::<material>` existiert,
|
||||||
mit Material-Farbe + Section-Hatch konfiguriert. Liefert Layer-Index.
|
mit Material-Farbe konfiguriert. Liefert Layer-Index.
|
||||||
|
Section-Hatch (2D-Schnitt) wird NICHT mehr aus dem Material gesetzt —
|
||||||
|
der kommt vom Parent-Layer (= Ebenen-Settings). Sub-Layer haben default
|
||||||
|
keine eigene Section-Hatch, sie inherit'en Rhino-typisch ihre Optik
|
||||||
|
fuer den Schnitt vom Parent.
|
||||||
Bei leerem oder unbekanntem Material: Fallback auf das normale
|
Bei leerem oder unbekanntem Material: Fallback auf das normale
|
||||||
Wand-Volume-Layer (= Standard fuer Solid-Waende)."""
|
Wand-Volume-Layer (= Standard fuer Solid-Waende)."""
|
||||||
all_mats = _get_all_materials(doc)
|
all_mats = _get_all_materials(doc)
|
||||||
@@ -2285,14 +2286,20 @@ def _ensure_material_sublayer(doc, geschoss_name, material_name):
|
|||||||
hex_str = mat["color"].lstrip("#")
|
hex_str = mat["color"].lstrip("#")
|
||||||
r = int(hex_str[0:2], 16); g = int(hex_str[2:4], 16); b = int(hex_str[4:6], 16)
|
r = int(hex_str[0:2], 16); g = int(hex_str[2:4], 16); b = int(hex_str[4:6], 16)
|
||||||
new_col = SD.Color.FromArgb(255, r, g, b)
|
new_col = SD.Color.FromArgb(255, r, g, b)
|
||||||
# Nur aendern wenn die Farbe abweicht (vermeidet unnoetige Doc-Dirty)
|
|
||||||
try:
|
try:
|
||||||
if int(layer.Color.ToArgb()) != int(new_col.ToArgb()):
|
if int(layer.Color.ToArgb()) != int(new_col.ToArgb()):
|
||||||
layer.Color = new_col
|
layer.Color = new_col
|
||||||
doc.Layers.Modify(layer, idx, True)
|
doc.Layers.Modify(layer, idx, True)
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
_set_layer_section_hatch(doc, idx, mat["hatch"],
|
# Section-Hatch: legacy-fields hatch/scale am Sub-Layer LOESCHEN
|
||||||
mat.get("scale", 1.0))
|
# damit alte Setzungen weg sind. Index=-1 = "kein eigener Hatch,
|
||||||
|
# User kann in Rhino-Layer-Dialog oder Ebenen-Settings setzen".
|
||||||
|
try:
|
||||||
|
if hasattr(layer, "SectionHatchIndex"):
|
||||||
|
if layer.SectionHatchIndex >= 0:
|
||||||
|
layer.SectionHatchIndex = -1
|
||||||
|
doc.Layers.Modify(layer, idx, True)
|
||||||
|
except Exception: pass
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] _ensure_material_sublayer:", ex)
|
print("[ELEMENTE] _ensure_material_sublayer:", ex)
|
||||||
return idx
|
return idx
|
||||||
@@ -6295,8 +6302,7 @@ class ElementeBridge(panel_base.BaseBridge):
|
|||||||
"siaFillMode": _sia_fill_enabled(doc),
|
"siaFillMode": _sia_fill_enabled(doc),
|
||||||
"hatchPatterns": _list_hatch_patterns(doc),
|
"hatchPatterns": _list_hatch_patterns(doc),
|
||||||
"materials": [
|
"materials": [
|
||||||
{"name": n, "color": m["color"],
|
{"name": n, "color": m["color"]}
|
||||||
"hatch": m.get("hatch", ""), "scale": m.get("scale", 1.0)}
|
|
||||||
for n, m in _get_all_materials(doc).items()],
|
for n, m in _get_all_materials(doc).items()],
|
||||||
"oeffStyles": list_oeff_styles(doc),
|
"oeffStyles": list_oeff_styles(doc),
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -82,28 +82,28 @@ def _write_seed_manifest(path):
|
|||||||
"type": "material", "version": 1,
|
"type": "material", "version": 1,
|
||||||
"name": "Beton — Sichtbeton",
|
"name": "Beton — Sichtbeton",
|
||||||
"tags": ["beton", "tragwerk", "roh"],
|
"tags": ["beton", "tragwerk", "roh"],
|
||||||
"data": {"color": "#a8a39b", "hatch": "Solid", "scale": 1.0},
|
"data": {"color": "#a8a39b"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mat-mauerwerk-backstein-v1",
|
"id": "mat-mauerwerk-backstein-v1",
|
||||||
"type": "material", "version": 1,
|
"type": "material", "version": 1,
|
||||||
"name": "Mauerwerk — Backstein",
|
"name": "Mauerwerk — Backstein",
|
||||||
"tags": ["mauerwerk", "stein"],
|
"tags": ["mauerwerk", "stein"],
|
||||||
"data": {"color": "#a45a3c", "hatch": "Solid", "scale": 1.0},
|
"data": {"color": "#a45a3c"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mat-daemmung-mineralwolle-v1",
|
"id": "mat-daemmung-mineralwolle-v1",
|
||||||
"type": "material", "version": 1,
|
"type": "material", "version": 1,
|
||||||
"name": "Daemmung — Mineralwolle",
|
"name": "Daemmung — Mineralwolle",
|
||||||
"tags": ["daemmung", "weich"],
|
"tags": ["daemmung", "weich"],
|
||||||
"data": {"color": "#e8d36b", "hatch": "Solid", "scale": 1.0},
|
"data": {"color": "#e8d36b"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mat-holz-fichte-v1",
|
"id": "mat-holz-fichte-v1",
|
||||||
"type": "material", "version": 1,
|
"type": "material", "version": 1,
|
||||||
"name": "Holz — Fichte",
|
"name": "Holz — Fichte",
|
||||||
"tags": ["holz", "ausbau"],
|
"tags": ["holz", "ausbau"],
|
||||||
"data": {"color": "#c8a06a", "hatch": "Solid", "scale": 1.0},
|
"data": {"color": "#c8a06a"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sym-nordpfeil-01",
|
"id": "sym-nordpfeil-01",
|
||||||
@@ -192,11 +192,13 @@ def import_material(doc, item):
|
|||||||
new_mat = {
|
new_mat = {
|
||||||
"name": item.get("name") or "Unbenannt",
|
"name": item.get("name") or "Unbenannt",
|
||||||
"color": data.get("color", "#888888"),
|
"color": data.get("color", "#888888"),
|
||||||
"hatch": data.get("hatch", "Solid"),
|
|
||||||
"scale": float(data.get("scale", 1.0) or 1.0),
|
|
||||||
"source": "library",
|
"source": "library",
|
||||||
"libraryId": item.get("id"),
|
"libraryId": item.get("id"),
|
||||||
}
|
}
|
||||||
|
# PBR + Textur-Felder, falls Library-Item welche hat
|
||||||
|
for k in ("roughness", "reflection", "transparency", "iorN",
|
||||||
|
"uvScaleM", "textures"):
|
||||||
|
if k in data: new_mat[k] = data[k]
|
||||||
# Lazy-Import um Zyklen zu vermeiden
|
# Lazy-Import um Zyklen zu vermeiden
|
||||||
import rhinopanel
|
import rhinopanel
|
||||||
settings = rhinopanel.load_project_settings(doc)
|
settings = rhinopanel.load_project_settings(doc)
|
||||||
|
|||||||
+6
-10
@@ -141,11 +141,13 @@ _PROJECT_SETTINGS_DEFAULTS = {
|
|||||||
|
|
||||||
|
|
||||||
def _normalize_material(m):
|
def _normalize_material(m):
|
||||||
"""Garantiert Material-Schema. Felder:
|
"""Garantiert Material-Schema. Material ist REIN 3D — Section-Hatch
|
||||||
|
(2D-Schnitt) wird via Ebenen-Settings am Layer konfiguriert.
|
||||||
|
Felder:
|
||||||
- Identitaet: name, source ('local'|'library'|'builtin'), libraryId
|
- Identitaet: name, source ('local'|'library'|'builtin'), libraryId
|
||||||
- Section-Hatch (2D): color, hatch, scale
|
- 3D-Farbe: color
|
||||||
- PBR (3D-Render): roughness (0..1), reflection (0..1),
|
- PBR (3D-Render): roughness, reflection, transparency (0..1),
|
||||||
transparency (0..1), iorN (1.0..2.5)
|
iorN (1.0..2.5)
|
||||||
- UV: uvScaleM (= 1 Welt-Meter ≙ wieviel der Textur)
|
- UV: uvScaleM (= 1 Welt-Meter ≙ wieviel der Textur)
|
||||||
- Texturen: textures = { diffuse, bump, roughness, transparency }
|
- Texturen: textures = { diffuse, bump, roughness, transparency }
|
||||||
pro Slot {path: absolute string} oder null. Strength fuer Bump."""
|
pro Slot {path: absolute string} oder null. Strength fuer Bump."""
|
||||||
@@ -158,7 +160,6 @@ def _normalize_material(m):
|
|||||||
p = t.get("path")
|
p = t.get("path")
|
||||||
if not p: return None
|
if not p: return None
|
||||||
out = {"path": str(p)}
|
out = {"path": str(p)}
|
||||||
# Bump hat zusaetzlich strength (-1..1, default 0.5)
|
|
||||||
if slot == "bump":
|
if slot == "bump":
|
||||||
try: out["strength"] = float(t.get("strength", 0.5))
|
try: out["strength"] = float(t.get("strength", 0.5))
|
||||||
except Exception: out["strength"] = 0.5
|
except Exception: out["strength"] = 0.5
|
||||||
@@ -166,18 +167,13 @@ def _normalize_material(m):
|
|||||||
return {
|
return {
|
||||||
"name": m.get("name") or "Unbenannt",
|
"name": m.get("name") or "Unbenannt",
|
||||||
"color": m.get("color") or "#888888",
|
"color": m.get("color") or "#888888",
|
||||||
"hatch": m.get("hatch") or "Solid",
|
|
||||||
"scale": float(m.get("scale", 1.0) or 1.0),
|
|
||||||
"source": m.get("source") or "local",
|
"source": m.get("source") or "local",
|
||||||
"libraryId": m.get("libraryId"),
|
"libraryId": m.get("libraryId"),
|
||||||
# PBR (3D-Render) — alle 0..1 ausser iorN
|
|
||||||
"roughness": _clamp01(m.get("roughness", 0.7)),
|
"roughness": _clamp01(m.get("roughness", 0.7)),
|
||||||
"reflection": _clamp01(m.get("reflection", 0.1)),
|
"reflection": _clamp01(m.get("reflection", 0.1)),
|
||||||
"transparency": _clamp01(m.get("transparency", 0.0)),
|
"transparency": _clamp01(m.get("transparency", 0.0)),
|
||||||
"iorN": _clamp(m.get("iorN", 1.0), 1.0, 2.5),
|
"iorN": _clamp(m.get("iorN", 1.0), 1.0, 2.5),
|
||||||
# UV-Skalierung (1 m = uvScaleM Textur-Tiles)
|
|
||||||
"uvScaleM": float(m.get("uvScaleM", 1.0) or 1.0),
|
"uvScaleM": float(m.get("uvScaleM", 1.0) or 1.0),
|
||||||
# Texturen
|
|
||||||
"textures": {
|
"textures": {
|
||||||
"diffuse": _tex("diffuse"),
|
"diffuse": _tex("diffuse"),
|
||||||
"bump": _tex("bump"),
|
"bump": _tex("bump"),
|
||||||
|
|||||||
@@ -208,8 +208,9 @@ function DetailSection({ title, children }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* MaterialDetail — rechte Seite (ArchiCAD-Stil): editiert das aktuell
|
/* MaterialDetail — rechte Seite (ArchiCAD-Stil): editiert das aktuell
|
||||||
ausgewaehlte Material. Builtin: Name read-only. */
|
ausgewaehlte Material. Material = REIN 3D (Color + PBR + Texturen).
|
||||||
function MaterialDetail({ mat, isBuiltin, hatchPatterns, onChange, onDelete }) {
|
Section-Hatch (2D-Schnitt) wird via Ebenen-Settings am Layer gesetzt. */
|
||||||
|
function MaterialDetail({ mat, isBuiltin, onChange, onDelete }) {
|
||||||
if (!mat) {
|
if (!mat) {
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: 40, textAlign: 'center',
|
<div style={{ padding: 40, textAlign: 'center',
|
||||||
@@ -271,27 +272,6 @@ function MaterialDetail({ mat, isBuiltin, hatchPatterns, onChange, onDelete }) {
|
|||||||
</div>
|
</div>
|
||||||
</DetailSection>
|
</DetailSection>
|
||||||
|
|
||||||
<DetailSection title="Schraffur (2D-Schnitt)">
|
|
||||||
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
||||||
<select value={mat.hatch || 'Solid'}
|
|
||||||
onChange={(ev) => onChange({ ...mat, hatch: ev.target.value })}
|
|
||||||
style={{ flex: 1, height: BAR_H, fontSize: 11 }}>
|
|
||||||
{(hatchPatterns || []).map(h => (
|
|
||||||
<option key={h} value={h}>{h}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<input type="number" step="0.1" min="0.1"
|
|
||||||
value={mat.scale ?? 1.0}
|
|
||||||
onChange={(ev) => onChange({ ...mat, scale: parseFloat(ev.target.value) || 1.0 })}
|
|
||||||
title="Skalierung"
|
|
||||||
style={{ width: 70, height: BAR_H, padding: '0 10px',
|
|
||||||
fontSize: 11, textAlign: 'right' }} />
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 9, color: 'var(--text-muted)', marginTop: 4 }}>
|
|
||||||
Hatch-Pattern + Skalierung fuer die Sektion-Ansicht (Clipping Plane).
|
|
||||||
</div>
|
|
||||||
</DetailSection>
|
|
||||||
|
|
||||||
<DetailSection title="Texturen (3D-Render)">
|
<DetailSection title="Texturen (3D-Render)">
|
||||||
<TextureSlot label="Diffuse" slot="diffuse"
|
<TextureSlot label="Diffuse" slot="diffuse"
|
||||||
tex={mat.textures?.diffuse}
|
tex={mat.textures?.diffuse}
|
||||||
@@ -364,7 +344,6 @@ export default function ProjectSettingsDialog({
|
|||||||
})
|
})
|
||||||
const [matSearch, setMatSearch] = useState('')
|
const [matSearch, setMatSearch] = useState('')
|
||||||
const builtin = initial.builtinMaterials || []
|
const builtin = initial.builtinMaterials || []
|
||||||
const hatchPatterns = initial.hatchPatterns || ['Solid']
|
|
||||||
|
|
||||||
// Aktuell ausgewaehltes Material aus Selection ableiten
|
// Aktuell ausgewaehltes Material aus Selection ableiten
|
||||||
const selectedMat = (() => {
|
const selectedMat = (() => {
|
||||||
@@ -572,7 +551,6 @@ export default function ProjectSettingsDialog({
|
|||||||
<MaterialDetail
|
<MaterialDetail
|
||||||
mat={selectedMat}
|
mat={selectedMat}
|
||||||
isBuiltin={selectedIsBuiltin}
|
isBuiltin={selectedIsBuiltin}
|
||||||
hatchPatterns={hatchPatterns}
|
|
||||||
onChange={updateSelected}
|
onChange={updateSelected}
|
||||||
onDelete={selMat?.kind === 'local' ? deleteSelected : null} />
|
onDelete={selMat?.kind === 'local' ? deleteSelected : null} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user