setup_analyser/analyser.js

724 lines
25 KiB
JavaScript
Raw Normal View History

2025-01-15 22:24:43 -05:00
var numDies = 0; // master counter for our number of dies
var outputVisible = 0; // Status of whether the output is displayed or not
2025-01-15 22:24:43 -05:00
// 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
2025-01-15 22:24:43 -05:00
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
2025-01-21 02:42:11 -05:00
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 /////
2025-01-15 22:24:43 -05:00
function addReduction() {
2025-01-21 02:42:11 -05:00
numDies ++; // Increment our die count
2025-01-15 22:24:43 -05:00
2025-01-21 02:42:11 -05:00
// 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;\"/>";
2025-01-15 22:24:43 -05:00
// create the row
2025-01-21 02:42:11 -05:00
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();
}
2025-01-15 22:24:43 -05:00
}
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;
2025-01-21 02:42:11 -05:00
}
2025-01-15 22:24:43 -05:00
}
function doMath() {
2025-01-21 02:42:11 -05:00
outputVisible = 1; // set visible status to enabled
2025-01-15 22:24:43 -05:00
outputTable = document.getElementById("output"); // Select our output data table
outputTable.innerHTML = "";
2025-01-21 02:42:11 -05:00
// Make the output graph visible
outputGraph = document.getElementById("outputChart");
outputGraph.innerHTML = "<canvas id=\"outputData\"></canvas>";
2025-01-21 02:42:11 -05:00
2025-01-15 22:24:43 -05:00
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"
2025-01-15 22:24:43 -05:00
// 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";
2025-01-15 22:24:43 -05:00
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
2025-01-21 02:42:11 -05:00
angle = document.getElementById("angle" + i).value;
2025-01-15 22:24:43 -05:00
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);
2025-01-15 22:24:43 -05:00
2025-01-21 02:42:11 -05:00
row[i] = outputTable.insertRow(i); // Add a new row to the END of the table
2025-01-15 22:24:43 -05:00
2025-01-21 02:42:11 -05:00
// 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);
2025-01-21 02:42:11 -05:00
cell1.id = "reductionNum";
cell2 = row[i].insertCell(1);
2025-01-15 22:24:43 -05:00
cell2.id = "startFinish";
cell3 = row[i].insertCell(2);
2025-01-15 22:24:43 -05:00
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";
2025-01-15 22:24:43 -05:00
2025-01-21 02:42:11 -05:00
// 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;
2025-01-15 22:24:43 -05:00
2025-01-21 02:42:11 -05:00
// 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)";
2025-01-21 02:42:11 -05:00
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;
2025-01-15 22:24:43 -05:00
}
getStatistics();
drawGraph();
// Add spacers
document.getElementById("spacer1").style.display = 'block';
document.getElementById("spacer2").style.display = 'block';
document.getElementById("spacer3").style.display = 'block';
2025-01-15 22:24:43 -05:00
}
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 #"
},
}
},
},
});
}
2025-01-21 02:42:11 -05:00
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 = "";
2025-01-15 22:24:43 -05:00
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 = [];
2025-01-15 22:24:43 -05:00
}
///// 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)";
2025-01-21 14:31:17 -05:00
// Create and set the header
subtitle.innerHTML = "For " + inputText + " to " + outputText;
2025-01-21 14:31:17 -05:00
// 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();
2025-01-21 14:31:17 -05:00
// 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
}
}