From faaad5f09214b445447990f66df9b861d812b5c0 Mon Sep 17 00:00:00 2001
From: Tomi6545 <tomi6545@test>
Date: Sun, 19 Jan 2025 19:25:12 +0100
Subject: [PATCH] update map + save

---
 public/ar_download.js                  |  73 +++
 public/ar_main.js                      | 520 ++++++++++++++++++
 public/ar_overviewmap.js               | 177 ++++---
 public/ar_start.js                     |  29 +
 public/index.html                      | 701 +------------------------
 public/previewImages/download-icon.png | Bin 0 -> 7008 bytes
 public/styles.css                      | 207 ++++++++
 7 files changed, 953 insertions(+), 754 deletions(-)
 create mode 100644 public/ar_download.js
 create mode 100644 public/ar_main.js
 create mode 100644 public/ar_start.js
 create mode 100644 public/previewImages/download-icon.png
 create mode 100644 public/styles.css

diff --git a/public/ar_download.js b/public/ar_download.js
new file mode 100644
index 0000000..8ea47a6
--- /dev/null
+++ b/public/ar_download.js
@@ -0,0 +1,73 @@
+function downloadScene() {
+    const placedModels = getAllPlacedModels().map(model => {
+      const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
+      return {
+        name: model.modelConfig.name,
+        position: model.position,
+        rotation: model.rotation,
+        scale: model.scale,
+        lat: lat,
+        lng: lng,
+      }
+    });
+
+    console.log("Modelle: " + placedModels.length)
+    if (placedModels.length === 0) {
+      showInfoDialog("Szene ist leer");
+      return;
+    }
+
+    if (!geoLocation) {
+      console.log("Standort nicht geladen");
+      showInfoDialog("Ihr Standort wurde noch nicht geladen");
+      return;
+    }
+
+    const data = {
+      lat: geoLocation.latitude,
+      lng: geoLocation.longitude,
+      models: placedModels,
+    }
+
+    const sceneData = JSON.stringify(data, null, 1);
+    const blob = new Blob([sceneData], { type: "application/json" });
+    const url = URL.createObjectURL(blob);
+    const a = document.createElement("a");
+    a.href = url;
+    a.download = "scene.json";
+    a.style.display = "none";
+  
+    document.body.appendChild(a);
+    a.click();
+  
+    URL.revokeObjectURL(url);
+    document.body.removeChild(a);
+}
+
+function loadSceneFromFile(loadedData) {
+  const fileInput = document.createElement('input');
+  fileInput.type = 'file';
+  fileInput.accept = 'application/json';
+
+  fileInput.addEventListener('change', function(event) {
+      const file = event.target.files[0];
+      const fileName = file ? file.name : "Keine Datei ausgewählt"
+      if (!file) {
+          console.log("Keine Datei ausgewählt");
+          return;
+      }
+
+      const reader = new FileReader();
+      reader.onload = function(e) {
+          try {
+              const sceneData = JSON.parse(e.target.result);
+              loadedData(sceneData, fileName);
+          } catch (error) {
+              console.error("Fehler beim Verarbeiten der Datei:", error);
+          }
+      };
+
+      reader.readAsText(file);
+  });
+  fileInput.click();
+}
\ No newline at end of file
diff --git a/public/ar_main.js b/public/ar_main.js
new file mode 100644
index 0000000..51f0387
--- /dev/null
+++ b/public/ar_main.js
@@ -0,0 +1,520 @@
+    // Variablen
+    let selectedModel = 'robot';
+    let selectedPlacedModel = null;
+    let currentSession = null;
+    let reticle = null;
+    let scene, camera;
+    let geoLocation;
+
+    let models = {
+      bench: {
+        name: "Bench",
+        image: "previewImages/bench.PNG",
+        file: "/assets/models/bench_model/scene.gltf",
+        scale: { x: 0.1, y: 0.1, z: 0.1 },
+        minScale: 0.05, // 50% der aktuellen Größe
+        maxScale: 0.5   // 500% der aktuellen Größe
+      },
+      trashbin: {
+        name: "Trash bin",
+        image: "previewImages/trash_can.PNG",
+        file: "/assets/models/trash_model/scene.gltf",
+        scale: { x: 0.03, y: 0.03, z: 0.03 },
+        minScale: 0.01, // 50% der aktuellen Größe
+        maxScale: 0.1   // 500% der aktuellen Größe
+      },
+      telephone_box: {
+        name: "Telephone Box",
+        image: "previewImages/telephone_box.PNG",
+        file: "/assets/models/telephone_box_model/scene.gltf",
+        scale: { x: 0.5, y: 0.5, z: 0.5 },
+        minScale: 0.05,
+        maxScale: 1
+      },
+      fire_hydrant_model: {
+        name: "Fire Hydrant",
+        image: "previewImages/hydrant.PNG",
+        file: "/assets/models/fire_hydrant_model/scene.gltf",
+        scale: { x: 0.3, y: 0.3, z: 0.3 },
+        minScale: 0.1,
+        maxScale: 1
+      },
+      statue: {
+        name: "Statue",
+        image: "previewImages/statue.PNG",
+        file: "/assets/models/statue_model/scene.gltf",
+        scale: { x: 0.5, y: 0.5, z: 0.5 },
+        minScale: 0.05,
+        maxScale: 2
+      },
+      fountain: {
+        name: "Fountain",
+        image: "previewImages/fountain.PNG",
+        file: "/assets/models/fountain_model/scene.gltf",
+        scale: { x: 0.001, y: 0.001, z: 0.001 },
+        minScale: 0.0005,
+        maxScale: 0.005
+      },
+      lantern: {
+        name: "Lantern",
+        image: "previewImages/lantern.jpg",
+        file: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/Lantern/glTF/Lantern.gltf",
+        scale: { x: 0.15, y: 0.15, z: 0.15 },
+        minScale: 0.05,
+        maxScale: 0.3
+      }
+    };
+
+    const menus = ['menu-bar', 'add-menu', 'edit-menu', 'options-menu', 'map-window'];
+
+    window.onload = () => {
+      initializeAddMenu();
+
+      // Fügt Sound zu allen Buttons hinzu
+      const buttons = document.querySelectorAll("button, .menu-item");
+      buttons.forEach(button => {
+        button.addEventListener("click", playButtonSound);
+      });
+    };
+
+    function initializeAddMenu() {
+      const addMenu = document.getElementById('add-menu');
+      addMenu.innerHTML = Object.entries(models)
+        .map(
+          ([key, model]) => `
+            <div class="menu-item" id="${key}-item" onclick="selectModel('${key}')">
+              <img src="${model.image}" alt="${model.name}" />
+            </div>
+          `
+        )
+        .join('') +
+        `
+        <div class="menu-item" onclick="showMenu('menu-bar')">
+          <img src="previewImages/back-icon.png" alt="Zurück" />
+        </div>
+      `;
+    }
+
+    function showMenu(menuId) {
+      const isMapWindow = menuId === 'map-window';
+      if (isMapWindow && !geoLocation) {
+        console.log("Standort nicht geladen");
+        showInfoDialog("Ihr Standort wurde noch nicht geladen");
+        return;
+      }
+
+      menus.forEach(id => {
+        document.getElementById(id).style.display = id === menuId ? 'flex' : 'none';
+      });
+      closeDynamicMenu();
+      if (menuId === 'menu-bar') clearSelectedModel();
+      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 selectModel(modelId) {
+      const model = models[modelId];
+      if (model && model.file) {
+        loadModel(model.file);
+        showMenu('menu-bar');
+      }
+    }
+
+    function loadModel(filePath) {
+      placeModel(filePath, reticle.position, (model) => {
+        selectedPlacedModel = model;
+        highlightSelectedModel();
+        showMenu('edit-menu');
+      });
+    }
+
+    function placeModel(filePath, position, onPlace = null) {
+      const modelConfig = Object.values(models).find(model => model.file === filePath);
+      const loader = new THREE.GLTFLoader();
+      loader.load(
+        filePath,
+        (gltf) => {
+          const model = gltf.scene;
+          if (modelConfig && modelConfig.scale) {
+            model.scale.set(modelConfig.scale.x, modelConfig.scale.y, modelConfig.scale.z);
+          }
+          model.position.copy(position);
+          model.isPlacedModel = true;
+          model.modelConfig = modelConfig;
+          scene.add(model);
+
+          if (onPlace) {
+            onPlace(model);
+          }
+        },
+        undefined,
+        (error) => {
+          console.error("Fehler beim Laden des Modells:", error);
+        }
+      );
+    }
+
+    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 selectModelFromScene(event) {
+      const mouse = new THREE.Vector2(
+        (event.clientX / window.innerWidth) * 2 - 1,
+        -(event.clientY / window.innerHeight) * 2 + 1
+      );
+
+      const raycaster = new THREE.Raycaster();
+      raycaster.setFromCamera(mouse, camera);
+
+      // Prüfe Kollisionen mit Objekten in der Szene
+      const intersects = raycaster.intersectObjects(scene.children, true);
+
+      if (intersects.length > 0) {
+        // Finde das Hauptobjekt (Root-Parent), falls Mesh ausgewählt wurde
+        let selectedObject = intersects[0].object;
+        while (selectedObject.parent && selectedObject.parent !== scene) {
+          selectedObject = selectedObject.parent;
+        }
+
+        // Markiere das gesamte Modell als ausgewählt
+        selectedPlacedModel = selectedObject;
+        highlightSelectedModel();
+        showMenu("edit-menu");
+      }
+    }
+
+    function completeEditing() {
+      removeHighlightFromSelectedModel();
+      closeDynamicMenu();
+      selectedPlacedModel = null;
+      document.getElementById('edit-menu').style.display = 'none';
+      document.getElementById('menu-bar').style.display = 'flex';
+    }
+
+    function openRotationMenu() {
+      if (!selectedPlacedModel) {
+        showInfoDialog("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 dynamicMenu = document.getElementById("dynamic-menu");
+      dynamicMenu.style.display = "flex";
+      dynamicMenu.innerHTML = `
+        <h3>Rotation anpassen</h3>
+        <label>Y-Achse: <span id="current-rotation">${currentRotation}</span>°<input type="range" min="0" max="360" step="10" onchange="updateRotation('y', this.value)"></label>
+        <button onclick="closeDynamicMenu()">Zurück</button>
+      `;
+    }
+
+    function updateRotation(axis, value) {
+      if (selectedPlacedModel) {
+        const radians = (value / 180) * Math.PI;
+        selectedPlacedModel.rotation[axis] = radians;
+
+        // Update der aktuellen Rotation im Menü
+        const currentRotationDisplay = document.getElementById("current-rotation");
+        if (currentRotationDisplay) {
+          currentRotationDisplay.textContent = value; // Zeige den aktuellen Wert in Grad an
+        }
+      }
+    }
+
+    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.");
+        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 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 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>
+        <button onclick="closeDynamicMenu()">Zurück</button>
+      `;
+    }
+
+    function updateScale(value) {
+      if (selectedPlacedModel) {
+        const scale = parseFloat(value);
+        selectedPlacedModel.scale.set(scale, scale, scale);
+
+        // Anzeige des aktuellen Wertes aktualisieren
+        const scaleValueDisplay = document.getElementById("scale-value");
+        if (scaleValueDisplay) {
+          scaleValueDisplay.textContent = `${scale.toFixed(2)}`;
+        }
+      }
+    }
+
+    function deleteModel() {
+      if (!selectedPlacedModel) {
+        showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.");
+        return;
+      }
+
+      // Dialog anzeigen
+      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 closeDynamicMenu() {
+      const dynamicMenu = document.getElementById("dynamic-menu");
+      dynamicMenu.style.display = "none";
+    }
+
+    function getAllPlacedModels() {
+      return scene.children.filter(child => child.isPlacedModel === true);
+    }    
+
+    function refreshMapDialog() {
+      const mapDialog = document.getElementById('map-dialog');
+      mapDialog.style.display = 'flex';
+    }
+
+    function closeMapDialog() {
+      const mapDialog = document.getElementById('map-dialog');
+      mapDialog.style.display = 'none';
+    }
+
+    async function activateXR(sceneData = null) {
+      const canvas = document.createElement('canvas');
+      document.body.appendChild(canvas);
+      const gl = canvas.getContext('webgl', { xrCompatible: true });
+      const renderer = new THREE.WebGLRenderer({ alpha: true, canvas, context: gl });
+      renderer.autoClear = false;
+
+      scene = new THREE.Scene();
+      camera = new THREE.PerspectiveCamera();
+      camera.matrixAutoUpdate = false;
+
+      const light = new THREE.DirectionalLight(0xffffff, 1);
+      light.position.set(10, 10, 10);
+      scene.add(light);
+
+      const loader = new THREE.GLTFLoader();
+      loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf", (gltf) => {
+        reticle = gltf.scene;
+        reticle.visible = false;
+        scene.add(reticle);
+      });
+
+      currentSession = await navigator.xr.requestSession('immersive-ar', {
+        optionalFeatures: ["dom-overlay"],
+        domOverlay: { root: document.body },
+        requiredFeatures: ['hit-test']
+      });
+
+      currentSession.updateRenderState({ baseLayer: new XRWebGLLayer(currentSession, gl) });
+      const referenceSpace = await currentSession.requestReferenceSpace('local');
+      const viewerSpace = await currentSession.requestReferenceSpace('viewer');
+      const hitTestSource = await currentSession.requestHitTestSource({ space: viewerSpace });
+
+      document.getElementById('menu-bar').style.display = 'flex';
+
+      currentSession.addEventListener("end", () => {
+        currentSession = null;
+        menus.forEach(id => {
+          document.getElementById(id).style.display = 'none';
+        });
+      });
+
+      canvas.addEventListener("pointerdown", selectModelFromScene);
+
+
+      if (navigator.geolocation) {
+        navigator.geolocation.getCurrentPosition(position => {
+          geoLocation = {
+            latitude: roundTo(position.coords.latitude, 5),
+            longitude: roundTo(position.coords.longitude, 5),
+          };
+          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);
+                    const positionVector = new THREE.Vector3(x, model.position.y, z);
+                    placeModel(filePath, positionVector, (placed) => {
+                        if (model.rotation) {
+                          placed.rotation._x = model.rotation._x;
+                          placed.rotation._y = model.rotation._y;
+                          placed.rotation._z = model.rotation._z;
+                        }
+                        if (model.scale) {
+                          placed.scale.x = model.scale.x;
+                          placed.scale.y = model.scale.y;
+                          placed.scale.z = model.scale.z;
+                        }
+                    });
+                }
+            });
+          }
+        }, function(error) {
+          console.error("Fehler bei der Geolokalisierung:", JSON.stringify(error));
+        }, {enableHighAccuracy: true, maximumAge: 2000, timeout: 5000});
+      }
+
+      currentSession.requestAnimationFrame(function onXRFrame(time, frame) {
+        currentSession.requestAnimationFrame(onXRFrame);
+        gl.bindFramebuffer(gl.FRAMEBUFFER, currentSession.renderState.baseLayer.framebuffer);
+
+        const pose = frame.getViewerPose(referenceSpace);
+        if (pose) {
+          const view = pose.views[0];
+          const viewport = currentSession.renderState.baseLayer.getViewport(view);
+          renderer.setSize(viewport.width, viewport.height);
+
+          camera.matrix.fromArray(view.transform.matrix);
+          camera.projectionMatrix.fromArray(view.projectionMatrix);
+          camera.updateMatrixWorld(true);
+
+          const hitTestResults = frame.getHitTestResults(hitTestSource);
+          if (hitTestResults.length > 0) {
+            const hitPose = hitTestResults[0].getPose(referenceSpace);
+            reticle.visible = true;
+            reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
+            reticle.updateMatrixWorld(true);
+          }
+
+          renderer.render(scene, camera);
+        }
+      });
+    }
+
+    function exitAR() {
+      document.getElementById('confirmation-dialog').style.display = 'flex';
+    }
+
+    function confirmExit(shouldExit) {
+      if (shouldExit && currentSession) {
+        currentSession.end();
+      }
+      document.getElementById('confirmation-dialog').style.display = 'none';
+    }
+
+    let soundTimeout = false;
+
+    function playButtonSound() {
+      if (!soundTimeout) {
+        const sound = document.getElementById("button-sound");
+        sound.currentTime = 0;
+        sound.play();
+
+        soundTimeout = true;
+        setTimeout(() => {
+          soundTimeout = false;
+        }, 200); // Verzögerung von 200ms
+      }
+    }
\ No newline at end of file
diff --git a/public/ar_overviewmap.js b/public/ar_overviewmap.js
index 2db762e..c7e0e47 100644
--- a/public/ar_overviewmap.js
+++ b/public/ar_overviewmap.js
@@ -1,70 +1,127 @@
-
-
+let init = false;
+let mymap;
+let userIcon;
+let intervalId;
+let modeMarkers = []
+const earthRadius = 6378137; // Erdradius in Metern (WGS84)
 
 function init_map() {
-    const mapContainer = document.getElementById('map-container');
+    const mapContainer = document.getElementById("map-container");
     if (mapContainer) {
-        var mymap = L.map(mapContainer);
-        var OpenStreetMap_DE = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
-            maxZoom: 30,
-            minZoom: 15,
-            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
-        });
-        // select one basemap from https://leaflet-extras.github.io/leaflet-providers/preview/
-        var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
-            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
-            subdomains: 'abcd',
-            maxZoom: 30,
-            minZoom: 15
+        if (!init) {
+            mymap = L.map(mapContainer);
+            var OpenStreetMap_DE = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
+                maxZoom: 30,
+                minZoom: 15,
+                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+            });
+            // select one basemap from https://leaflet-extras.github.io/leaflet-providers/preview/
+            var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
+                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
+                subdomains: 'abcd',
+                maxZoom: 30,
+                minZoom: 15
+            });
+            CartoDB_Positron.addTo(mymap);
+    
+            userIcon = L.icon({
+                iconUrl: 'previewImages/map-user.png',
+                iconSize: [32, 32],
+                iconAnchor: [16, 32],
+                popupAnchor: [0, -32]
+            });
+
+            mymap.setView([geoLocation.latitude, geoLocation.longitude], 20)
+            userIcon = L.marker([geoLocation.latitude, geoLocation.longitude], { icon: userIcon }).bindPopup('Ihre Position').addTo(mymap);
+            init = true;
+        }
+
+        modeMarkers.forEach(marker => {
+            marker.remove();
         });
-        CartoDB_Positron.addTo(mymap);
- 
-        // Marker initialisieren
-        var userIcon = L.icon({
-            iconUrl: 'previewImages/map-user.png',  // Dein Männchen-Bild
-            iconSize: [32, 32],               // Die Größe des Icons
-            iconAnchor: [16, 32],             // Der Punkt des Markers, der auf der Position zeigt
-            popupAnchor: [0, -32]             // Die Position des Popups relativ zum Marker
+        const models = getAllPlacedModels();
+        models.forEach(model => {
+            const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
+            const modelMarker = L.marker([lat, lng], { icon: L.icon({
+                iconUrl: model.modelConfig.image,
+                iconSize: [16, 16],
+                iconAnchor: [16, 32],
+                popupAnchor: [0, -32]
+            })}).addTo(mymap).bindPopup(model.modelConfig?.name || "Unbenannt");
+            modeMarkers.push(modelMarker);
         });
+        intervalId = setInterval(updateMap, 500);
+    }
+ }
 
-        // Marker initialisieren
-        var userMarker = L.marker([0, 0], { icon: userIcon }).bindPopup('Ihre Position').addTo(mymap);
-        let initPosition = false;
-        let lastPosition = { lat: 0, lng: 0 }; // Letzte bekannte Position
-        const updateThreshold = 1.5; // Schwellenwert für den Abstand (in Metern)
-        
-        // Funktion, um die Position zu aktualisieren
-        function updatePosition(position) {
-            const userLat = position.coords.latitude;
-            const userLng = position.coords.longitude;
-
-            const currentPosition = L.latLng(userLat, userLng);
-            const lastKnownPosition = L.latLng(lastPosition.lat, lastPosition.lng);
-            const distance = currentPosition.distanceTo(lastKnownPosition);
-
-            // Nur aktualisieren, wenn der Abstand größer als der Schwellenwert ist
-            if (distance > updateThreshold || !initPosition) {
-                if (!initPosition) {
-                    mymap.setView([userLat, userLng], 20); // Zentrische Ansicht beim ersten Mal
-                    initPosition = true;
-                }
-
-                // Marker auf die neue Position setzen
-                userMarker.setLatLng([userLat, userLng]);
-                console.log(`Setze Position auf lat=${userLat} lng=${userLng}, Abstand: ${distance}m`);
-                lastPosition = { lat: userLat, lng: userLng };
-            }
-        }
+ function updateMap() {
+    if (document.getElementById("map-window").style.display === 'none' && intervalId) {
+        clearInterval(intervalId);
+        intervalId = null;
+        return;
+    }
 
-        // Die Position alle 5 Sekunden aktualisieren
-        if (navigator.geolocation) {
-            navigator.geolocation.getCurrentPosition(updatePosition);
-            navigator.geolocation.watchPosition(updatePosition, function(error) {
-                console.error("Fehler bei der Geolokalisierung:", error);
-            }, {enableHighAccuracy: true, maximumAge: 2000,timeout: 5000});
-        } else {
-            console.error("Geolocation wird nicht unterstützt.");
-        }
+    let vec = new THREE.Vector3();
+    const userPosition = camera.getWorldPosition(vec);
+    const { lat, lng } = threeToLeaflet(userPosition.x, userPosition.z);
+
+    if (userIcon) {
+        userIcon.setLatLng([lat, lng]);
     }
  }
+
+
+
+ function threeToLeaflet(threeX, threeZ) {
+    if (!geoLocation) {
+        console.error("GeoLocation ist noch nicht verfügbar");
+        return;
+    }
+
+    const originLat = geoLocation.latitude;
+    const originLng = geoLocation.longitude;
+
+    const deltaLat = (threeZ / earthRadius) * (180 / Math.PI);
+    const deltaLng = (threeX / (earthRadius * Math.cos(Math.PI * originLat / 180))) * (180 / Math.PI);
+
+    const globalLat = originLat + deltaLat;
+    const globalLng = originLng + deltaLng;
+    return { lat: globalLat, lng: globalLng };
+}
+
+function leafletToThree(lat, lng) {
+    if (!geoLocation) {
+        console.error("GeoLocation ist noch nicht verfügbar");
+        return;
+    }
+
+    const originLat = geoLocation.latitude;
+    const originLng = geoLocation.longitude;
+
+    const deltaLat = lat - originLat;
+    const deltaLng = lng - originLng;
+
+    const threeZ = deltaLat * (Math.PI / 180) * earthRadius;
+    const threeX = deltaLng * (Math.PI / 180) * earthRadius * Math.cos(Math.PI * originLat / 180);
+
+    return { x: threeX, z: threeZ };
+}
+
+function roundTo(n, digits) {
+    var negative = false;
+    if (digits === undefined) {
+        digits = 0;
+    }
+    if (n < 0) {
+        negative = true;
+        n = n * -1;
+    }
+    var multiplicator = Math.pow(10, digits);
+    n = parseFloat((n * multiplicator).toFixed(11));
+    n = (Math.round(n) / multiplicator).toFixed(digits);
+    if (negative) {
+        n = (n * -1).toFixed(digits);
+    }
+    return parseFloat(n);
+}
  
\ No newline at end of file
diff --git a/public/ar_start.js b/public/ar_start.js
new file mode 100644
index 0000000..f537a70
--- /dev/null
+++ b/public/ar_start.js
@@ -0,0 +1,29 @@
+if (navigator.xr) {
+    let sceneData;
+    const startButton = document.createElement('button');
+    startButton.textContent = 'Start AR';
+    startButton.style.cssText = "position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;";
+    document.body.appendChild(startButton);
+    startButton.onclick = () => {
+        startButton.remove();
+        loadButton.remove();
+        activateXR(sceneData);
+    };
+
+    const loadButton = document.createElement('button');
+    loadButton.textContent = 'Umgebung Laden';
+    loadButton.style.cssText = "position: fixed; top: 55%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;";
+    document.body.appendChild(loadButton);
+    loadButton.onclick = () => {
+        loadSceneFromFile((data, fileName) => {
+            if (data && fileName) {
+                loadButton.textContent = fileName;
+                sceneData = data
+            } else {
+                loadButton.textContent = 'Umgebung Laden';
+            }
+        });
+    };
+  } else {
+    alert('WebXR wird nicht unterstützt.');
+  }
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 8686486..f297f1b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,215 +5,7 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>GeoVis AR Projekt</title>
-  <style>
-    body {
-      margin: 0;
-      font-family: Arial, sans-serif;
-      background-color: #f0f0f0;
-      color: #333;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      height: 100vh;
-      background-image: url('https://www.hft-stuttgart.de/fileadmin/Dateien/Hochschule/-_R_Juergen_Pollak_HFT_18.04.18-0091.jpg');
-      background-size: cover;
-      background-position: center;
-    }
-
-    .container {
-      text-align: center;
-      background-color: rgba(255, 255, 255, 0.9);
-      padding: 50px;
-      border-radius: 10px;
-      color: #333;
-      max-width: 80%;
-      box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
-    }
-
-    h1 {
-      font-size: 3em;
-      margin-bottom: 20px;
-      color: #444;
-    }
-
-    p {
-      font-size: 1.2em;
-      margin-bottom: 30px;
-    }
-
-    #add-menu {
-      overflow-y: auto;
-
-    }
-
-    #menu-bar,
-    .menu-placeholder {
-      position: absolute;
-      bottom: 0;
-      width: 100%;
-      height: 80px;
-      display: flex;
-      justify-content: space-around;
-      align-items: center;
-      background: #121212;
-      padding: 10px;
-      box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.8);
-      color: white;
-      z-index: 10;
-      border-top: 1px solid #2a2a2a;
-    }
-
-    .menu-item {
-      background: #2c2c2c;
-      border-radius: 20px;
-      padding: 8px;
-      transition: background-color 0.3s, transform 0.2s;
-    }
-
-    .menu-item:hover {
-      background-color: #3a3a3a;
-      transform: scale(1.1);
-    }
-
-    .menu-item img {
-      width: 50px;
-      height: 50px;
-    }
-
-    .menu-placeholder .menu-item img {
-      width: 50px;
-      height: 50px;
-    }
-
-    button {
-      background-color: #4CAF50;
-      color: white;
-      font-size: 1em;
-      padding: 10px 20px;
-      border: none;
-      border-radius: 5px;
-      cursor: pointer;
-      transition: background-color 0.3s ease-in-out, transform 0.2s;
-      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-      margin: 10px;
-    }
-
-    button:hover {
-      background-color: #45a049;
-    }
-
-    button:active {
-      background-color: #387a39;
-    }
-
-    /* Confirmation Dialog */
-    #confirmation-dialog,
-    #delete-confirmation-dialog,
-    #info-dialog {
-      position: fixed;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      z-index: 20;
-    }
-
-    .dialog-overlay {
-      position: absolute;
-      top: 0;
-      left: 0;
-      width: 100%;
-      height: 100%;
-      background: rgba(0, 0, 0, 0.7);
-    }
-
-    .dialog-box {
-      position: relative;
-      background: #2c2c2c;
-      padding: 20px;
-      border-radius: 10px;
-      text-align: center;
-      color: white;
-      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.8);
-    }
-
-    .dialog-box p {
-      margin-bottom: 20px;
-      font-size: 1.2em;
-      color: #f0f0f0;
-    }
-
-    .dialog-box button {
-      margin: 5px;
-      padding: 10px 20px;
-      font-size: 16px;
-      border: none;
-      border-radius: 5px;
-      cursor: pointer;
-      transition: background-color 0.3s ease, transform 0.2s;
-    }
-
-    .dialog-box button:first-child {
-      background-color: #e74c3c;
-      color: white;
-    }
-
-    .dialog-box button:first-child:hover {
-      background-color: #c0392b;
-      transform: scale(1.05);
-    }
-
-    .dialog-box button:last-child {
-      background-color: #3498db;
-      color: white;
-    }
-
-    .dialog-box button:last-child:hover {
-      background-color: #2980b9;
-      transform: scale(1.05);
-    }
-
-
-    #dynamic-menu {
-      position: absolute;
-      bottom: 80px;
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      justify-content: center;
-      gap: 20px;
-      background: #1e1e1e;
-      box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.8);
-      color: white;
-      width: 100vw;
-      height: 200px;
-      z-index: 20;
-      overflow-y: auto;
-      border-radius: 8px;
-    }
-
-
-    #dynamic-menu input[type="range"] {
-      width: 100%;
-      margin: 10px auto;
-      background: #3a3a3a;
-      border-radius: 5px;
-    }
-
-    #dynamic-menu input[type="range"]::-webkit-slider-thumb {
-      -webkit-appearance: none;
-      appearance: none;
-      width: 20px;
-      height: 20px;
-      background: #fff;
-      border: 2px solid #555;
-      border-radius: 50%;
-      cursor: pointer;
-    }
-  </style>
+  <link rel="stylesheet" href="styles.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>
@@ -289,6 +81,9 @@
     <div class="menu-item" onclick="exitAR()">
       <img src="previewImages/exit-icon.png" alt="Beenden" />
     </div>
+    <div class="menu-item" onclick="downloadScene()">
+      <img src="previewImages/download-icon.png" alt="Download" />
+    </div>
     <div class="menu-item" onclick="showMenu('menu-bar')">
       <img src="previewImages/back-icon.png" alt="Zurück" />
     </div>
@@ -336,498 +131,16 @@
   </div>
 
 
-
+  <script src="ar_start.js"></script>
+  <script src="ar_main.js"></script>
   <script src="ar_debug_console.js"></script>
   <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
   <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script> <!-- Leaflet einbinden -->
   <script src="ar_overviewmap.js"></script>
+  <script src="ar_download.js"></script>
 
   <!-- Audio-Element für Button-Klick -->
   <audio id="button-sound" src="sounds/button-sound.mp3" preload="auto"></audio>
-
-  <script>
-    // Variablen
-    let selectedModel = 'robot';
-    let selectedPlacedModel = null;
-    let currentSession = null;
-    let reticle = null;
-    let scene, camera;
-
-    let models = {
-      bench: {
-        name: "Bench",
-        image: "previewImages/bench.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/bench_model/scene.gltf",
-        scale: { x: 0.1, y: 0.1, z: 0.1 },
-        minScale: 0.05, // 50% der aktuellen Größe
-        maxScale: 0.5   // 500% der aktuellen Größe
-      },
-      trashbin: {
-        name: "Trash bin",
-        image: "previewImages/trash_can.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/trash_model/scene.gltf",
-        scale: { x: 0.03, y: 0.03, z: 0.03 },
-        minScale: 0.01, // 50% der aktuellen Größe
-        maxScale: 0.1   // 500% der aktuellen Größe
-      },
-      telephone_box: {
-        name: "Telephone Box",
-        image: "previewImages/telephone_box.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/telephone_box_model/scene.gltf",
-        scale: { x: 0.5, y: 0.5, z: 0.5 },
-        minScale: 0.05,
-        maxScale: 1
-      },
-      fire_hydrant_model: {
-        name: "Fire Hydrant",
-        image: "previewImages/hydrant.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fire_hydrant_model/scene.gltf",
-        scale: { x: 0.3, y: 0.3, z: 0.3 },
-        minScale: 0.1,
-        maxScale: 1
-      },
-      statue: {
-        name: "Statue",
-        image: "previewImages/statue.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/statue_model/scene.gltf",
-        scale: { x: 0.5, y: 0.5, z: 0.5 },
-        minScale: 0.05,
-        maxScale: 2
-      },
-      fountain: {
-        name: "Fountain",
-        image: "previewImages/fountain.PNG",
-        file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fountain_model/scene.gltf",
-        scale: { x: 0.001, y: 0.001, z: 0.001 },
-        minScale: 0.0005,
-        maxScale: 0.005
-      },
-      lantern: {
-        name: "Lantern",
-        image: "previewImages/lantern.jpg",
-        file: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/Lantern/glTF/Lantern.gltf",
-        scale: { x: 0.15, y: 0.15, z: 0.15 },
-        minScale: 0.05,
-        maxScale: 0.3
-      }
-    };
-
-    const menus = ['menu-bar', 'add-menu', 'edit-menu', 'options-menu', 'map-window'];
-
-    window.onload = () => {
-      initializeAddMenu();
-
-      // Fügt Sound zu allen Buttons hinzu
-      const buttons = document.querySelectorAll("button, .menu-item");
-      buttons.forEach(button => {
-        button.addEventListener("click", playButtonSound);
-      });
-    };
-
-    function initializeAddMenu() {
-      const addMenu = document.getElementById('add-menu');
-      addMenu.innerHTML = Object.entries(models)
-        .map(
-          ([key, model]) => `
-            <div class="menu-item" id="${key}-item" onclick="selectModel('${key}')">
-              <img src="${model.image}" alt="${model.name}" />
-            </div>
-          `
-        )
-        .join('') +
-        `
-        <div class="menu-item" onclick="showMenu('menu-bar')">
-          <img src="previewImages/back-icon.png" alt="Zurück" />
-        </div>
-      `;
-    }
-
-    function showMenu(menuId) {
-      menus.forEach(id => {
-        document.getElementById(id).style.display = id === menuId ? 'flex' : 'none';
-      });
-      closeDynamicMenu();
-      if (menuId === 'menu-bar') clearSelectedModel();
-      else if (menuId === 'map-window') 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 selectModel(modelId) {
-      const model = models[modelId];
-      if (model && model.file) {
-        loadModel(model.file);
-        showMenu('menu-bar');
-      }
-    }
-
-    function loadModel(filePath) {
-      const modelConfig = Object.values(models).find(model => model.file === filePath);
-      const loader = new THREE.GLTFLoader();
-      loader.load(
-        filePath,
-        (gltf) => {
-          const model = gltf.scene;
-          if (modelConfig && modelConfig.scale) {
-            model.scale.set(modelConfig.scale.x, modelConfig.scale.y, modelConfig.scale.z);
-          }
-          model.position.copy(reticle.position);
-          scene.add(model);
-
-          // Speichere das platzierte Modell und die Konfiguration
-          selectedPlacedModel = model;
-          selectedPlacedModel.modelConfig = modelConfig;
-
-          highlightSelectedModel();
-          showMenu('edit-menu');
-        },
-        undefined,
-        (error) => {
-          console.error("Fehler beim Laden des Modells:", error);
-        }
-      );
-    }
-
-    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 selectModelFromScene(event) {
-      const mouse = new THREE.Vector2(
-        (event.clientX / window.innerWidth) * 2 - 1,
-        -(event.clientY / window.innerHeight) * 2 + 1
-      );
-
-      const raycaster = new THREE.Raycaster();
-      raycaster.setFromCamera(mouse, camera);
-
-      // Prüfe Kollisionen mit Objekten in der Szene
-      const intersects = raycaster.intersectObjects(scene.children, true);
-
-      if (intersects.length > 0) {
-        // Finde das Hauptobjekt (Root-Parent), falls Mesh ausgewählt wurde
-        let selectedObject = intersects[0].object;
-        while (selectedObject.parent && selectedObject.parent !== scene) {
-          selectedObject = selectedObject.parent;
-        }
-
-        // Markiere das gesamte Modell als ausgewählt
-        selectedPlacedModel = selectedObject;
-        highlightSelectedModel();
-        showMenu("edit-menu");
-      }
-    }
-
-    function completeEditing() {
-      removeHighlightFromSelectedModel();
-      closeDynamicMenu();
-      selectedPlacedModel = null;
-      document.getElementById('edit-menu').style.display = 'none';
-      document.getElementById('menu-bar').style.display = 'flex';
-    }
-
-    function openRotationMenu() {
-      if (!selectedPlacedModel) {
-        showInfoDialog("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 dynamicMenu = document.getElementById("dynamic-menu");
-      dynamicMenu.style.display = "flex";
-      dynamicMenu.innerHTML = `
-        <h3>Rotation anpassen</h3>
-        <label>Y-Achse: <span id="current-rotation">${currentRotation}</span>°<input type="range" min="0" max="360" step="10" onchange="updateRotation('y', this.value)"></label>
-        <button onclick="closeDynamicMenu()">Zurück</button>
-      `;
-    }
-
-    function updateRotation(axis, value) {
-      if (selectedPlacedModel) {
-        const radians = (value / 180) * Math.PI;
-        selectedPlacedModel.rotation[axis] = radians;
-
-        // Update der aktuellen Rotation im Menü
-        const currentRotationDisplay = document.getElementById("current-rotation");
-        if (currentRotationDisplay) {
-          currentRotationDisplay.textContent = value; // Zeige den aktuellen Wert in Grad an
-        }
-      }
-    }
-
-    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.");
-        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 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 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>
-        <button onclick="closeDynamicMenu()">Zurück</button>
-      `;
-    }
-
-    function updateScale(value) {
-      if (selectedPlacedModel) {
-        const scale = parseFloat(value);
-        selectedPlacedModel.scale.set(scale, scale, scale);
-
-        // Anzeige des aktuellen Wertes aktualisieren
-        const scaleValueDisplay = document.getElementById("scale-value");
-        if (scaleValueDisplay) {
-          scaleValueDisplay.textContent = `${scale.toFixed(2)}`;
-        }
-      }
-    }
-
-    function deleteModel() {
-      if (!selectedPlacedModel) {
-        showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.");
-        return;
-      }
-
-      // Dialog anzeigen
-      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 closeDynamicMenu() {
-      const dynamicMenu = document.getElementById("dynamic-menu");
-      dynamicMenu.style.display = "none";
-    }
-
-    function refreshMapDialog() {
-      const mapDialog = document.getElementById('map-dialog');
-      mapDialog.style.display = 'flex';
-    }
-
-    function closeMapDialog() {
-      const mapDialog = document.getElementById('map-dialog');
-      mapDialog.style.display = 'none';
-    }
-
-    async function activateXR() {
-      const canvas = document.createElement('canvas');
-      document.body.appendChild(canvas);
-      const gl = canvas.getContext('webgl', { xrCompatible: true });
-      const renderer = new THREE.WebGLRenderer({ alpha: true, canvas, context: gl });
-      renderer.autoClear = false;
-
-      scene = new THREE.Scene();
-      camera = new THREE.PerspectiveCamera();
-      camera.matrixAutoUpdate = false;
-
-      const light = new THREE.DirectionalLight(0xffffff, 1);
-      light.position.set(10, 10, 10);
-      scene.add(light);
-
-      const loader = new THREE.GLTFLoader();
-      loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf", (gltf) => {
-        reticle = gltf.scene;
-        reticle.visible = false;
-        scene.add(reticle);
-      });
-
-      currentSession = await navigator.xr.requestSession('immersive-ar', {
-        optionalFeatures: ["dom-overlay"],
-        domOverlay: { root: document.body },
-        requiredFeatures: ['hit-test']
-      });
-
-      currentSession.updateRenderState({ baseLayer: new XRWebGLLayer(currentSession, gl) });
-      const referenceSpace = await currentSession.requestReferenceSpace('local');
-      const viewerSpace = await currentSession.requestReferenceSpace('viewer');
-      const hitTestSource = await currentSession.requestHitTestSource({ space: viewerSpace });
-
-      document.getElementById('menu-bar').style.display = 'flex';
-
-      currentSession.addEventListener("end", () => {
-        currentSession = null;
-        menus.forEach(id => {
-          document.getElementById(id).style.display = 'none';
-        });
-      });
-
-      canvas.addEventListener("pointerdown", selectModelFromScene);
-
-      currentSession.requestAnimationFrame(function onXRFrame(time, frame) {
-        currentSession.requestAnimationFrame(onXRFrame);
-        gl.bindFramebuffer(gl.FRAMEBUFFER, currentSession.renderState.baseLayer.framebuffer);
-
-        const pose = frame.getViewerPose(referenceSpace);
-        if (pose) {
-          const view = pose.views[0];
-          const viewport = currentSession.renderState.baseLayer.getViewport(view);
-          renderer.setSize(viewport.width, viewport.height);
-
-          camera.matrix.fromArray(view.transform.matrix);
-          camera.projectionMatrix.fromArray(view.projectionMatrix);
-          camera.updateMatrixWorld(true);
-
-          const hitTestResults = frame.getHitTestResults(hitTestSource);
-          if (hitTestResults.length > 0) {
-            const hitPose = hitTestResults[0].getPose(referenceSpace);
-            reticle.visible = true;
-            reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
-            reticle.updateMatrixWorld(true);
-          }
-
-          renderer.render(scene, camera);
-        }
-      });
-    }
-
-    function exitAR() {
-      document.getElementById('confirmation-dialog').style.display = 'flex';
-    }
-
-    function confirmExit(shouldExit) {
-      if (shouldExit && currentSession) {
-        currentSession.end();
-      }
-      document.getElementById('confirmation-dialog').style.display = 'none';
-    }
-
-    let soundTimeout = false;
-
-    function playButtonSound() {
-      if (!soundTimeout) {
-        const sound = document.getElementById("button-sound");
-        sound.currentTime = 0;
-        sound.play();
-
-        soundTimeout = true;
-        setTimeout(() => {
-          soundTimeout = false;
-        }, 200); // Verzögerung von 200ms
-      }
-    }
-
-
-    if (navigator.xr) {
-      const startButton = document.createElement('button');
-      startButton.textContent = 'Start AR';
-      startButton.style.cssText = "position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;";
-      document.body.appendChild(startButton);
-      startButton.onclick = () => {
-        startButton.remove();
-        activateXR();
-      };
-    } else {
-      alert('WebXR wird nicht unterstützt.');
-    }
-
-  </script>
 </body>
 
 </html>
diff --git a/public/previewImages/download-icon.png b/public/previewImages/download-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..663c90144dd713cc4abddfb476d1dfa428571231
GIT binary patch
literal 7008
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
Kj7m>jzVl!BdeS!l

literal 0
HcmV?d00001

diff --git a/public/styles.css b/public/styles.css
new file mode 100644
index 0000000..b34fa5f
--- /dev/null
+++ b/public/styles.css
@@ -0,0 +1,207 @@
+body {
+    margin: 0;
+    font-family: Arial, sans-serif;
+    background-color: #f0f0f0;
+    color: #333;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100vh;
+    background-image: url('https://www.hft-stuttgart.de/fileadmin/Dateien/Hochschule/-_R_Juergen_Pollak_HFT_18.04.18-0091.jpg');
+    background-size: cover;
+    background-position: center;
+  }
+
+  .container {
+    text-align: center;
+    background-color: rgba(255, 255, 255, 0.9);
+    padding: 50px;
+    border-radius: 10px;
+    color: #333;
+    max-width: 80%;
+    box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
+  }
+
+  h1 {
+    font-size: 3em;
+    margin-bottom: 20px;
+    color: #444;
+  }
+
+  p {
+    font-size: 1.2em;
+    margin-bottom: 30px;
+  }
+
+  #add-menu {
+    overflow-y: auto;
+
+  }
+
+  #menu-bar,
+  .menu-placeholder {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 80px;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    background: #121212;
+    padding: 10px;
+    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.8);
+    color: white;
+    z-index: 10;
+    border-top: 1px solid #2a2a2a;
+  }
+
+  .menu-item {
+    background: #2c2c2c;
+    border-radius: 20px;
+    padding: 8px;
+    transition: background-color 0.3s, transform 0.2s;
+  }
+
+  .menu-item:hover {
+    background-color: #3a3a3a;
+    transform: scale(1.1);
+  }
+
+  .menu-item img {
+    width: 50px;
+    height: 50px;
+  }
+
+  .menu-placeholder .menu-item img {
+    width: 50px;
+    height: 50px;
+  }
+
+  button {
+    background-color: #4CAF50;
+    color: white;
+    font-size: 1em;
+    padding: 10px 20px;
+    border: none;
+    border-radius: 5px;
+    cursor: pointer;
+    transition: background-color 0.3s ease-in-out, transform 0.2s;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+    margin: 10px;
+  }
+
+  button:hover {
+    background-color: #45a049;
+  }
+
+  button:active {
+    background-color: #387a39;
+  }
+
+  /* Confirmation Dialog */
+  #confirmation-dialog,
+  #delete-confirmation-dialog,
+  #info-dialog {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    z-index: 20;
+  }
+
+  .dialog-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.7);
+  }
+
+  .dialog-box {
+    position: relative;
+    background: #2c2c2c;
+    padding: 20px;
+    border-radius: 10px;
+    text-align: center;
+    color: white;
+    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.8);
+  }
+
+  .dialog-box p {
+    margin-bottom: 20px;
+    font-size: 1.2em;
+    color: #f0f0f0;
+  }
+
+  .dialog-box button {
+    margin: 5px;
+    padding: 10px 20px;
+    font-size: 16px;
+    border: none;
+    border-radius: 5px;
+    cursor: pointer;
+    transition: background-color 0.3s ease, transform 0.2s;
+  }
+
+  .dialog-box button:first-child {
+    background-color: #e74c3c;
+    color: white;
+  }
+
+  .dialog-box button:first-child:hover {
+    background-color: #c0392b;
+    transform: scale(1.05);
+  }
+
+  .dialog-box button:last-child {
+    background-color: #3498db;
+    color: white;
+  }
+
+  .dialog-box button:last-child:hover {
+    background-color: #2980b9;
+    transform: scale(1.05);
+  }
+
+
+  #dynamic-menu {
+    position: absolute;
+    bottom: 80px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 20px;
+    background: #1e1e1e;
+    box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.8);
+    color: white;
+    width: 100vw;
+    height: 200px;
+    z-index: 20;
+    overflow-y: auto;
+    border-radius: 8px;
+  }
+
+
+  #dynamic-menu input[type="range"] {
+    width: 100%;
+    margin: 10px auto;
+    background: #3a3a3a;
+    border-radius: 5px;
+  }
+
+  #dynamic-menu input[type="range"]::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    appearance: none;
+    width: 20px;
+    height: 20px;
+    background: #fff;
+    border: 2px solid #555;
+    border-radius: 50%;
+    cursor: pointer;
+  }
\ No newline at end of file
-- 
GitLab