DXF-Import: HATCH via Custom-Handler -> gefuellte geschlossene Drawing2D-Flaeche
This commit is contained in:
+105
-1
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { parseDxf } from "./dxfParser";
|
import { parseDxf } from "./dxfParser";
|
||||||
import type { Contour, Vec2 } from "../model/types";
|
import { contoursToDrawings } from "./dxfToDrawings";
|
||||||
|
import type { Contour, ContourSet, Vec2 } from "../model/types";
|
||||||
|
|
||||||
/** Minimales DXF aus Gruppencode/Wert-Paaren; nur eine ENTITIES-Sektion. */
|
/** Minimales DXF aus Gruppencode/Wert-Paaren; nur eine ENTITIES-Sektion. */
|
||||||
function dxf(...entities: string[][]): string {
|
function dxf(...entities: string[][]): string {
|
||||||
@@ -286,3 +287,106 @@ describe("parseDxf — INSERT", () => {
|
|||||||
expect(c.pts[1].y).toBeCloseTo(3, 9);
|
expect(c.pts[1].y).toBeCloseTo(3, 9);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── HATCH (gefüllte Flächen) ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** HATCH mit Polyline-Randpfad (Flag 3 = external+polyline). */
|
||||||
|
function hatchPoly(verts: Array<[number, number]>): string[] {
|
||||||
|
const g = ["0", "HATCH", "8", "0", "2", "SOLID", "70", "1", "91", "1", "92", "3", "72", "0", "73", "1", "93", `${verts.length}`];
|
||||||
|
for (const [x, y] of verts) g.push("10", `${x}`, "20", `${y}`);
|
||||||
|
g.push("97", "0");
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** HATCH mit Kanten-Randpfad aus Linienkanten (Flag 1 = external, kein Polyline-Bit). */
|
||||||
|
function hatchLineEdges(verts: Array<[number, number]>): string[] {
|
||||||
|
const g = ["0", "HATCH", "8", "0", "2", "SOLID", "70", "1", "91", "1", "92", "1", "93", `${verts.length}`];
|
||||||
|
for (let k = 0; k < verts.length; k++) {
|
||||||
|
const a = verts[k];
|
||||||
|
const b = verts[(k + 1) % verts.length];
|
||||||
|
g.push("72", "1", "10", `${a[0]}`, "20", `${a[1]}`, "11", `${b[0]}`, "21", `${b[1]}`);
|
||||||
|
}
|
||||||
|
g.push("97", "0");
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** HATCH mit einer Bogenkante (Kantentyp 2), Winkel in Grad. */
|
||||||
|
function hatchArc(cx: number, cy: number, r: number, s: number, e: number): string[] {
|
||||||
|
return [
|
||||||
|
"0", "HATCH", "8", "0", "2", "SOLID", "70", "1",
|
||||||
|
"91", "1", "92", "1", "93", "1",
|
||||||
|
"72", "2", "10", `${cx}`, "20", `${cy}`, "40", `${r}`, "50", `${s}`, "51", `${e}`, "73", "1",
|
||||||
|
"97", "0",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("parseDxf — HATCH", () => {
|
||||||
|
it("Polyline-Rand → geschlossene gefüllte Kontur", () => {
|
||||||
|
const c = onlyContour(dxf(hatchPoly([[0, 0], [10, 0], [10, 10], [0, 10]])));
|
||||||
|
expect(c.closed).toBe(true);
|
||||||
|
expect(c.filled).toBe(true);
|
||||||
|
expect(c.pts).toHaveLength(4);
|
||||||
|
expect(c.pts[1].x).toBeCloseTo(10, 9);
|
||||||
|
expect(c.pts[1].y).toBeCloseTo(0, 9);
|
||||||
|
expect(c.pts[2].x).toBeCloseTo(10, 9);
|
||||||
|
expect(c.pts[2].y).toBeCloseTo(10, 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Linienkanten-Rand → geschlossene gefüllte Kontur (Eckpunkte)", () => {
|
||||||
|
const c = onlyContour(dxf(hatchLineEdges([[0, 0], [4, 0], [4, 4], [0, 4]])));
|
||||||
|
expect(c.closed).toBe(true);
|
||||||
|
expect(c.filled).toBe(true);
|
||||||
|
expect(c.pts).toHaveLength(4);
|
||||||
|
const xs = c.pts.map((p) => p.x).sort((a, b) => a - b);
|
||||||
|
expect(xs[0]).toBeCloseTo(0, 9);
|
||||||
|
expect(xs[3]).toBeCloseTo(4, 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Bogenkante → tessellierter Halbkreis auf Radius", () => {
|
||||||
|
const c = onlyContour(dxf(hatchArc(0, 0, 5, 0, 180)));
|
||||||
|
expect(c.filled).toBe(true);
|
||||||
|
for (const p of c.pts) expect(Math.hypot(p.x, p.y)).toBeCloseTo(5, 6);
|
||||||
|
expect(c.pts[0].x).toBeCloseTo(5, 6);
|
||||||
|
expect(c.pts[0].y).toBeCloseTo(0, 6);
|
||||||
|
const last = c.pts[c.pts.length - 1];
|
||||||
|
expect(last.x).toBeCloseTo(-5, 6);
|
||||||
|
expect(last.y).toBeCloseTo(0, 6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("mehrere Randpfade → mehrere Loops (Insel als eigener Ring)", () => {
|
||||||
|
// Zwei Polyline-Pfade in EINER HATCH: numPaths = 2.
|
||||||
|
const g = [
|
||||||
|
"0", "HATCH", "8", "0", "2", "SOLID", "70", "1", "91", "2",
|
||||||
|
"92", "3", "72", "0", "73", "1", "93", "4",
|
||||||
|
"10", "0", "20", "0", "10", "10", "20", "0", "10", "10", "20", "10", "10", "0", "20", "10",
|
||||||
|
"97", "0",
|
||||||
|
"92", "3", "72", "0", "73", "1", "93", "4",
|
||||||
|
"10", "3", "20", "3", "10", "7", "20", "3", "10", "7", "20", "7", "10", "3", "20", "7",
|
||||||
|
"97", "0",
|
||||||
|
];
|
||||||
|
const res = parseDxf(dxf(g));
|
||||||
|
expect(res.contours[0].contours).toHaveLength(2);
|
||||||
|
expect(res.contours[0].contours.every((c) => c.filled && c.closed)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("contoursToDrawings — HATCH-Füllung", () => {
|
||||||
|
it("gefüllte Kontur → Drawing2D mit fillColor und polyline-Form", () => {
|
||||||
|
const set: ContourSet = {
|
||||||
|
id: "s", type: "contourSet", name: "h",
|
||||||
|
contours: [{ z: 0, closed: true, filled: true, pts: [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }] }],
|
||||||
|
};
|
||||||
|
const [d] = contoursToDrawings([set], "lvl", "active", "CODE", (s) => s);
|
||||||
|
expect(d.geom.shape).toBe("polyline");
|
||||||
|
expect(d.fillColor).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ungefüllte Kontur → Drawing2D ohne fillColor", () => {
|
||||||
|
const set: ContourSet = {
|
||||||
|
id: "s", type: "contourSet", name: "h",
|
||||||
|
contours: [{ z: 0, closed: true, pts: [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }] }],
|
||||||
|
};
|
||||||
|
const [d] = contoursToDrawings([set], "lvl", "active", "CODE", (s) => s);
|
||||||
|
expect(d.fillColor).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
// • ELLIPSE → Kontur (Ellipsenbogen/-umlauf, tesselliert).
|
// • ELLIPSE → Kontur (Ellipsenbogen/-umlauf, tesselliert).
|
||||||
// • SPLINE → Kontur (B-Spline via De Boor; Fallback fitPoints).
|
// • SPLINE → Kontur (B-Spline via De Boor; Fallback fitPoints).
|
||||||
// • INSERT → Block-Konturen, transformiert (Scale/Rot/Array, verschachtelt).
|
// • INSERT → Block-Konturen, transformiert (Scale/Rot/Array, verschachtelt).
|
||||||
|
// • HATCH → gefüllte Kontur(en) je Randpfad (Custom-Handler,
|
||||||
|
// Polyline- + Linien-/Bogen-Kanten; Fill via `Contour.filled`).
|
||||||
|
|
||||||
import DxfParser from "dxf-parser";
|
import DxfParser from "dxf-parser";
|
||||||
import type { Contour, ContourSet, ImportedMesh, Vec2 } from "../model/types";
|
import type { Contour, ContourSet, ImportedMesh, Vec2 } from "../model/types";
|
||||||
@@ -93,8 +95,15 @@ interface DxfEntity {
|
|||||||
rowCount?: number;
|
rowCount?: number;
|
||||||
columnSpacing?: number;
|
columnSpacing?: number;
|
||||||
rowSpacing?: number;
|
rowSpacing?: number;
|
||||||
|
// HATCH: rohe Gruppencodes (vom Custom-Handler gesammelt, siehe HatchHandler).
|
||||||
|
rawCodes?: RawCode[];
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
|
/** Ein rohes DXF-Gruppencode/Wert-Paar (HATCH-Randpfad-Auswertung). */
|
||||||
|
interface RawCode {
|
||||||
|
code: number;
|
||||||
|
value: number | string;
|
||||||
|
}
|
||||||
/** Ein Block (BLOCKS-Tabelle): Basispunkt `position` + eigene Entity-Liste. */
|
/** Ein Block (BLOCKS-Tabelle): Basispunkt `position` + eigene Entity-Liste. */
|
||||||
interface DxfBlock {
|
interface DxfBlock {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -117,6 +126,12 @@ const nextId = (prefix: string) =>
|
|||||||
*/
|
*/
|
||||||
export function parseDxf(text: string): DxfImportResult {
|
export function parseDxf(text: string): DxfImportResult {
|
||||||
const parser = new DxfParser();
|
const parser = new DxfParser();
|
||||||
|
// dxf-parser bringt keinen HATCH-Handler mit (verwirft HATCH sonst stumm).
|
||||||
|
// Eigenen registrieren, der die rohen Gruppencodes einsammelt; die Auswertung
|
||||||
|
// der Randpfade läuft separat und testbar in `hatchContours`.
|
||||||
|
(parser as unknown as {
|
||||||
|
registerEntityHandler: (h: new () => unknown) => void;
|
||||||
|
}).registerEntityHandler(HatchHandler);
|
||||||
// parseSync wirft bei strukturell kaputtem DXF; das soll nach oben.
|
// parseSync wirft bei strukturell kaputtem DXF; das soll nach oben.
|
||||||
const doc = parser.parseSync(text) as unknown as DxfDocument;
|
const doc = parser.parseSync(text) as unknown as DxfDocument;
|
||||||
const entities = doc?.entities ?? [];
|
const entities = doc?.entities ?? [];
|
||||||
@@ -668,6 +683,9 @@ function collectContours(
|
|||||||
if (c) out.push(c);
|
if (c) out.push(c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "HATCH":
|
||||||
|
for (const c of hatchContours(e)) out.push(c);
|
||||||
|
break;
|
||||||
case "INSERT":
|
case "INSERT":
|
||||||
expandInsert(e, blocks, out, depth);
|
expandInsert(e, blocks, out, depth);
|
||||||
break;
|
break;
|
||||||
@@ -676,3 +694,176 @@ function collectContours(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── HATCH (Custom-Handler + Randpfad-Auswertung → gefüllte Konturen) ──────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom dxf-parser-Handler für HATCH: sammelt die rohen Gruppencodes der Entity
|
||||||
|
* (bis zum nächsten Code 0), OHNE zu interpretieren. Muster wie die eingebauten
|
||||||
|
* Handler (Schleife bricht bei Code 0; `lastReadGroup` bleibt darauf stehen).
|
||||||
|
* Die eigentliche Randpfad-Logik liegt testbar in `hatchContours`.
|
||||||
|
*/
|
||||||
|
class HatchHandler {
|
||||||
|
ForEntityName = "HATCH";
|
||||||
|
parseEntity(
|
||||||
|
scanner: {
|
||||||
|
next: () => { code: number; value: number | string };
|
||||||
|
isEOF: () => boolean;
|
||||||
|
},
|
||||||
|
curr: { code: number; value: number | string },
|
||||||
|
): DxfEntity {
|
||||||
|
const raw: RawCode[] = [];
|
||||||
|
const entity: DxfEntity = { type: String(curr.value), rawCodes: raw };
|
||||||
|
curr = scanner.next();
|
||||||
|
while (!scanner.isEOF()) {
|
||||||
|
if (curr.code === 0) break;
|
||||||
|
if (curr.code === 8) entity.layer = String(curr.value);
|
||||||
|
raw.push({ code: curr.code, value: curr.value });
|
||||||
|
curr = scanner.next();
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zahlenwert eines Gruppencodes (tolerant). */
|
||||||
|
function rc(v: number | string | undefined): number {
|
||||||
|
if (typeof v === "number") return Number.isFinite(v) ? v : 0;
|
||||||
|
const n = typeof v === "string" ? Number.parseFloat(v) : NaN;
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Erster Wert eines Codes aus der Kanten-Code-Sammlung. */
|
||||||
|
function firstOf(m: Map<number, number[]>, code: number): number | undefined {
|
||||||
|
const a = m.get(code);
|
||||||
|
return a && a.length > 0 ? a[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bogenkante (Kantentyp 2): Zentrum/Radius, Winkel in GRAD, ggf. im Uhrzeigersinn. */
|
||||||
|
function pushArcEdge(pts: Vec2[], m: Map<number, number[]>): void {
|
||||||
|
const cx = firstOf(m, 10);
|
||||||
|
const cy = firstOf(m, 20);
|
||||||
|
const r = firstOf(m, 40);
|
||||||
|
if (cx === undefined || cy === undefined || r === undefined || r <= 0) return;
|
||||||
|
const s = (firstOf(m, 50) ?? 0) * (Math.PI / 180);
|
||||||
|
const e = (firstOf(m, 51) ?? 360) * (Math.PI / 180);
|
||||||
|
const ccw = (firstOf(m, 73) ?? 1) !== 0;
|
||||||
|
// Spanne in Laufrichtung auf (0, 2π] normieren.
|
||||||
|
let sweep = ccw ? e - s : -(e - s);
|
||||||
|
sweep = ((sweep % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);
|
||||||
|
if (sweep === 0) sweep = Math.PI * 2;
|
||||||
|
const dir = ccw ? 1 : -1;
|
||||||
|
const segs = segmentsFor(sweep);
|
||||||
|
// Startpunkt weglassen, wenn er den vorherigen Kanten-Endpunkt dupliziert.
|
||||||
|
for (let i = 0; i <= segs; i++) {
|
||||||
|
const t = s + dir * (sweep * i) / segs;
|
||||||
|
pts.push({ x: cx + r * Math.cos(t), y: cy + r * Math.sin(t) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Punkte einer HATCH-Randkante an die Loop-Punkte anhängen (Typ 1 Linie, 2 Bogen). */
|
||||||
|
function appendEdge(pts: Vec2[], edgeType: number, m: Map<number, number[]>): void {
|
||||||
|
if (edgeType === 1) {
|
||||||
|
// Linie: Start (10/20) — der Endpunkt ist der Start der nächsten Kante.
|
||||||
|
const x = firstOf(m, 10);
|
||||||
|
const y = firstOf(m, 20);
|
||||||
|
if (x !== undefined && y !== undefined) pts.push({ x, y });
|
||||||
|
// Bei der LETZTEN Kante fehlt sonst der Endpunkt; der closed-Ring schließt ihn.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (edgeType === 2) {
|
||||||
|
pushArcEdge(pts, m);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Kantentyp 3 (Ellipse) / 4 (Spline): im ersten Wurf nicht unterstützt.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HATCH-Entity (rohe Gruppencodes) → gefüllte, geschlossene Konturen (ein Loop
|
||||||
|
* je Randpfad). Unterstützt Polyline-Randpfade (Flag-Bit 2) sowie Kanten-Pfade
|
||||||
|
* mit Linien- und Bogenkanten. Bulges an Polyline-Rändern und Ellipse-/Spline-
|
||||||
|
* Kanten werden im ersten Wurf ignoriert (als Sehne bzw. übersprungen). Insel-
|
||||||
|
* Loops entstehen als eigene geschlossene Ringe (keine echten Löcher).
|
||||||
|
*/
|
||||||
|
function hatchContours(e: DxfEntity): Contour[] {
|
||||||
|
const raw = e.rawCodes;
|
||||||
|
if (!raw || raw.length === 0) return [];
|
||||||
|
const n = raw.length;
|
||||||
|
const layer = e.layer;
|
||||||
|
|
||||||
|
// Zum ersten Code 91 (Anzahl Randpfade).
|
||||||
|
let i = 0;
|
||||||
|
while (i < n && raw[i].code !== 91) i++;
|
||||||
|
if (i >= n) return [];
|
||||||
|
const numPaths = rc(raw[i].value);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
const loops: Contour[] = [];
|
||||||
|
for (let p = 0; p < numPaths && i < n; p++) {
|
||||||
|
// Zum Pfadtyp-Flag (92) des nächsten Pfads.
|
||||||
|
while (i < n && raw[i].code !== 92) i++;
|
||||||
|
if (i >= n) break;
|
||||||
|
const flag = rc(raw[i].value);
|
||||||
|
i++;
|
||||||
|
const isPolyline = (flag & 2) !== 0;
|
||||||
|
const pts: Vec2[] = [];
|
||||||
|
|
||||||
|
if (isPolyline) {
|
||||||
|
// Optionale 72/73 überspringen, bis 93 (Vertex-Anzahl).
|
||||||
|
while (i < n && raw[i].code !== 93) i++;
|
||||||
|
if (i >= n) break;
|
||||||
|
const numVerts = rc(raw[i].value);
|
||||||
|
i++;
|
||||||
|
let cx: number | null = null;
|
||||||
|
let got = 0;
|
||||||
|
while (i < n && got < numVerts) {
|
||||||
|
const c = raw[i];
|
||||||
|
if (c.code === 91 || c.code === 92) break; // Sicherheitsnetz
|
||||||
|
if (c.code === 10) {
|
||||||
|
cx = rc(c.value);
|
||||||
|
} else if (c.code === 20 && cx !== null) {
|
||||||
|
pts.push({ x: cx, y: rc(c.value) });
|
||||||
|
cx = null;
|
||||||
|
got++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Kanten-Pfad: 93 = Anzahl Kanten.
|
||||||
|
while (i < n && raw[i].code !== 93) i++;
|
||||||
|
if (i >= n) break;
|
||||||
|
const numEdges = rc(raw[i].value);
|
||||||
|
i++;
|
||||||
|
for (let ed = 0; ed < numEdges && i < n; ed++) {
|
||||||
|
// Zum Kantentyp (72), aber nicht über den Pfad/Kanten-Block hinaus.
|
||||||
|
while (
|
||||||
|
i < n &&
|
||||||
|
raw[i].code !== 72 &&
|
||||||
|
raw[i].code !== 97 &&
|
||||||
|
raw[i].code !== 92 &&
|
||||||
|
raw[i].code !== 91
|
||||||
|
) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= n || raw[i].code !== 72) break;
|
||||||
|
const edgeType = rc(raw[i].value);
|
||||||
|
i++;
|
||||||
|
// Kanten-Codes bis zur nächsten Kante/Blockgrenze sammeln.
|
||||||
|
const m = new Map<number, number[]>();
|
||||||
|
while (i < n) {
|
||||||
|
const c = raw[i];
|
||||||
|
if (c.code === 72 || c.code === 97 || c.code === 92 || c.code === 91) break;
|
||||||
|
const arr = m.get(c.code) ?? [];
|
||||||
|
arr.push(rc(c.value));
|
||||||
|
m.set(c.code, arr);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
appendEdge(pts, edgeType, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pts.length >= 2) {
|
||||||
|
loops.push({ z: 0, pts, closed: true, layer, filled: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loops;
|
||||||
|
}
|
||||||
|
|||||||
+11
-2
@@ -23,6 +23,9 @@ export type CategoryMode = "active" | "byLayer";
|
|||||||
let drawingSeq = 0;
|
let drawingSeq = 0;
|
||||||
const nextId = (): string => `dxf2d-${Date.now()}-${drawingSeq++}`;
|
const nextId = (): string => `dxf2d-${Date.now()}-${drawingSeq++}`;
|
||||||
|
|
||||||
|
/** Default-Füllfarbe für aus HATCH importierte Flächen (neutral, restylebar). */
|
||||||
|
const DXF_HATCH_FILL = "#c8c8c8";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wandelt einen Satz Konturen-Sätze in Drawing2D-Elemente für die Ziel-Ebene
|
* Wandelt einen Satz Konturen-Sätze in Drawing2D-Elemente für die Ziel-Ebene
|
||||||
* `levelId` um. Bei `categoryMode === "byLayer"` liefert `layerToCode` den
|
* `levelId` um. Bei `categoryMode === "byLayer"` liefert `layerToCode` den
|
||||||
@@ -45,13 +48,19 @@ export function contoursToDrawings(
|
|||||||
categoryMode === "byLayer" && layerName
|
categoryMode === "byLayer" && layerName
|
||||||
? layerToCode(layerName)
|
? layerToCode(layerName)
|
||||||
: fallbackCode;
|
: fallbackCode;
|
||||||
out.push({
|
const drawing: Drawing2D = {
|
||||||
id: nextId(),
|
id: nextId(),
|
||||||
type: "drawing2d",
|
type: "drawing2d",
|
||||||
levelId,
|
levelId,
|
||||||
categoryCode,
|
categoryCode,
|
||||||
geom,
|
geom,
|
||||||
});
|
};
|
||||||
|
// Aus einer HATCH stammende Kontur → geschlossene, gefüllte Form. Vollton-
|
||||||
|
// Füllung als restylebarer Default (neutrales Grau); Umriss bleibt Kategorie.
|
||||||
|
if (contour.filled && geom.shape === "polyline") {
|
||||||
|
drawing.fillColor = DXF_HATCH_FILL;
|
||||||
|
}
|
||||||
|
out.push(drawing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@@ -1093,6 +1093,11 @@ export interface Contour {
|
|||||||
closed: boolean;
|
closed: boolean;
|
||||||
/** Ursprünglicher DXF-Layer-Name (für die Kategorisierung beim 2D-Import). */
|
/** Ursprünglicher DXF-Layer-Name (für die Kategorisierung beim 2D-Import). */
|
||||||
layer?: string;
|
layer?: string;
|
||||||
|
/**
|
||||||
|
* Gefüllte Fläche (aus einer DXF-HATCH). Beim Drawing-Import wird daraus eine
|
||||||
|
* geschlossene, gefüllte 2D-Form (Vollton-Füllung); sonst nur ein Umriss.
|
||||||
|
*/
|
||||||
|
filled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ein Satz Konturen (z. B. alle Höhenlinien eines DXF-Imports). */
|
/** Ein Satz Konturen (z. B. alle Höhenlinien eines DXF-Imports). */
|
||||||
|
|||||||
Reference in New Issue
Block a user