From cc7439685419a2e851a3e9e9895aec8fc1eef9b3 Mon Sep 17 00:00:00 2001 From: Karim Date: Sun, 12 Jul 2026 20:19:20 +0200 Subject: [PATCH] =?UTF-8?q?Fenster:=20Rahmen-Kontur=20in=20Aufsicht=20sepa?= =?UTF-8?q?rat=20von=20den=20geschnittenen=20Laibungsbl=C3=B6cken=20styleb?= =?UTF-8?q?ar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisher teilten sich die geschnittenen Laibungs-/Stulp-Blöcke (O-Blöcke, Schnittfläche des Rahmenmaterials) und die durchlaufende Rahmen-Kontur zwischen ihnen (Aufsicht, nicht geschnitten) dieselbe frameLine-Farbe. Neues Opening.frameViewLine übersteuert nur noch die Kontur; ohne Angabe fällt sie weiterhin auf frameLine zurück (bisheriges Verhalten unverändert). frameLine im UI zu "Rahmen (Schnitt)" umbenannt, um die beiden Kategorien zu unterscheiden. --- src/App.tsx | 3 ++ src/i18n/de.ts | 3 +- src/i18n/en.ts | 3 +- src/model/types.ts | 18 +++++++++-- src/panels/ObjectInfoPanel.tsx | 7 +++++ src/panels/host.ts | 4 ++- src/plan/generatePlan.ts | 20 +++++++----- src/plan/generatePlan.windowFields.test.ts | 36 ++++++++++++++++++++++ src/state/selectionInfo.ts | 2 ++ 9 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 07423bc..054c0db 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3374,6 +3374,9 @@ export default function App() { onSetOpeningFrameLine: (v) => { if (selection?.kind === "opening") updateOpening(selection.id, { frameLine: v }); }, + onSetOpeningFrameViewLine: (v) => { + if (selection?.kind === "opening") updateOpening(selection.id, { frameViewLine: v }); + }, onSetOpeningSashLine: (v) => { if (selection?.kind === "opening") updateOpening(selection.id, { sashLine: v }); }, diff --git a/src/i18n/de.ts b/src/i18n/de.ts index 9402d64..9b847d8 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -482,7 +482,8 @@ export const de = { "objinfo.opening.lintelLines.innen": "Innen", "objinfo.opening.lintelLines.aussen": "Außen", "objinfo.opening.lintelLines.beide": "Beide", - "objinfo.opening.frameLine": "Rahmen", + "objinfo.opening.frameLine": "Rahmen (Schnitt)", + "objinfo.opening.frameViewLine": "Rahmen (Aufsicht)", "objinfo.opening.sashLine": "Fenster (Flügel/Sprossen)", "objinfo.opening.sillLine": "Sims", "objinfo.lineWeight": "Strichstärke (mm)", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index d37f725..f214e70 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -481,7 +481,8 @@ export const en: Record = { "objinfo.opening.lintelLines.innen": "Inside", "objinfo.opening.lintelLines.aussen": "Outside", "objinfo.opening.lintelLines.beide": "Both", - "objinfo.opening.frameLine": "Frame", + "objinfo.opening.frameLine": "Frame (cut)", + "objinfo.opening.frameViewLine": "Frame (view)", "objinfo.opening.sashLine": "Window (sash/muntins)", "objinfo.opening.sillLine": "Sill", "objinfo.lineWeight": "Line weight (mm)", diff --git a/src/model/types.ts b/src/model/types.ts index bca1c13..8c4ecb4 100644 --- a/src/model/types.ts +++ b/src/model/types.ts @@ -1219,11 +1219,23 @@ export interface Opening { */ color?: string; /** - * Nur Fenster, Grundriss: Übersteuerung von Farbe/Strichstärke des - * Blendrahmens + Stulp-/Laibungsblöcke ({@link LineStyleOverride}). - * `undefined` ⇒ {@link Opening.color}, sonst Kategorie-Farbe/Haarlinie. + * Nur Fenster, Grundriss: Übersteuerung von Farbe/Strichstärke der + * GESCHNITTENEN Blendrahmen-Querschnittsblöcke an den Laibungs-Enden + * ({@link LineStyleOverride}, entspricht `windowSymbol.jambMarks` — die + * Schnittfläche des Rahmenmaterials an der Wand-Laibung). `undefined` ⇒ + * {@link Opening.color}, sonst Kategorie-Farbe/Haarlinie. Separat von + * {@link frameViewLine} (die durchlaufende Rahmen-Kontur in Aufsicht). */ frameLine?: LineStyleOverride; + /** + * Nur Fenster, Grundriss: Übersteuerung von Farbe/Strichstärke der + * durchlaufenden Blendrahmen-KONTUR in Aufsicht (das Rechteck aus + * `windowSymbol.frame` — die beiden Linien zwischen den geschnittenen + * Laibungs-/Stulp-Blöcken, NICHT geschnitten, sondern von oben/unten + * projiziert). `undefined` ⇒ fällt auf {@link frameLine} zurück (bisheriges + * Verhalten: eine gemeinsame Farbe/Stärke für Schnitt UND Aufsicht). + */ + frameViewLine?: LineStyleOverride; /** * Nur Fenster, Grundriss: Übersteuerung von Farbe/Strichstärke des * Flügelrahmens/der Sprossen (window-sash/window-mullion). diff --git a/src/panels/ObjectInfoPanel.tsx b/src/panels/ObjectInfoPanel.tsx index ed80ff1..e774983 100644 --- a/src/panels/ObjectInfoPanel.tsx +++ b/src/panels/ObjectInfoPanel.tsx @@ -1002,6 +1002,13 @@ export function OpeningSection({ fallbackWeight={OPENING_LINE_DEFAULT_WEIGHT} onChange={host.onSetOpeningFrameLine} /> + void; /** Setzt die Sturzlinien-Darstellung der Tür. */ onSetOpeningLintelLines: (lintelLines: "keine" | "innen" | "aussen" | "beide") => void; - /** Übersteuert Farbe/Strichstärke des Blendrahmens + Stulp-/Laibungsblöcke. */ + /** Übersteuert Farbe/Strichstärke der geschnittenen Laibungs-/Stulp-Blöcke. */ onSetOpeningFrameLine: (v: { color?: string; weight?: number } | undefined) => void; + /** Übersteuert Farbe/Strichstärke der durchlaufenden Rahmen-Kontur in Aufsicht. */ + onSetOpeningFrameViewLine: (v: { color?: string; weight?: number } | undefined) => void; /** Übersteuert Farbe/Strichstärke von Flügelrahmen/Sprossen. */ onSetOpeningSashLine: (v: { color?: string; weight?: number } | undefined) => void; /** Übersteuert Farbe/Strichstärke der Auf-/Untersicht-Andeutung (Sims). */ diff --git a/src/plan/generatePlan.ts b/src/plan/generatePlan.ts index 20c75b5..fc16b99 100644 --- a/src/plan/generatePlan.ts +++ b/src/plan/generatePlan.ts @@ -2281,23 +2281,29 @@ function addOpeningSymbol( }); } } else { - // Linien-Kategorien einzeln übersteuerbar (Nutzer-Wunsch): Rahmen (Blend- - // rahmen-Kontur + Stulp-/Laibungsblöcke), Fenster (Flügelrahmen/Sprossen), - // Sims (Auf-/Untersicht-Andeutung, s. u.). `undefined` ⇒ bisheriges - // Verhalten (strokeColor/SYMBOL_HAIRLINE_MM). + // Linien-Kategorien einzeln übersteuerbar (Nutzer-Wunsch): Rahmen-Schnitt + // (Laibungs-/Stulp-Blöcke, geschnittenes Profil), Rahmen-Aufsicht (die + // durchlaufende Kontur DAZWISCHEN, projiziert statt geschnitten), Fenster + // (Flügelrahmen/Sprossen), Sims (Auf-/Untersicht-Andeutung, s. u.). + // `undefined` ⇒ bisheriges Verhalten (strokeColor/SYMBOL_HAIRLINE_MM); + // `frameViewLine` fällt zusätzlich auf `frameLine` zurück (eine Farbe für + // Schnitt+Aufsicht, bis der Nutzer sie explizit trennt). const frameColor = o.frameLine?.color ?? strokeColor; const frameWeight = o.frameLine?.weight ?? lwMm; const frameHairline = o.frameLine?.weight ?? SYMBOL_HAIRLINE_MM; + const frameViewColor = o.frameViewLine?.color ?? frameColor; + const frameViewWeight = o.frameViewLine?.weight ?? frameWeight; const sashColor = o.sashLine?.color ?? strokeColor; const sashWeight = o.sashLine?.weight ?? SYMBOL_HAIRLINE_MM; - // Blendrahmen-Rechteck (mittel + fein). + // Blendrahmen-Rechteck in Aufsicht (mittel + fein) — die durchlaufende + // Kontur zwischen den geschnittenen Laibungs-/Stulp-Blöcken. out.push({ kind: "polygon", pts: sym.frame, fill: "none", - stroke: frameColor, - strokeWidthMm: frameWeight, + stroke: frameViewColor, + strokeWidthMm: frameViewWeight, hatch: NO_HATCH, greyed, openingId: o.id, diff --git a/src/plan/generatePlan.windowFields.test.ts b/src/plan/generatePlan.windowFields.test.ts index 7c5bc70..c47fae0 100644 --- a/src/plan/generatePlan.windowFields.test.ts +++ b/src/plan/generatePlan.windowFields.test.ts @@ -390,6 +390,42 @@ describe("generatePlan — Linien-Übersteuerung je Kategorie (Opening.frameLine expect(sill[0].kind === "line" && sill[0].weightMm).toBeCloseTo(0.4, 6); }); + it("frameViewLine ohne Übersteuerung: die Rahmen-Kontur (Aufsicht) fällt auf frameLine zurück", () => { + const p = project( + { ...baseWindow, typeId: "wt1", frameLine: { color: "#ff0000", weight: 0.5 } }, + [baseWindowType], + ); + const frame = generatePlan(p, "eg", visible, undefined, "mittel").primitives.find( + (pr) => pr.kind === "polygon" && pr.openingId === "F1" && pr.stroke !== "none", + ); + expect(frame).toBeDefined(); + expect(frame!.kind === "polygon" && frame!.stroke).toBe("#ff0000"); + expect(frame!.kind === "polygon" && frame!.strokeWidthMm).toBeCloseTo(0.5, 6); + }); + + it("frameViewLine übersteuert NUR die Rahmen-Kontur (Aufsicht) — die Laibungs-Blöcke (Schnitt) bleiben beim frameLine-Default", () => { + const p = project( + { + ...baseWindow, + typeId: "wt1", + frameLine: { color: "#ff0000", weight: 0.5 }, + frameViewLine: { color: "#00ffff", weight: 0.2 }, + }, + [baseWindowType], + ); + const out = generatePlan(p, "eg", visible, undefined, "mittel").primitives; + const frame = out.find((pr) => pr.kind === "polygon" && pr.openingId === "F1" && pr.stroke !== "none"); + expect(frame!.kind === "polygon" && frame!.stroke).toBe("#00ffff"); + expect(frame!.kind === "polygon" && frame!.strokeWidthMm).toBeCloseTo(0.2, 6); + // Laibungs-Blöcke (window-stulp) bleiben unverändert bei frameLine, NICHT frameViewLine. + const stulp = out.filter((pr) => pr.kind === "line" && pr.cls === "window-stulp"); + expect(stulp.length).toBeGreaterThan(0); + for (const l of stulp) { + expect(l.kind === "line" && l.color).toBe("#ff0000"); + expect(l.kind === "line" && l.weightMm).toBeCloseTo(0.5, 6); + } + }); + it("sashLine wirkt auch bei 'grob' (1:100) auf die einzige dort sichtbare Glaslinie", () => { const p = project( { ...baseWindow, typeId: "wt1", sashLine: { color: "#00ff00", weight: 0.3 } }, diff --git a/src/state/selectionInfo.ts b/src/state/selectionInfo.ts index 40604b6..51ecd36 100644 --- a/src/state/selectionInfo.ts +++ b/src/state/selectionInfo.ts @@ -235,6 +235,7 @@ export interface OpeningInfo { zTop: number; /** Nur Fenster: Grundriss-Linien-Übersteuerungen je Kategorie. */ frameLine?: { color?: string; weight?: number }; + frameViewLine?: { color?: string; weight?: number }; sashLine?: { color?: string; weight?: number }; sillLineStyle?: { color?: string; weight?: number }; } @@ -728,6 +729,7 @@ function openingSelection(project: Project, o: Opening): Selection { zBottom: ext.zBottom, zTop: ext.zTop, frameLine: o.frameLine, + frameViewLine: o.frameViewLine, sashLine: o.sashLine, sillLineStyle: o.sillLineStyle, };