Schiebefenster-Öffnungssymbol (DIN-Pfeil) in Ansicht und 3D

Schiebeflügel hatten bisher kein Öffnungssymbol (nur Dreh/Kipp/Drehkipp). DIN
zeichnet für Schiebeelemente keinen Anschlag-Winkel, sondern einen Pfeil in
Laufrichtung — ergänzt in der 2D-Ansicht (toElevation) und im 3D-fein
(toWalls3d), Richtung aus der Griffseite. Test deckt Schiebe/Fest/Drehkipp ab.
This commit is contained in:
2026-07-11 13:11:58 +02:00
parent cdae20acbe
commit f98c962ba3
3 changed files with 72 additions and 0 deletions
+45
View File
@@ -353,6 +353,51 @@ describe("toElevation — Öffnungen: Verdeckung + Reveal-Schatten", () => {
expect(symbols.length).toBeGreaterThan(0);
});
it("Öffnungssymbolik je Art: Schiebe=Pfeil (3 Linien), Fest=keine, Drehkipp=4", () => {
const base = elevationProject({ overhang: false });
const mkProject = (kind: "schiebe" | "fest" | "drehkipp"): Project =>
({
...base,
windowTypes: [
{
id: "wt",
name: "F",
kind,
wingCount: 1,
glazing: "zweifach",
frameWidth: 0.06,
defaultSillHeight: 0.9,
defaultWidth: 1,
defaultHeight: 1.2,
},
],
openings: [
{
id: "op",
type: "opening",
hostWallId: "NEAR",
categoryCode: "21",
kind: "window",
typeId: "wt",
position: 1.5,
width: 1,
height: 1.2,
sillHeight: 0.9,
},
],
}) as unknown as Project;
const symCount = (kind: "schiebe" | "fest" | "drehkipp"): number => {
const p = mkProject(kind);
const plan = generateElevationPlan(p, p.drawingLevels[1])!;
return plan.primitives.filter(
(pr) => pr.kind === "line" && (pr as { cls?: string }).cls === "elevation-opening-symbol",
).length;
};
expect(symCount("schiebe")).toBe(3); // Pfeil: Schaft + 2 Spitzen
expect(symCount("fest")).toBe(0); // Festverglasung ohne Symbol
expect(symCount("drehkipp")).toBe(4); // Dreh (2) + Kipp (2)
});
it("Reveal-Schatten: shadows=true erzeugt Öffnungs-Schattenbänder, false keine", () => {
const project = openingProject();
const level = project.drawingLevels[1];
+16
View File
@@ -799,6 +799,22 @@ function collectOpenings(project: Project, frame: ElevationFrame): ProjectedOpen
[quadAt(pr.pts, gB, gz0), apex],
);
}
if (kind === "schiebe") {
// Schiebeflügel: KEIN Dreieck (keine Bandseite), sondern ein Pfeil in
// Laufrichtung (horizontal) auf halber Glashöhe — die Griffseite
// (hingeSide) bestimmt die Pfeilrichtung. Klassische DIN-Konvention.
const zc = (gz0 + gz1) / 2;
const toRight = (sd.hingeSide ?? "left") === "left"; // Griff links ⇒ schiebt nach rechts
const tail = toRight ? gA : gB;
const head = toRight ? gB : gA;
const barb = (head - tail) * 0.22; // Pfeilspitzen-Rücksprung (Glasbreite-Anteil)
const headPt = quadAt(pr.pts, head, zc);
pr.symbol.push(
[quadAt(pr.pts, tail, zc), headPt], // Schaft
[quadAt(pr.pts, head - barb, gz0 + (gz1 - gz0) * 0.32), headPt], // obere Spitze
[quadAt(pr.pts, head - barb, gz0 + (gz1 - gz0) * 0.68), headPt], // untere Spitze
);
}
}
// Fensterbank (Sims, VW): Bank + Tropfkante als zwei Bänder unter dem
// Rahmen, seitlich leicht überstehend. Default AN (sillBoard fehlt),
+11
View File
@@ -2298,6 +2298,17 @@ function frameMeshesForOpening(
pushSymbol(gA, gbz, apexU, gtz);
pushSymbol(gB, gbz, apexU, gtz);
}
if (kind === "schiebe") {
// Schiebe: horizontaler Pfeil in Laufrichtung (Griff links ⇒ nach rechts).
const zc = (gbz + gtz) / 2;
const toRight = (s.hingeSide ?? "left") === "left";
const tail = toRight ? gA : gB;
const head = toRight ? gB : gA;
const barb = (head - tail) * 0.22;
pushSymbol(tail, zc, head, zc); // Schaft
pushSymbol(head - barb, gbz + (gtz - gbz) * 0.32, head, zc); // obere Spitze
pushSymbol(head - barb, gbz + (gtz - gbz) * 0.68, head, zc); // untere Spitze
}
}
}