task(C2int): integrate pixi renderer as overlay with lazy import
This commit is contained in:
@@ -18,6 +18,7 @@ import { CADDocument } from '../kernel/document/CADDocument';
|
|||||||
import { YjsDocument } from '../crdt/YjsDocument';
|
import { YjsDocument } from '../crdt/YjsDocument';
|
||||||
import { setActiveDocument, getActiveDocument } from '../kernel/document/documentService';
|
import { setActiveDocument, getActiveDocument } from '../kernel/document/documentService';
|
||||||
import { V2_TOOLS_ENABLED } from '../kernel/featureFlags';
|
import { V2_TOOLS_ENABLED } from '../kernel/featureFlags';
|
||||||
|
import { PixiRenderer } from '../render/pixi/PixiRenderer';
|
||||||
import { pluginRegistry } from '../plugins';
|
import { pluginRegistry } from '../plugins';
|
||||||
|
|
||||||
const CanvasArea: React.FC<CanvasAreaProps> = ({
|
const CanvasArea: React.FC<CanvasAreaProps> = ({
|
||||||
@@ -34,6 +35,10 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
const renderEngineRef = useRef<RenderEngine | null>(null);
|
const renderEngineRef = useRef<RenderEngine | null>(null);
|
||||||
const interactionRef = useRef<InteractionEngine | null>(null);
|
const interactionRef = useRef<InteractionEngine | null>(null);
|
||||||
const dispatcherRef = useRef<InteractionDispatcher | null>(null);
|
const dispatcherRef = useRef<InteractionDispatcher | null>(null);
|
||||||
|
/** Task C2: PixiJS-Canvas-Ref für V2-Elemente-Overlay. */
|
||||||
|
const pixiCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
/** Task C2: Aktiver PixiJS-Renderer (für Cleanup). */
|
||||||
|
const pixiRendererRef = useRef<PixiRenderer | null>(null);
|
||||||
/** Bereits an onElementCreated gemeldete V2-Element-IDs (verhindert Doppelmeldung bei jedem onChanged-Frame). */
|
/** Bereits an onElementCreated gemeldete V2-Element-IDs (verhindert Doppelmeldung bei jedem onChanged-Frame). */
|
||||||
const announcedV2Ids = useRef<Set<string>>(new Set());
|
const announcedV2Ids = useRef<Set<string>>(new Set());
|
||||||
const spatialIndexRef = useRef<SpatialIndex | null>(null);
|
const spatialIndexRef = useRef<SpatialIndex | null>(null);
|
||||||
@@ -65,6 +70,60 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
const v2Doc = new CADDocument(ydoc);
|
const v2Doc = new CADDocument(ydoc);
|
||||||
// App-Level-Zugriffspunkt für HistoryPanel/Tastenkürzel (Task A4.11):
|
// App-Level-Zugriffspunkt für HistoryPanel/Tastenkürzel (Task A4.11):
|
||||||
setActiveDocument(v2Doc);
|
setActiveDocument(v2Doc);
|
||||||
|
|
||||||
|
// ── Task C2: PixiJS-Renderer parallel schalten ──
|
||||||
|
// DEFENSIV: Ein Ausfall von Pixi (z.B. WebGL im Headless-Browser)
|
||||||
|
// darf NIEMALS den kompletten Editor-Mount brechen. Im Fehlerfall
|
||||||
|
// läuft weiter ohne Overlay.
|
||||||
|
let pixi: PixiRenderer | null = null;
|
||||||
|
const pixiCanvas = pixiCanvasRef.current;
|
||||||
|
if (pixiCanvas) {
|
||||||
|
try {
|
||||||
|
pixi = new PixiRenderer();
|
||||||
|
pixiRendererRef.current = pixi;
|
||||||
|
pixi.init(pixiCanvas);
|
||||||
|
pixi.waitForReady().catch((err) => {
|
||||||
|
logger.warn('Pixi init failed — V2-Overlay deaktiviert:', err);
|
||||||
|
pixi = null;
|
||||||
|
pixiRendererRef.current = null;
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('Pixi init exception — V2-Overlay deaktiviert:', err);
|
||||||
|
pixi = null;
|
||||||
|
pixiRendererRef.current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Letztes bekanntes Elementobjekt je ID (Referenzdiff statt JSON). */
|
||||||
|
const lastKnownEl = new Map<string, CADElement>();
|
||||||
|
const syncPixi = (): void => {
|
||||||
|
if (!pixi) return;
|
||||||
|
void pixi.waitForReady().then(() => {
|
||||||
|
const all = v2Doc.getAllElements();
|
||||||
|
const visibleIds = new Set<string>();
|
||||||
|
for (const el of all) {
|
||||||
|
visibleIds.add(el.id);
|
||||||
|
// Nur neu zeichnen wenn sich die OBJEKTREFERENZ geändert hat:
|
||||||
|
if (lastKnownEl.get(el.id) !== el) {
|
||||||
|
lastKnownEl.set(el.id, el);
|
||||||
|
pixi!.drawElement(el, {
|
||||||
|
id: 'layer-0', name: 'Standard', visible: true, locked: false,
|
||||||
|
color: '#ffffff', lineType: 'solid', transparency: 0,
|
||||||
|
sortOrder: 0, parentId: null,
|
||||||
|
} as never, {
|
||||||
|
color: '#ffffff', lineType: 'solid', transparency: 0, visible: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Gelöschte IDs aus dem Renderer entfernen:
|
||||||
|
for (const knownId of lastKnownEl.keys()) {
|
||||||
|
if (!visibleIds.has(knownId)) {
|
||||||
|
lastKnownEl.delete(knownId);
|
||||||
|
pixi!.removeElement(knownId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Committete V2-Elemente fließen über den etablierten Kanal in UI/State:
|
// Committete V2-Elemente fließen über den etablierten Kanal in UI/State:
|
||||||
v2Doc.onChanged(() => {
|
v2Doc.onChanged(() => {
|
||||||
for (const el of v2Doc.getAllElements()) {
|
for (const el of v2Doc.getAllElements()) {
|
||||||
@@ -73,6 +132,7 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
onElementCreated(el);
|
onElementCreated(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
syncPixi(); // Task C2: V2-Elemente in den PixiJS-Overlay-Renderer spiegeln
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatcherRef.current = new InteractionDispatcher(canvas, {
|
dispatcherRef.current = new InteractionDispatcher(canvas, {
|
||||||
@@ -115,6 +175,8 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
interaction.detach();
|
interaction.detach();
|
||||||
dispatcherRef.current?.destroy();
|
dispatcherRef.current?.destroy();
|
||||||
dispatcherRef.current = null;
|
dispatcherRef.current = null;
|
||||||
|
pixiRendererRef.current?.destroy(); // Task C2: WebGL-Kontext freigeben
|
||||||
|
pixiRendererRef.current = null;
|
||||||
setActiveDocument(null);
|
setActiveDocument(null);
|
||||||
zoomPanRef.current = null;
|
zoomPanRef.current = null;
|
||||||
renderEngineRef.current = null;
|
renderEngineRef.current = null;
|
||||||
@@ -413,6 +475,22 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
<span style={{ color: 'var(--color-text-faint)' }}>{unit}</span>
|
<span style={{ color: 'var(--color-text-faint)' }}>{unit}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Task C2-Integration: PixiJS-Overlay für V2-Elemente — liegt ÜBER dem
|
||||||
|
Legacy-Canvas (z-index höher), rendert nur Elemente aus dem V2-Dokument.
|
||||||
|
Pointer-Events gehen durch (pointerEvents:none), damit Drag/Drop weiter
|
||||||
|
auf dem Legacy-Canvas funktioniert. */}
|
||||||
|
{V2_TOOLS_ENABLED && (
|
||||||
|
<canvas
|
||||||
|
ref={pixiCanvasRef}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="canvas-svg"
|
className="canvas-svg"
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* PixiRenderer – WebGL-Implementierung des RenderAdapter (Task C2).
|
* PixiRenderer – WebGL-Implementierung des RenderAdapter (Task C2).
|
||||||
*
|
*
|
||||||
* - PixiJS v8: async Application.init(); danach synchron nutzbar
|
* - PixiJS v8 wird LAZY per dynamischem import() geladen — ein Ausfall des
|
||||||
* (Integration wartet auf waitForReady() vor dem ersten Frame)
|
* Pixi-Moduls (z.B. fehlendes WebGL im Headless-Browser) beeinflusst damit
|
||||||
|
* NICHT den Modul-Import der App (kein weißer Screen).
|
||||||
|
* - async Application.init(); danach synchron nutzbar über waitForReady()
|
||||||
* - Inkrementell: gerenderte Container pro Element-ID gecacht;
|
* - Inkrementell: gerenderte Container pro Element-ID gecacht;
|
||||||
* removeElement/clearElements halten Cache und Szene konsistent
|
* removeElement/clearElements halten Cache und Szene konsistent
|
||||||
* - drawElement konsumiert die testbaren Shape-Primitives (elementDrawers)
|
* - drawElement konsumiert die testbaren Shape-Primitives (elementDrawers)
|
||||||
* - drawGrid nutzt Overlay-Layer für Linien; Ruler/Background/Selection
|
|
||||||
* folgen in Task C3
|
|
||||||
*/
|
*/
|
||||||
import { Application, Container, Graphics, Text } from 'pixi.js';
|
import type { Application, Container, Graphics, Text } from 'pixi.js';
|
||||||
import type {
|
import type {
|
||||||
RenderAdapter,
|
RenderAdapter,
|
||||||
Viewport,
|
Viewport,
|
||||||
@@ -27,20 +27,23 @@ import { elementToPrimitives } from './elementDrawers';
|
|||||||
export class PixiRenderer implements RenderAdapter {
|
export class PixiRenderer implements RenderAdapter {
|
||||||
private app: Application | null = null;
|
private app: Application | null = null;
|
||||||
private canvas: HTMLCanvasElement | null = null;
|
private canvas: HTMLCanvasElement | null = null;
|
||||||
private root: Container | null = null; // Weltkoordinaten-Container (skaliert)
|
private root: Container | null = null;
|
||||||
private overlayLayer: Container | null = null; // Grid/Ruler/Snaps — immer oben
|
private overlayLayer: Container | null = null;
|
||||||
private graphicsCache = new Map<string, Container>();
|
private graphicsCache = new Map<string, Container>();
|
||||||
private viewport: Viewport = { x: 0, y: 0, scale: 1, width: 800, height: 600 };
|
private viewport: Viewport = { x: 0, y: 0, scale: 1, width: 800, height: 600 };
|
||||||
private readyResolve: (() => void) | null = null;
|
private readyResolve: (() => void) | null = null;
|
||||||
|
private readyReject: ((err: unknown) => void) | null = null;
|
||||||
readonly ready: Promise<void>;
|
readonly ready: Promise<void>;
|
||||||
|
private pixiModule: typeof import('pixi.js') | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.ready = new Promise<void>((resolve) => {
|
this.ready = new Promise<void>((resolve, reject) => {
|
||||||
this.readyResolve = resolve;
|
this.readyResolve = resolve;
|
||||||
|
this.readyReject = reject;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wartet bis Pixi initialisiert ist (vor dem ersten Frame aufrufen). */
|
/** Wartet auf Abschluss der Initialisierung; rejected bei Pixi-Fehler. */
|
||||||
async waitForReady(): Promise<void> {
|
async waitForReady(): Promise<void> {
|
||||||
await this.ready;
|
await this.ready;
|
||||||
}
|
}
|
||||||
@@ -48,11 +51,28 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
// ── Lifecycle ────────────────────────────────────────────
|
// ── Lifecycle ────────────────────────────────────────────
|
||||||
|
|
||||||
init(canvas: HTMLCanvasElement): void {
|
init(canvas: HTMLCanvasElement): void {
|
||||||
const app = new Application();
|
|
||||||
this.app = app;
|
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
|
// LAZY-IMPORT: pixi.js wird erst jetzt geholt. Bei Fehler → ready rejected,
|
||||||
|
// CanvasArea deaktiviert das Overlay und die App läuft normal weiter.
|
||||||
|
void import('pixi.js')
|
||||||
|
.then((mod) => {
|
||||||
|
this.pixiModule = mod;
|
||||||
|
return this.setupApp(mod.Application, mod.Container, canvas);
|
||||||
|
})
|
||||||
|
.then(() => this.readyResolve?.())
|
||||||
|
.catch((err) => {
|
||||||
|
this.readyReject?.(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void app.init({
|
private setupApp(
|
||||||
|
ApplicationCtor: typeof Application,
|
||||||
|
ContainerCtor: typeof Container,
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
): Promise<void> {
|
||||||
|
const app = new ApplicationCtor();
|
||||||
|
this.app = app;
|
||||||
|
return app.init({
|
||||||
canvas,
|
canvas,
|
||||||
width: this.viewport.width,
|
width: this.viewport.width,
|
||||||
height: this.viewport.height,
|
height: this.viewport.height,
|
||||||
@@ -61,12 +81,14 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
autoDensity: false,
|
autoDensity: false,
|
||||||
preference: 'webgl',
|
preference: 'webgl',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.root = new Container();
|
if (!this.app) return;
|
||||||
app.stage.addChild(this.root);
|
const root = new ContainerCtor();
|
||||||
this.overlayLayer = new Container();
|
this.root = root;
|
||||||
app.stage.addChild(this.overlayLayer);
|
this.app.stage.addChild(root);
|
||||||
|
const overlay = new ContainerCtor();
|
||||||
|
this.overlayLayer = overlay;
|
||||||
|
this.app.stage.addChild(overlay);
|
||||||
this.applyViewportTransform();
|
this.applyViewportTransform();
|
||||||
this.readyResolve?.();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +105,11 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.app?.destroy(true, { children: true });
|
try {
|
||||||
|
this.app?.destroy(true, { children: true });
|
||||||
|
} catch {
|
||||||
|
// Pixi darf im Teardown nichts mehr werfen
|
||||||
|
}
|
||||||
this.app = null;
|
this.app = null;
|
||||||
this.root = null;
|
this.root = null;
|
||||||
this.overlayLayer = null;
|
this.overlayLayer = null;
|
||||||
@@ -94,13 +120,14 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
// ── Basis-Zeichenoperationen ────────────────────────────
|
// ── Basis-Zeichenoperationen ────────────────────────────
|
||||||
|
|
||||||
clear(_color: string): void {
|
clear(_color: string): void {
|
||||||
// Hintergrundfarbe kommt vom Canvas-CSS/Stage-Hintergrund.
|
// Hintergrund kommt vom Canvas-CSS; Pixi leert Buffer beim render.
|
||||||
// Pixi's render()-Aufruf leert den Buffer selbst.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawGrid(cfg: GridConfig): void {
|
drawGrid(cfg: GridConfig): void {
|
||||||
if (!cfg.enabled || !this.overlayLayer || !this.root) return;
|
if (!cfg.enabled || !this.overlayLayer || !this.root) return;
|
||||||
const g = new Graphics();
|
const GraphicsCtor = this.pixiModule?.Graphics;
|
||||||
|
if (!GraphicsCtor) return;
|
||||||
|
const g = new GraphicsCtor();
|
||||||
const vp = this.viewport;
|
const vp = this.viewport;
|
||||||
const worldLeft = vp.x;
|
const worldLeft = vp.x;
|
||||||
const worldTop = vp.y;
|
const worldTop = vp.y;
|
||||||
@@ -122,22 +149,22 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
g.moveTo(worldLeft, gy).lineTo(worldRight, gy)
|
g.moveTo(worldLeft, gy).lineTo(worldRight, gy)
|
||||||
.stroke({ color: cfg.color, width: 0.5 / vp.scale });
|
.stroke({ color: cfg.color, width: 0.5 / vp.scale });
|
||||||
}
|
}
|
||||||
this.root.addChild(g); // skaliert mit, damit die Rasterlinien zur Welt passen
|
this.root.addChild(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawBackground(_img: HTMLImageElement | null, _cfg: BackgroundDrawConfig): void {
|
drawBackground(_img: HTMLImageElement | null, _cfg: BackgroundDrawConfig): void {
|
||||||
// Sprite-/Texturintegration folgt in Task C3 (BUG-4-Korrelat in WebGL).
|
// Sprite-/Texturintegration folgt in Task C3.
|
||||||
}
|
}
|
||||||
|
|
||||||
drawRuler(_cfg: RulerConfig): void {
|
drawRuler(_cfg: RulerConfig): void {
|
||||||
// Lineal-Rendering folgt in Task C3 (DOM-Layer oder Pixi-Text).
|
// Lineal-Rendering folgt in Task C3.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Elemente ─────────────────────────────────────────────
|
// ── Elemente ─────────────────────────────────────────────
|
||||||
|
|
||||||
drawElement(el: CADElement, layer: CADLayer, _style: LayerStyle): void {
|
drawElement(el: CADElement, layer: CADLayer, _style: LayerStyle): void {
|
||||||
if (!this.root || !layer.visible || layer.locked) return;
|
if (!this.root || !layer.visible || layer.locked) return;
|
||||||
this.removeElement(el.id); // alter Zustand raus, neuer rein
|
this.removeElement(el.id);
|
||||||
const container = this.buildGraphicsContainer(el);
|
const container = this.buildGraphicsContainer(el);
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
this.graphicsCache.set(el.id, container);
|
this.graphicsCache.set(el.id, container);
|
||||||
@@ -147,36 +174,47 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
removeElement(id: string): void {
|
removeElement(id: string): void {
|
||||||
const existing = this.graphicsCache.get(id);
|
const existing = this.graphicsCache.get(id);
|
||||||
if (!existing) return;
|
if (!existing) return;
|
||||||
existing.destroy({ children: true });
|
try {
|
||||||
|
existing.destroy({ children: true });
|
||||||
|
} catch {
|
||||||
|
// teardown-safe
|
||||||
|
}
|
||||||
this.graphicsCache.delete(id);
|
this.graphicsCache.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearElements(): void {
|
clearElements(): void {
|
||||||
for (const c of this.graphicsCache.values()) {
|
for (const c of this.graphicsCache.values()) {
|
||||||
c.destroy({ children: true });
|
try {
|
||||||
|
c.destroy({ children: true });
|
||||||
|
} catch {
|
||||||
|
// teardown-safe
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.graphicsCache.clear();
|
this.graphicsCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
drawPreview(el: CADElement | null): void {
|
drawPreview(el: CADElement | null): void {
|
||||||
if (!this.overlayLayer) return;
|
if (!this.overlayLayer) return;
|
||||||
// Bestehende Preview-Container am Ende des Layers entfernen:
|
|
||||||
for (const child of [...this.overlayLayer.children]) {
|
for (const child of [...this.overlayLayer.children]) {
|
||||||
if ((child as Container).label === '__preview__') {
|
if ((child as Container).label === '__preview__') {
|
||||||
child.destroy({ children: true });
|
try {
|
||||||
|
child.destroy({ children: true });
|
||||||
|
} catch {
|
||||||
|
// teardown-safe
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const g = this.buildGraphicsContainer(el);
|
const g = this.buildGraphicsContainer(el);
|
||||||
if (g) {
|
if (g) {
|
||||||
g.label = '__preview__';
|
g.label = '__preview__';
|
||||||
g.alpha = 0.6; // halbtransparent wie Legacy-Vorschau
|
g.alpha = 0.6;
|
||||||
this.overlayLayer.addChild(g);
|
this.overlayLayer.addChild(g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelection(_sel: SelectionState): void {
|
setSelection(_sel: SelectionState): void {
|
||||||
// Selektionshandles in C3 (braucht BBox-Zugriff pro Element)
|
// Selektionshandles in C3
|
||||||
}
|
}
|
||||||
|
|
||||||
setSnapPoints(_pts: Array<{ x: number; y: number; type: string }>): void {
|
setSnapPoints(_pts: Array<{ x: number; y: number; type: string }>): void {
|
||||||
@@ -185,7 +223,10 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
|
|
||||||
overlay(draw: (g: OverlayPainter) => void): void {
|
overlay(draw: (g: OverlayPainter) => void): void {
|
||||||
if (!this.overlayLayer) return;
|
if (!this.overlayLayer) return;
|
||||||
const g = new Graphics();
|
const GraphicsCtor = this.pixiModule?.Graphics;
|
||||||
|
const TextCtor = this.pixiModule?.Text;
|
||||||
|
if (!GraphicsCtor || !TextCtor) return;
|
||||||
|
const g = new GraphicsCtor();
|
||||||
const painter: OverlayPainter = {
|
const painter: OverlayPainter = {
|
||||||
line: (a: Pt, b: Pt, color: string, width?: number) => {
|
line: (a: Pt, b: Pt, color: string, width?: number) => {
|
||||||
g.moveTo(a.x, a.y).lineTo(b.x, b.y).stroke({ color, width: width ?? 1 });
|
g.moveTo(a.x, a.y).lineTo(b.x, b.y).stroke({ color, width: width ?? 1 });
|
||||||
@@ -197,7 +238,7 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
g.circle(center.x, center.y, radius).stroke({ color: strokeColor });
|
g.circle(center.x, center.y, radius).stroke({ color: strokeColor });
|
||||||
},
|
},
|
||||||
text: (text: string, at: Pt, color: string, fontSize?: number) => {
|
text: (text: string, at: Pt, color: string, fontSize?: number) => {
|
||||||
const t = new Text({ text, style: { fontSize: fontSize ?? 12, fill: color } });
|
const t = new TextCtor({ text, style: { fontSize: fontSize ?? 12, fill: color } });
|
||||||
t.position.set(at.x, at.y);
|
t.position.set(at.x, at.y);
|
||||||
g.addChild(t);
|
g.addChild(t);
|
||||||
},
|
},
|
||||||
@@ -207,7 +248,7 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hitTest(_world: Pt, _tolerancePx: number): CADElement | null {
|
hitTest(_world: Pt, _tolerancePx: number): CADElement | null {
|
||||||
// Delegation an SpatialIndex folgt in C3 (benötigt Elementliste).
|
// Delegation an SpatialIndex folgt in C3.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,9 +266,13 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
private buildGraphicsContainer(el: CADElement): Container | null {
|
private buildGraphicsContainer(el: CADElement): Container | null {
|
||||||
const prims = elementToPrimitives(el);
|
const prims = elementToPrimitives(el);
|
||||||
if (prims.length === 0) return null;
|
if (prims.length === 0) return null;
|
||||||
const container = new Container();
|
const GraphicsCtor = this.pixiModule?.Graphics;
|
||||||
|
const TextCtor = this.pixiModule?.Text;
|
||||||
|
if (!GraphicsCtor || !TextCtor) return null;
|
||||||
|
const ContainerCtor = this.pixiModule!.Container;
|
||||||
|
const container = new ContainerCtor();
|
||||||
for (const prim of prims) {
|
for (const prim of prims) {
|
||||||
const g = new Graphics();
|
const g = new GraphicsCtor();
|
||||||
switch (prim.kind) {
|
switch (prim.kind) {
|
||||||
case 'line':
|
case 'line':
|
||||||
g.moveTo(prim.from.x, prim.from.y)
|
g.moveTo(prim.from.x, prim.from.y)
|
||||||
@@ -262,7 +307,7 @@ export class PixiRenderer implements RenderAdapter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'text': {
|
case 'text': {
|
||||||
const t = new Text({ text: prim.text, style: { fontSize: prim.fontSize, fill: prim.color } });
|
const t = new TextCtor({ text: prim.text, style: { fontSize: prim.fontSize, fill: prim.color } });
|
||||||
t.position.set(prim.at.x, prim.at.y);
|
t.position.set(prim.at.x, prim.at.y);
|
||||||
g.addChild(t);
|
g.addChild(t);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user