109 lines
3.7 KiB
TypeScript
109 lines
3.7 KiB
TypeScript
|
|
/**
|
||
|
|
* 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();
|
||
|
|
});
|
||
|
|
});
|