Tür: Teilverglasung (glazingRatio) im 3D wirksam

DoorType.glazingRatio wurde bisher von keinem Renderer gelesen (Designdoc).
Bei Glasblatt (leafStyle 'glas') füllt das Glas jetzt nur den oberen
glazingRatio-Anteil des Hauptfelds; darunter bleibt ein blindes Blattfeld
(z. B. Tür mit Oberglas). Fenster unberührt (glazedFraction = 1). +1 Test
(Halbglas: Unterkante angehoben, Oberkante am Sturz). 673/673 grün.
This commit is contained in:
2026-07-10 02:14:33 +02:00
parent db44317d95
commit 897a2dcd43
2 changed files with 66 additions and 3 deletions
+46
View File
@@ -604,6 +604,52 @@ describe("Rahmen-/Sprossen-Riegel typisierter Öffnungen (emitOpeningFrames)", (
expect(operableCount).toBe(8); // + 4 Flügelrahmen-Riegel expect(operableCount).toBe(8); // + 4 Flügelrahmen-Riegel
}); });
it("Glastür mit glazingRatio < 1: Glas nur im oberen Blattanteil (Teilverglasung)", () => {
const glassDoor = (glazingRatio?: number): Project => ({
...sampleProject,
doorTypes: [
{
id: "dt-glass",
name: "Glastür Test",
kind: "dreh",
leafCount: 1,
leafStyle: "glas",
glazingRatio,
frameThickness: 0.06,
frameWidth: 0.06,
defaultWidth: 0.9,
defaultHeight: 2.0,
},
],
openings: [
{
id: "DG",
type: "opening",
hostWallId: "W1",
categoryCode: "21",
kind: "door",
typeId: "dt-glass",
position: 3.0,
width: 0.9,
height: 2.0,
sillHeight: 0,
},
],
doors: [],
context: [],
});
// Vollglas (ratio 1 / fehlt): Glas über (fast) die ganze Blatthöhe.
const full = projectToModel3d(glassDoor(1)).meshes.filter(isGlass);
expect(full.length).toBe(1);
const fullR = zRange(full[0]);
// Halbglas (ratio 0.5): Glas nur in der oberen Hälfte -> Unterkante deutlich höher.
const half = projectToModel3d(glassDoor(0.5)).meshes.filter(isGlass);
expect(half.length).toBe(1);
const halfR = zRange(half[0]);
expect(halfR.max).toBeCloseTo(fullR.max, 6); // Oberkante gleich (an OK/Sturz)
expect(halfR.min).toBeGreaterThan(fullR.min + 0.5); // Unterkante klar angehoben
});
it("insetFromFace verschiebt Rahmen-Position entlang der Wand-Normalen gegenüber bündig (kein Einzug)", () => { it("insetFromFace verschiebt Rahmen-Position entlang der Wand-Normalen gegenüber bündig (kein Einzug)", () => {
// W1-Achse (0,0)->(5,0): u=(1,0) -> Normale n=(0,-1); der Quer-Versatz // W1-Achse (0,0)->(5,0): u=(1,0) -> Normale n=(0,-1); der Quer-Versatz
// bildet sich rein in der Y-Koordinate ab (y = -t). insetFromFace 0.1 muss // bildet sich rein in der Y-Koordinate ab (y = -t). insetFromFace 0.1 muss
+20 -3
View File
@@ -1942,6 +1942,13 @@ interface OpeningFrameParams {
* {@link glassPanesForOpening}. * {@link glassPanesForOpening}.
*/ */
glazingPanes: 1 | 2 | 3; glazingPanes: 1 | 2 | 3;
/**
* Verglasungs-Anteil des HAUPTFELDS (0..1, von OBEN gemessen): 1 = voll
* verglast (Fenster, Ganzglastür), < 1 = nur der obere Anteil ist Glas, unten
* bleibt ein blindes Feld (z. B. Tür mit Oberglas, `DoorType.glazingRatio`).
* Fenster: immer 1.
*/
glazedFraction: number;
/** /**
* Flügeleinteilung ({@link sashesOfWindowType}) je ÖFFENBAREM Flügel wird im * Flügeleinteilung ({@link sashesOfWindowType}) je ÖFFENBAREM Flügel wird im
* 3D ein Flügelrahmen (Rahmen-Riegel innerhalb des Blendrahmens) erzeugt * 3D ein Flügelrahmen (Rahmen-Riegel innerhalb des Blendrahmens) erzeugt
@@ -1975,6 +1982,8 @@ function resolveOpeningFrame(project: Project, wall: Wall, op: Opening): Opening
mullionRows: 1, mullionRows: 1,
glazed: dt.leafStyle === "glas", glazed: dt.leafStyle === "glas",
glazingPanes: 1, glazingPanes: 1,
// Teilverglasung: glazingRatio (Glasanteil oben) bei Glasblatt; sonst voll.
glazedFraction: Math.max(0.05, Math.min(1, dt.glazingRatio ?? 1)),
sashes: [], sashes: [],
}; };
} }
@@ -2014,6 +2023,7 @@ function resolveOpeningFrame(project: Project, wall: Wall, op: Opening): Opening
mullionRows: Math.max(1, Math.round(wt.mullionRows ?? 1)), mullionRows: Math.max(1, Math.round(wt.mullionRows ?? 1)),
glazed: true, glazed: true,
glazingPanes: glazingPanesOf(wt), glazingPanes: glazingPanesOf(wt),
glazedFraction: 1,
sashes, sashes,
}; };
} }
@@ -2184,12 +2194,19 @@ function glassPanesForOpening(
if (n1 <= params.nMin) break; if (n1 <= params.nMin) break;
} }
}; };
// Teilverglasung (Türen mit glazingRatio < 1): das HAUPTFELD ist nur im oberen
// `glazedFraction`-Anteil Glas; darunter bleibt ein blindes Blattfeld. Von der
// OBERKANTE des Hauptbands gemessen. Fenster: glazedFraction = 1 (voll).
const frac = Math.max(0.05, Math.min(1, params.glazedFraction));
const glazedBottom = (bBottom: number, bTop: number) =>
bTop - frac * (bTop - bBottom);
if (params.transomHeight > EPS) { if (params.transomHeight > EPS) {
const splitZ = clampRange(vert.zTop - params.transomHeight, sashBottom, sashTopFull); const splitZ = clampRange(vert.zTop - params.transomHeight, sashBottom, sashTopFull);
push(sashBottom, splitZ - fw / 2); // Hauptscheibe/-blatt unterhalb des Kämpfers. const mainTop = splitZ - fw / 2;
push(splitZ + fw / 2, sashTopFull); // Oberlicht-Scheibe oberhalb des Kämpfers. push(glazedBottom(sashBottom, mainTop), mainTop); // Hauptglas (ggf. nur oben).
push(splitZ + fw / 2, sashTopFull); // Oberlicht-Scheibe (immer voll).
} else { } else {
push(sashBottom, sashTopFull); push(glazedBottom(sashBottom, sashTopFull), sashTopFull);
} }
return out; return out;
} }