2026-08-27 08:49:18 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* elementToPrimitives Tests (Task C2).
|
|
|
|
|
|
* Deckt alle unterstützten Typen + Legacy-Konventionen + Grenzfälle ab.
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
|
|
import { elementToPrimitives } from '../src/render/pixi/elementDrawers';
|
|
|
|
|
|
import type { CADElement } from '../src/types/cad.types';
|
|
|
|
|
|
|
|
|
|
|
|
function el(id: string, type: string, props: Record<string, unknown> = {}, geom: Partial<CADElement> = {}): CADElement {
|
|
|
|
|
|
return {
|
|
|
|
|
|
id,
|
|
|
|
|
|
type,
|
|
|
|
|
|
layerId: 'l',
|
|
|
|
|
|
x: 0,
|
|
|
|
|
|
y: 0,
|
|
|
|
|
|
width: 10,
|
|
|
|
|
|
height: 10,
|
|
|
|
|
|
properties: props,
|
|
|
|
|
|
...geom,
|
|
|
|
|
|
} as unknown as CADElement;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
describe('elementToPrimitives', () => {
|
|
|
|
|
|
it('line: aus properties.x1..y2 mit stroke/sw', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('l', 'line', {
|
|
|
|
|
|
x1: 0, y1: 0, x2: 100, y2: 50, stroke: '#f00', strokeWidth: 3,
|
|
|
|
|
|
}));
|
|
|
|
|
|
expect(prims.length).toBe(1);
|
|
|
|
|
|
expect(prims[0]).toEqual({
|
|
|
|
|
|
kind: 'line', from: { x: 0, y: 0 }, to: { x: 100, y: 50 },
|
|
|
|
|
|
stroke: '#f00', strokeWidth: 3,
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('line ohne Koordinaten ergibt leere Liste', () => {
|
|
|
|
|
|
expect(elementToPrimitives(el('l', 'line', {}))).toEqual([]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('rect: x/y ist Zentrum der BBox (Legacy-Konvention)', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('r', 'rect', { fill: '#123456' }, {
|
|
|
|
|
|
x: 100, y: 50, width: 80, height: 40,
|
|
|
|
|
|
}));
|
|
|
|
|
|
expect(prims.length).toBe(1);
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'rect') throw new Error('erwartete rect');
|
|
|
|
|
|
expect(p.x).toBe(60); // 100 − 40
|
|
|
|
|
|
expect(p.y).toBe(30); // 50 − 20
|
|
|
|
|
|
expect(p.w).toBe(80);
|
|
|
|
|
|
expect(p.h).toBe(40);
|
|
|
|
|
|
expect(p.fill).toBe('#123456');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('rect fill none wird zu null', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('r', 'rect', { fill: 'none' }));
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'rect') throw new Error('erwartete rect');
|
|
|
|
|
|
expect(p.fill).toBeNull();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('circle: radius mit Fallback width/2', () => {
|
|
|
|
|
|
const withExplicit = elementToPrimitives(el('c', 'circle', { radius: 25, fill: '#abcdef' }, { x: 5, y: 5 }));
|
|
|
|
|
|
const p = withExplicit[0];
|
|
|
|
|
|
if (p.kind !== 'circle') throw new Error('erwartete circle');
|
|
|
|
|
|
expect(p.radius).toBe(25);
|
|
|
|
|
|
expect(p.center).toEqual({ x: 5, y: 5 });
|
|
|
|
|
|
expect(p.fill).toBe('#abcdef');
|
|
|
|
|
|
|
|
|
|
|
|
const byWidth = elementToPrimitives(el('c', 'circle', {}, { width: 120 }));
|
|
|
|
|
|
const pb = byWidth[0];
|
|
|
|
|
|
if (pb.kind !== 'circle') throw new Error('erwartete circle');
|
|
|
|
|
|
expect(pb.radius).toBe(60); // fallback
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('arc: Winkel in Grad wie Legacy', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('a', 'arc', {
|
|
|
|
|
|
radius: 40, startAngle: 0, endAngle: 90,
|
|
|
|
|
|
}, { x: 0, y: 0 }));
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'arc') throw new Error('erwartete arc');
|
|
|
|
|
|
expect(p.startDeg).toBe(0);
|
|
|
|
|
|
expect(p.endDeg).toBe(90);
|
|
|
|
|
|
expect(p.radius).toBe(40);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('polyline/polygon: points-Array, close je nach Typ', () => {
|
|
|
|
|
|
const pts = [{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }];
|
|
|
|
|
|
const open = elementToPrimitives(el('pl', 'polyline', { points: pts }));
|
|
|
|
|
|
const closed = elementToPrimitives(el('pg', 'polygon', { points: pts }));
|
|
|
|
|
|
expect(open.length).toBe(1);
|
|
|
|
|
|
expect(closed.length).toBe(1);
|
|
|
|
|
|
if (open[0].kind !== 'polyline' || closed[0].kind !== 'polyline') {
|
|
|
|
|
|
throw new Error('erwartete polyline');
|
|
|
|
|
|
}
|
|
|
|
|
|
expect(open[0].close).toBe(false);
|
|
|
|
|
|
expect(closed[0].close).toBe(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('text: leer nicht gerendert; gefuellt mit fontSize/color', () => {
|
|
|
|
|
|
expect(elementToPrimitives(el('t', 'text', { text: '' }))).toEqual([]);
|
|
|
|
|
|
|
|
|
|
|
|
const filled = elementToPrimitives(el('t', 'text', {
|
|
|
|
|
|
text: 'Bühne A', fontSize: 18, color: '#ffffff',
|
|
|
|
|
|
}, { x: 50, y: 75 }));
|
|
|
|
|
|
expect(filled.length).toBe(1);
|
|
|
|
|
|
const p = filled[0];
|
|
|
|
|
|
if (p.kind !== 'text') throw new Error('erwartete text');
|
|
|
|
|
|
expect(p.text).toBe('Bühne A');
|
|
|
|
|
|
expect(p.fontSize).toBe(18);
|
|
|
|
|
|
expect(p.color).toBe('#ffffff');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('unbekannter Typ ergibt leere Liste (renderer-sicher)', () => {
|
|
|
|
|
|
expect(elementToPrimitives(el('x', 'definitely-unknown'))).toEqual([]);
|
|
|
|
|
|
});
|
2026-08-27 10:07:35 +02:00
|
|
|
|
|
|
|
|
|
|
it('dimension: Linie + Label aus value/text', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('d', 'dimension', {
|
|
|
|
|
|
x1: 0, y1: 0, x2: 100, y2: 0, value: '250 cm',
|
|
|
|
|
|
}, { x: 50, y: 0 }));
|
|
|
|
|
|
expect(prims.length).toBe(2);
|
|
|
|
|
|
expect(prims[0].kind).toBe('line');
|
|
|
|
|
|
const txt = prims[1];
|
|
|
|
|
|
if (txt.kind !== 'text') throw new Error('erwartete text');
|
|
|
|
|
|
expect(txt.text).toBe('250 cm');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('leader: Linie + Label am Ende', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('ld', 'leader', {
|
|
|
|
|
|
x1: 10, y1: 10, x2: 80, y2: 60, text: 'A1', fontSize: 14,
|
|
|
|
|
|
}));
|
|
|
|
|
|
expect(prims.length).toBe(2);
|
|
|
|
|
|
expect(prims[0].kind).toBe('line');
|
|
|
|
|
|
const txt = prims[1];
|
|
|
|
|
|
if (txt.kind !== 'text') throw new Error('erwartete text');
|
|
|
|
|
|
expect(txt.text).toBe('A1');
|
|
|
|
|
|
expect(txt.fontSize).toBe(14);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('revcloud: geschlossener Punktzug', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('rc', 'revcloud', {
|
|
|
|
|
|
points: [{ x: 0, y: 0 }, { x: 50, y: 0 }, { x: 50, y: 30 }],
|
|
|
|
|
|
}));
|
|
|
|
|
|
expect(prims.length).toBe(1);
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'polyline') throw new Error('erwartete polyline');
|
|
|
|
|
|
expect(p.close).toBe(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('chair: gefuelltes Rechteck mit Fallback-Farbe', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('ch', 'chair', {}, { x: 50, y: 50, width: 20, height: 20 }));
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'rect') throw new Error('erwartete rect');
|
|
|
|
|
|
expect(p.fill).toBe('#4a90d9');
|
|
|
|
|
|
expect(p.x).toBe(40);
|
|
|
|
|
|
expect(p.y).toBe(40);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('table: Kreis mit Fallback-Farbe und Radius aus width', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('tb', 'table', {}, { x: 0, y: 0, width: 60, height: 60 }));
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'circle') throw new Error('erwartete circle');
|
|
|
|
|
|
expect(p.radius).toBe(30);
|
|
|
|
|
|
expect(p.fill).toBe('#8b7355');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('stage: dunkles Rechteck', () => {
|
|
|
|
|
|
const prims = elementToPrimitives(el('st', 'stage', {}, { x: 0, y: 0, width: 200, height: 100 }));
|
|
|
|
|
|
const p = prims[0];
|
|
|
|
|
|
if (p.kind !== 'rect') throw new Error('erwartete rect');
|
|
|
|
|
|
expect(p.fill).toBe('#3a3a4a');
|
|
|
|
|
|
expect(p.w).toBe(200);
|
|
|
|
|
|
expect(p.h).toBe(100);
|
|
|
|
|
|
});
|
2026-08-27 08:49:18 +02:00
|
|
|
|
});
|