'use client' import React, { useEffect, useRef, useState } from 'react' import { AbstractMesh, Vector3 } from '@babylonjs/core' interface Canvas2DPlanProps { meshes: AbstractMesh[] sensorStatusMap: Record onClose: () => void onSensorClick?: (sensorId: string) => void } interface Sensor2D { id: string x: number y: number status: string } const Canvas2DPlan: React.FC = ({ meshes, sensorStatusMap, onClose, onSensorClick, }) => { const canvasRef = useRef(null) const [sensors, setSensors] = useState([]) const [hoveredSensor, setHoveredSensor] = useState(null) const [scale, setScale] = useState(10) const [offset, setOffset] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) // Извлечение датчиков из mesh'ей useEffect(() => { const extractedSensors: Sensor2D[] = [] console.log('[Canvas2DPlan] Extracting sensors from meshes:', meshes.length) console.log('[Canvas2DPlan] sensorStatusMap:', sensorStatusMap) let meshesWithMetadata = 0 let meshesWithSensorID = 0 let meshesInStatusMap = 0 meshes.forEach((mesh, index) => { if (mesh.metadata) { meshesWithMetadata++ if (index < 3) { console.log(`[Canvas2DPlan] Sample mesh[${index}] metadata:`, mesh.metadata) } } const sensorId = mesh.metadata?.Sensor_ID if (sensorId) { meshesWithSensorID++ if (index < 3) { console.log(`[Canvas2DPlan] Sample mesh[${index}] Sensor_ID:`, sensorId, 'in map?', !!sensorStatusMap[sensorId]) } } if (sensorId && sensorStatusMap[sensorId]) { meshesInStatusMap++ const position = mesh.getAbsolutePosition() extractedSensors.push({ id: sensorId, x: position.x, y: position.z, // Используем Z как Y для вида сверху status: sensorStatusMap[sensorId], }) } }) console.log('[Canvas2DPlan] Meshes with metadata:', meshesWithMetadata) console.log('[Canvas2DPlan] Meshes with Sensor_ID:', meshesWithSensorID) console.log('[Canvas2DPlan] Meshes in statusMap:', meshesInStatusMap) console.log('[Canvas2DPlan] Extracted sensors:', extractedSensors.length, extractedSensors) setSensors(extractedSensors) // Автоматическое центрирование if (extractedSensors.length > 0 && canvasRef.current) { const minX = Math.min(...extractedSensors.map((s) => s.x)) const maxX = Math.max(...extractedSensors.map((s) => s.x)) const minY = Math.min(...extractedSensors.map((s) => s.y)) const maxY = Math.max(...extractedSensors.map((s) => s.y)) const centerX = (minX + maxX) / 2 const centerY = (minY + maxY) / 2 const canvas = canvasRef.current setOffset({ x: canvas.width / 2 - centerX * scale, y: canvas.height / 2 - centerY * scale, }) } }, [meshes, sensorStatusMap, scale]) // Рендеринг canvas useEffect(() => { const canvas = canvasRef.current if (!canvas) return const ctx = canvas.getContext('2d') if (!ctx) return // Очистка ctx.clearRect(0, 0, canvas.width, canvas.height) // Фон ctx.fillStyle = '#0e111a' ctx.fillRect(0, 0, canvas.width, canvas.height) // Сетка ctx.strokeStyle = '#1a1d2e' ctx.lineWidth = 1 const gridSize = 50 for (let x = 0; x < canvas.width; x += gridSize) { ctx.beginPath() ctx.moveTo(x, 0) ctx.lineTo(x, canvas.height) ctx.stroke() } for (let y = 0; y < canvas.height; y += gridSize) { ctx.beginPath() ctx.moveTo(0, y) ctx.lineTo(canvas.width, y) ctx.stroke() } // Рисуем датчики sensors.forEach((sensor) => { const x = sensor.x * scale + offset.x const y = sensor.y * scale + offset.y // Определяем цвет по статусу let color = '#6b7280' // gray if (sensor.status === 'critical') color = '#ef4444' // red else if (sensor.status === 'warning') color = '#f59e0b' // amber else if (sensor.status === 'normal') color = '#10b981' // green // Внешний круг (подсветка при hover) if (hoveredSensor === sensor.id) { ctx.fillStyle = color + '40' ctx.beginPath() ctx.arc(x, y, 20, 0, Math.PI * 2) ctx.fill() } // Основной круг датчика ctx.fillStyle = color ctx.beginPath() ctx.arc(x, y, 8, 0, Math.PI * 2) ctx.fill() // Обводка ctx.strokeStyle = '#ffffff' ctx.lineWidth = 2 ctx.stroke() // Подпись ctx.fillStyle = '#ffffff' ctx.font = '12px Inter, sans-serif' ctx.textAlign = 'center' ctx.fillText(sensor.id, x, y - 15) }) // Легенда const legendX = 20 const legendY = canvas.height - 80 ctx.fillStyle = '#161824cc' ctx.fillRect(legendX - 10, legendY - 10, 180, 70) const statuses = [ { label: 'Критический', color: '#ef4444' }, { label: 'Предупреждение', color: '#f59e0b' }, { label: 'Нормальный', color: '#10b981' }, ] statuses.forEach((status, index) => { const y = legendY + index * 20 ctx.fillStyle = status.color ctx.beginPath() ctx.arc(legendX, y, 6, 0, Math.PI * 2) ctx.fill() ctx.fillStyle = '#ffffff' ctx.font = '12px Inter, sans-serif' ctx.textAlign = 'left' ctx.fillText(status.label, legendX + 15, y + 4) }) }, [sensors, scale, offset, hoveredSensor]) // Обработка клика const handleCanvasClick = (e: React.MouseEvent) => { const canvas = canvasRef.current if (!canvas) return const rect = canvas.getBoundingClientRect() const clickX = e.clientX - rect.left const clickY = e.clientY - rect.top // Проверяем клик по датчику for (const sensor of sensors) { const x = sensor.x * scale + offset.x const y = sensor.y * scale + offset.y const distance = Math.sqrt((clickX - x) ** 2 + (clickY - y) ** 2) if (distance <= 10) { onSensorClick?.(sensor.id) return } } } // Обработка hover const handleCanvasMove = (e: React.MouseEvent) => { if (isDragging) { const dx = e.clientX - dragStart.x const dy = e.clientY - dragStart.y setOffset((prev) => ({ x: prev.x + dx, y: prev.y + dy })) setDragStart({ x: e.clientX, y: e.clientY }) return } const canvas = canvasRef.current if (!canvas) return const rect = canvas.getBoundingClientRect() const mouseX = e.clientX - rect.left const mouseY = e.clientY - rect.top let foundSensor: string | null = null for (const sensor of sensors) { const x = sensor.x * scale + offset.x const y = sensor.y * scale + offset.y const distance = Math.sqrt((mouseX - x) ** 2 + (mouseY - y) ** 2) if (distance <= 10) { foundSensor = sensor.id break } } setHoveredSensor(foundSensor) } // Обработка zoom const handleWheel = (e: React.WheelEvent) => { e.preventDefault() const delta = e.deltaY > 0 ? 0.9 : 1.1 setScale((prev) => Math.max(1, Math.min(50, prev * delta))) } // Обработка drag const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true) setDragStart({ x: e.clientX, y: e.clientY }) } const handleMouseUp = () => { setIsDragging(false) } return (
{/* Заголовок */}

2D План-схема

{/* Canvas */}
{/* Подсказка */}

Колесико мыши - масштаб | Перетаскивание - перемещение | Клик по датчику - подробности

) } export default Canvas2DPlan