Sindbad~EG File Manager
// ThreeBodyControls.js
// to create and handle the tools in the left pane of DrawSolution.php
document.addEventListener("DOMContentLoaded", function() {
// Create a control panel div for the slider and Save button in the left 65px of the window.
var controlPanel = document.createElement("div");
controlPanel.id = "controlPanel";
controlPanel.style.position = "absolute";
controlPanel.style.top = "0";
controlPanel.style.left = "0";
controlPanel.style.width = "65px";
controlPanel.style.height = "100%";
controlPanel.style.backgroundColor = "#eee"; // light gray background for visibility
controlPanel.style.zIndex = "2"; // ensure it stays on top
document.body.appendChild(controlPanel);
// Create the slider (an <input type="range"> element)
var slider = document.createElement("input");
slider.type = "range";
slider.id = "timeSlider";
// Make the slider vertical and in the control panel
slider.style.transform = "rotate(-90deg) translateX(-200px) translateY(-65px)";
slider.style.width = "200px"; // Adjust as needed
slider.style.height = "20px"; // Adjust as needed
controlPanel.appendChild(slider);
if(! indatabase){
// Create the Save button
var saveButton = document.createElement("button");
saveButton.style.width = "62px";
saveButton.style.height = "32px";
saveButton.style.backgroundColor = "darkblue";
saveButton.style.color = "white";
saveButton.style.fontFamily = "Arial";
saveButton.textContent = "Save";
saveButton.id = "save";
// Append the Save button to the control panel
controlPanel.appendChild(saveButton);
}
// Create the Edit button
var editButton = document.createElement("button");
editButton.style.width = "62px";
editButton.style.height = "32px";
editButton.style.backgroundColor = "darkblue";
editButton.style.color = "white";
editButton.style.fontFamily = "Arial";
editButton.textContent = "Edit";
editButton.id = "edit";
// Append the Edit button to the control panel
controlPanel.appendChild(editButton);
async function SaveButtonHandler(event) {
event.preventDefault();
// Ask for caption (optional, max 127 chars)
const caption = await GetCaption();
if (caption === null || caption === "") return; // User canceled
// Create a hidden form to POST data to SaveThreeBody.php.
var form = document.createElement("form");
form.method = "POST";
form.action = "SaveThreeBody.php";
// Retrieve document-level data.
// These elements (if they exist) hold the user-entered tmax value.
// tmax and caption should be defined before including this file
// Assume the bodies array is available globally.
var bodiesArray = window.bodies || [];
var nbodies = bodiesArray.length;
// Use "Pending" as a placeholder since the new SVG filename is constructed in SaveThreeBody.php.
var imageFilename = "Pending";
// Create hidden inputs for document-level fields.
var docFields = {
caption: caption,
tmax: tmax,
nbodies: nbodies,
integrationMethod: integrationMethod
};
for (var key in docFields) {
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = docFields[key];
form.appendChild(input);
}
// For each body, create hidden inputs for its properties.
bodiesArray.forEach(function(body) {
["mass", "color", "r", "x0", "y0", "p0", "q0"].forEach(function(prop) {
var input = document.createElement("input");
input.type = "hidden";
input.name = prop + "[]";
input.value = body[prop];
form.appendChild(input);
});
});
// Append the form to the document and submit it.
document.body.appendChild(form);
form.submit();
}
function EditButtonHandler(event) {
event.preventDefault();
// Build a hidden form.
const form = document.createElement("form");
form.method = "post";
form.action = "ThreeBody.php";
caption = document.getElementById("caption") ? document.getElementById("caption").value : "";
tmax = document.getElementById("tmax") ? document.getElementById("tmax").value : "1000";
integrationMethod= document.getElementById("integrationMethod") ? document.getElementById("integrationMethod").value : "rk4";
// document-level fields.
var docFields = {
caption: caption,
tmax: tmax,
nbodies: nbodies,
integrationMethod: integrationMethod
};
// var image_filename = document.getElementById("image_filenameInput") ? document.getElementById("image_filenameInput").value : "oops";
// Assume the bodies array is available globally.
var bodiesArray = window.bodies || [];
var nbodies = bodiesArray.length;
console.log(bodiesArray);
// Create hidden inputs for document-level fields.
for (var key in docFields) {
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = docFields[key];
form.appendChild(input);
}
// Populate body data as arrays (e.g., mass[], color[], etc.).
const bodyFields = ["mass", "color", "r", "x0", "y0", "p0", "q0"];
if (bodiesArray && bodiesArray.length > 0) {
bodiesArray.forEach(body => {
bodyFields.forEach(field => {
const input = document.createElement("input");
input.type = "hidden";
input.name = field + "[]";
input.value = (body[field] !== null && body[field] !== undefined) ? body[field] : "";
form.appendChild(input);
});
});
}
document.body.appendChild(form);
// console.log(form);
form.submit();
}
// Attach the event handlers to the Save and Edit buttons
if(!indatabase){
saveButton.addEventListener("click", SaveButtonHandler);
}
editButton.addEventListener("click", EditButtonHandler);
// Add an "Animate" button to the control panel.
// This doesn't (yet) work very well so I commented it out.
// var animateButton = document.createElement("button");
// animateButton.textContent = "Animate";
// controlPanel.appendChild(animateButton);
// Get the SVG container element.
var svgContainer = document.getElementById("svgContainer");
if (!svgContainer) {
console.error("SVG container with id 'svgContainer' not found.");
return;
}
// Find all <path> elements with a "body" attribute inside the svgContainer.
var pathElems = svgContainer.querySelectorAll("path[body]");
// We'll build two objects:
// - bodyPaths: maps a numeric body id to an array of points [{x, y}, ...]
// - bodyCircles: maps a numeric body id to its corresponding SVG circle element.
var bodyPaths = {};
var bodyCircles = {};
var numPoints = 0; // assume all paths have the same number of points
// Function to parse the "d" attribute of a path.
// Expected format: "M x1 y1 L x2 y2 L x3 y3 ..."
function parsePathPoints(d) {
var points = [];
// Replace commas with spaces and split on "M" or "L"
d = d.replace(/,/g, " ");
var parts = d.split(/[ML]/i);
for (var i = 0; i < parts.length; i++) {
var part = parts[i].trim();
if (!part) continue;
var coords = part.split(/\s+/);
if (coords.length >= 2) {
var x = parseFloat(coords[0]);
var y = parseFloat(coords[1]);
points.push({ x: x, y: y });
}
}
return points;
}
// Process each path element.
pathElems.forEach(function(elem) {
var bodyIdStr = elem.getAttribute("body");
var bodyId = parseInt(bodyIdStr, 10); // these will be 1,2,...
var d = elem.getAttribute("d");
var points = parsePathPoints(d);
bodyPaths[bodyId] = points;
if (numPoints === 0) {
numPoints = points.length;
} else if (points.length !== numPoints) {
console.warn("Path for body " + bodyId + " has a different number of points.");
}
});
// Set up the slider range.
slider.min = 0;
slider.max = numPoints - 1;
slider.value = 0;
// Create a circle for each body.
// Assumes global array "bodies" exists where bodies[id].radius and bodies[id].color exist.
for (var id in bodyPaths) {
var bodyId = parseInt(id, 10);
// Create an SVG circle element using the proper namespace.
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
// Use the radius and color from the bodies array; if not available, use defaults.
var bodyObj = (typeof bodies !== "undefined" && bodies[bodyId-1])
? bodies[bodyId-1]
: { radius: 5, color: "#000" };
// Ensure the radius is a valid number (fallback to 5 if invalid)
var radius = (typeof bodyObj.r === "number" && bodyObj.r > 0)
? bodyObj.r
: 5;
// Set the radius attribute properly.
circle.setAttribute("r", radius);
let colorstring = bodyObj.color; // Now the Engine sends them in rgb(r,g,b) form as strings
// let red = colorNumber & 0xff; // this is the way they come from the Engine, red not shifted
// let green = colorNumber & 0xff00;
// let blue = colorNumber & 0xff0000;
// let colorstring = "rgb("+red+ "," + green + "," + blue + ")";
circle.setAttribute("fill", colorstring);
circle.setAttribute("id", "bodyCircle" + bodyId);
circle.style.display = 'block';
// Initially position the circle at the first point of the corresponding path.
var pt = bodyPaths[bodyId][0];
circle.setAttribute("cx", pt.x);
circle.setAttribute("cy", pt.y);
graph0 = document.getElementById('graph0');
// Append the circle to that svg element
graph0.appendChild(circle);
bodyCircles[bodyId] = circle;
}
// Function to update all circles to a given index along their paths.
function updateCircles(index) {
for (var id in bodyPaths) {
var points = bodyPaths[id];
if (index < 0) index = 0;
if (index >= points.length) index = points.length - 1;
var pt = points[index];
var circle = bodyCircles[id];
if (circle && pt) {
circle.setAttribute("cx", pt.x);
circle.setAttribute("cy", pt.y);
}
}
}
// Attach an event listener to the slider.
slider.addEventListener("input", function() {
var idx = parseInt(slider.value, 10);
updateCircles(idx);
});
// Set up animation variables.
var animationInterval = null;
var duration = 1; // 1 seconds per full cycle
var minVal = parseInt(slider.min, 10);
var maxVal = parseInt(slider.max, 10);
var steps = maxVal - minVal;
var stepDuration = steps > 0 ? duration / steps : duration;
/*
// Animate button toggles the animation on/off.
animateButton.addEventListener("click", function() {
if (animationInterval === null) {
// Recalculate stepDuration each time you start the animation.
var duration = 1; // change this value as desired
var minVal = parseInt(slider.min, 10);
var maxVal = parseInt(slider.max, 10);
var steps = maxVal - minVal;
var stepDuration = steps > 0 ? duration / steps : duration;
animationInterval = setInterval(function() {
var current = parseInt(slider.value, 10);
if (current < maxVal) {
slider.value = current + 1;
} else {
slider.value = minVal;
}
updateCircles(parseInt(slider.value, 10));
}, stepDuration);
animateButton.textContent = "Stop Animation";
} else {
clearInterval(animationInterval);
animationInterval = null;
animateButton.textContent = "Animate";
}
});
*/
});
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists