fix(CAD-13): double rendering - legacy canvas skips element rendering when pixi renderer is active (grid/background stay), defensive fallback if pixi fails
This commit is contained in:
@@ -55,6 +55,8 @@ export class RenderEngine {
|
|||||||
/** src des zuletzt erfolgreich geladenen Hintergrundbilds. */
|
/** src des zuletzt erfolgreich geladenen Hintergrundbilds. */
|
||||||
private bgSrcLoaded = '';
|
private bgSrcLoaded = '';
|
||||||
private dpr = 1;
|
private dpr = 1;
|
||||||
|
/** CAD-13-fix: true = Legacy-Canvas zeichnet KEINE Elemente (Pixi-Overlay zeichnet sie). Grid/Hintergrund bleiben. */
|
||||||
|
skipElementRendering = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
canvas: HTMLCanvasElement,
|
canvas: HTMLCanvasElement,
|
||||||
@@ -161,6 +163,15 @@ export class RenderEngine {
|
|||||||
if (this.options.showGrid) this.drawGrid(w, h);
|
if (this.options.showGrid) this.drawGrid(w, h);
|
||||||
if (this.options.backgroundSrc) this.drawBackground();
|
if (this.options.backgroundSrc) this.drawBackground();
|
||||||
this.drawRuler(w, h);
|
this.drawRuler(w, h);
|
||||||
|
|
||||||
|
// CAD-13-fix: Wenn Pixi-Renderer aktiv ist (skipElementRendering=true),
|
||||||
|
// zeichnet das Legacy-Canvas KEINE Elemente mehr (sonst doppelt) —
|
||||||
|
// nur Grid/Hintergrund/Lineal bleiben hier.
|
||||||
|
if (this.skipElementRendering) {
|
||||||
|
this.ctx.restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const viewport = this.zoomPan.getViewport();
|
const viewport = this.zoomPan.getViewport();
|
||||||
const visibleElements = this.spatialIndex.search({
|
const visibleElements = this.spatialIndex.search({
|
||||||
minX: viewport.minX, minY: viewport.minY,
|
minX: viewport.minX, minY: viewport.minY,
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
|||||||
pixi = new PixiRenderer();
|
pixi = new PixiRenderer();
|
||||||
pixiRendererRef.current = pixi;
|
pixiRendererRef.current = pixi;
|
||||||
pixi.init(pixiCanvas);
|
pixi.init(pixiCanvas);
|
||||||
|
// CAD-13-fix: Pixi rendert jetzt die Elemente — Legacy-Canvas
|
||||||
|
// stellt nur noch Grid/Hintergrund/Lineal dar (sonst doppelt).
|
||||||
|
renderEngine.skipElementRendering = true;
|
||||||
pixi.waitForReady().then(() => {
|
pixi.waitForReady().then(() => {
|
||||||
// Task C4: Debug-Hook für Playwright-E2E-Assertions
|
// Task C4: Debug-Hook für Playwright-E2E-Assertions
|
||||||
(window as unknown as { __v2Debug: unknown }).__v2Debug = {
|
(window as unknown as { __v2Debug: unknown }).__v2Debug = {
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Task CAD-13-fix: Legacy-Rendering doppelt (Legacy-Canvas + Pixi-Overlay
|
||||||
|
* zeichnen dieselben Elemente). skipElementRendering legt das Legacy-
|
||||||
|
* Element-Rendering still, wenn Pixi aktiv ist — Grid/Hintergrund/Lineal
|
||||||
|
* bleiben sichtbar. Defensiv: Ohne Flag ist Verhalten unveraendert.
|
||||||
|
*/
|
||||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
|
import { RenderEngine } from '../src/canvas/RenderEngine';
|
||||||
|
import { ZoomPanController } from '../src/canvas/ZoomPanController';
|
||||||
|
import { SpatialIndex } from '../src/canvas/SpatialIndex';
|
||||||
|
import { LayerManager } from '../src/canvas/LayerManager';
|
||||||
|
import type { CADElement, CADLayer } from '../src/types/cad.types';
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
const zpc = new ZoomPanController(canvas);
|
||||||
|
const spatialIndex = new SpatialIndex();
|
||||||
|
const layerManager = new LayerManager();
|
||||||
|
const engine = new RenderEngine(canvas, zpc, spatialIndex, layerManager);
|
||||||
|
return { canvas, engine, zpc, spatialIndex, layerManager };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('CAD-13-fix: RenderEngine.skipElementRendering', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Default: Flag ist false (Verhalten unveraendert)', () => {
|
||||||
|
const { engine } = setup();
|
||||||
|
expect(engine.skipElementRendering).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skipElementRendering=true: Elemente werden NICHT gezeichnet, Grid schon', () => {
|
||||||
|
const { engine, spatialIndex, layerManager } = setup();
|
||||||
|
const layer: CADLayer = { id: 'l1', name: 'L1', visible: true, locked: false, color: '#fff', lineType: 'solid', transparency: 0, sortOrder: 0, parentId: null } as CADLayer;
|
||||||
|
layerManager.addLayer(layer);
|
||||||
|
const el: CADElement = {
|
||||||
|
id: 'e1', type: 'line', layerId: 'l1',
|
||||||
|
x: 10, y: 10, width: 100, height: 100,
|
||||||
|
properties: { x1: 10, y1: 10, x2: 110, y2: 110 },
|
||||||
|
} as unknown as CADElement;
|
||||||
|
spatialIndex.bulkInsert([el]);
|
||||||
|
engine.setLayers([layer]);
|
||||||
|
// drawElement darf nicht aufgerufen werden — wir spionieren die private
|
||||||
|
// Methode ueber einen Prototyp-Hook
|
||||||
|
const spy = vi.spyOn(engine as unknown as { drawElement: (el: CADElement, l: CADLayer) => void }, 'drawElement');
|
||||||
|
engine.skipElementRendering = true;
|
||||||
|
engine.render();
|
||||||
|
expect(spy).not.toHaveBeenCalled();
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skipElementRendering=false (Default): Elemente werden gezeichnet', () => {
|
||||||
|
const { engine, spatialIndex, layerManager } = setup();
|
||||||
|
const layer: CADLayer = { id: 'l1', name: 'L1', visible: true, locked: false, color: '#fff', lineType: 'solid', transparency: 0, sortOrder: 0, parentId: null } as CADLayer;
|
||||||
|
layerManager.addLayer(layer);
|
||||||
|
const el: CADElement = {
|
||||||
|
id: 'e1', type: 'line', layerId: 'l1',
|
||||||
|
x: 10, y: 10, width: 100, height: 100,
|
||||||
|
properties: { x1: 10, y1: 10, x2: 110, y2: 110 },
|
||||||
|
} as unknown as CADElement;
|
||||||
|
spatialIndex.bulkInsert([el]);
|
||||||
|
engine.setLayers([layer]);
|
||||||
|
const spy = vi.spyOn(engine as unknown as { drawElement: (el: CADElement, layer: CADLayer) => void }, 'drawElement');
|
||||||
|
engine.render();
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user