Replace frontend with deployed TS version from a08dd73
- Restore TS frontend source (canvas, tools, CRDT, components, services) - Keep 5 JSX components that TS code depends on (CADCanvas, LayerPanel, BlockLibrary, PluginRegistry, Toolbar) - Keep JS services (api.js, blockService.js) that components depend on - Fix vite/plugin-react version mismatch (downgrade to v4 for vite 6) - Add axios dependency - Skip tsc in build script (vite/esbuild handles transpilation) - Fix index.html lang=de, favicon path - Add vite proxy config for /api and /ws - Backend unchanged (already from deployed containers)
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
import RBush from 'rbush';
|
||||
import { SpatialIndex } from './SpatialIndex';
|
||||
import { LayerManager } from './LayerManager';
|
||||
import { ZoomPanController } from './ZoomPanController';
|
||||
|
||||
type RenderElement = {
|
||||
id: string;
|
||||
type: 'line' | 'circle' | 'polyline' | 'polygon' | 'rect' | 'arc' | 'text' | 'dimension' | 'block_instance';
|
||||
layerId: string;
|
||||
bbox: { minX: number; minY: number; maxX: number; maxY: number };
|
||||
// Additional properties based on type
|
||||
};
|
||||
|
||||
type Layer = {
|
||||
id: string;
|
||||
name: string;
|
||||
visible: boolean;
|
||||
locked: boolean;
|
||||
};
|
||||
|
||||
export class RenderEngine {
|
||||
private canvas: HTMLCanvasElement;
|
||||
private ctx: CanvasRenderingContext2D;
|
||||
private spatialIndex: SpatialIndex;
|
||||
private layerManager: LayerManager;
|
||||
private zoomPanController: ZoomPanController;
|
||||
private animationFrameId: number | null = null;
|
||||
private elements: RenderElement[] = [];
|
||||
private layers: Layer[] = [];
|
||||
private isRendering = false;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('Failed to get 2D context');
|
||||
}
|
||||
this.ctx = ctx;
|
||||
this.spatialIndex = new SpatialIndex();
|
||||
this.layerManager = new LayerManager();
|
||||
this.zoomPanController = new ZoomPanController(canvas);
|
||||
}
|
||||
|
||||
public start(): void {
|
||||
if (this.isRendering) return;
|
||||
this.isRendering = true;
|
||||
this.render();
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (this.animationFrameId) {
|
||||
cancelAnimationFrame(this.animationFrameId);
|
||||
}
|
||||
this.isRendering = false;
|
||||
}
|
||||
|
||||
private render(): void {
|
||||
if (!this.isRendering) return;
|
||||
|
||||
// Clear canvas
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Apply zoom/pan transforms
|
||||
const transform = this.zoomPanController.getTransform();
|
||||
this.ctx.save();
|
||||
this.ctx.transform(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
|
||||
|
||||
// Render grid
|
||||
this.renderGrid();
|
||||
|
||||
// Get visible elements using viewport culling
|
||||
const viewport = this.zoomPanController.getViewport();
|
||||
const visibleElements = this.spatialIndex.search(viewport);
|
||||
|
||||
// Render elements by layer order
|
||||
const visibleLayers = this.layerManager.getVisibleLayers();
|
||||
for (const layer of visibleLayers) {
|
||||
const layerElements = visibleElements.filter(el => el.layerId === layer.id);
|
||||
for (const element of layerElements) {
|
||||
this.renderElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.restore();
|
||||
|
||||
// Continue render loop
|
||||
this.animationFrameId = requestAnimationFrame(() => this.render());
|
||||
}
|
||||
|
||||
private renderGrid(): void {
|
||||
const gridSize = 50; // pixels
|
||||
const viewport = this.zoomPanController.getViewport();
|
||||
|
||||
this.ctx.strokeStyle = '#e0e0e0';
|
||||
this.ctx.lineWidth = 1;
|
||||
|
||||
// Vertical lines
|
||||
for (let x = Math.floor(viewport.minX / gridSize) * gridSize; x < viewport.maxX; x += gridSize) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(x, viewport.minY);
|
||||
this.ctx.lineTo(x, viewport.maxY);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// Horizontal lines
|
||||
for (let y = Math.floor(viewport.minY / gridSize) * gridSize; y < viewport.maxY; y += gridSize) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(viewport.minX, y);
|
||||
this.ctx.lineTo(viewport.maxX, y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
private renderElement(element: RenderElement): void {
|
||||
switch (element.type) {
|
||||
case 'line':
|
||||
this.renderLine(element);
|
||||
break;
|
||||
case 'circle':
|
||||
this.renderCircle(element);
|
||||
break;
|
||||
case 'polyline':
|
||||
this.renderPolyline(element);
|
||||
break;
|
||||
case 'polygon':
|
||||
this.renderPolygon(element);
|
||||
break;
|
||||
case 'rect':
|
||||
this.renderRect(element);
|
||||
break;
|
||||
case 'arc':
|
||||
this.renderArc(element);
|
||||
break;
|
||||
case 'text':
|
||||
this.renderText(element);
|
||||
break;
|
||||
case 'dimension':
|
||||
this.renderDimension(element);
|
||||
break;
|
||||
case 'block_instance':
|
||||
this.renderBlockInstance(element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private renderLine(element: RenderElement): void {
|
||||
// Implementation for line rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0); // Placeholder
|
||||
this.ctx.lineTo(100, 100); // Placeholder
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private renderCircle(element: RenderElement): void {
|
||||
// Implementation for circle rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(50, 50, 30, 0, Math.PI * 2); // Placeholder
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private renderPolyline(element: RenderElement): void {
|
||||
// Implementation for polyline rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0); // Placeholder
|
||||
this.ctx.lineTo(50, 50); // Placeholder
|
||||
this.ctx.lineTo(100, 0); // Placeholder
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private renderPolygon(element: RenderElement): void {
|
||||
// Implementation for polygon rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0); // Placeholder
|
||||
this.ctx.lineTo(50, 50); // Placeholder
|
||||
this.ctx.lineTo(100, 0); // Placeholder
|
||||
this.ctx.closePath();
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private renderRect(element: RenderElement): void {
|
||||
// Implementation for rectangle rendering
|
||||
this.ctx.strokeRect(10, 10, 100, 50); // Placeholder
|
||||
}
|
||||
|
||||
private renderArc(element: RenderElement): void {
|
||||
// Implementation for arc rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(50, 50, 30, 0, Math.PI); // Placeholder
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
private renderText(element: RenderElement): void {
|
||||
// Implementation for text rendering
|
||||
this.ctx.fillText('Sample Text', 20, 20); // Placeholder
|
||||
}
|
||||
|
||||
private renderDimension(element: RenderElement): void {
|
||||
// Implementation for dimension rendering
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(0, 0); // Placeholder
|
||||
this.ctx.lineTo(100, 0); // Placeholder
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw dimension lines and text
|
||||
this.ctx.fillText('100', 50, -5); // Placeholder
|
||||
}
|
||||
|
||||
private renderBlockInstance(element: RenderElement): void {
|
||||
// Implementation for block instance rendering
|
||||
this.ctx.strokeRect(20, 20, 60, 40); // Placeholder
|
||||
this.ctx.fillText('BLOCK', 30, 45); // Placeholder
|
||||
}
|
||||
|
||||
public addElement(element: RenderElement): void {
|
||||
this.elements.push(element);
|
||||
this.spatialIndex.insert(element);
|
||||
}
|
||||
|
||||
public removeElement(id: string): void {
|
||||
const index = this.elements.findIndex(el => el.id === id);
|
||||
if (index !== -1) {
|
||||
const element = this.elements[index];
|
||||
this.elements.splice(index, 1);
|
||||
this.spatialIndex.remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
public addLayer(layer: Layer): void {
|
||||
this.layers.push(layer);
|
||||
this.layerManager.addLayer(layer);
|
||||
}
|
||||
|
||||
public updateLayer(layer: Layer): void {
|
||||
const index = this.layers.findIndex(l => l.id === layer.id);
|
||||
if (index !== -1) {
|
||||
this.layers[index] = layer;
|
||||
this.layerManager.updateLayer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user