From 1c31e680c188b9d9d016e4cc4d1d24b29a58fcba Mon Sep 17 00:00:00 2001 From: Karim Date: Sat, 11 Jul 2026 12:50:10 +0200 Subject: [PATCH] Fenster-Rahmentiefe: realistischer Default statt volle Wanddicke MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recherche zu Vectorworks/ArchiCAD/realem Fensterbau ergab: ein Rahmen ohne explizite frameDepth füllte bisher die GESAMTE Wanddicke — echte Fenster sind unabhängig von der Wanddicke nur ~70-90mm tief und sitzen mit sichtbarer Laibung/Leibung in der Öffnung, statt als massiver Block über die volle Tiefe. Neuer Default 70mm (2D-Grundriss und 3D konsistent), Glas-Falzmass von 3cm auf 2cm reduziert und Mehrfachverglasungs-Scheibendicke/-abstand kompakter, damit Dreifachverglasung noch in den schlankeren Rahmen passt. --- src/geometry/opening.ts | 13 ++++++++-- src/plan/toWalls3d.ts | 54 ++++++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/geometry/opening.ts b/src/geometry/opening.ts index c2a6a9f..2c7b43e 100644 --- a/src/geometry/opening.ts +++ b/src/geometry/opening.ts @@ -263,7 +263,7 @@ export interface WindowRenderSpec { sashes: SashDef[]; /** Sichtbare Profilbreite (Meter) von Blend-/Flügelrahmen in der Öffnungsebene. */ frameWidth: number; - /** Rahmentiefe quer zur Wand (Meter); fehlt ⇒ volle Wanddicke. */ + /** Rahmentiefe quer zur Wand (Meter); fehlt ⇒ {@link DEFAULT_FRAME_DEPTH}. */ frameDepth?: number; /** Schichteinzug: Abstand der Rahmen-Vorderkante von {@link insetFace} (Meter). */ insetFromFace?: number; @@ -282,6 +282,15 @@ interface SashSegment { /** Default-Pfostenbreite (Meter), wenn ein `kind:"pfosten"`-Eintrag keine `postWidth` angibt. */ const DEFAULT_POST_WIDTH = 0.06; +/** + * Default-Rahmentiefe (Meter), wenn ein Fenster-/Türtyp keine explizite + * `frameDepth` trägt: reale Fensterrahmen sind ~70–90 mm tief (Holz/PVC/Alu), + * unabhängig von der Wanddicke — NICHT die volle Wanddicke (das liess Fenster + * wie einen massiven Block in der Öffnung wirken statt eines schlanken Rahmens + * mit sichtbarer Laibung/Leibung ringsum, s. Recherche zu VW/ArchiCAD-Fenster- + * konstruktion). Deckt sich mit `toWalls3d.ts::DEFAULT_FRAME_DEPTH` (2D=3D). + */ +const DEFAULT_FRAME_DEPTH = 0.07; /** * Löst eine Flügeleinteilung ({@link SashDef}-Liste) zu konkreten Segment- @@ -352,7 +361,7 @@ export function windowSymbol( // Blendrahmen (kein Flügelrahmen) — so unterscheidet sich fest von öffenbar. if (spec && spec.sashes.length > 0) { const fw = Math.max(0.01, spec.frameWidth); - const depth = spec.frameDepth && spec.frameDepth > 1e-9 ? spec.frameDepth : total; + const depth = Math.min(total, spec.frameDepth && spec.frameDepth > 1e-9 ? spec.frameDepth : DEFAULT_FRAME_DEPTH); const inset = Math.max(0, spec.insetFromFace ?? 0); const halfT = total / 2; const sign = (spec.insetFace ?? "aussen") === "innen" ? -1 : 1; diff --git a/src/plan/toWalls3d.ts b/src/plan/toWalls3d.ts index 44ef3e7..c9f2f56 100644 --- a/src/plan/toWalls3d.ts +++ b/src/plan/toWalls3d.ts @@ -390,8 +390,15 @@ const EXTRUSION_RGB: RRgb = [0.85, 0.55, 0.25]; const COLUMN_RGB: RRgb = [0.7, 0.7, 0.72]; /** Helles, leicht bläuliches Fenster-Glas (Scheibe in der Öffnung). */ const GLASS_RGB: RRgb = [0.62, 0.76, 0.85]; -/** Halbe Dicke der Fensterscheibe (Meter) — sehr dünn, mittig in der Wand. */ -const GLASS_HALF_THICK = 0.01; +/** + * Halbe Dicke EINER Fensterscheibe (Meter, Mehrfachverglasungs-Pfad). Kompakt + * gehalten (8 mm Gesamtdicke je Scheibe, wie ein reales Einzelglas), damit + * z. B. eine Dreifachverglasung noch in den realistisch schlanken Default- + * Rahmen ({@link DEFAULT_FRAME_DEPTH}, ~70 mm) passt — bei der früheren + * "volle Wanddicke"-Rahmentiefe gab es dafür reichlich Platz, im schlanken + * Rahmen nicht mehr. + */ +const GLASS_HALF_THICK = 0.004; /** * Helles, warmes Rahmen-Grau (Zarge/Blendrahmen/Kämpfer/Sprossen) — bewusst * deutlich heller als Wand ({@link WALL_RGB}) und klar von der bläulichen @@ -427,6 +434,14 @@ const DOOR_LEAF_RGB: RRgb = [0.93, 0.9, 0.85]; const DOOR_LEAF_THICK = 0.04; /** Default-Ansichtsbreite des Rahmenprofils (Meter), wenn `TYPE.frameWidth` fehlt. */ const DEFAULT_FRAME_WIDTH = 0.06; +/** + * Default-Rahmentiefe (Meter), wenn ein Typ keine `frameDepth` nennt: reale + * Fensterrahmen sind ~70–90 mm tief (Holz/PVC/Alu), UNABHÄNGIG von der + * Wanddicke — ein schlanker Rahmen mit sichtbarer Laibung ringsum, nicht ein + * massiver Block über die volle Wandtiefe. Deckt sich mit + * `geometry/opening.ts::DEFAULT_FRAME_DEPTH` (2D=3D). + */ +const DEFAULT_FRAME_DEPTH = 0.07; /** Warmer Terrakotta-Grauton für Dachflächen/-giebel (s. {@link emitRoofs}) — hebt das Dach klar von Wand/Decke/Kontext ab. */ const ROOF_RGB: RRgb = [0.72, 0.45, 0.36]; /** Fallback-Farbe für 2D-Zeichnungen ohne color/lineStyle/Kategorie (wie drawing2DColor in Viewport3D.tsx, "#888888"). */ @@ -1958,21 +1973,16 @@ function clampRange(v: number, lo: number, hi: number): number { } /** - * Rahmen-Tiefe quer zur Wand (Meter): `frameThickness` des Typs, sonst dessen - * `frameDepth`, sonst die volle Wanddicke (Fallback, wenn der Typ keine - * brauchbare Tiefe nennt). Beide Typ-Felder sind wörtlich Meter ≤ 0 ⇒ ungültig - * (übersprungen, nächster Fallback greift). + * Rahmen-Tiefe quer zur Wand (Meter): explizite `frameDepth` des Typs, sonst + * {@link DEFAULT_FRAME_DEPTH} (auf die Wanddicke geklemmt, falls die Wand + * dünner als der Default-Rahmen ist). */ function resolveAcrossWallDepth( frameDepth: number | undefined, wallThickness: number, ): number { - // Rahmen-Tiefe quer zur Wand: explizite `frameDepth` gewinnt, sonst füllt der - // Rahmen die VOLLE Wanddicke (tiefe Laibung/Reveal). Das `frameThickness` ist - // die Profil-Breite des Rahmenstabs (2D/Ansicht), NICHT die Einbautiefe — es - // hier zu verwenden liess Fenster im 3D flach wirken (nur wenige cm tief). - if (frameDepth !== undefined && frameDepth > EPS) return frameDepth; - return wallThickness; + const depth = frameDepth !== undefined && frameDepth > EPS ? frameDepth : DEFAULT_FRAME_DEPTH; + return Math.min(depth, wallThickness); } /** @@ -2237,12 +2247,12 @@ function frameMeshesForOpening( const sfw = fw * 0.7; // Verschachteltes Flügelrahmen-Band: ~5 mm HINTER der Blendrahmen-Vorderkante // (nMax = Aussenseite) und nur ~50 mm tief — abgesetzte Stufe nach innen, - // die Glas-Vorderkante (nMax − 0.03) liegt INNERHALB dieses Bands. + // die Glas-Vorderkante (nMax − 0.02, Falzmass) liegt INNERHALB dieses Bands. const sashFrontN = params.nMax - 0.005; const sashBackN = Math.max(params.nMin, params.nMax - 0.055); - // Glas-Vorderkante (s. glassPanesForOpening: n1 = nMax − 0.03); die Symbole + // Glas-Vorderkante (s. glassPanesForOpening: n1 = nMax − 0.02); die Symbole // liegen als ~2.5 mm dünne dunkle Prismen KNAPP davor (weiter aussen). - const glassFrontN = params.nMax - 0.03; + const glassFrontN = params.nMax - 0.02; const symBackN = glassFrontN; const symFrontN = glassFrontN + 0.003; const pushSash = (from: number, to: number, zB: number, zT: number): void => @@ -2367,7 +2377,7 @@ function resolveSashSpans( } /** Luftraum zwischen zwei Scheiben einer Mehrfachverglasung (Meter, informativ). */ -const GLASS_PANE_GAP = 0.01; +const GLASS_PANE_GAP = 0.006; /** * Glasscheibe(n) EINER typisierten Öffnung (Fenster immer, Tür nur bei @@ -2395,14 +2405,14 @@ function glassPanesForOpening( const sashTopFull = vert.zTop - fw; // Die Scheibe(n) sitzen als DÜNNE Tafeln NAHE der Aussenfläche (nMax = +n- // Seite, s. resolveFrameNormalRange) — mittig tief im Reveal wirkte das - // schwach/kaum sichtbar. Direkt hinter der Aussenkante (3 cm zurück) liest - // sich das Fenster klar, die Laibung/Reveal-Tiefe zeigt nach innen. Bei - // mehreren Scheiben rücken die weiteren mit `GLASS_PANE_GAP` Luftraum - // dazwischen weiter nach innen (Richtung `nMin`). + // schwach/kaum sichtbar. Direkt hinter der Aussenkante (Falzmass ~2 cm, s. + // Fensterbau-Recherche) liest sich das Fenster klar, die Laibung/Reveal- + // Tiefe zeigt nach innen. Bei mehreren Scheiben rücken die weiteren mit + // `GLASS_PANE_GAP` Luftraum dazwischen weiter nach innen (Richtung `nMin`). const glassThick = GLASS_HALF_THICK * 2; const panes = Math.max(1, Math.min(3, Math.round(params.glazingPanes))); const push = (zBottom: number, zTop: number): void => { - let n1 = params.nMax - 0.03; + let n1 = params.nMax - 0.02; for (let p = 0; p < panes; p++) { const n0 = Math.max(params.nMin, n1 - glassThick); if (n1 - n0 <= EPS) break; @@ -2459,7 +2469,7 @@ function doorLeafPanels( const frac = params.glazed ? Math.max(0.05, Math.min(1, params.glazedFraction)) : 0; const leafTop = fieldTop - frac * (fieldTop - leafBottom); if (leafTop - leafBottom <= EPS || leafTo - leafFrom <= EPS) return out; - const n1 = params.nMax - 0.03 + DOOR_LEAF_THICK / 2; + const n1 = params.nMax - 0.02 + DOOR_LEAF_THICK / 2; const n0 = Math.max(params.nMin, n1 - DOOR_LEAF_THICK); const mesh = openingAxisBox(wall, leafFrom, leafTo, leafBottom, leafTop, n0, n1, DOOR_LEAF_RGB); if (mesh) out.push(mesh);