setup_analyser/analyser.js
Taylor Courage 725dfc9508 Added link parsing
Fixed issue where larger graphs wouldn't reset
2025-01-25 11:47:30 -05:00

724 lines
25 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var numDies = 0; // master counter for our number of dies
var outputVisible = 0; // Status of whether the output is displayed or not
// Arrays to store values for graphs
var dieCount = [];
var dataROA = [];
var dataROD = [];
var dataElong = [];
var dataAngle = [];
var dataDelta = [];
///// MATHS FUNCTIONS /////
// Next two functions are for timers... ripped from geeksforgeeks.org
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function waitAndReset() {
await sleep(2000);
var tooltip = document.getElementById("shareButton");
tooltip.innerHTML = "Share";
}
// Calculate Reduction of Area
function getReduction(startSize, finalSize) {
var startArea = Math.PI * ((startSize / 2) * (startSize / 2));
var finalArea = Math.PI * ((finalSize / 2) * (finalSize / 2));
return ((startArea - finalArea) / startArea) * 100;
}
// This function gets the elongation
function getElongation(startSize, finalSize) {
return (Math.pow(startSize / finalSize, 2) - 1) * 100;
}
// Calculate delta
function getDelta(startSize, finalSize, angle) {
angle = (angle * 0.5) * (Math.PI / 180); // Convert to semi-angle and radians
return ((startSize + finalSize) / (startSize - finalSize)) * Math.sin(angle);
}
// Calculate Reduction of Diameter
function getRoDiameter(startSize, finishSize){
return (startSize - finishSize) / ((startSize + finishSize) / 2) * 100;
}
// Convert number to millimetres
function toMillimetres(size) { //convert to mm
size = Math.round((size * 100) * 25.4) / 100;
return size;
}
// Convert number to inches
function toInches(size) { //convert to inches
size = Math.round((size * 1000) / 25.4) / 1000;
return size;
}
///// END OF MATHS SECTION /////
///// ALGORITHMS SECTION /////
function addReduction() {
numDies ++; // Increment our die count
// Create the data going into the row
var inputLabel = "<label for=\"die" + numDies + "\" style=\"text-align:right;\">#" + (numDies) + ": </label>";
var inputSize = "<input id=\"die" + numDies + "\" type=\"text\" autocomplete=\"off\" value=\"0.000\" size=\"4\" onchange=\"doMath()\" />";
var inputAngle = "<input id=\"angle" + numDies + "\" type=\"text\" autocomplete=\"off\" value=\"16\" size=\"2\" onchange=\"doMath()\" style=\"text-align:center;\"/>";
// create the row
var table = document.getElementById("data"); // get table ID
var row = table.insertRow(-1); // Insert a new row (the -1 means we add to the END of the table)
var cell1 = row.insertCell(0); // Create and add a cell to the table
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
cell1.innerHTML = inputLabel; // Set the values of the cells we've created
cell2.innerHTML = inputSize
cell3.innerHTML = inputAngle;
if (outputVisible == 1) { // Check if out output is visible and update immediately when adding dies
doMath();
}
}
function removeReduction() { // function to remove the last row
if (numDies > 1) {
numDies--;
document.getElementById("data").deleteRow(-1); // delete the last row in the table
// Remove the last item in each array to remove the tick from the graph
dieCount.splice(-1);
dataROA.splice(-1);
dataElong.splice(-1);
dataDelta.splice(-1);
if (outputVisible == 1) { // Check if out output is visible and update immediately when adding dies
doMath();
}
} else {
numDies = 1;
}
}
function doMath() {
outputVisible = 1; // set visible status to enabled
outputTable = document.getElementById("output"); // Select our output data table
outputTable.innerHTML = "";
// Make the output graph visible
outputGraph = document.getElementById("outputChart");
outputGraph.innerHTML = "<canvas id=\"outputData\"></canvas>";
var row = [];
row[0] = outputTable.insertRow(0);
row[0].id = "outputHeader"; // set the header row
var cell1 = row[0].insertCell(0); //blank
var cell2 = row[0].insertCell(1); // "Start -> Finish"
var cell3 = row[0].insertCell(2); // "ROA"
var cell4 = row[0].insertCell(3); // "ROD"
var cell5 = row[0].insertCell(4); // "Elong"
var cell6 = row[0].insertCell(5); // "Angle"
var cell7 = row[0].insertCell(6); // "Delta"
// Create the header of the table
cell1.innerHTML = "Draft";
cell1.id = "reductionNumHeader";
cell2.innerHTML = "Start -> Finish";
cell2.id = "startFinish";
cell3.innerHTML = "R. Area (%)";
cell3.id = "roa";
cell4.innerHTML = "R. Dia. (%)";
cell4.id = "rod";
cell5.innerHTML = "Elong (%)";
cell5.id = "elong";
cell6.innerHTML = "Angle";
cell6.id = "angle";
cell7.innerHTML = "Δ Factor";
cell7.id = "delta";
for (var i = 1; i < numDies + 1; i++) {
inSize = document.getElementById("die" + (i - 1)).value; // the input size
outSize = document.getElementById("die" + i).value; // output size
angle = document.getElementById("angle" + i).value;
if (i == 1 && document.getElementById("metric").checked == true) {
// If this is the first die in the setup, check if it's a metric/rod start and convert it
inSize = toInches(inSize);
}
// Format our numbers to prevent maximum user stupidity
inSize = formatCheck(inSize);
outSize = formatCheck(outSize);
row[i] = outputTable.insertRow(i); // Add a new row to the END of the table
// Add all our cells to the table
// We also give them HTML ID's to format things with CSS nicely
cell1 = row[i].insertCell(0);
cell1.id = "reductionNum";
cell2 = row[i].insertCell(1);
cell2.id = "startFinish";
cell3 = row[i].insertCell(2);
cell3.id = "roa";
cell4 = row[i].insertCell(3);
cell4.id = "rod";
cell5 = row[i].insertCell(4);
cell5.id = "elong";
cell6 = row[i].insertCell(5);
cell6.id = "angle";
cell7 = row[i].insertCell(6);
cell7.id = "delta";
// These next lines calculate and round the data to two decimal places
dataROA[i - 1] = (Math.round(getReduction(inSize, outSize) * 100) / 100).toFixed(2);
dataROD[i - 1] = (Math.round(getRoDiameter(inSize, outSize) * 100) / 100).toFixed(2);
dataElong[i - 1] = (Math.round(getElongation(inSize, outSize) * 100) / 100).toFixed(2);
dataDelta[i - 1] = (Math.round(getDelta(inSize, outSize, angle) * 100) / 100).toFixed(2);
dataAngle[i - 1] = angle;
// Set the values of the cells in our table
cell1.innerHTML = "#" + i + ":&nbsp;";
cell2.innerHTML = inSize.toFixed(3) + "\" (" + toMillimetres(inSize) + " mm) -> " + outSize.toFixed(3) + "\" (" + toMillimetres(outSize) + " mm)";
cell3.innerHTML = dataROA[i - 1];
cell4.innerHTML = dataROD[i - 1];
cell5.innerHTML = dataElong[i - 1];
cell6.innerHTML = dataAngle[i - 1];
cell7.innerHTML = dataDelta[i - 1];
dieCount[i - 1] = i;
}
getStatistics();
drawGraph();
// Add spacers
document.getElementById("spacer1").style.display = 'block';
document.getElementById("spacer2").style.display = 'block';
document.getElementById("spacer3").style.display = 'block';
}
function drawGraph() {
// Documentation for the following chart-building command can be found at https://chartjs.org
const outputData = new Chart("outputData", {
type: "line",
data: {
labels: dieCount,
datasets: [{
data: dataROA,
label: "Reduction of Area",
borderColor: "green",
fill: false,
pointStyle: 'circle',
pointRadius: 4,
pointHoverRadius: 7,
yAxisID: 'y',
tension: 0,
tooltip: {
callbacks: {
label: function(tooltipItem, d){
return "RoA: " + Number(tooltipItem.formattedValue).toFixed(2) + "%";
},
},
},
},{
data: dataROD,
label: "Reduction of Dia.",
borderColor: "orange",
fill: false,
pointStyle: 'rect',
pointRadius: 5,
pointHoverRadius: 8,
yAxisID: 'y',
tension: 0,
tooltip: {
callbacks: {
label: function(tooltipItem, d){ // Add a % sign to the value
return "RoD: " + Number(tooltipItem.formattedValue).toFixed(2) + "%";
},
},
},
},{
data: dataElong,
label: "Elongation",
borderColor: "blue",
fill: false,
pointStyle: 'rect',
pointRadius: 5,
pointHoverRadius: 8,
yAxisID: 'y',
tension: 0,
tooltip: {
callbacks: {
label: function(tooltipItem, d){ // Add a % sign to the value
return "Elong: " + Number(tooltipItem.formattedValue).toFixed(2) + "%";
},
},
},
},{
data: dataDelta,
label: "Δ Factor",
borderColor: "red",
fill: false,
pointStyle: 'triangle',
pointRadius: 5,
pointHoverRadius: 8,
yAxisID: 'y1',
tension: 0
}],
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
stacked: false,
plugins: {
title: {
display: true,
text: "Draft Analysis"
},
legend: {
labels: {
usePointStyle: true
}
},
tooltip: {
callbacks: {
title: function(tooltipItem, d){
var i = Number(tooltipItem[0].label); // Grab the label (or die number) and use it for start/finish
// We need our start and finish sizes...
inSize = document.getElementById("die" + (i - 1)).value; // the input size
outSize = document.getElementById("die" + i).value; // output size
if (i == 1 && document.getElementById("metric").checked == true) {
// If this is the first die in the setup, check if it's a metric/rod start and convert it
inSize = toInches(inSize);
}
// Format our numbers to prevent maximum user stupidity
if (inSize < 10.0 && inSize > 0) { // If we have a 'proper' number i.e. ".130"
inSize = Number(inSize);
} else { // re-format the number if it's 'wrong' i.e. "130"
inSize = inSize / 1000;
}
if (outSize < 10.0 && outSize > 0) { // If we have a 'proper' number i.e. ".130"
outSize = Number(outSize);
} else { // re-format the number if it's 'wrong' i.e. "130"
outSize = outSize / 1000;
}
// Finally write the new header
return "Draft #" + tooltipItem[0].label + "\n" + inSize.toFixed(3) + "\" -> " + outSize.toFixed(3) + "\"";
},
},
},
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: "% (RoA/Elong)"
},
suggestedMin: 0,
suggestedMax: 35,
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: "Δ Factor"
},
suggestedMin: 0,
suggestedMax: 7,
grid: {
drawOnChartArea: false
}
},
x: {
title: {
display: true,
text: "Draft #"
},
}
},
},
});
}
function getStatistics() {
// Get data from page
var div = document.getElementById("statistics");
var startSize = formatCheck(document.getElementById("die0").value);
var finalSize = formatCheck(document.getElementById("die" + numDies).value);
// Check if the start size is metric/rod and convert to inches
if (document.getElementById("metric").checked == true) {
startSize = toInches(startSize);
}
// We'll do a lot of the heavy math-lifting here to make things easier to read below
var avgDelta = 0;
var avgROA = 0;
var avgElong = 0;
var totalROA = Math.round(getReduction(startSize, finalSize) * 100) / 100;
var totalElong = Math.round(getElongation(startSize, finalSize) * 100) / 100;
var totalROD = Math.round(getRoDiameter(startSize, finalSize) * 100) / 100;
// Get average ROA
for (var i = 0; i < numDies; i++) {
avgROA += Number(dataROA[i]);
}
avgROA = (Math.round((avgROA / numDies) * 100) / 100).toFixed(2);
// Get average Elong
for (var i = 0; i < numDies; i++) {
avgElong += Number(dataElong[i]);
}
avgElong = (Math.round((avgElong / numDies) * 100) / 100).toFixed(2);
// Get average delta
for (var i = 0; i < numDies; i++) {
avgDelta += Number(dataDelta[i]);
}
avgDelta = (Math.round((avgDelta / numDies) * 100) / 100).toFixed(2);
// Create the table to display statistics
div.innerHTML = "<h5 align=\"center\" id=\"statsSummaryHeader\">Summary</h5><table id=\"statsTable\"></table>";
var statsTable = document.getElementById("statsTable");
//// ROW 1 ////
var row = statsTable.insertRow(0);
var c1 = row.insertCell(0);
var c2 = row.insertCell(1);
var c3 = row.insertCell(2);
var c4 = row.insertCell(3);
var c5 = row.insertCell(4);
// COL 1
c1.innerHTML = "# Of Reductions: ";
c1.id = "statsCol1";
c2.innerHTML = "<b>" + numDies + "</b>";
c2.id = "statsCol2";
//
c3.innerHTML = "|&nbsp;&nbsp;";
c3.id = "statsCol3";
// COL 2
c4.innerHTML = "Avg. Δ Factor: ";
c4.id = "statsCol4";
c5.innerHTML = "<b>" + avgDelta + "</b>";
c5.id = "statsCol5";
//// ROW 2 ////
row = statsTable.insertRow(1);
c1 = row.insertCell(0);
c2 = row.insertCell(1);
c3 = row.insertCell(2);
c4 = row.insertCell(3);
c5 = row.insertCell(4);
// COL 1
c1.innerHTML = "Total R. Of Diameter: ";
c1.id = "statsCol4";
c2.innerHTML = "<b>" + (Math.round((startSize - finalSize) * 1000) / 1000).toFixed(3) + "\"</b>";
c2.id = "statsCol5";
//
c3.innerHTML = "|&nbsp;&nbsp;";
c3.id = "statsCol3";
// COL 2
c4.innerHTML = "% R. Of Diameter";
c4.id = "statsCol4";
c5.innerHTML = "<b>" + totalROD.toFixed(2) + "%</b>";
c5.id = "statsCol5";
//// ROW 3 ////
row = statsTable.insertRow(2);
c1 = row.insertCell(0);
c2 = row.insertCell(1);
c3 = row.insertCell(2);
c4 = row.insertCell(3);
c5 = row.insertCell(4);
// COL 1
c1.innerHTML = "Total R. Of Area: ";
c1.id = "statsCol1";
c2.innerHTML = "<b>" + totalROA + "%</b>";
c2.id = "statsCol2";
//
c3.innerHTML = "|&nbsp;&nbsp;";
c3.id = "statsCol3";
// COL 2
c4.innerHTML = "Avg. R. of Area: ";
c4.id = "statsCol4";
c5.innerHTML = "<b>" + avgROA + "%</b>";
c5.id = "statsCol5";
//// ROW 4 ////
row = statsTable.insertRow(3);
c1 = row.insertCell(0);
c2 = row.insertCell(1);
c3 = row.insertCell(2);
c4 = row.insertCell(3);
c5 = row.insertCell(4);
// COL 1
c1.innerHTML = "Total Elongation: ";
c1.id = "statsCol1";
c2.innerHTML = "<b>" + totalElong + "%</b>";
c2.id = "statsCol2";
//
c3.innerHTML = "|&nbsp;&nbsp;";
c3.id = "statsCol3";
// COL 2
c4.innerHTML = "Avg. Elongation: ";
c4.id = "statsCol4";
c5.innerHTML = "<b>" + avgElong + "%</b>";
c5.id = "statsCol5";
}
function clearScreen() {
// Reset variables back to defaults
numDies = 0;
outputVisible = 0;
// Clear and reset inputs
var inputStart = document.getElementById("die0");
inputStart.value = "0.0";
var unitsType = document.getElementById("metric");
unitsType.checked = true;
var inputTable = document.getElementById("data");
inputTable.innerHTML = "<tr style=\"font-size: 75%;\"><td><p></p>Draft<p></p></td><td>Exit Dia.</td><td>Angle (2α)</td></tr>";
// Delete the output table
var outputTable = document.getElementById("output");
outputTable.innerHTML = "";
// Delete the output graph
outputGraph = document.getElementById("outputChart");
outputGraph.innerHTML = "";
statistics = document.getElementById("statistics");
statistics.innerHTML = "";
// Delete the spacers
document.getElementById("spacer1").style.display = 'none';
document.getElementById("spacer2").style.display = 'none';
document.getElementById("spacer3").style.display = 'none';
// If we have any search parameters in the URL, clear them too
var address = window.location;
if ((address.href).includes('?')) {
let domain = address.protocol + "//" + address.host + "/";
window.history.pushState({},"Wiredraw Setup Analyser",domain);
}
// Reset the arrays used for graph generation
dieCount = [];
dataROA = [];
dataROD = [];
dataElong = [];
dataAngle = [];
dataDelta = [];
}
///// END ALGORITHMS SECTION /////
///// EXTRAs SECTION /////
// Convert the page to a print layout, and open the OS/browser print dialog
function printScreen() {
var subtitle = document.getElementById("subtitle");
var reductionCount = document.getElementById("numReductions");
// Get input size, and format it
var inputSize = document.getElementById("die0").value;
// If our start size is metric show that, otherwise swap them to show 'murican first
if (document.getElementById("metric").checked) {
var inputText = inputSize + " mm (" + toInches(inputSize) + "\")";
} else {
var inputText = formatCheck(inputSize) + "\" (" + toMillimetres(formatCheck(inputSize)) + " mm)";
}
// Get the final size and format it
var outputSize = document.getElementById("die" + numDies).value;
outputSize = formatCheck(outputSize).toFixed(3);
var outputText = (outputSize) + "\" (" + toMillimetres(outputSize) + " mm)";
// Create and set the header
subtitle.innerHTML = "For " + inputText + " to " + outputText;
// Change the name of the window to save the file with the same
var pageTitle = document.getElementById("pageTitle");
var title = pageTitle.innerHTML;
pageTitle.innerHTML = "Draft Analysis for " + inputSize + " to " + outputSize;
// Let the browser handle printing
window.print();
// Reset the page title back to normal
pageTitle.innerHTML = title;
}
function formatCheck(value) {
if (value < 10.0 && value > 0) { // If we have a 'proper' number i.e. ".130"
value = Number(value);
} else { // re-format the number if it's 'wrong' i.e. "130"
value = value / 1000;
}
return value;
}
function createLink() {
// Set our domain to create our link. We will default to the URL hosting the page
var domain = window.location.protocol + "//" + window.location.host + "/";
var shareLink = new URL(domain);
// Create the starting point for the setup manually
var size = document.getElementById("die0").value;
size = formatCheck(size);
// Check if the metric option is checked and save that
if (document.getElementById("metric").checked == true) {
var isMetric = "true";
} else {
var isMetric = "false";
}
// Add the starting information to the link
shareLink.searchParams.append("dies", numDies);
shareLink.searchParams.append("metric", isMetric);
shareLink.searchParams.append("size0", size);
// Next iterate through all available reductions and add them as well, including their angle
for (var i = 1; i <= numDies; i++) {
var dieNum = "die" + i;
var angleNum = "angle" + i;
size = document.getElementById(dieNum).value;
size = formatCheck(size);
var angle = document.getElementById(angleNum).value;
// If values are null, change them to zero
if (size == null) {
size = 0;
}
if (angle == null) {
angle = 0;
}
// Append the data to the link
shareLink.searchParams.append("size" + i, size);
shareLink.searchParams.append("angle" + i, angle);
}
// Display the link to the user... I want to make this prettier some day but this crude solution is GoodEnough™
//window.prompt("Your share link: (Note: it has already been copied to your clipboard!)", shareLink.href);
navigator.clipboard.writeText(shareLink.href);
var tooltip = document.getElementById("shareButton");
tooltip.innerHTML = "Copied";
waitAndReset();
}
function readLink() {
var address = window.location.href;
if (address.includes('?')) { // Check if we actually have search parameters or not, otherwise ignore all of this
// Get address and setup parsing
var params = address.split('?')[1];
var query = new URLSearchParams(params);
// Setup arrays for sizes and angles
let size = [];
let angle = [];
var metric = true; // Metric status variable
var tableCreated = false; // This variable keeps track of whether or not we've re-drawn the output table after clearing the screen
var i = 0; // Keep track of SIZES
var j = 1; // Keep track of ANGLES
clearScreen(); // Start by blanking everything to start fresh
for (var pair of query.entries()){
// Check our total die count
if (pair[0] == "dies") {
numberDies = pair[1];
}
// Add entries for each die
if (tableCreated != true) {
for (var k = 0; k < numberDies; k++) {
addReduction();
}
tableCreated = true;
}
// Check if our starting size is metric or 'murican
if (pair[0] == "metric" && pair[1] == "true"){ // If metric is checked...
document.getElementById("metric").checked = true;
metric = true;
} else if (pair[0] == "metric" && pair[1] == "false"){ // If inches is checked...
document.getElementById("inches").checked = true;
metric = false;
}
// Incremental variables for both size and angle
// These are separate because it's easier to parse this way
sizeNum = "size" + i;
angleNum = "angle" + j;
// Grab angle or size from the data pair
if (pair[0] == angleNum) {
angle[i] = pair[1];
}
if (pair[0] == sizeNum) {
size[i] = pair[1];
}
// If values exist, and are defined, write them to the page
if (size[i] != undefined || size[i] != null){
// Check if it's metric/rod, if so we only need one decimal
if (metric == true && pair[0] == "size0") {
document.getElementById("die" + i).value = Number(size[i]).toFixed(1);
} else { // everything else gets three decimal places
document.getElementById("die" + i).value = Number(size[i]).toFixed(3);
}
i++;
}
if (angle[i] != undefined || angle[i] != null){
document.getElementById(angleNum).value = Number(angle[i]);
j++;
}
}
doMath(); // Run the calculations and display the result
} else {
// If there are no parameters... we will do nothing
}
}