UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

826 lines (696 loc) 24.1 kB
/** * Advanced Visualization System - מערכת ויזואליזציה מתקדמת * מספקת דשבורדים אינטראקטיביים, גרפים בזמן אמת וויזואליזציות מתקדמות */ export interface VisualizationConfig { theme: 'light' | 'dark' | 'auto'; animations: { enabled: boolean; duration: number; easing: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out'; }; performance: { maxDataPoints: number; refreshRate: number; // ms enableWebGL: boolean; enableWorkers: boolean; }; accessibility: { highContrast: boolean; reducedMotion: boolean; screenReaderSupport: boolean; }; } export interface ChartData { id: string; name: string; data: DataPoint[]; type: 'line' | 'bar' | 'pie' | 'scatter' | 'heatmap' | 'radar'; color?: string; metadata?: { unit?: string; format?: string; description?: string; }; } export interface DataPoint { x: number | string | Date; y: number; label?: string; metadata?: any; } export interface DashboardWidget { id: string; title: string; type: 'chart' | 'metric' | 'table' | 'heatmap' | 'gauge' | 'text' | 'custom'; position: { x: number; y: number; width: number; height: number }; data: ChartData[] | any; config: { refreshInterval?: number; autoUpdate?: boolean; interactive?: boolean; exportable?: boolean; filters?: FilterConfig[]; }; styling: { backgroundColor?: string; borderColor?: string; textColor?: string; fontSize?: number; padding?: number; }; } export interface FilterConfig { id: string; type: 'date' | 'select' | 'range' | 'search'; label: string; options?: any[]; defaultValue?: any; } export interface Dashboard { id: string; name: string; description?: string; widgets: DashboardWidget[]; layout: 'grid' | 'free' | 'responsive'; filters: FilterConfig[]; metadata: { created: number; updated: number; author?: string; version: string; tags?: string[]; }; } export interface RealTimeDataSource { id: string; name: string; type: 'websocket' | 'polling' | 'sse' | 'manual'; endpoint?: string; interval?: number; transform?: (data: any) => ChartData[]; filters?: (data: any) => boolean; } export interface VisualizationExport { format: 'png' | 'svg' | 'pdf' | 'json' | 'csv'; quality?: 'low' | 'medium' | 'high'; dimensions?: { width: number; height: number }; includeLegend?: boolean; includeTitle?: boolean; } class AdvancedVisualizationSystem { private config: VisualizationConfig; private dashboards: Map<string, Dashboard> = new Map(); private dataSources: Map<string, RealTimeDataSource> = new Map(); private activeSubscriptions: Map<string, any> = new Map(); private renderingContext: any = null; private animationFrameId?: number; private worker?: Worker; private performanceMetrics = { renderTime: 0, dataPoints: 0, fps: 0, memoryUsage: 0 }; constructor(config?: Partial<VisualizationConfig>) { this.config = { theme: 'auto', animations: { enabled: true, duration: 300, easing: 'ease-in-out' }, performance: { maxDataPoints: 10000, refreshRate: 1000, enableWebGL: true, enableWorkers: true }, accessibility: { highContrast: false, reducedMotion: false, screenReaderSupport: true }, ...config }; this.initializeVisualizationSystem(); console.log('🎨 Advanced Visualization System initialized'); } /** * אתחול מערכת הויזואליזציה */ private async initializeVisualizationSystem(): Promise<void> { this.initializeRenderingContext(); if (this.config.performance.enableWorkers) { this.initializeWorkers(); } this.setupPerformanceMonitoring(); this.setupThemeDetection(); this.setupAccessibilityFeatures(); } /** * אתחול rendering context */ private initializeRenderingContext(): void { if (typeof window !== 'undefined' && this.config.performance.enableWebGL) { try { const canvas = document.createElement('canvas'); this.renderingContext = canvas.getContext('webgl2') || canvas.getContext('webgl'); if (this.renderingContext) { console.log('🎨 WebGL rendering context initialized'); } else { console.log('🎨 Falling back to 2D rendering context'); this.renderingContext = canvas.getContext('2d'); } } catch (error) { console.warn('WebGL initialization failed:', error); } } } /** * אתחול Web Workers */ private initializeWorkers(): void { if (typeof Worker !== 'undefined') { try { const workerCode = ` self.onmessage = function(e) { const { type, data } = e.data; switch (type) { case 'PROCESS_DATA': const processed = processChartData(data); self.postMessage({ type: 'DATA_PROCESSED', data: processed }); break; case 'CALCULATE_STATISTICS': const stats = calculateStatistics(data); self.postMessage({ type: 'STATISTICS_CALCULATED', data: stats }); break; } }; function processChartData(data) { return data.map(point => ({ ...point, processed: true, timestamp: Date.now() })); } function calculateStatistics(data) { const values = data.map(d => d.y); return { mean: values.reduce((a, b) => a + b, 0) / values.length, min: Math.min(...values), max: Math.max(...values), count: values.length }; } `; const blob = new Blob([workerCode], { type: 'application/javascript' }); this.worker = new Worker(URL.createObjectURL(blob)); this.worker.onmessage = (e) => { this.handleWorkerMessage(e.data); }; console.log('🛠️ Visualization worker initialized'); } catch (error) { console.warn('Worker initialization failed:', error); } } } private handleWorkerMessage(message: any): void { switch (message.type) { case 'DATA_PROCESSED': console.log('🔄 Visualizations updated with processed data'); break; case 'STATISTICS_CALCULATED': console.log('📈 Statistics displays updated', message.data); break; } } private setupPerformanceMonitoring(): void { let frameCount = 0; let lastTime = performance.now(); const measurePerformance = () => { const currentTime = performance.now(); frameCount++; if (currentTime - lastTime >= 1000) { this.performanceMetrics.fps = frameCount; frameCount = 0; lastTime = currentTime; if ((performance as any).memory) { this.performanceMetrics.memoryUsage = (performance as any).memory.usedJSHeapSize; } } this.animationFrameId = requestAnimationFrame(measurePerformance); }; measurePerformance(); } private setupThemeDetection(): void { if (this.config.theme === 'auto' && typeof window !== 'undefined') { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const updateTheme = () => { const isDark = mediaQuery.matches; this.applyTheme(isDark ? 'dark' : 'light'); }; mediaQuery.addListener(updateTheme); updateTheme(); } } private applyTheme(theme: 'light' | 'dark'): void { const themeColors = { light: { background: '#ffffff', surface: '#f5f5f5', primary: '#2196f3', text: '#212121', textSecondary: '#757575' }, dark: { background: '#121212', surface: '#1e1e1e', primary: '#64b5f6', text: '#ffffff', textSecondary: '#b0b0b0' } }; this.dashboards.forEach(dashboard => { dashboard.widgets.forEach(widget => { widget.styling.backgroundColor = themeColors[theme].surface; widget.styling.textColor = themeColors[theme].text; }); }); } private setupAccessibilityFeatures(): void { if (typeof window !== 'undefined') { const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); if (reducedMotionQuery.matches) { this.config.accessibility.reducedMotion = true; this.config.animations.enabled = false; } const highContrastQuery = window.matchMedia('(prefers-contrast: high)'); if (highContrastQuery.matches) { this.config.accessibility.highContrast = true; } } } /** * יצירת דשבורד חדש */ createDashboard(config: { name: string; description?: string; layout?: Dashboard['layout']; author?: string; tags?: string[]; }): string { const dashboardId = `dashboard_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const dashboard: Dashboard = { id: dashboardId, name: config.name, description: config.description, widgets: [], layout: config.layout || 'grid', filters: [], metadata: { created: Date.now(), updated: Date.now(), author: config.author, version: '1.0.0', tags: config.tags } }; this.dashboards.set(dashboardId, dashboard); console.log(`📊 Dashboard created: ${config.name}`); return dashboardId; } /** * הוספת widget לדשבורד */ addWidget(dashboardId: string, widget: Omit<DashboardWidget, 'id'>): string { const dashboard = this.dashboards.get(dashboardId); if (!dashboard) { throw new Error(`Dashboard not found: ${dashboardId}`); } const widgetId = `widget_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const fullWidget: DashboardWidget = { id: widgetId, ...widget }; dashboard.widgets.push(fullWidget); dashboard.metadata.updated = Date.now(); if (fullWidget.config.autoUpdate && fullWidget.config.refreshInterval) { this.setupWidgetAutoRefresh(dashboardId, widgetId); } console.log(`📋 Widget added: ${widget.title}`); return widgetId; } private setupWidgetAutoRefresh(dashboardId: string, widgetId: string): void { const dashboard = this.dashboards.get(dashboardId); const widget = dashboard?.widgets.find(w => w.id === widgetId); if (!widget || !widget.config.refreshInterval) return; const intervalId = setInterval(() => { this.refreshWidget(dashboardId, widgetId); }, widget.config.refreshInterval); this.activeSubscriptions.set(`${dashboardId}_${widgetId}`, intervalId); } private async refreshWidget(dashboardId: string, widgetId: string): Promise<void> { try { const dashboard = this.dashboards.get(dashboardId); const widget = dashboard?.widgets.find(w => w.id === widgetId); if (!widget) return; switch (widget.type) { case 'chart': await this.refreshChartWidget(widget); break; case 'metric': await this.refreshMetricWidget(widget); break; case 'table': await this.refreshTableWidget(widget); break; } console.log(`🔄 Widget refreshed: ${widget.title}`); } catch (error) { console.error('Widget refresh failed:', error); } } private async refreshChartWidget(widget: DashboardWidget): Promise<void> { const newData = this.generateMockChartData(widget.data[0]?.type || 'line'); widget.data = [newData]; if (this.worker) { this.worker.postMessage({ type: 'PROCESS_DATA', data: newData.data }); } } private generateMockChartData(type: ChartData['type']): ChartData { const dataPoints: DataPoint[] = []; const pointCount = Math.min(100, this.config.performance.maxDataPoints); for (let i = 0; i < pointCount; i++) { dataPoints.push({ x: Date.now() - (pointCount - i) * 60000, y: Math.random() * 100 + Math.sin(i * 0.1) * 20, label: `Point ${i}` }); } return { id: `mock_data_${Date.now()}`, name: 'Mock Data', data: dataPoints, type, color: '#2196f3' }; } private async refreshMetricWidget(widget: DashboardWidget): Promise<void> { widget.data = { value: Math.floor(Math.random() * 1000), change: (Math.random() - 0.5) * 20, trend: Math.random() > 0.5 ? 'up' : 'down', unit: 'units', timestamp: Date.now() }; } private async refreshTableWidget(widget: DashboardWidget): Promise<void> { const rows = []; for (let i = 0; i < 10; i++) { rows.push({ id: i, name: `Item ${i}`, value: Math.floor(Math.random() * 100), status: Math.random() > 0.5 ? 'active' : 'inactive', timestamp: Date.now() }); } widget.data = { columns: ['ID', 'Name', 'Value', 'Status', 'Timestamp'], rows }; } /** * הגדרת מקור נתונים בזמן אמת */ addRealTimeDataSource(source: RealTimeDataSource): void { this.dataSources.set(source.id, source); switch (source.type) { case 'polling': this.setupPollingDataSource(source); break; case 'websocket': this.setupWebSocketDataSource(source); break; case 'sse': this.setupSSEDataSource(source); break; } console.log(`🌍 Real-time data source added: ${source.name}`); } private setupPollingDataSource(source: RealTimeDataSource): void { if (!source.endpoint || !source.interval) return; const intervalId = setInterval(async () => { try { const response = await fetch(source.endpoint!); const data = await response.json(); if (source.filters && !source.filters(data)) return; const transformedData = source.transform ? source.transform(data) : data; this.updateWidgetsWithData(source.id, transformedData); } catch (error) { console.error(`Polling data source error (${source.id}):`, error); } }, source.interval); this.activeSubscriptions.set(`datasource_${source.id}`, intervalId); } private setupWebSocketDataSource(source: RealTimeDataSource): void { if (!source.endpoint) return; try { const ws = new WebSocket(source.endpoint); ws.onmessage = (event) => { try { const data = JSON.parse(event.data); if (source.filters && !source.filters(data)) return; const transformedData = source.transform ? source.transform(data) : data; this.updateWidgetsWithData(source.id, transformedData); } catch (error) { console.error(`WebSocket data parsing error (${source.id}):`, error); } }; this.activeSubscriptions.set(`datasource_${source.id}`, ws); } catch (error) { console.error(`WebSocket setup error (${source.id}):`, error); } } private setupSSEDataSource(source: RealTimeDataSource): void { if (!source.endpoint) return; try { const eventSource = new EventSource(source.endpoint); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); if (source.filters && !source.filters(data)) return; const transformedData = source.transform ? source.transform(data) : data; this.updateWidgetsWithData(source.id, transformedData); } catch (error) { console.error(`SSE data parsing error (${source.id}):`, error); } }; this.activeSubscriptions.set(`datasource_${source.id}`, eventSource); } catch (error) { console.error(`SSE setup error (${source.id}):`, error); } } private updateWidgetsWithData(sourceId: string, data: ChartData[]): void { this.dashboards.forEach(dashboard => { dashboard.widgets.forEach(widget => { if (widget.config.autoUpdate) { widget.data = data; console.log(`🔄 Widget re-rendered: ${widget.title}`); } }); }); } /** * ייצוא דשבורד או widget */ async exportVisualization( dashboardId: string, widgetId?: string, exportConfig?: VisualizationExport ): Promise<Blob> { const config: VisualizationExport = { format: 'png', quality: 'high', dimensions: { width: 1920, height: 1080 }, includeLegend: true, includeTitle: true, ...exportConfig }; try { if (widgetId) { return await this.exportWidget(dashboardId, widgetId, config); } else { return await this.exportDashboard(dashboardId, config); } } catch (error) { console.error('Export failed:', error); throw new Error('Visualization export failed'); } } private async exportWidget(dashboardId: string, widgetId: string, config: VisualizationExport): Promise<Blob> { const dashboard = this.dashboards.get(dashboardId); const widget = dashboard?.widgets.find(w => w.id === widgetId); if (!widget) { throw new Error('Widget not found'); } const canvas = document.createElement('canvas'); canvas.width = config.dimensions!.width; canvas.height = config.dimensions!.height; const ctx = canvas.getContext('2d')!; await this.renderWidgetToCanvas(widget, ctx, config); return new Promise((resolve) => { canvas.toBlob((blob) => { resolve(blob!); }, `image/${config.format}`, config.quality === 'high' ? 1 : config.quality === 'medium' ? 0.8 : 0.6); }); } private async renderWidgetToCanvas(widget: DashboardWidget, ctx: CanvasRenderingContext2D, config: VisualizationExport): Promise<void> { ctx.fillStyle = widget.styling.backgroundColor || '#ffffff'; ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); if (config.includeTitle) { ctx.fillStyle = widget.styling.textColor || '#000000'; ctx.font = `${widget.styling.fontSize || 16}px Arial`; ctx.textAlign = 'center'; ctx.fillText(widget.title, ctx.canvas.width / 2, 30); } switch (widget.type) { case 'chart': await this.renderChartToCanvas(widget, ctx, config); break; case 'metric': await this.renderMetricToCanvas(widget, ctx, config); break; } } private async renderChartToCanvas(widget: DashboardWidget, ctx: CanvasRenderingContext2D, config: VisualizationExport): Promise<void> { const chartData = widget.data[0] as ChartData; if (!chartData || !chartData.data.length) return; const padding = 60; const chartWidth = ctx.canvas.width - 2 * padding; const chartHeight = ctx.canvas.height - 2 * padding - (config.includeTitle ? 50 : 0); const startY = config.includeTitle ? 80 : 60; // Draw chart based on type if (chartData.type === 'line') { await this.drawLineChart(ctx, chartData, padding, startY, chartWidth, chartHeight); } } private async drawLineChart(ctx: CanvasRenderingContext2D, chartData: ChartData, x: number, y: number, width: number, height: number): Promise<void> { if (!chartData.data.length) return; const minY = Math.min(...chartData.data.map(d => d.y)); const maxY = Math.max(...chartData.data.map(d => d.y)); const range = maxY - minY || 1; ctx.strokeStyle = chartData.color || '#2196f3'; ctx.lineWidth = 2; ctx.beginPath(); chartData.data.forEach((point, index) => { const chartX = x + (index / (chartData.data.length - 1)) * width; const chartY = y + height - ((point.y - minY) / range) * height; if (index === 0) { ctx.moveTo(chartX, chartY); } else { ctx.lineTo(chartX, chartY); } }); ctx.stroke(); } private async renderMetricToCanvas(widget: DashboardWidget, ctx: CanvasRenderingContext2D, config: VisualizationExport): Promise<void> { const metric = widget.data; if (!metric) return; const centerX = ctx.canvas.width / 2; const centerY = ctx.canvas.height / 2; ctx.fillStyle = widget.styling.textColor || '#000000'; ctx.font = 'bold 48px Arial'; ctx.textAlign = 'center'; ctx.fillText(metric.value.toString(), centerX, centerY); } private async exportDashboard(dashboardId: string, config: VisualizationExport): Promise<Blob> { const dashboard = this.dashboards.get(dashboardId); if (!dashboard) { throw new Error('Dashboard not found'); } const canvas = document.createElement('canvas'); canvas.width = config.dimensions!.width; canvas.height = config.dimensions!.height; const ctx = canvas.getContext('2d')!; ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, canvas.width, canvas.height); if (config.includeTitle) { ctx.fillStyle = '#000000'; ctx.font = 'bold 32px Arial'; ctx.textAlign = 'center'; ctx.fillText(dashboard.name, canvas.width / 2, 40); } return new Promise((resolve) => { canvas.toBlob((blob) => { resolve(blob!); }, `image/${config.format}`, config.quality === 'high' ? 1 : 0.8); }); } /** * Public API Methods */ getDashboard(dashboardId: string): Dashboard | null { return this.dashboards.get(dashboardId) || null; } getAllDashboards(): Dashboard[] { return Array.from(this.dashboards.values()); } updateWidget(dashboardId: string, widgetId: string, updates: Partial<DashboardWidget>): boolean { const dashboard = this.dashboards.get(dashboardId); const widget = dashboard?.widgets.find(w => w.id === widgetId); if (!widget) return false; Object.assign(widget, updates); dashboard!.metadata.updated = Date.now(); console.log(`📋 Widget updated: ${widget.title}`); return true; } removeWidget(dashboardId: string, widgetId: string): boolean { const dashboard = this.dashboards.get(dashboardId); if (!dashboard) return false; const index = dashboard.widgets.findIndex(w => w.id === widgetId); if (index === -1) return false; dashboard.widgets.splice(index, 1); dashboard.metadata.updated = Date.now(); const subscriptionKey = `${dashboardId}_${widgetId}`; const subscription = this.activeSubscriptions.get(subscriptionKey); if (subscription) { clearInterval(subscription); this.activeSubscriptions.delete(subscriptionKey); } console.log(`🗑️ Widget removed: ${widgetId}`); return true; } getPerformanceMetrics(): typeof this.performanceMetrics { return { ...this.performanceMetrics }; } updateConfig(updates: Partial<VisualizationConfig>): void { this.config = { ...this.config, ...updates }; console.log('⚙️ Visualization config updated'); } dispose(): void { if (this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); } if (this.worker) { this.worker.terminate(); } this.activeSubscriptions.forEach((subscription, key) => { if (typeof subscription === 'number') { clearInterval(subscription); } else if (subscription.close) { subscription.close(); } }); this.dashboards.clear(); this.dataSources.clear(); this.activeSubscriptions.clear(); console.log('🎨 Advanced Visualization System disposed'); } } export default AdvancedVisualizationSystem;