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:
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Task CAD-2 - Echte Hatch-Muster + Boundary-Erkennung.
|
||||
*
|
||||
* HATCH_PATTERNS: Standard-CAD-Muster (ANSI31, NET, GRASS etc.)
|
||||
* als Linien-Segment-Generatoren fuer eine BBox.
|
||||
* computeBoundary: umschlossene Flaeche aus Kontur-Punkten.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
HATCH_PATTERNS,
|
||||
getHatchPattern,
|
||||
generateHatchLines,
|
||||
computeBoundary,
|
||||
type HatchPatternId,
|
||||
} from '../src/services/hatchPatternService';
|
||||
|
||||
describe('CAD-2: HATCH_PATTERNS Bibliothek', () => {
|
||||
it('enthaelt die Standard-CAD-Muster', () => {
|
||||
const ids = HATCH_PATTERNS.map((p) => p.id);
|
||||
expect(ids).toContain('ANSI31');
|
||||
expect(ids).toContain('ANSI32');
|
||||
expect(ids).toContain('ANSI37');
|
||||
expect(ids).toContain('NET');
|
||||
expect(ids).toContain('GRASS');
|
||||
expect(ids).toContain('DOTS');
|
||||
expect(ids).toContain('SOLID');
|
||||
expect(ids).toContain('lines'); // Alte bleiben erhalten
|
||||
expect(ids).toContain('cross');
|
||||
});
|
||||
|
||||
it('jedes Muster hat id, label, description; spacing nur bei Nicht-SOLID', () => {
|
||||
for (const p of HATCH_PATTERNS) {
|
||||
expect(p.id.length).toBeGreaterThan(0);
|
||||
expect(p.label.length).toBeGreaterThan(0);
|
||||
// SOLID braucht keinen Abstand (Vollfuellung)
|
||||
if (!p.solid) {
|
||||
expect(p.defaultSpacing).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('getHatchPattern liefert undefined fuer unbekannte ID', () => {
|
||||
expect(getHatchPattern('gibts-nicht')).toBeUndefined();
|
||||
expect(getHatchPattern('ANSI31')).toBeDefined();
|
||||
expect(getHatchPattern('ANSI31')!.label).toContain('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CAD-2: generateHatchLines (Muster-Geometrie)', () => {
|
||||
const bbox = { minX: 0, minY: 0, maxX: 100, maxY: 50 };
|
||||
const spacing = 10;
|
||||
|
||||
it('ANSI31 (45 Grad): erzeugt Diagonal-Linien', () => {
|
||||
const lines = generateHatchLines('ANSI31', bbox, spacing);
|
||||
expect(lines.length).toBeGreaterThan(3);
|
||||
for (const seg of lines) {
|
||||
const dx = seg.to.x - seg.from.x;
|
||||
const dy = seg.to.y - seg.from.y;
|
||||
expect(Math.abs(dy / dx)).toBeCloseTo(1, 1); // 45 Grad
|
||||
}
|
||||
});
|
||||
|
||||
it('NET: erzeugt horizontale UND vertikale Linien', () => {
|
||||
const lines = generateHatchLines('NET', bbox, spacing);
|
||||
const horiz = lines.filter((l) => l.from.y === l.to.y);
|
||||
const vert = lines.filter((l) => l.from.x === l.to.x);
|
||||
expect(horiz.length).toBeGreaterThan(0);
|
||||
expect(vert.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('DOTS: erzeugt kleine Kreise (als 4-Punkt-Polygone)', () => {
|
||||
const lines = generateHatchLines('DOTS', bbox, spacing);
|
||||
expect(lines.length).toBeGreaterThan(0);
|
||||
// DOTS sind als polyline-segments repraesentiert
|
||||
});
|
||||
|
||||
it('SOLID: erzeugt EINE grosse Flaeche (fill)', () => {
|
||||
const lines = generateHatchLines('SOLID', bbox, spacing);
|
||||
expect(lines).toHaveLength(1);
|
||||
expect((lines[0] as { fill?: string }).fill).toBeDefined();
|
||||
});
|
||||
|
||||
it('unbekanntes Muster: leeres Array (renderer-sicher)', () => {
|
||||
expect(generateHatchLines('gibts-nicht', bbox, spacing)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CAD-2: computeBoundary (Boundary-Erkennung)', () => {
|
||||
it('Rechteck-Kontur: BBox wird korrekt berechnet', () => {
|
||||
const pts = [{ x: 0, y: 0 }, { x: 100, y: 0 }, { x: 100, y: 50 }, { x: 0, y: 50 }];
|
||||
const b = computeBoundary(pts);
|
||||
expect(b).toEqual({ minX: 0, minY: 0, maxX: 100, maxY: 50 });
|
||||
});
|
||||
|
||||
it('Dreieck: BBox umschliesst alle Punkte', () => {
|
||||
const pts = [{ x: 10, y: 0 }, { x: 90, y: 80 }, { x: 10, y: 80 }];
|
||||
const b = computeBoundary(pts);
|
||||
expect(b.minX).toBe(10);
|
||||
expect(b.maxX).toBe(90);
|
||||
expect(b.minY).toBe(0);
|
||||
expect(b.maxY).toBe(80);
|
||||
});
|
||||
|
||||
it('leere/wenige Punkte: null-Boundary', () => {
|
||||
expect(computeBoundary([])).toBeNull();
|
||||
expect(computeBoundary([{ x: 0, y: 0 }])).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user