import { getScene, getSceneElement, getCategory } from "./api"; import { populateDestinationDropdown } from "./editor/teleport"; import "../css/editor.css"; let clickTimestamp = 0; // Function to get the CSRF token cookie function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== "") { const cookies = document.cookie.split(";"); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === name + "=") { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } // Find parent quackscape-scene for ID function findParentScene(element) { var parent = element.parentElement; while (parent.tagName != "QUACKSCAPE-SCENE") { parent = parent.parentElement; } return parent; } // Distinguishing clicks from drags based on duration. // TODO: Find a better way to distinguish these. function addEventListeners(element) { element.addEventListener("mousedown", function (event) { clickTimestamp = event.timeStamp; }); element.addEventListener("mouseup", function (event) { if (event.timeStamp - clickTimestamp > 200) { // Ignoring this, we only handle regular short clicks. // TODO: Find a way to handle drags of elements return; } else { handleClick(event); } // Right-clicks are definitely intentional. element.addEventListener("contextmenu", function (event) { handleClick(event); }); }); } // Open a modal for creating a new Element function startCreateElement(event) { var propertiesTitle = document.getElementById("propertiesTitle"); modalLabel.textContent = "Create Element"; var propertiesContent = document.getElementById("propertiesContent"); var thetaStart = cartesianToTheta( event.detail.intersection.point.x, event.detail.intersection.point.z ); modalContent.innerHTML = `Creating element at:
X: ${event.detail.intersection.point.x}
Y: ${event.detail.intersection.point.y}
Z: ${event.detail.intersection.point.z}
Calculated Theta: ${thetaStart}
Parent Element Type: ${event.srcElement.tagName}

`; } function startModifyElement(event) { var propertiesTitle = document.getElementById("propertiesTitle"); propertiesTitle.textContent = "Modify Element"; // Get element from API var scene = findParentScene(event.target); var element_data_request = getSceneElement( scene.getAttribute("id"), event.target.getAttribute("id") ); element_data_request.then((element_data) => { var propertiesContent = document.getElementById("propertiesContent"); propertiesContent.innerHTML = `Modifying element:
Element Type: ${event.srcElement.tagName}
Element ID: ${event.target.getAttribute("id")}
Element data: ${JSON.stringify(element_data.obj)}

`; var scene_data_request = getScene(scene.getAttribute("id")); scene_data_request.then((scene_data) => { var category_data_request = getCategory(scene_data.obj.category); category_data_request.then((category_data) => { populateDestinationDropdown( element_data.obj.destination, category_data ); }); }); document .getElementById("elementUpload") .addEventListener("change", function () { if (this.files && this.files[0]) { var reader = new FileReader(); reader.onload = function (e) { // Set the image to the file contents if (event.target.getAttribute("data-original-src") == undefined) { event.target.setAttribute( "data-original-src", event.target.getAttribute("src") ); } event.target.setAttribute("src", e.target.result); }; reader.readAsDataURL(this.files[0]); } }); if (element_data.obj.destination_x != -1) { document.getElementById("destination_x").value = element_data.obj.destination_x; } if (element_data.obj.destination_y != -1) { document.getElementById("destination_y").value = element_data.obj.destination_y; } if (element_data.obj.destination_z != -1) { document.getElementById("destination_z").value = element_data.obj.destination_z; } document.getElementById("resetButton").style = "display: inline;"; document.getElementById("buttons").style = "display: block;"; }); } function cartesianToTheta(x, z) { // Calculate the angle in radians let angleRadians = Math.atan2(z, x); // Convert to degrees let angleDegrees = angleRadians * (180 / Math.PI); // A-Frame's thetaStart is measured from the positive X-axis ("right" direction) // and goes counter-clockwise, so this should directly give us the thetaStart value. let thetaStart = 90 - angleDegrees; // Since atan2 returns values from -180 to 180, let's normalize this to 0 - 360 thetaStart = thetaStart < 0 ? thetaStart + 360 : thetaStart; return thetaStart; } function latLonToXYZ(lat, lon, radius = 5) { // Convert lat/lon to X/Y/Z coordinates on the sphere const phi = (90 - lat) * (Math.PI / 180); const theta = (lon + 180) * (Math.PI / 180); const x = -(radius * Math.sin(phi) * Math.cos(theta)); const y = radius * Math.cos(phi); const z = radius * Math.sin(phi) * Math.sin(theta); return { x, y, z }; } function handleClick(event) { // If clicked on the sky, start creating a new element if (event.target.tagName == "A-SKY") { startCreateElement(event); } else { // Else we are modifying an existing element startModifyElement(event); } } document.addEventListener("loadedQuackscapeScene", function (event) { // Get the scene var scene = document.querySelector("a-scene"); // Get all children var children = scene.children; for (var i = 0; i < children.length; i++) { var child = children[i]; if (child.tagName.startsWith("A-")) { // Remove original onclick events if (child.hasAttribute("onclick")) { child.removeAttribute("onclick"); } // Add new event listeners addEventListeners(child); // Add click-drag component to all a-entity elements child.setAttribute("click-drag", ""); } } });