diff --git a/src/frontend/src/pages/Dashboard.js b/src/frontend/src/pages/Dashboard.js index e12bf92..b8348bf 100644 --- a/src/frontend/src/pages/Dashboard.js +++ b/src/frontend/src/pages/Dashboard.js @@ -1,43 +1,43 @@ import React, { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; -import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'; -import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'; import Drone from './model/Drone'; import HeightPoint from './model/HeightPoint'; -import {RandomHeightPoint} from './helpers/generateRandomHeightPoints' -import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; +import { RandomHeightPoint } from './helpers/generateRandomHeightPoints'; + +// class HeightPoint { +// constructor(x, y, height) { +// this.x = x; +// this.y = y; +// this.height = height; +// } +// } const Dashboard = () => { - const mountRef = useRef(null); // Указатель монтирования - const sceneRef = useRef(null); // Указатель на сцену - const [heightData, setHeightData] = useState(RandomHeightPoint()); // Высоты - const [formData, setFormData] = useState({ x: '', y: '', height: '' }); // Форма для ввода данных карты - const [mouseState, setMouseState] = useState({ x: 0, y: 0, z: 0 }); // Форма для ввода данных карты + const mountRef = useRef(null); + const sceneRef = useRef(null); + const [state, setState] = useState({ + heightData: RandomHeightPoint(), + drones: [], + selectedDrone: null, + baseStation: [], + selectedBaseStation: null, + formData: { x: '', y: '', height: '' }, + mapSettings: { maxHeight: 20, coordX: 0, coordY: 0 }, + contextMenu: { visible: false, x: 0, y: 0 }, + mouseState: { x: 0, y: 0, z: 0 }, + }); const mouse = new THREE.Vector2(); - const [drones, setDrones] = useState([]) // Все дроны - const [selectedDrone, setSelectedDrone] = useState(null); // Состояние для выбранного дрона - - const [baseStation, setBaseStation] = useState([]) // Все дроны - const [selectedBaseStation, setSelectedBaseStation] = useState(null); // Состояние для выбранного дрона - - const [mapSettings, setMapSettings] = useState({ - maxHeight: 20, - coordX: 0, - coordY: 0, - }); // Настройки карты - - const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }); // контекстное меню сцены useEffect(() => { if (!mountRef.current) return; + let width = mountRef.current.clientWidth; let height = mountRef.current.clientHeight; - // Создаем объект сцены + + // Создание сцены и камеры const scene = new THREE.Scene(); - // Сохраняем ссылку на сцену sceneRef.current = scene; - // Создаем камеру const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); camera.position.set(0, 20, 30); @@ -45,21 +45,21 @@ const Dashboard = () => { renderer.setSize(width, height); mountRef.current.appendChild(renderer.domElement); - // Плоскость для высотной карты :: TODO :: W/H/Ws/Hs change + // Плоскость для высотной карты const geometry = new THREE.PlaneGeometry(20, 20, 4, 4); - const vertices = geometry.attributes.position.array; const colors = []; const color = new THREE.Color(); + + const { heightData, mapSettings } = state; + const minHeight = Math.min(...heightData.map(point => point.height)); + const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map(point => point.height)); - const minHeight = Math.min(...heightData.map((point) => point.height)); - const maxHeight = Math.max(mapSettings.maxHeight, ...heightData.map((point) => point.height)); - + // Задание высот и цветов вершин for (let i = 0; i < vertices.length; i += 3) { const x = Math.floor(i / 3) % 5; const y = Math.floor(i / (3 * 5)); - - const heightPoint = heightData.find((point) => point.x === x && point.y === y); + const heightPoint = heightData.find(point => point.x === x && point.y === y); const heightValue = heightPoint ? heightPoint.height : 0; vertices[i + 2] = heightValue; @@ -68,7 +68,6 @@ const Dashboard = () => { color.setHSL(0.7 * (1 - normalizedHeight), 1, 0.5); colors.push(color.r, color.g, color.b); } - geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); const material = new THREE.MeshBasicMaterial({ @@ -81,9 +80,6 @@ const Dashboard = () => { mesh.rotation.x = -Math.PI / 2; scene.add(mesh); - const axesHelper = new THREE.AxesHelper(10); - scene.add(axesHelper); - const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; @@ -91,248 +87,139 @@ const Dashboard = () => { light.position.set(0, 50, 50).normalize(); scene.add(light); - // Обработка кликов на сцене - const handleClick = (event) => { - mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; - mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; - console.log('Mouse coordinates:', mouse); - const raycaster = new THREE.Raycaster(); - raycaster.setFromCamera(mouse, camera); - - const intersects = raycaster.intersectObjects(drones.map(drone => drone.getObject()), true); - console.log('Intersections:', intersects) - console.log('Drones:', drones.map(drone => [drone.getObject(), drone.getObject().position, drone.name])) - if (intersects.length > 0) { - const selected = intersects[0].object; - console.log('Clicked on:', selected); // Лог для проверки объекта - const drone = drones.find(drone => drone.getObject().children[0] === selected); - if (drone) { - if (selectedDrone) { - selectedDrone.getObject().children[0].material.color.set(0xff0000); - } - drone.getObject().children[0].material.color.set(0xff1111); - setSelectedDrone(drone); - } - } else { - if (selectedDrone) { - selectedDrone.getObject().children[0].material.color.set(0xff0000); - setSelectedDrone(null); - } - } - }; - - - - renderer.domElement.addEventListener('click', handleClick); - renderer.domElement.addEventListener('mousemove', (event) => { - let x = event.x; - let y = event.y; - setMouseState({x: x, y: y, z: 0}); - }) const animate = () => { requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); }; - animate(); - // Добавление сохранённых дронов в сцену при рендере - drones.forEach(droneData => { - // const drone = new Drone(); - // drone.setPosition(droneData.x, droneData.y, droneData.z); - scene.add(droneData.getObject()); //drone.getObject() + + // События мыши + const handleClick = (event) => { + const { drones, selectedDrone } = state; + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; + + const raycaster = new THREE.Raycaster(); + raycaster.setFromCamera(mouse, camera); + const intersects = raycaster.intersectObjects(drones.map(drone => drone.getObject()), true); + + if (intersects.length > 0) { + const selected = intersects[0].object; + const drone = drones.find(drone => drone.getObject().children[0] === selected); + if (drone) { + if (selectedDrone) selectedDrone.getObject().children[0].material.color.set(0xff0000); + drone.getObject().children[0].material.color.set(0xff1111); + setState(prevState => ({ ...prevState, selectedDrone: drone })); + } + } else if (selectedDrone) { + selectedDrone.getObject().children[0].material.color.set(0xff0000); + setState(prevState => ({ ...prevState, selectedDrone: null })); + } + }; + + renderer.domElement.addEventListener('click', handleClick); + renderer.domElement.addEventListener('mousemove', (event) => { + setState(prevState => ({ + ...prevState, + mouseState: { x: event.clientX, y: event.clientY, z: 0 } + })); }); - // Обработка нажатия ПКМ - renderer.domElement.addEventListener('contextmenu', (event) => { - event.preventDefault(); - setContextMenu({ - visible: true, - x: event.clientX, - y: event.clientY, - }); - }); - // const gui = new GUI(); - // gui.add( effectController, 'focalLength', 1, 135, 0.01 ).onChange( matChanger ); return () => { if (mountRef.current) { mountRef.current.removeChild(renderer.domElement); } }; - }, [drones,heightData, mapSettings]); + }, [state.drones, state.heightData, state.mapSettings]); - // Добавление дрона в сцену const handleAddDrone = () => { - if (sceneRef.current) { - const current = Date.now(); - const drone = new Drone(current/1000); - // Создаем новый материал для каждого дрона - const droneMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); - drone.getObject().children[0].material = droneMaterial; - drone.setPosition(Math.random() * 20 - 10, 3, Math.random() * 20 - 10); - setDrones((prevDrones) => [...prevDrones, drone]); - console.log(drones.map(drone => drone.getObject().children[0])); - sceneRef.current.add(drone.getObject()); - } + const { drones } = state; + const drone = new Drone(Date.now() / 1000); + const droneMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); + drone.getObject().children[0].material = droneMaterial; + drone.setPosition(Math.random() * 20 - 10, 3, Math.random() * 20 - 10); + setState(prevState => ({ + ...prevState, + drones: [...prevState.drones, drone] + })); + sceneRef.current.add(drone.getObject()); }; - - // + const handleInputChange = (event) => { const { name, value } = event.target; - setFormData({ - ...formData, - [name]: value, - }); + setState(prevState => ({ + ...prevState, + formData: { ...prevState.formData, [name]: value } + })); }; - // Добавление элемента карты высот (высоты) + const handleAddHeight = (event) => { event.preventDefault(); - - const { x, y, height } = formData; - - const newPoint = new HeightPoint(parseInt(x), parseInt(y), parseFloat(height)); - - setHeightData((prevData) => [...prevData, newPoint]); - setFormData({ x: '', y: '', height: '' }); + const { formData, heightData } = state; + const newPoint = new HeightPoint(parseInt(formData.x), parseInt(formData.y), parseFloat(formData.height)); + setState(prevState => ({ + ...prevState, + heightData: [...prevState.heightData, newPoint], + formData: { x: '', y: '', height: '' } + })); }; - // Изменение настроек + const handleSettingsChange = (event) => { const { name, value } = event.target; - setMapSettings({ - ...mapSettings, - [name]: value, - }); + setState(prevState => ({ + ...prevState, + mapSettings: { ...prevState.mapSettings, [name]: value } + })); }; - // Открытие контекстного меню + const handleContextMenuClick = (action) => { - if (action === 'add') { - handleAddDrone(); - console.log('Добавить объект'); - } else if (action === 'save') { - console.log('Сохранить промежуточный результат'); - } - setContextMenu({ ...contextMenu, visible: false }); + if (action === 'add') handleAddDrone(); + setState(prevState => ({ + ...prevState, + contextMenu: { ...prevState.contextMenu, visible: false } + })); }; return (
-

Drone Network Simulator - окно разработки

- +

Drone Network Simulator

-
-

x: {mouseState.x} y: {mouseState.y} z: {mouseState.z}

-
+

x: {state.mouseState.x} y: {state.mouseState.y} z: {state.mouseState.z}

- {/* Контекстное меню */} - {contextMenu.visible && ( -