task(CAD-2): real hatch patterns - 9 patterns (ANSI31/32/37, NET, GRASS, DOTS, SOLID + legacy), boundary detection, render adapter hatch lines

This commit is contained in:
Agent Zero
2026-08-29 10:03:01 +02:00
parent 33f6f2d21b
commit 11a0dcb4e8
4 changed files with 334 additions and 5 deletions
+63 -3
View File
@@ -16,6 +16,7 @@
*/
import type { CADElement } from '../../types/cad.types';
import { getDimStyle } from '../../services/dimStyleService';
import { generateHatchLines, computeBoundary, getHatchPattern } from '../../services/hatchPatternService';
import type { Pt } from '../../tools/modification/geometry';
/** Zeichenprimitiv — kleinste darstellbare Einheit. */
@@ -66,6 +67,62 @@ function arrowPrimitives(x: number, y: number, ang: number, size: number, color:
}];
}
/**
* Task CAD-2: Hatch-Linien fuer ein Element mit hatch-Properties.
* Nutzt hatchPatternService.generateHatchLines mit Boundary aus der
* Element-Kontur. Ohne Muster: leeres Array.
*/
function hatchPrimitives(el: CADElement, stroke: string): ShapePrimitive[] {
const props = el.properties as Record<string, unknown>;
if (props.hatch !== true) return [];
const patternId = (props.hatchPattern as string | undefined) ?? 'ANSI31';
if (!getHatchPattern(patternId)) return [];
const spacing = (props.hatchSpacing as number | undefined) ?? 8;
// Boundary aus Kontur je Elementtyp
let bbox: { minX: number; minY: number; maxX: number; maxY: number } | null = null;
if (Array.isArray(props.points)) {
bbox = computeBoundary(props.points as Array<{ x: number; y: number }>);
} else if (el.type === 'circle' || el.type === 'arc') {
const r = (props.radius as number | undefined) ?? el.width / 2;
bbox = { minX: el.x - r, minY: el.y - r, maxX: el.x + r, maxY: el.y + r };
} else {
bbox = {
minX: el.x - el.width / 2, minY: el.y - el.height / 2,
maxX: el.x + el.width / 2, maxY: el.y + el.height / 2,
};
}
if (!bbox) return [];
const segs = generateHatchLines(patternId, bbox, spacing);
const out: ShapePrimitive[] = [];
for (const s of segs) {
if (s.fill) {
const rectPrim: ShapePrimitive = {
kind: 'rect',
x: (bbox.minX + bbox.maxX) / 2,
y: (bbox.minY + bbox.maxY) / 2,
w: bbox.maxX - bbox.minX,
h: bbox.maxY - bbox.minY,
fill: s.fill,
stroke,
strokeWidth: 1,
};
out.push(rectPrim);
} else {
const linePrim: ShapePrimitive = {
kind: 'line',
from: s.from,
to: s.to,
stroke,
strokeWidth: 0.5,
};
out.push(linePrim);
}
}
return out;
}
export function elementToPrimitives(el: CADElement, attrDefs?: Array<{ tag: string; prompt: string; defaultValue?: string }>): ShapePrimitive[] {
const props = (el.properties ?? {}) as Record<string, unknown>;
const stroke = (props.stroke as string | undefined) ?? FALLBACK_STROKE;
@@ -90,7 +147,7 @@ export function elementToPrimitives(el: CADElement, attrDefs?: Array<{ tag: stri
case 'rect': {
// Legacy-BBox-Konvention: x/y ist das Zentrum des Rechtecks
return [{
const base: ShapePrimitive[] = [{
kind: 'rect',
x: el.x - el.width / 2,
y: el.y - el.height / 2,
@@ -100,11 +157,12 @@ export function elementToPrimitives(el: CADElement, attrDefs?: Array<{ tag: stri
stroke,
strokeWidth: sw,
}];
return [...base, ...hatchPrimitives(el, stroke)];
}
case 'circle': {
const radius = (props.radius as number | undefined) ?? el.width / 2;
return [{
const circBase: ShapePrimitive[] = [{
kind: 'circle',
center: { x: el.x, y: el.y },
radius,
@@ -112,6 +170,7 @@ export function elementToPrimitives(el: CADElement, attrDefs?: Array<{ tag: stri
stroke,
strokeWidth: sw,
}];
return [...circBase, ...hatchPrimitives(el, stroke)];
}
case 'arc': {
@@ -133,13 +192,14 @@ export function elementToPrimitives(el: CADElement, attrDefs?: Array<{ tag: stri
? (props.points as unknown[]).map(asPoint)
: [];
if (pts.length < 2) return [];
return [{
const polyBase: ShapePrimitive[] = [{
kind: 'polyline',
points: pts,
close: el.type === 'polygon',
stroke,
strokeWidth: sw,
}];
return [...polyBase, ...hatchPrimitives(el, stroke)];
}
case 'text': {