Boolean-Ops: vereinigte Form erbt volle Attribute des ersten Elements
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.
This commit is contained in:
@@ -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> = {},
|
||||||
|
): 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");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -85,12 +85,25 @@ function ringDrawing(src: Drawing2D, pts: Vec2[], filled: boolean): Drawing2D {
|
|||||||
categoryCode: src.categoryCode,
|
categoryCode: src.categoryCode,
|
||||||
geom: { shape: "polyline", pts, closed: true },
|
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.lineStyleId !== undefined) out.lineStyleId = src.lineStyleId;
|
||||||
if (src.color !== undefined) out.color = src.color;
|
if (src.color !== undefined) out.color = src.color;
|
||||||
if (src.weightMm !== undefined) out.weightMm = src.weightMm;
|
if (src.weightMm !== undefined) out.weightMm = src.weightMm;
|
||||||
|
if (src.strokeWeightSource !== undefined) out.strokeWeightSource = src.strokeWeightSource;
|
||||||
if (filled) {
|
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.hatchId !== undefined) out.hatchId = src.hatchId;
|
||||||
if (src.fillColor !== undefined) out.fillColor = src.fillColor;
|
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;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user