diff --git a/public/ar_main.js b/public/ar_main.js index 51f038738b059c8dc8e2e11bceda62a09c9bbde0..dfaec946636c5d9205f3a44ffe7608d43c7afb55 100644 --- a/public/ar_main.js +++ b/public/ar_main.js @@ -1,11 +1,16 @@ - // Variablen - let selectedModel = 'robot'; + /* ========================= */ + /* GLOBALE VARIABLEN */ + /* ========================= */ let selectedPlacedModel = null; let currentSession = null; let reticle = null; let scene, camera; let geoLocation; - + const menus = ['menu-bar', 'add-menu', 'edit-menu', 'options-menu', 'map-window']; + + /* ========================= */ + /* MODELLE */ + /* ========================= */ let models = { bench: { name: "Bench", @@ -65,12 +70,13 @@ } }; - const menus = ['menu-bar', 'add-menu', 'edit-menu', 'options-menu', 'map-window']; - + /* ========================= */ + /* INITIALISIERUNG */ + /* ========================= */ window.onload = () => { initializeAddMenu(); - // Fügt Sound zu allen Buttons hinzu + // Allen Buttons den Sound hinzufügen const buttons = document.querySelectorAll("button, .menu-item"); buttons.forEach(button => { button.addEventListener("click", playButtonSound); @@ -95,6 +101,9 @@ `; } + /* ========================= */ + /* MENÜ-STEUERUNG */ + /* ========================= */ function showMenu(menuId) { const isMapWindow = menuId === 'map-window'; if (isMapWindow && !geoLocation) { @@ -111,23 +120,16 @@ else if (isMapWindow) init_map(); } - function clearSelectedModel() { - if (selectedPlacedModel) { - selectedPlacedModel.traverse((child) => { - if (child.isMesh) { - child.material.emissive.setHex(0x000000); // Markierung entfernen - } - }); - selectedPlacedModel = null; // Kein Modell mehr ausgewählt - } + function closeDynamicMenu() { + const dynamicMenu = document.getElementById("dynamic-menu"); + dynamicMenu.style.display = "none"; } - function selectModel(modelId) { - const model = models[modelId]; - if (model && model.file) { - loadModel(model.file); - showMenu('menu-bar'); - } + /* ========================= */ + /* MODELL-HANDLING */ + /* ========================= */ + function getAllPlacedModels() { + return scene.children.filter(child => child.isPlacedModel === true); } function loadModel(filePath) { @@ -164,30 +166,11 @@ ); } - function highlightSelectedModel() { - if (selectedPlacedModel) { - removeHighlightFromAllModels(); - selectedPlacedModel.traverse((child) => { - if (child.isMesh) { - child.material.emissive.setHex(0xff0000); // Rote Hervorhebung - } - }); - } - } - - function removeHighlightFromAllModels() { - scene.traverse((child) => { - if (child.isMesh && child.material && child.material.emissive) { - child.material.emissive.setHex(0x000000); // Markierung entfernen - } - }); - } - - function removeHighlightFromSelectedModel() { - if (selectedPlacedModel) { - selectedPlacedModel.traverse((child) => { - if (child.isMesh) child.material.emissive.setHex(0x000000); // Markierung entfernen - }); + function selectModel(modelId) { + const model = models[modelId]; + if (model && model.file) { + loadModel(model.file); + showMenu('menu-bar'); } } @@ -210,6 +193,12 @@ selectedObject = selectedObject.parent; } + // Überprüfe, ob das ausgewählte Objekt das Reticle ist + if (selectedObject === reticle) { + console.log("Reticle kann nicht ausgewählt werden."); + return; + } + // Markiere das gesamte Modell als ausgewählt selectedPlacedModel = selectedObject; highlightSelectedModel(); @@ -217,6 +206,66 @@ } } + function clearSelectedModel() { + if (selectedPlacedModel) { + selectedPlacedModel.traverse((child) => { + if (child.isMesh) { + child.material.emissive.setHex(0x000000); // Markierung entfernen + } + }); + selectedPlacedModel = null; + } + } + + function highlightSelectedModel() { + if (selectedPlacedModel) { + removeHighlightFromAllModels(); + selectedPlacedModel.traverse((child) => { + if (child.isMesh) { + child.material.emissive.setHex(0xff0000); // Rote Hervorhebung + } + }); + } + } + + function removeHighlightFromSelectedModel() { + if (selectedPlacedModel) { + selectedPlacedModel.traverse((child) => { + if (child.isMesh) child.material.emissive.setHex(0x000000); // Markierung entfernen + }); + } + } + + + function removeHighlightFromAllModels() { + scene.traverse((child) => { + if (child.isMesh && child.material && child.material.emissive) { + child.material.emissive.setHex(0x000000); // Markierung entfernen + } + }); + } + + function deleteModel() { + if (!selectedPlacedModel) { + console.log("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen."); + return; + } + + const deleteDialog = document.getElementById('delete-confirmation-dialog'); + deleteDialog.style.display = 'flex'; + } + + function confirmDelete(shouldDelete) { + const deleteDialog = document.getElementById('delete-confirmation-dialog'); + deleteDialog.style.display = 'none'; + + if (shouldDelete && selectedPlacedModel) { + scene.remove(selectedPlacedModel); + selectedPlacedModel = null; + showMenu('menu-bar'); + } + } + function completeEditing() { removeHighlightFromSelectedModel(); closeDynamicMenu(); @@ -225,14 +274,16 @@ document.getElementById('menu-bar').style.display = 'flex'; } + /* ========================= */ + /* BEARBEITUNGS-MENÜS */ + /* ========================= */ function openRotationMenu() { if (!selectedPlacedModel) { - showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."); + console.log("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."); return; } - // Holen Sie die aktuelle Y-Rotation des Modells (in Grad) - const currentRotation = Math.round(THREE.MathUtils.radToDeg(selectedPlacedModel.rotation.y)); + const currentRotation = Math.round(THREE.MathUtils.radToDeg(selectedPlacedModel.rotation.y)); // Aktuelle Y-Rotation des Modells (in Grad) const dynamicMenu = document.getElementById("dynamic-menu"); dynamicMenu.style.display = "flex"; @@ -248,7 +299,7 @@ const radians = (value / 180) * Math.PI; selectedPlacedModel.rotation[axis] = radians; - // Update der aktuellen Rotation im Menü + // Anzeige der aktuellen Rotation im Dynamic-Menü aktualisieren const currentRotationDisplay = document.getElementById("current-rotation"); if (currentRotationDisplay) { currentRotationDisplay.textContent = value; // Zeige den aktuellen Wert in Grad an @@ -256,127 +307,101 @@ } } - function calculateBoundingBox(object) { - const box = new THREE.Box3().setFromObject(object); - const size = new THREE.Vector3(); - box.getSize(size); - return size; - } - - - function calculateMaxScale(object) { - const boundingBox = calculateBoundingBox(object); - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - - // Berechne die maximal mögliche Skalierung, um im Viewport zu bleiben - const scaleWidth = viewportWidth / boundingBox.x; - const scaleHeight = viewportHeight / boundingBox.y; - - // Wähle den kleineren Wert und reduziere ihn leicht, um sicherzugehen, dass das Objekt nicht über den Rand hinausgeht - const safeScaleFactor = 0.95; // Puffer, um sicherzustellen, dass es nicht zu groß wird - - return Math.min(scaleWidth, scaleHeight) * safeScaleFactor; - } - - - /** - function openScaleMenu() { - if (!selectedPlacedModel) { - showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."); - return; - } - - // Berechne die maximale Skalierung für das spezifische Objekt - const maxScale = calculateMaxScale(selectedPlacedModel); - - // Aktuelle Skalierung des Modells bestimmen - const currentScale = selectedPlacedModel.scale.x; - - const dynamicMenu = document.getElementById("dynamic-menu"); - dynamicMenu.style.display = "flex"; - dynamicMenu.innerHTML = ` - <h3>Skalierung anpassen</h3> - <label>Größe: <span id="scale-value">${currentScale.toFixed(2)}</span><input type="range" min="0.01" max="${maxScale.toFixed(2)}" step="0.0001" value="${currentScale}" onchange="updateScale(this.value)"></label> - <button onclick="closeDynamicMenu()">Zurück</button> - `; - } * */ - - - - - function openScaleMenu() { if (!selectedPlacedModel) { - showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."); + console.log("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."); return; } - // Berechne die maximale Skalierung für das spezifische Objekt - // const maxScale = calculateMaxScale(selectedPlacedModel); - - // Aktuelle Skalierung des Modells bestimmen + // Aktuelle Skalierung und Grenzen bestimmen const currentScale = selectedPlacedModel.scale.x; const minScale = selectedPlacedModel.modelConfig.minScale; const maxScale = selectedPlacedModel.modelConfig.maxScale; const step = (maxScale - minScale) / 100; // Dynamische Schrittgröße basierend auf Grenzen - console.log("Slider-Werte:", { minScale, maxScale, currentScale }); + const currentScalePercent = ((currentScale - minScale) / (maxScale - minScale)) * 100; // Umrechnung der Werte in Prozent const dynamicMenu = document.getElementById("dynamic-menu"); dynamicMenu.style.display = "flex"; dynamicMenu.innerHTML = ` <h3>Skalierung anpassen</h3> - <label>Größe: <span id="scale-value">${currentScale.toFixed(2)}</span> - <input type="range" min="${minScale}" max="${maxScale}" step="${step}" value="${currentScale}" onchange="updateScale(this.value)"></label> + <label>Größe: <span id="scale-value">${currentScalePercent.toFixed(0)}%</span> + <input type="range" min="0" max="100" step="1" value="${currentScalePercent}" onchange="updateScale(this.value, ${minScale}, ${maxScale})"></label> <button onclick="closeDynamicMenu()">Zurück</button> `; } - function updateScale(value) { + function updateScale(percentValue, minScale, maxScale) { if (selectedPlacedModel) { - const scale = parseFloat(value); + // Berechnung der Skalierung basierend auf dem Prozentwert + const scale = minScale + (percentValue / 100) * (maxScale - minScale); selectedPlacedModel.scale.set(scale, scale, scale); - // Anzeige des aktuellen Wertes aktualisieren + // Anzeige des aktuellen Prozentsatzes im Dynamic-Menü aktualisieren const scaleValueDisplay = document.getElementById("scale-value"); if (scaleValueDisplay) { - scaleValueDisplay.textContent = `${scale.toFixed(2)}`; + scaleValueDisplay.textContent = `${parseInt(percentValue, 10)}%`; } } } - function deleteModel() { + let moveDelta = 0.1; // Standardwert für die Verschiebungsgröße, kann mit dem Slider geändert werden + + function openMoveMenu() { if (!selectedPlacedModel) { - showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen."); + console.log("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bewegen."); return; } - // Dialog anzeigen - const deleteDialog = document.getElementById('delete-confirmation-dialog'); - deleteDialog.style.display = 'flex'; - } + const dynamicMenu = document.getElementById("dynamic-menu"); + dynamicMenu.style.display = "flex"; - function confirmDelete(shouldDelete) { - const deleteDialog = document.getElementById('delete-confirmation-dialog'); - deleteDialog.style.display = 'none'; + dynamicMenu.innerHTML = ` + <h3>Modell bewegen</h3> + <div id="position-info"> + <p>Aktuelle Position: X=${selectedPlacedModel.position.x.toFixed(2)}, Z=${selectedPlacedModel.position.z.toFixed(2)}</p> + </div> + <label>Verschiebungsgröße: <span id="move-delta-display">${moveDelta.toFixed(2)}</span></label> + <input type="range" min="0.01" max="1.0" step="0.01" value="${moveDelta}" onchange="updateMoveDelta(this.value)"> + <div style="display: flex; flex-direction: column; align-items: center;"> + <div> + <button onclick="moveModelDynamic('x', -1)">↠X</button> + <button onclick="moveModelDynamic('x', 1)">→ X</button> + </div> + <div> + <button onclick="moveModelDynamic('z', -1)">- Z</button> + <button onclick="moveModelDynamic('z', 1)">+ Z</button> + </div> + </div> + <button onclick="closeDynamicMenu()">Zurück</button> + `; + } - if (shouldDelete && selectedPlacedModel) { - scene.remove(selectedPlacedModel); - selectedPlacedModel = null; - showMenu('menu-bar'); + function updateMoveDelta(value) { + moveDelta = parseFloat(value); + const moveDeltaDisplay = document.getElementById("move-delta-display"); + if (moveDeltaDisplay) { + moveDeltaDisplay.textContent = moveDelta.toFixed(2); } } - function closeDynamicMenu() { - const dynamicMenu = document.getElementById("dynamic-menu"); - dynamicMenu.style.display = "none"; + function moveModelDynamic(axis, direction) { + if (selectedPlacedModel) { + const delta = direction * moveDelta; // Dynamischer Wert basierend auf Slider + selectedPlacedModel.position[axis] += delta; + + // Position im Menü aktualisieren + const positionInfo = document.getElementById("position-info"); + if (positionInfo) { + positionInfo.innerHTML = ` + <p>Aktuelle Position: X=${selectedPlacedModel.position.x.toFixed(2)}, Z=${selectedPlacedModel.position.z.toFixed(2)}</p>`; + } + } } - function getAllPlacedModels() { - return scene.children.filter(child => child.isPlacedModel === true); - } - + /* ========================= */ + /* KARTENSTEUERUNG */ + /* ========================= */ function refreshMapDialog() { const mapDialog = document.getElementById('map-dialog'); mapDialog.style.display = 'flex'; @@ -387,6 +412,9 @@ mapDialog.style.display = 'none'; } + /* ========================= */ + /* AR-HANDLING */ + /* ========================= */ async function activateXR(sceneData = null) { const canvas = document.createElement('canvas'); document.body.appendChild(canvas); @@ -424,6 +452,7 @@ currentSession.addEventListener("end", () => { currentSession = null; + document.getElementById("dynamic-menu").style.display = "none"; menus.forEach(id => { document.getElementById(id).style.display = 'none'; }); @@ -441,7 +470,6 @@ console.log("GeoLocation: " + JSON.stringify(geoLocation)); if (sceneData) { sceneData.models.forEach((model) => { - const offSet = leafletToThree(sceneData.lat, sceneData.lng) if (model.name) { const filePath = Object.values(models).find(m => model.name === m.name).file; const { x, z } = leafletToThree(model.lat, model.lng); @@ -498,14 +526,14 @@ } function confirmExit(shouldExit) { - if (shouldExit && currentSession) { - currentSession.end(); - } + if (shouldExit && currentSession) currentSession.end(); document.getElementById('confirmation-dialog').style.display = 'none'; } + /* ========================= */ + /* BENUTZERINTERAKTIONEN */ + /* ========================= */ let soundTimeout = false; - function playButtonSound() { if (!soundTimeout) { const sound = document.getElementById("button-sound"); diff --git a/public/ar_start.js b/public/ar_start.js index f537a70fd2a5c83a87eb210fcdcff68be681147c..a1aa176c8d0bb1759df625cc717236c039e76a01 100644 --- a/public/ar_start.js +++ b/public/ar_start.js @@ -1,3 +1,6 @@ +/* ========================= */ +/* DEBUGGING UND START */ +/* ========================= */ if (navigator.xr) { let sceneData; const startButton = document.createElement('button'); diff --git a/public/assets/models/park_light_model/.gitkeep b/public/assets/models/park_light_model/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/public/assets/models/park_light_model/.gitkeep @@ -0,0 +1 @@ + diff --git a/public/assets/models/park_light_model/license.txt b/public/assets/models/park_light_model/license.txt new file mode 100644 index 0000000000000000000000000000000000000000..a5b34919e38dd57567fc2e1ca6fda3a786567b89 --- /dev/null +++ b/public/assets/models/park_light_model/license.txt @@ -0,0 +1,11 @@ +Model Information: +* title: NYC Park Streetlight, +* source: https://sketchfab.com/3d-models/nyc-park-streetlight-4efdeb23f30e49aab10fac0c3064443d +* author: Anticipateants (https://sketchfab.com/anticipateants) + +Model License: +* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) +* requirements: Author must be credited. Commercial use is allowed. + +If you use this 3D model in your project be sure to copy paste this credit wherever you share it: +This work is based on "NYC Park Streetlight," (https://sketchfab.com/3d-models/nyc-park-streetlight-4efdeb23f30e49aab10fac0c3064443d) by Anticipateants (https://sketchfab.com/anticipateants) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/) diff --git a/public/assets/models/park_light_model/scene.bin b/public/assets/models/park_light_model/scene.bin new file mode 100644 index 0000000000000000000000000000000000000000..0c87b02a74cc5a405e5527cea61530c4827f5f2d Binary files /dev/null and b/public/assets/models/park_light_model/scene.bin differ diff --git a/public/assets/models/park_light_model/scene.gltf b/public/assets/models/park_light_model/scene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..b1c096b5968d3ca5fa3a17697c05c089fa731271 --- /dev/null +++ b/public/assets/models/park_light_model/scene.gltf @@ -0,0 +1,238 @@ +{ + "accessors": [ + { + "bufferView": 2, + "componentType": 5126, + "count": 2944, + "max": [ + 1.1778082847595215, + 71.68600463867188, + 1.177808165550232 + ], + "min": [ + -1.1778085231781006, + -1.0, + -1.177808403968811 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 35328, + "componentType": 5126, + "count": 2944, + "max": [ + 0.9807855486869812, + 0.7628369927406311, + 0.9871309399604797 + ], + "min": [ + -0.9807854294776917, + -1.0, + -0.9807857275009155 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 2944, + "max": [ + 0.9722591042518616, + 0.971050500869751 + ], + "min": [ + 0.017211854457855225, + 0.012609779834747314 + ], + "type": "VEC2" + }, + { + "bufferView": 0, + "componentType": 5125, + "count": 4410, + "type": "SCALAR" + } + ], + "asset": { + "extras": { + "author": "Anticipateants (https://sketchfab.com/anticipateants)", + "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)", + "source": "https://sketchfab.com/3d-models/nyc-park-streetlight-4efdeb23f30e49aab10fac0c3064443d", + "title": "NYC Park Streetlight," + }, + "generator": "Sketchfab-13.53.0", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 17640, + "name": "floatBufferViews", + "target": 34963 + }, + { + "buffer": 0, + "byteLength": 23552, + "byteOffset": 17640, + "byteStride": 8, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 70656, + "byteOffset": 41192, + "byteStride": 12, + "name": "floatBufferViews", + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 111848, + "uri": "scene.bin" + } + ], + "images": [ + { + "uri": "textures/Material.001_baseColor.png" + } + ], + "materials": [ + { + "doubleSided": true, + "name": "Material.001", + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "name": "Object_0", + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 0, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.220446049250313e-16, + -1.0, + 0.0, + 0.0, + 1.0, + 2.220446049250313e-16, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "Sketchfab_model" + }, + { + "children": [ + 2 + ], + "name": "root" + }, + { + "children": [ + 3 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.220446049250313e-16, + 1.0, + 0.0, + 0.0, + -1.0, + 2.220446049250313e-16, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "name": "GLTF_SceneRootNode" + }, + { + "children": [ + 4 + ], + "matrix": [ + 0.03656700626015663, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010605139657855034, + 0.0, + 0.0, + 0.0, + 0.0, + 0.03656700626015663, + 0.0, + -0.006100017577409744, + 0.010527204722166061, + -0.0016316026449203491, + 1.0 + ], + "name": "main_0" + }, + { + "mesh": 0, + "name": "Object_4" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "scene": 0, + "scenes": [ + { + "name": "Sketchfab_Scene", + "nodes": [ + 0 + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ] +} diff --git a/public/assets/models/park_light_model/textures/.gitkeep b/public/assets/models/park_light_model/textures/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/public/assets/models/park_light_model/textures/Material.001_baseColor.png b/public/assets/models/park_light_model/textures/Material.001_baseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..2cdce9e040f476f8b907f81cc48ae262aec1ee64 Binary files /dev/null and b/public/assets/models/park_light_model/textures/Material.001_baseColor.png differ diff --git a/public/index.html b/public/index.html index f297f1b57a0b6c50f6c2f9ab78990f3e6db07424..dda81652d1f34a2cdc33e137304071c8c39d1b75 100644 --- a/public/index.html +++ b/public/index.html @@ -5,43 +5,16 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GeoVis AR Projekt</title> - <link rel="stylesheet" href="styles.css"> + <link rel="stylesheet" href="style.css"> <script src="https://unpkg.com/three@0.126.0/build/three.js"></script> <script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script> </head> <body> <!-- Debug --> - <div id="debug-container" style=" - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 9999; - font-family: monospace; - font-size: 12px; - display: flex; - flex-direction: column; - align-items: flex-start; - "> - <div id="debug-console" style=" - width: 100%; - max-height: 200px; - overflow-y: auto; - background: rgba(0, 0, 0, 0.7); - color: #00ff00; - padding: 5px; - display: none; - "></div> - <button id="debug-toggle-btn" style=" - background: rgba(0, 0, 0, 0.7); - color: #00ff00; - font-size: 14px; - border: none; - padding: 5px 10px; - cursor: pointer; - display: none; - "></button> + <div id="debug-container"> + <div id="debug-console"></div> + <button id="debug-toggle-btn"></button> </div> <!-- Standardmenü --> @@ -57,7 +30,7 @@ </div> </div> - <!-- Hinzufügen-Menü --> + <!-- Hinzufügen-Menü für das Auswählen der Modelle--> <div id="add-menu" class="menu-placeholder" style="display: none;"></div> <!-- Bearbeiten-Menü --> @@ -68,6 +41,9 @@ <div class="menu-item" onclick="openScaleMenu()"> <img src="previewImages/scale-icon.png" alt="Skalierung" /> </div> + <div class="menu-item" onclick="openMoveMenu()"> + <img src="previewImages/move-icon.png" alt="Bewegen" /> + </div> <div class="menu-item" onclick="deleteModel()"> <img src="previewImages/delete-icon.png" alt="Löschen" /> </div> @@ -89,7 +65,7 @@ </div> </div> - <!-- Dynamisches Menü --> + <!-- Dynamisches Menü für Rotation und Scaling--> <div id="dynamic-menu" style="display: none;"></div> <!-- Bestätigungsdialog für Löschen --> @@ -112,15 +88,6 @@ </div> </div> - <!-- Informations-Dialog --> - <div id="info-dialog" style="display: none;"> - <div class="dialog-overlay"></div> - <div class="dialog-box"> - <p id="info-text">Hier kommt die Nachricht hin</p> - <button onclick="closeInfoDialog()">OK</button> - </div> - </div> - <div id="map-window" class="menu-placeholder" style="display: none; flex-direction: column; width: 100vw; height: 100vh;"> <div id="map-container" style="z-index: 1500; flex: 15; width: 100%; height: 100%;"></div> diff --git a/public/previewImages/move-icon.png b/public/previewImages/move-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb4e252fdbbd509a05b7b0988e14d8e36443244f Binary files /dev/null and b/public/previewImages/move-icon.png differ diff --git a/public/previewImages/park_light.png b/public/previewImages/park_light.png new file mode 100644 index 0000000000000000000000000000000000000000..14c76b36526d462552946b017963bf1b7dc0a091 Binary files /dev/null and b/public/previewImages/park_light.png differ diff --git a/public/styles.css b/public/style.css similarity index 84% rename from public/styles.css rename to public/style.css index b34fa5f74f9f0bebbba193416f507528d616fa42..866ee0e740e7199ee3df8403eb41bdcd4dd7b925 100644 --- a/public/styles.css +++ b/public/style.css @@ -100,8 +100,7 @@ body { /* Confirmation Dialog */ #confirmation-dialog, - #delete-confirmation-dialog, - #info-dialog { + #delete-confirmation-dialog { position: fixed; top: 0; left: 0; @@ -171,17 +170,17 @@ body { #dynamic-menu { position: absolute; - bottom: 80px; + bottom: 90px; display: flex; flex-direction: column; align-items: center; justify-content: center; - gap: 20px; + gap: 10px; background: #1e1e1e; box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.8); color: white; width: 100vw; - height: 200px; + height: 250px; z-index: 20; overflow-y: auto; border-radius: 8px; @@ -204,4 +203,37 @@ body { border: 2px solid #555; border-radius: 50%; cursor: pointer; - } \ No newline at end of file + } + + #debug-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 9999; + font-family: monospace; + font-size: 12px; + display: flex; + flex-direction: column; + align-items: flex-start; + } + + #debug-console { + width: 100%; + max-height: 200px; + overflow-y: auto; + background: rgba(0, 0, 0, 0.7); + color: #00ff00; + padding: 5px; + display: none; + } + + #debug-toggle-btn { + background: rgba(0, 0, 0, 0.7); + color: #00ff00; + font-size: 14px; + border: none; + padding: 5px 10px; + cursor: pointer; + display: none; + }