From fa40429e637f67a374fa65d6b7b9182a8773c932 Mon Sep 17 00:00:00 2001 From: Karim Date: Thu, 9 Jul 2026 01:03:05 +0200 Subject: [PATCH] =?UTF-8?q?T=C3=BCr/Fenster-Typmodell=20vertiefen:=20Rahme?= =?UTF-8?q?nart,=20Rahmenbreite,=20Schichteinzug,=20Oberlicht,=20K=C3=A4mp?= =?UTF-8?q?fer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Erweitert DoorType/WindowType um architektonische Detailparameter und verdrahtet sie im ResourceManager-Typeditor (Tür-/Fenstertypen): - Tür: frameKind (Zarge/Blockrahmen), frameWidth (Ansichtsbreite Profil), insetFromFace + insetFace (Schichteinzug ab Aussen-/Innenfläche), transomHeight (Oberlicht über Kämpfer). - Fenster: mullionRows (horizontale Kämpfer-Teilung), frameWidth, insetFromFace + insetFace, transomHeight. - Seeds: Haustür „Blockrahmen (Oberlicht)" + Fenster „2-flügl. + Oberlicht" demonstrieren Einzug/Oberlicht/Kämpfer. - i18n de/en für alle neuen Felder + Optionen. Reine Modell-/UI-Schicht; 2D- und 3D-Darstellung folgen separat. Alle Felder optional → rückwärtskompatibel. tsc sauber. --- src/i18n/de.ts | 10 ++++ src/i18n/en.ts | 10 ++++ src/model/sampleProject.ts | 38 +++++++++++++++ src/model/types.ts | 55 ++++++++++++++++++++++ src/ui/ResourceManager.tsx | 94 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 207 insertions(+) diff --git a/src/i18n/de.ts b/src/i18n/de.ts index e6ef921..f61c650 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -632,6 +632,16 @@ export const de = { "resources.field.defaultWidth": "Lichtbreite (m)", "resources.field.defaultHeight": "Lichthöhe (m)", "resources.field.threshold": "Bodenschwelle", + "resources.field.frameKind": "Rahmenart", + "resources.field.frameWidth": "Rahmenbreite (m)", + "resources.field.insetFromFace": "Schichteinzug (m)", + "resources.field.insetFace": "Einzug ab Fläche", + "resources.field.transomHeight": "Oberlicht-Höhe (m)", + "resources.field.mullionRows": "Kämpfer-Zeilen", + "resources.frameKind.zarge": "Zarge", + "resources.frameKind.blockrahmen": "Blockrahmen", + "resources.insetFace.aussen": "Aussen", + "resources.insetFace.innen": "Innen", "resources.field.windowKind": "Öffnungsart", "resources.field.wingCount": "Flügelzahl", "resources.field.glazing": "Verglasung", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index d1eeaff..e78f71c 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -627,6 +627,16 @@ export const en: Record = { "resources.field.defaultWidth": "Clear width (m)", "resources.field.defaultHeight": "Clear height (m)", "resources.field.threshold": "Threshold", + "resources.field.frameKind": "Frame type", + "resources.field.frameWidth": "Frame width (m)", + "resources.field.insetFromFace": "Layer inset (m)", + "resources.field.insetFace": "Inset from face", + "resources.field.transomHeight": "Transom height (m)", + "resources.field.mullionRows": "Transom rows", + "resources.frameKind.zarge": "Lining", + "resources.frameKind.blockrahmen": "Block frame", + "resources.insetFace.aussen": "Outer", + "resources.insetFace.innen": "Inner", "resources.field.windowKind": "Opening style", "resources.field.wingCount": "Wing count", "resources.field.glazing": "Glazing", diff --git a/src/model/sampleProject.ts b/src/model/sampleProject.ts index d3a5270..2e3b5ba 100644 --- a/src/model/sampleProject.ts +++ b/src/model/sampleProject.ts @@ -212,9 +212,29 @@ export const sampleProject: Project = { leafCount: 1, leafStyle: "glatt", frameThickness: 0.05, + frameKind: "zarge", + frameWidth: 0.06, defaultWidth: 0.9, defaultHeight: 2.0, }, + { + // Aussentür: Blockrahmen, in die Aussenschale zurückgesetzt, mit Oberlicht. + id: "door-entry", + name: "Haustür Blockrahmen (Oberlicht)", + kind: "dreh", + leafCount: 1, + leafStyle: "glas", + glazingRatio: 0.35, + frameThickness: 0.07, + frameKind: "blockrahmen", + frameWidth: 0.09, + insetFromFace: 0.14, + insetFace: "aussen", + transomHeight: 0.45, + threshold: true, + defaultWidth: 1.1, + defaultHeight: 2.1, + }, ], windowTypes: [ { @@ -224,10 +244,28 @@ export const sampleProject: Project = { wingCount: 1, glazing: "zweifach", frameThickness: 0.06, + frameWidth: 0.06, defaultSillHeight: 0.9, defaultWidth: 1.2, defaultHeight: 1.4, }, + { + // Zweiflügliges Fenster mit Kämpfer, Oberlicht und Schichteinzug. + id: "window-double", + name: "Fenster 2-flügl. + Oberlicht", + kind: "drehkipp", + wingCount: 2, + mullionRows: 1, + glazing: "dreifach", + frameThickness: 0.07, + frameWidth: 0.07, + insetFromFace: 0.12, + insetFace: "aussen", + transomHeight: 0.4, + defaultSillHeight: 0.85, + defaultWidth: 1.8, + defaultHeight: 1.9, + }, ], stairTypes: [ { diff --git a/src/model/types.ts b/src/model/types.ts index 8b77a01..156f8c0 100644 --- a/src/model/types.ts +++ b/src/model/types.ts @@ -311,6 +311,36 @@ export interface DoorType { frameThickness: number; /** Rahmen-/Zargen-Tiefe in Wandrichtung (Meter); fehlt ⇒ aus Wanddicke. */ frameDepth?: number; + /** + * Rahmenart: + * • "zarge" — Zarge, umschliesst die Laibung (schmales Profil, in der + * Wandlaibung sitzend — der Standard im Innenausbau). + * • "blockrahmen" — Blockrahmen, sitzt als kräftiges Rechteckprofil VOR/auf + * der Laibung (typisch bei Aussentüren/älterem Bestand). + * Fehlt es, gilt "zarge". + */ + frameKind?: "zarge" | "blockrahmen"; + /** + * Ansichtsbreite des Rahmenprofils (Meter) — die sichtbare Rahmen-Randbreite + * in der Öffnungsebene (Elevation), NICHT die Tiefe quer zur Wand. Fehlt es, + * gilt ein schmaler Default (~0.06 m). Steuert 2D-Rahmenkontur + 3D-Rahmen. + */ + frameWidth?: number; + /** + * Schichteinzug: Abstand der Rahmen-Vorderkante von einer Wandfläche (Meter). + * 0/fehlt ⇒ bündig. > 0 ⇒ der Rahmen sitzt um diesen Betrag in die Wand + * zurückgesetzt (z. B. hinter die Aussenschale eines mehrschichtigen Aufbaus). + * Gemessen von der über {@link insetFace} gewählten Fläche. + */ + insetFromFace?: number; + /** Von welcher Wandfläche der {@link insetFromFace} gemessen wird; Default "aussen". */ + insetFace?: "aussen" | "innen"; + /** + * Oberlicht: Höhe eines festen, verglasten Oberlichts über dem Türblatt + * (Meter). 0/fehlt ⇒ kein Oberlicht. Das Oberlicht sitzt oberhalb eines + * Kämpfers innerhalb derselben lichten Öffnungshöhe. + */ + transomHeight?: number; /** Default-Lichtbreite (Meter) für neu platzierte Türen dieses Typs. */ defaultWidth: number; /** Default-Lichthöhe (Meter). */ @@ -331,12 +361,37 @@ export interface WindowType { kind: "dreh" | "kipp" | "drehkipp" | "fest" | "schiebe"; /** Anzahl Flügel (1–4) → Mittelpfosten = wingCount − 1 (vgl. `Opening.wingCount`). */ wingCount: number; + /** + * Anzahl horizontaler Felder (Kämpfer-Zeilen): 1 = keine Querteilung, 2 = ein + * Kämpfer, usw. Zusammen mit {@link wingCount} (Spalten) ergibt sich das + * Sprossen-/Flügelraster. Fehlt es, gilt 1. + */ + mullionRows?: number; /** Verglasung: Einfach/Zweifach/Dreifach (3D-Scheibenzahl). */ glazing: "einfach" | "zweifach" | "dreifach"; /** Rahmenstärke quer zur Wand (Meter). */ frameThickness: number; /** Rahmen-Tiefe in Wandrichtung (Meter); fehlt ⇒ aus Wanddicke. */ frameDepth?: number; + /** + * Ansichtsbreite des Rahmen-/Flügelprofils (Meter) — sichtbare Randbreite in + * der Öffnungsebene. Fehlt es, gilt ein schmaler Default (~0.06 m). Steuert die + * 2D-Rahmen-/Sprossenkontur und den 3D-Rahmen. + */ + frameWidth?: number; + /** + * Schichteinzug: Abstand der Rahmen-Vorderkante von einer Wandfläche (Meter). + * 0/fehlt ⇒ bündig; > 0 ⇒ der Rahmen sitzt zurückgesetzt (klassisch hinter der + * Aussenschale eines mehrschichtigen Wandaufbaus). Von {@link insetFace} gemessen. + */ + insetFromFace?: number; + /** Von welcher Wandfläche der {@link insetFromFace} gemessen wird; Default "aussen". */ + insetFace?: "aussen" | "innen"; + /** + * Oberlicht: Höhe eines festen, verglasten Oberlichts über dem Hauptflügel + * (Meter), abgeteilt durch einen Kämpfer. 0/fehlt ⇒ kein Oberlicht. + */ + transomHeight?: number; /** Default-Brüstungshöhe über Wand-UK (Meter). */ defaultSillHeight: number; /** Default-Lichtbreite (Meter). */ diff --git a/src/ui/ResourceManager.tsx b/src/ui/ResourceManager.tsx index 5fcca60..dfb4e46 100644 --- a/src/ui/ResourceManager.tsx +++ b/src/ui/ResourceManager.tsx @@ -1977,6 +1977,18 @@ const DOOR_LEAF_STYLE_OPTIONS: { value: DoorType["leafStyle"]; labelKey: string { value: "glas", labelKey: "resources.leafStyle.glas" }, ]; +const DOOR_FRAME_KIND_OPTIONS: { value: NonNullable; labelKey: string }[] = [ + { value: "zarge", labelKey: "resources.frameKind.zarge" }, + { value: "blockrahmen", labelKey: "resources.frameKind.blockrahmen" }, +]; + +// Bezugsfläche des Schichteinzugs — von aussen oder innen gemessen. Für Tür UND +// Fenster genutzt (gleiche Semantik). +const INSET_FACE_OPTIONS: { value: "aussen" | "innen"; labelKey: string }[] = [ + { value: "aussen", labelKey: "resources.insetFace.aussen" }, + { value: "innen", labelKey: "resources.insetFace.innen" }, +]; + const WINDOW_KIND_OPTIONS: { value: WindowType["kind"]; labelKey: string }[] = [ { value: "dreh", labelKey: "resources.windowKind.dreh" }, { value: "kipp", labelKey: "resources.windowKind.kipp" }, @@ -2172,6 +2184,46 @@ function DoorStylesTab({ min={0} /> + + patch({ frameKind })} + options={DOOR_FRAME_KIND_OPTIONS.map((o) => ({ value: o.value, label: t(o.labelKey) }))} + /> + + + patch({ frameWidth })} + step={0.005} + min={0} + /> + + + patch({ insetFromFace })} + step={0.01} + min={0} + /> + + {(door.insetFromFace ?? 0) > 0 && ( + + patch({ insetFace })} + options={INSET_FACE_OPTIONS.map((o) => ({ value: o.value, label: t(o.labelKey) }))} + /> + + )} + + patch({ transomHeight })} + step={0.01} + min={0} + /> + + + patch({ mullionRows: Math.max(1, Math.round(mullionRows)) })} + step={1} + min={1} + max={4} + /> + + + patch({ frameWidth })} + step={0.005} + min={0} + /> + + + patch({ insetFromFace })} + step={0.01} + min={0} + /> + + {(win.insetFromFace ?? 0) > 0 && ( + + patch({ insetFace })} + options={INSET_FACE_OPTIONS.map((o) => ({ value: o.value, label: t(o.labelKey) }))} + /> + + )} + + patch({ transomHeight })} + step={0.01} + min={0} + /> +