From f2b21d1f69dc92016bd73ca238be18ca84250e83 Mon Sep 17 00:00:00 2001 From: Karim Date: Sat, 4 Jul 2026 12:58:10 +0200 Subject: [PATCH] Boolean-Ops: vereinigte Form erbt volle Attribute des ersten Elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ringDrawing kopierte nur color/hatchId/fillColor/lineStyleId/weightMm — die neueren Attribut-Felder (background = Nachfolger von fillColor, foreground + die By-Layer/By-Object-Quellen) gingen bei Union/Difference/ Intersection verloren. Dadurch verlor z. B. eine über das Attribut-Panel rot gefüllte Fläche ihre Füllung nach dem Vereinigen. Jetzt erbt die zusammengesetzte Form das volle Erscheinungsbild des ZUERST gewählten Elements (Auswahl-Reihenfolge = Index 0). Füll-Attribute nur auf dem gefüllten Aussenring, Strich-/Ebenen-Attribute auf jedem Ring. --- src/editors/booleanOps.test.ts | 65 ++++++++++++++++++++++++++++++++++ src/editors/booleanOps.ts | 13 +++++++ 2 files changed, 78 insertions(+) create mode 100644 src/editors/booleanOps.test.ts diff --git a/src/editors/booleanOps.test.ts b/src/editors/booleanOps.test.ts new file mode 100644 index 0000000..7972f0d --- /dev/null +++ b/src/editors/booleanOps.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect } from "vitest"; +import { unionDrawings } from "./booleanOps"; +import type { Drawing2D } from "../model/types"; + +/** Geschlossenes Quadrat als Drawing2D mit optionalen Attributen. */ +function square( + id: string, + x: number, + extra: Partial = {}, +): Drawing2D { + return { + id, + type: "drawing2d", + levelId: "L1", + categoryCode: "10", + geom: { + shape: "polyline", + pts: [ + { x, y: 0 }, + { x: x + 2, y: 0 }, + { x: x + 2, y: 2 }, + { x, y: 2 }, + ], + closed: true, + }, + ...extra, + }; +} + +describe("unionDrawings — Attribut-Vererbung vom ERST gewählten Element", () => { + it("übernimmt Füllung/Ebene des ersten Elements, auch wenn das zweite keine hat", () => { + // Element 1 (zuerst gewählt): rote Füllung via `background`, Schraffur, eigene + // Ebene/Kategorie. Element 2: nackt. Die Formen überlappen. + const first = square("a", 0, { + background: "#ff0000", + hatchId: "h1", + color: "#333333", + levelId: "L2", + categoryCode: "20", + foregroundSource: "layer", + }); + const second = square("b", 1); // ohne Attribute, überlappt „a" + + const { created, replacedIds } = unionDrawings([first, second]); + + expect(replacedIds).toEqual(["a", "b"]); + expect(created.length).toBeGreaterThanOrEqual(1); + const outer = created[0]; + // Volles Erscheinungsbild des ersten Elements bleibt erhalten: + expect(outer.background).toBe("#ff0000"); + expect(outer.hatchId).toBe("h1"); + expect(outer.color).toBe("#333333"); + expect(outer.levelId).toBe("L2"); + expect(outer.categoryCode).toBe("20"); + expect(outer.foregroundSource).toBe("layer"); + }); + + it("die Reihenfolge entscheidet: das ZWEITE Element diktiert nichts", () => { + const red = square("a", 0, { background: "#ff0000" }); + const blue = square("b", 1, { background: "#0000ff" }); + // „blue" zuerst gewählt → Ergebnis blau. + const out = unionDrawings([blue, red]).created[0]; + expect(out.background).toBe("#0000ff"); + }); +}); diff --git a/src/editors/booleanOps.ts b/src/editors/booleanOps.ts index 9c7e05d..cac6b13 100644 --- a/src/editors/booleanOps.ts +++ b/src/editors/booleanOps.ts @@ -85,12 +85,25 @@ function ringDrawing(src: Drawing2D, pts: Vec2[], filled: boolean): Drawing2D { categoryCode: src.categoryCode, geom: { shape: "polyline", pts, closed: true }, }; + // Strich-/Ebenen-bezogene Attribute gelten für JEDEN Ring (Aussen wie Loch): + // die zusammengesetzte Form erbt das volle Erscheinungsbild des Quell- + // Elements (bei Boolean-Ops die des ERSTEN gewählten, s. unionDrawings). if (src.lineStyleId !== undefined) out.lineStyleId = src.lineStyleId; if (src.color !== undefined) out.color = src.color; if (src.weightMm !== undefined) out.weightMm = src.weightMm; + if (src.strokeWeightSource !== undefined) out.strokeWeightSource = src.strokeWeightSource; if (filled) { + // Füll-/Schraffur-bezogene Attribute nur auf dem gefüllten Aussenring (Löcher + // bleiben ungefüllt). `background` ist der Nachfolger von `fillColor` und wird + // mitgeführt, sonst ginge eine über das Attribut-Panel gesetzte Füllung (z. B. + // rote Fläche) bei der Vereinigung verloren. if (src.hatchId !== undefined) out.hatchId = src.hatchId; if (src.fillColor !== undefined) out.fillColor = src.fillColor; + if (src.foreground !== undefined) out.foreground = src.foreground; + if (src.background !== undefined) out.background = src.background; + if (src.foregroundSource !== undefined) out.foregroundSource = src.foregroundSource; + if (src.backgroundSource !== undefined) out.backgroundSource = src.backgroundSource; + if (src.hatchSource !== undefined) out.hatchSource = src.hatchSource; } return out; }