2D: By-Layer/By-Object-Attribute für Wand/Decke konsistent verdrahtet

resolveBackground (generatePlan/shared.ts) existierte korrekt implementiert,
wurde aber nirgends aufgerufen — Wand-/Decken-Poché nutzte überall fest
HATCH_INK/HATCH_PAPER, die By-Layer-Hintergrundfarbe kam nie im Renderer an.
Signatur an resolveForeground angeglichen (string | undefined statt
erzwungenem Component.color-Fallback), damit "kein Override gesetzt" weiter
auf die neutrale SIA-Poché-Konvention fällt statt auf die rohe Bauteilfarbe —
sonst hätte jedes bestehende Projekt ohne gesetzten Override optisch
umgeschlagen. Verdrahtet in walls.ts/ceilings.ts (Grundriss) und neu auch in
splitWallLayers/splitSlabLayers (Schnitt, toSection.ts) — dort fehlte bei
MEHRSCHICHTIGEM Wand-/Deckentyp zusätzlich foreground/hatchId komplett
(owner.foreground/hatchSource wurden nie an resolveForeground/resolveHatchId
übergeben, nur der Default lief); splitSlabLayers bekam dafür einen neuen
`owner: Ceiling`-Parameter (die einschichtigen Pfade über
resolveWallSectionStyle/resolveCeilingSectionStyle waren bereits korrekt).

Ceiling.strokeWeight/strokeWeightSource war komplett unverdrahtet (fixe
LAYER_LINE_MM-Konstante) — das Attribut-Panel bietet aber einen Editier-
Umschalter dafür (identisch zur Wand). addCeilingPoche bekommt jetzt eine
aufgelöste lwMm (resolveStrokeWeight, wie schon bei der Wand); die
Deckenkontur bleibt weiterhin die gestrichelte Überkopf-Ansichtslinie, nur
die Dicke folgt jetzt dem Override — Verhaltensänderung auch im Default-Fall,
wenn eine Kategorie eine von LAYER_LINE_MM abweichende Strichstärke trägt.

Bewusst NICHT angefasst: Wand-/Decken-Strichstärke im Schnitt-Umriss
(SECTION_CUT_OUTLINE_MM in generateSectionPlan) — eine einzige, uniforme
Konstante für ALLE Schnitt-Polygone, kein Per-Bauteil-Wert; das wirkt wie
eine bewusste Zeichnungskonvention (analog zur "grob"-Vollschwarz-Poché nach
SIA 400 B.9/36), nicht wie der "UI verspricht etwas, Renderer hält es nicht"-
Bugmuster der übrigen Funde. Würde ausserdem eine Erweiterung von
SectionCutPolygon um ein Gewichtsfeld brauchen (heute keins) — grösserer,
separat zu scopender Eingriff.

6 neue Tests (splitWallLayers/splitSlabLayers mit/ohne Override, Grundriss
weiterhin über die 907 Gesamttests abgedeckt). tsc -b / vitest run (907/907)
/ npm run build grün.
This commit is contained in:
2026-08-20 20:41:28 +02:00
parent 1c3fb289d0
commit f450a1aa08
6 changed files with 140 additions and 25 deletions
+12 -4
View File
@@ -58,6 +58,7 @@ import {
MONO_INK, MONO_INK,
SHOW_ALL, SHOW_ALL,
WALL_FALLBACK_MM, WALL_FALLBACK_MM,
resolveBackground,
resolveForeground, resolveForeground,
resolveHatch, resolveHatch,
resolveHatchId, resolveHatchId,
@@ -81,7 +82,7 @@ import { addReferenceLine, addWallPoche, buildWallFootprints, wallGaps } from ".
// Re-Export des gemeinsamen Vokabulars — externe Konsumenten (Export-Pipeline, // Re-Export des gemeinsamen Vokabulars — externe Konsumenten (Export-Pipeline,
// PlanView, toSection.ts, toWalls3d.ts, toRenderScene.ts, …) importieren // PlanView, toSection.ts, toWalls3d.ts, toRenderScene.ts, …) importieren
// weiterhin ausschliesslich von hier, unverändert zur Zeit vor dem Domänen-Split. // weiterhin ausschliesslich von hier, unverändert zur Zeit vor dem Domänen-Split.
export { HATCH_INK, HATCH_PAPER, resolveForeground, resolveHatch }; export { HATCH_INK, HATCH_PAPER, resolveBackground, resolveForeground, resolveHatch, resolveHatchId };
export type { CategoryDisplayResolver, DetailLevel, HatchRender, Primitive }; export type { CategoryDisplayResolver, DetailLevel, HatchRender, Primitive };
/** /**
@@ -309,9 +310,16 @@ export function generatePlan(
for (const ceiling of ceilings) { for (const ceiling of ceilings) {
const greyed = categoryDisplay(ceiling.categoryCode).greyed; const greyed = categoryDisplay(ceiling.categoryCode).greyed;
const category = catByCode.get(ceiling.categoryCode); const category = catByCode.get(ceiling.categoryCode);
// Kein lwMm mehr: der Decken-Umriss ist eine feste Überkopf-Haarlinie // Strichstärke wie bei der Wand (Attribut-Override > Kategorie > Fallback);
// (gestrichelt), unabhängig von der Kategorie-Strichstärke. // der Umriss bleibt trotzdem eine gestrichelte Überkopf-Ansichtslinie
addCeilingPoche(primitives, project, ceiling, greyed, detail, category, wallFootprints); // (Dash-Muster unverändert, s. addCeilingPoche) — nur die Dicke ist editierbar.
const ceilingLwMm = resolveStrokeWeight(
ceiling.strokeWeight,
ceiling.strokeWeightSource,
category,
lwByCode.get(ceiling.categoryCode) ?? LAYER_LINE_MM,
);
addCeilingPoche(primitives, project, ceiling, greyed, detail, category, wallFootprints, ceilingLwMm);
} }
// Dächer dieses Geschosses (Dachaufsicht): reine Grundriss-Linien (Traufe/ // Dächer dieses Geschosses (Dachaufsicht): reine Grundriss-Linien (Traufe/
+13 -5
View File
@@ -13,8 +13,8 @@ import {
OVERHEAD_DASH, OVERHEAD_DASH,
POCHE_STROKE, POCHE_STROKE,
LAYER_DETAIL_FACTOR, LAYER_DETAIL_FACTOR,
LAYER_LINE_MM,
pocheFill, pocheFill,
resolveBackground,
resolveForeground, resolveForeground,
resolveHatch, resolveHatch,
resolveHatchId, resolveHatchId,
@@ -38,7 +38,8 @@ import { segmentInsideWallBand, subtractIntervals, type WallFootprint } from "./
* hier. Im Detailgrad „grob" entfällt die Schraffur (nur Fläche + Umriss); * hier. Im Detailgrad „grob" entfällt die Schraffur (nur Fläche + Umriss);
* „mittel"/„fein" zeigen die Ansichts-Schraffur des Bauteils (`viewHatchId`, * „mittel"/„fein" zeigen die Ansichts-Schraffur des Bauteils (`viewHatchId`,
* Default weiss) — die frontal gesehene Aufsicht ist kein Schnitt. `lwMm` ist * Default weiss) — die frontal gesehene Aufsicht ist kein Schnitt. `lwMm` ist
* die Kategorie-Strichstärke. * die aufgelöste Umriss-Strichstärke (Attribut-Override > Kategorie > Fallback,
* vom Aufrufer via `resolveStrokeWeight` bestimmt, analog zur Wand).
*/ */
export function addCeilingPoche( export function addCeilingPoche(
out: Primitive[], out: Primitive[],
@@ -48,6 +49,7 @@ export function addCeilingPoche(
detail: DetailLevel, detail: DetailLevel,
category: LayerCategory | undefined, category: LayerCategory | undefined,
wallFootprints: WallFootprint[], wallFootprints: WallFootprint[],
lwMm: number,
): void { ): void {
const pts = ceiling.outline; const pts = ceiling.outline;
if (pts.length < 3) return; if (pts.length < 3) return;
@@ -86,6 +88,11 @@ export function addCeilingPoche(
resolveForeground(comp, ceiling.foreground, category, ceiling.foregroundSource), resolveForeground(comp, ceiling.foreground, category, ceiling.foregroundSource),
) )
: NO_HATCH; : NO_HATCH;
// Poché-Füllfarbe: Attribut-Override (By-Layer/By-Object) hat Vorrang vor der
// neutralen SIA-Fläche, analog zur Wand-Poché (siehe walls.ts).
const bgOverride = comp
? resolveBackground(comp, ceiling.background, category, ceiling.backgroundSource)
: undefined;
// Gefüllte Fläche OHNE eigenen Umriss (stroke:"none"): die Decke liegt unter // Gefüllte Fläche OHNE eigenen Umriss (stroke:"none"): die Decke liegt unter
// der Wand-Poché; ihre Kante wird separat als verdeckungs-geclippte Linie // der Wand-Poché; ihre Kante wird separat als verdeckungs-geclippte Linie
// gezeichnet (s. u.), damit sie nicht durch die darüberstehenden Wände schlägt. // gezeichnet (s. u.), damit sie nicht durch die darüberstehenden Wände schlägt.
@@ -93,7 +100,7 @@ export function addCeilingPoche(
out.push({ out.push({
kind: "polygon", kind: "polygon",
pts: ring, pts: ring,
fill: comp ? pocheFill(hatch.pattern) : "none", fill: comp ? bgOverride ?? pocheFill(hatch.pattern) : "none",
stroke: "none", stroke: "none",
strokeWidthMm: 0, strokeWidthMm: 0,
hatch, hatch,
@@ -128,8 +135,9 @@ export function addCeilingPoche(
b: add(a, scale(d, t1)), b: add(a, scale(d, t1)),
cls: "ceiling-outline", cls: "ceiling-outline",
// Überkopf-Ansichtslinie (Decke liegt als Slab über der Schnittebene) → // Überkopf-Ansichtslinie (Decke liegt als Slab über der Schnittebene) →
// gestrichelte Haarlinie statt kräftiger Volllinie (BIM-Konvention). // gestrichelt statt durchgezogen (BIM-Konvention) — die DICKE folgt
weightMm: LAYER_LINE_MM * LAYER_DETAIL_FACTOR[detail], // aber ceiling.strokeWeight/-Source wie bei der Wand (s. Aufrufer).
weightMm: lwMm * LAYER_DETAIL_FACTOR[detail],
dash: OVERHEAD_DASH, dash: OVERHEAD_DASH,
color: stroke, color: stroke,
greyed, greyed,
+11 -7
View File
@@ -438,21 +438,25 @@ export function resolveForeground(
} }
/** /**
* Kollabiert die Hintergrund-/Füllfarbkette (Poché) eines Bauteils, analog zu * Kollabiert die Hintergrund-/Füllfarbkette (Poché-Override) eines Bauteils zu
* {@link resolveForeground}: * einem einzelnen Wert, exakt analog zu {@link resolveForeground}:
* (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.color`) ?? * (Attribut-Override `override`) ?? (`source==="layer"` ⇒ `category.color`) ??
* (`Component.background`) ?? `Component.color`. * (`Component.background`) ?? `undefined`.
* Immer definiert (endet spätestens bei `Component.color` = heutiges Verhalten). * `undefined` als Endergebnis heißt „kein Override gesetzt" — Aufrufer fallen
* dann auf ihren eigenen Default zurück (bei Wand/Decke die neutrale SIA-
* Poché-Farbe HATCH_INK/HATCH_PAPER, NICHT `Component.color` — die reale
* Bauteilfarbe soll die Schnitt-Konvention nicht überschreiben, nur ein
* bewusster Override).
*/ */
export function resolveBackground( export function resolveBackground(
comp: Component, comp: Component,
override?: string, override?: string,
category?: LayerCategory, category?: LayerCategory,
source?: AttributeSource, source?: AttributeSource,
): string { ): string | undefined {
if (override != null) return override; if (override != null) return override;
if (source === "layer") return category?.color ?? comp.background ?? comp.color; if (source === "layer") return category?.color ?? comp.background;
return comp.background ?? comp.color; return comp.background;
} }
/** /**
+3 -1
View File
@@ -28,6 +28,7 @@ import {
NO_HATCH, NO_HATCH,
OUTLINE_DETAIL_FACTOR, OUTLINE_DETAIL_FACTOR,
POCHE_STROKE, POCHE_STROKE,
resolveBackground,
resolveForeground, resolveForeground,
resolveHatch, resolveHatch,
resolveHatchId, resolveHatchId,
@@ -341,6 +342,7 @@ export function addWallPoche(
// Schichtschraffur (fest in HATCH_INK, siehe resolveHatch) liegt darüber; // Schichtschraffur (fest in HATCH_INK, siehe resolveHatch) liegt darüber;
// Vollmuster ("solid") = Tinten-Poché. // Vollmuster ("solid") = Tinten-Poché.
const layerSolid = layerHatch.pattern === "solid"; const layerSolid = layerHatch.pattern === "solid";
const bgOverride = resolveBackground(comp, wall.background, category, wall.backgroundSource);
const layerStartCut = s <= 1e-6 ? (layerCuts ? layerCuts.start[li] : startCut) : null; const layerStartCut = s <= 1e-6 ? (layerCuts ? layerCuts.start[li] : startCut) : null;
const layerEndCut = e >= axisLen - 1e-6 ? (layerCuts ? layerCuts.end[li] : endCut) : null; const layerEndCut = e >= axisLen - 1e-6 ? (layerCuts ? layerCuts.end[li] : endCut) : null;
// Durchgangswand-Aussparung: liegt der Quer-Offsetbereich dieses Bandes // Durchgangswand-Aussparung: liegt der Quer-Offsetbereich dieses Bandes
@@ -363,7 +365,7 @@ export function addWallPoche(
out.push({ out.push({
kind: "polygon", kind: "polygon",
pts: clippedBand(axisPt(a), axisPt(b), off, off + layer.thickness, sc, ec), pts: clippedBand(axisPt(a), axisPt(b), off, off + layer.thickness, sc, ec),
fill: layerSolid ? HATCH_INK : HATCH_PAPER, fill: bgOverride ?? (layerSolid ? HATCH_INK : HATCH_PAPER),
// Band ohne Umriss: keine Schichtfuge über die Bandkante, keine 45°-Naht // Band ohne Umriss: keine Schichtfuge über die Bandkante, keine 45°-Naht
// am Knoten. Die Fugen kommen als separate `line`-Primitive (siehe unten). // am Knoten. Die Fugen kommen als separate `line`-Primitive (siehe unten).
stroke: "none", stroke: "none",
+73
View File
@@ -0,0 +1,73 @@
/**
* `splitWallLayers`/`splitSlabLayers` — Attribut-Override (By-Layer/By-Object)
* bei MEHRSCHICHTIGEM Wand-/Deckentyp im Schnitt. Vorher wurde `owner.foreground`/
* `hatchId`/`hatchSource`/`background`/`backgroundSource` hier komplett ignoriert
* (nur der einschichtige Pfad über resolveWallSectionStyle/resolveCeilingSectionStyle
* respektierte den Override) — Regressionsschutz für den Nachzieh-Fix
* (2D-Vervollständigung, Fund D).
*/
import { describe, it, expect } from "vitest";
import { sampleProject } from "../model/sampleProject";
import { getCeilingType, getWallType } from "../model/types";
import { splitWallLayers, splitSlabLayers, type SectionCutPolygon } from "./toSection";
const cp: SectionCutPolygon = {
component: { kind: "wall", index: 0 },
color: [0.5, 0.5, 0.5],
pts: [
[0, 0],
[1, 0],
[1, 2],
[0, 2],
],
};
describe("splitWallLayers — Attribut-Override greift auf ALLE Schichten", () => {
const wall = sampleProject.walls.find((w) => w.id === "W1")!; // wallTypeId "aw", 4 Schichten
const wt = getWallType(sampleProject, wall);
it("Testvoraussetzung: Wandtyp ist tatsächlich mehrschichtig", () => {
expect(wt.layers.length).toBeGreaterThan(1);
});
it("ohne Override: Füllung bleibt SIA-neutral, nicht die Override-Testfarbe", () => {
const bands = splitWallLayers(cp, sampleProject, wt, wall, { x: 1, y: 0 });
expect(bands.length).toBe(wt.layers.length);
for (const b of bands) {
expect(typeof b.fill).toBe("string");
expect(b.fill).not.toBe("#ff00ff");
}
});
it("mit explizitem background-Override: JEDE Schicht bekommt die Override-Farbe", () => {
const overridden = { ...wall, background: "#ff00ff" };
const bands = splitWallLayers(cp, sampleProject, wt, overridden, { x: 1, y: 0 });
expect(bands.length).toBe(wt.layers.length);
for (const b of bands) expect(b.fill).toBe("#ff00ff");
});
});
describe("splitSlabLayers — Attribut-Override greift auf ALLE Schichten", () => {
const ceiling = (sampleProject.ceilings ?? []).find((c) => c.id === "C1")!; // ceilingTypeId "dg-massiv"
const wt = getCeilingType(sampleProject, ceiling);
it("Testvoraussetzung: Deckentyp ist tatsächlich mehrschichtig", () => {
expect(wt.layers.length).toBeGreaterThan(1);
});
it("mit explizitem background-Override: JEDE Schicht bekommt die Override-Farbe", () => {
const overridden = { ...ceiling, background: "#00ffff" };
const bands = splitSlabLayers(cp, sampleProject, wt, overridden);
expect(bands.length).toBe(wt.layers.length);
for (const b of bands) expect(b.fill).toBe("#00ffff");
});
it("mit backgroundSource:'layer': category.color wird für ALLE Schichten übernommen", () => {
const category = sampleProject.layers.find((l) => l.code === ceiling.categoryCode)!;
const overridden = { ...ceiling, backgroundSource: "layer" as const };
const bands = splitSlabLayers(cp, sampleProject, wt, overridden);
expect(bands.length).toBe(wt.layers.length);
for (const b of bands) expect(b.fill).toBe(category.color);
});
});
+28 -8
View File
@@ -18,7 +18,7 @@
// v = absolute Höhe (world.y). Siehe src-tauri/render3d/src/section.rs. // v = absolute Höhe (world.y). Siehe src-tauri/render3d/src/section.rs.
import type { Ceiling, DrawingLevel, Project, Vec2, Wall } from "../model/types"; import type { Ceiling, DrawingLevel, Project, Vec2, Wall } from "../model/types";
import { getCeilingType, getComponent, getWallType, openingsOfWall, roofLayers } from "../model/types"; import { getCeilingType, getComponent, getLayerCategory, getWallType, openingsOfWall, roofLayers } from "../model/types";
import { ceilingVerticalExtent, wallVerticalExtent } from "../model/wall"; import { ceilingVerticalExtent, wallVerticalExtent } from "../model/wall";
import { dot, leftNormal, normalize, sub } from "../model/geometry"; import { dot, leftNormal, normalize, sub } from "../model/geometry";
import { openingInterval, openingVerticalExtent } from "../geometry/opening"; import { openingInterval, openingVerticalExtent } from "../geometry/opening";
@@ -28,9 +28,11 @@ import { loadEngine3d } from "../engine/engine3d";
import { import {
HATCH_INK, HATCH_INK,
HATCH_PAPER, HATCH_PAPER,
resolveBackground,
resolveCeilingSectionStyle, resolveCeilingSectionStyle,
resolveForeground, resolveForeground,
resolveHatch, resolveHatch,
resolveHatchId,
resolveWallSectionStyle, resolveWallSectionStyle,
} from "./generatePlan"; } from "./generatePlan";
import type { HatchRender } from "./generatePlan"; import type { HatchRender } from "./generatePlan";
@@ -343,7 +345,7 @@ function attachCutStyles(output: SectionOutput, project: Project, plane: Section
if (owner) { if (owner) {
const wt = getCeilingType(project, owner); const wt = getCeilingType(project, owner);
if (wt.layers.length > 1) { if (wt.layers.length > 1) {
bands.push(...splitSlabLayers(cp, project, wt)); bands.push(...splitSlabLayers(cp, project, wt, owner));
continue; continue;
} }
const style = resolveCeilingSectionStyle(project, owner); const style = resolveCeilingSectionStyle(project, owner);
@@ -761,10 +763,11 @@ function bandFromRect(src: SectionCutPolygon, r: Rect): SectionCutPolygon {
* aneinanderstoßenden Rechtecken (jedes bekommt in `generateSectionPlan` die * aneinanderstoßenden Rechtecken (jedes bekommt in `generateSectionPlan` die
* generische Schnitt-Umrisslinie). * generische Schnitt-Umrisslinie).
*/ */
function splitSlabLayers( export function splitSlabLayers(
cp: SectionCutPolygon, cp: SectionCutPolygon,
project: Project, project: Project,
wt: ReturnType<typeof getCeilingType>, wt: ReturnType<typeof getCeilingType>,
owner: Ceiling,
): SectionCutPolygon[] { ): SectionCutPolygon[] {
const totalT = wt.layers.reduce((s, l) => s + l.thickness, 0); const totalT = wt.layers.reduce((s, l) => s + l.thickness, 0);
const us = cp.pts.map((p) => p[0]); const us = cp.pts.map((p) => p[0]);
@@ -776,17 +779,23 @@ function splitSlabLayers(
const vSpan = vMax - vMin; const vSpan = vMax - vMin;
if (totalT <= 1e-9 || vSpan <= 1e-9) return [cp]; if (totalT <= 1e-9 || vSpan <= 1e-9) return [cp];
const scale = vSpan / totalT; const scale = vSpan / totalT;
// Attribut-Override der Decke (By-Layer/By-Object) gilt uniform für ALLE
// Schichten — analog zu splitWallLayers und dem Grundriss (addCeilingPoche).
const category = getLayerCategory(project, owner.categoryCode);
const out: SectionCutPolygon[] = []; const out: SectionCutPolygon[] = [];
let acc = 0; // kumulierte Dicke von OK (oben) nach UK (unten) let acc = 0; // kumulierte Dicke von OK (oben) nach UK (unten)
for (const layer of wt.layers) { for (const layer of wt.layers) {
const comp = getComponent(project, layer.componentId); const comp = getComponent(project, layer.componentId);
const hatchId =
resolveHatchId(owner.hatchId, owner.hatchSource, category, comp.hatchId) ?? comp.hatchId;
const hatch = resolveHatch( const hatch = resolveHatch(
project, project,
comp.hatchId, hatchId,
SECTION_SLAB_AXIS_ANGLE_DEG, SECTION_SLAB_AXIS_ANGLE_DEG,
resolveForeground(comp), resolveForeground(comp, owner.foreground, category, owner.foregroundSource),
); );
const bgOverride = resolveBackground(comp, owner.background, category, owner.backgroundSource);
const vTop = vMax - acc * scale; const vTop = vMax - acc * scale;
acc += layer.thickness; acc += layer.thickness;
const vBottom = vMax - acc * scale; const vBottom = vMax - acc * scale;
@@ -799,7 +808,7 @@ function splitSlabLayers(
[uMax, vBottom], [uMax, vBottom],
[uMin, vBottom], [uMin, vBottom],
], ],
fill: hatch.pattern === "solid" ? HATCH_INK : HATCH_PAPER, fill: bgOverride ?? (hatch.pattern === "solid" ? HATCH_INK : HATCH_PAPER),
hatch, hatch,
joinPriority: comp.joinPriority, joinPriority: comp.joinPriority,
componentId: comp.id, componentId: comp.id,
@@ -870,6 +879,9 @@ export function splitWallLayers(
// Aussen/Innen-Orientierung: per Default liegt layers[0] an uMin; zeigt die // Aussen/Innen-Orientierung: per Default liegt layers[0] an uMin; zeigt die
// Wand-Aufbaurichtung gegen die Schnitt-u-Achse, werden die Bänder gespiegelt. // Wand-Aufbaurichtung gegen die Schnitt-u-Achse, werden die Bänder gespiegelt.
const layers = wallLayersReversedInU(owner, uWorld) ? [...wt.layers].reverse() : wt.layers; const layers = wallLayersReversedInU(owner, uWorld) ? [...wt.layers].reverse() : wt.layers;
// Attribut-Override der Wand (By-Layer/By-Object) gilt uniform für ALLE
// Schichten — analog zum Grundriss (addWallPoche in walls.ts).
const category = getLayerCategory(project, owner.categoryCode);
const out: SectionCutPolygon[] = []; const out: SectionCutPolygon[] = [];
let acc = 0; // kumulierte Dicke von uMin (erste einsortierte Schicht) nach uMax let acc = 0; // kumulierte Dicke von uMin (erste einsortierte Schicht) nach uMax
@@ -880,7 +892,15 @@ export function splitWallLayers(
// entlang v, Modellwinkel 90°). Wie im Grundriss (`addWallPoche` übergibt // entlang v, Modellwinkel 90°). Wie im Grundriss (`addWallPoche` übergibt
// die Wandachse) wird der Achswinkel mitgegeben, sonst liefe die Dämmung // die Wandachse) wird der Achswinkel mitgegeben, sonst liefe die Dämmung
// vertikal statt quer zur Wanddicke (horizontal). // vertikal statt quer zur Wanddicke (horizontal).
const hatch = resolveHatch(project, comp.hatchId, SECTION_WALL_AXIS_ANGLE_DEG, resolveForeground(comp)); const hatchId =
resolveHatchId(owner.hatchId, owner.hatchSource, category, comp.hatchId) ?? comp.hatchId;
const hatch = resolveHatch(
project,
hatchId,
SECTION_WALL_AXIS_ANGLE_DEG,
resolveForeground(comp, owner.foreground, category, owner.foregroundSource),
);
const bgOverride = resolveBackground(comp, owner.background, category, owner.backgroundSource);
const uLeft = uMin + acc * scale; const uLeft = uMin + acc * scale;
acc += layer.thickness; acc += layer.thickness;
const uRight = uMin + acc * scale; const uRight = uMin + acc * scale;
@@ -893,7 +913,7 @@ export function splitWallLayers(
[uRight, vMin], [uRight, vMin],
[uLeft, vMin], [uLeft, vMin],
], ],
fill: hatch.pattern === "solid" ? HATCH_INK : HATCH_PAPER, fill: bgOverride ?? (hatch.pattern === "solid" ? HATCH_INK : HATCH_PAPER),
hatch, hatch,
joinPriority: comp.joinPriority, joinPriority: comp.joinPriority,
componentId: comp.id, componentId: comp.id,