Fenster-Rahmentiefe: realistischer Default statt volle Wanddicke

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.
This commit is contained in:
2026-07-11 12:50:10 +02:00
parent d34e1cb1c1
commit 1c31e680c1
2 changed files with 43 additions and 24 deletions
+11 -2
View File
@@ -263,7 +263,7 @@ export interface WindowRenderSpec {
sashes: SashDef[]; sashes: SashDef[];
/** Sichtbare Profilbreite (Meter) von Blend-/Flügelrahmen in der Öffnungsebene. */ /** Sichtbare Profilbreite (Meter) von Blend-/Flügelrahmen in der Öffnungsebene. */
frameWidth: number; frameWidth: number;
/** Rahmentiefe quer zur Wand (Meter); fehlt ⇒ volle Wanddicke. */ /** Rahmentiefe quer zur Wand (Meter); fehlt ⇒ {@link DEFAULT_FRAME_DEPTH}. */
frameDepth?: number; frameDepth?: number;
/** Schichteinzug: Abstand der Rahmen-Vorderkante von {@link insetFace} (Meter). */ /** Schichteinzug: Abstand der Rahmen-Vorderkante von {@link insetFace} (Meter). */
insetFromFace?: number; insetFromFace?: number;
@@ -282,6 +282,15 @@ interface SashSegment {
/** Default-Pfostenbreite (Meter), wenn ein `kind:"pfosten"`-Eintrag keine `postWidth` angibt. */ /** Default-Pfostenbreite (Meter), wenn ein `kind:"pfosten"`-Eintrag keine `postWidth` angibt. */
const DEFAULT_POST_WIDTH = 0.06; const DEFAULT_POST_WIDTH = 0.06;
/**
* Default-Rahmentiefe (Meter), wenn ein Fenster-/Türtyp keine explizite
* `frameDepth` trägt: reale Fensterrahmen sind ~7090 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- * 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. // Blendrahmen (kein Flügelrahmen) — so unterscheidet sich fest von öffenbar.
if (spec && spec.sashes.length > 0) { if (spec && spec.sashes.length > 0) {
const fw = Math.max(0.01, spec.frameWidth); 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 inset = Math.max(0, spec.insetFromFace ?? 0);
const halfT = total / 2; const halfT = total / 2;
const sign = (spec.insetFace ?? "aussen") === "innen" ? -1 : 1; const sign = (spec.insetFace ?? "aussen") === "innen" ? -1 : 1;
+32 -22
View File
@@ -390,8 +390,15 @@ const EXTRUSION_RGB: RRgb = [0.85, 0.55, 0.25];
const COLUMN_RGB: RRgb = [0.7, 0.7, 0.72]; const COLUMN_RGB: RRgb = [0.7, 0.7, 0.72];
/** Helles, leicht bläuliches Fenster-Glas (Scheibe in der Öffnung). */ /** Helles, leicht bläuliches Fenster-Glas (Scheibe in der Öffnung). */
const GLASS_RGB: RRgb = [0.62, 0.76, 0.85]; 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 * Helles, warmes Rahmen-Grau (Zarge/Blendrahmen/Kämpfer/Sprossen) — bewusst
* deutlich heller als Wand ({@link WALL_RGB}) und klar von der bläulichen * 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; const DOOR_LEAF_THICK = 0.04;
/** Default-Ansichtsbreite des Rahmenprofils (Meter), wenn `TYPE.frameWidth` fehlt. */ /** Default-Ansichtsbreite des Rahmenprofils (Meter), wenn `TYPE.frameWidth` fehlt. */
const DEFAULT_FRAME_WIDTH = 0.06; const DEFAULT_FRAME_WIDTH = 0.06;
/**
* Default-Rahmentiefe (Meter), wenn ein Typ keine `frameDepth` nennt: reale
* Fensterrahmen sind ~7090 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. */ /** 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]; 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"). */ /** 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 * Rahmen-Tiefe quer zur Wand (Meter): explizite `frameDepth` des Typs, sonst
* `frameDepth`, sonst die volle Wanddicke (Fallback, wenn der Typ keine * {@link DEFAULT_FRAME_DEPTH} (auf die Wanddicke geklemmt, falls die Wand
* brauchbare Tiefe nennt). Beide Typ-Felder sind wörtlich Meter ≤ 0 ⇒ ungültig * dünner als der Default-Rahmen ist).
* (übersprungen, nächster Fallback greift).
*/ */
function resolveAcrossWallDepth( function resolveAcrossWallDepth(
frameDepth: number | undefined, frameDepth: number | undefined,
wallThickness: number, wallThickness: number,
): number { ): number {
// Rahmen-Tiefe quer zur Wand: explizite `frameDepth` gewinnt, sonst füllt der const depth = frameDepth !== undefined && frameDepth > EPS ? frameDepth : DEFAULT_FRAME_DEPTH;
// Rahmen die VOLLE Wanddicke (tiefe Laibung/Reveal). Das `frameThickness` ist return Math.min(depth, wallThickness);
// 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;
} }
/** /**
@@ -2237,12 +2247,12 @@ function frameMeshesForOpening(
const sfw = fw * 0.7; const sfw = fw * 0.7;
// Verschachteltes Flügelrahmen-Band: ~5 mm HINTER der Blendrahmen-Vorderkante // Verschachteltes Flügelrahmen-Band: ~5 mm HINTER der Blendrahmen-Vorderkante
// (nMax = Aussenseite) und nur ~50 mm tief — abgesetzte Stufe nach innen, // (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 sashFrontN = params.nMax - 0.005;
const sashBackN = Math.max(params.nMin, params.nMax - 0.055); 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). // 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 symBackN = glassFrontN;
const symFrontN = glassFrontN + 0.003; const symFrontN = glassFrontN + 0.003;
const pushSash = (from: number, to: number, zB: number, zT: number): void => 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). */ /** 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 * Glasscheibe(n) EINER typisierten Öffnung (Fenster immer, Tür nur bei
@@ -2395,14 +2405,14 @@ function glassPanesForOpening(
const sashTopFull = vert.zTop - fw; const sashTopFull = vert.zTop - fw;
// Die Scheibe(n) sitzen als DÜNNE Tafeln NAHE der Aussenfläche (nMax = +n- // Die Scheibe(n) sitzen als DÜNNE Tafeln NAHE der Aussenfläche (nMax = +n-
// Seite, s. resolveFrameNormalRange) — mittig tief im Reveal wirkte das // Seite, s. resolveFrameNormalRange) — mittig tief im Reveal wirkte das
// schwach/kaum sichtbar. Direkt hinter der Aussenkante (3 cm zurück) liest // schwach/kaum sichtbar. Direkt hinter der Aussenkante (Falzmass ~2 cm, s.
// sich das Fenster klar, die Laibung/Reveal-Tiefe zeigt nach innen. Bei // Fensterbau-Recherche) liest sich das Fenster klar, die Laibung/Reveal-
// mehreren Scheiben rücken die weiteren mit `GLASS_PANE_GAP` Luftraum // Tiefe zeigt nach innen. Bei mehreren Scheiben rücken die weiteren mit
// dazwischen weiter nach innen (Richtung `nMin`). // `GLASS_PANE_GAP` Luftraum dazwischen weiter nach innen (Richtung `nMin`).
const glassThick = GLASS_HALF_THICK * 2; const glassThick = GLASS_HALF_THICK * 2;
const panes = Math.max(1, Math.min(3, Math.round(params.glazingPanes))); const panes = Math.max(1, Math.min(3, Math.round(params.glazingPanes)));
const push = (zBottom: number, zTop: number): void => { 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++) { for (let p = 0; p < panes; p++) {
const n0 = Math.max(params.nMin, n1 - glassThick); const n0 = Math.max(params.nMin, n1 - glassThick);
if (n1 - n0 <= EPS) break; 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 frac = params.glazed ? Math.max(0.05, Math.min(1, params.glazedFraction)) : 0;
const leafTop = fieldTop - frac * (fieldTop - leafBottom); const leafTop = fieldTop - frac * (fieldTop - leafBottom);
if (leafTop - leafBottom <= EPS || leafTo - leafFrom <= EPS) return out; 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 n0 = Math.max(params.nMin, n1 - DOOR_LEAF_THICK);
const mesh = openingAxisBox(wall, leafFrom, leafTo, leafBottom, leafTop, n0, n1, DOOR_LEAF_RGB); const mesh = openingAxisBox(wall, leafFrom, leafTo, leafBottom, leafTop, n0, n1, DOOR_LEAF_RGB);
if (mesh) out.push(mesh); if (mesh) out.push(mesh);