From 41ace88838045233fe0aeb66f9695d257195e746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Solla?= <danielgarciasolla0@gmail.com> Date: Sun, 15 Dec 2024 17:47:08 +0100 Subject: [PATCH] Pages --- index.html | 400 +---------------------------------------------------- script.js | 296 +++++++++++++++++++++++++++++++++++++++ styles.css | 95 +++++++++++++ 3 files changed, 395 insertions(+), 396 deletions(-) create mode 100644 script.js create mode 100644 styles.css diff --git a/index.html b/index.html index 82f3928..b115f3d 100644 --- a/index.html +++ b/index.html @@ -6,103 +6,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Proyecto Visualización</title> <script src="https://d3js.org/d3.v7.min.js"></script> - <style> - body { - font-family: Arial, sans-serif; - justify-content: center; - align-items: center; - height: 100%; - margin: 0; - background-color: #fff; - } - - .div-histograma { - border: 0em solid #222222; - width: 40em; - height: 20em; - text-align: center; - margin: 1em; - padding: 2em; - color: #f9f9f9; - font-weight: bold; - border-radius: 0em; - box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); - transition: transform 0.3s; - } - - .div-histograma:hover { - transform: scale(1.05); - } - - #div1 { - background-color: #fff; - } - - #div2 { - background-color: #fff; - } - - #dropdownDiv { - display: flex; - flex-direction: column; - align-items: flex-start; - margin: 1em; - padding: 1em; - border: 0em solid #222222; - border-radius: 0em; - box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); - background-color: #f9f9f9; - transition: transform 0.3s; - } - - #keyDropdown { - margin-top: 0.5em; - padding: 0.5em; - border-radius: 0em; - border: 0em solid #ccc; - font-size: 1em; - transition: transform 0.3s; - } - - #opcinesDiv1 { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - margin: 1em; - padding: 0em; - } - - #sliderAnimationContainer, - #sliderBarrasContainer, - #checkboxContainer, - #distributionCheckboxContainer { - width: max-content; - height: fit-content; - display: block; - margin: 1em 1em 1em 1em; - padding: 1em; - border: 0em solid #222222; - border-radius: 0em; - box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); - background-color: #f9f9f9; - transition: transform 0.3s; - } - - #dropdownDiv:hover, - #keyDropdown:hover, - #checkboxContainer:hover, - #sliderAnimationContainer:hover, - #sliderBarrasContainer:hover, - #distributionCheckboxContainer:hover { - transform: scale(1.05); - } - - label { - font-weight: bold; - margin-bottom: 0.5em; - } - </style> + <link rel="stylesheet" href="styles.css"> </head> <body> @@ -110,7 +14,8 @@ <h1 style="color: #222222; font-size: 2em; font-weight: bold;">Resultados de simulaciones de un proceso aleatorio</h1> <p style="color: #555555; font-size: 1em;">Visualización de las distribuciones que determinan la duración y - estado final del sistema. (<a href="https://www.kaggle.com/datasets/cardstdani/2d-site-percolation-threshold" target="_blank" + estado final del sistema. (<a + href="https://www.kaggle.com/datasets/cardstdani/2d-site-percolation-threshold" target="_blank" style="color: #1e90ff; text-decoration: none;">Fuente</a>)</p> </div> <div style="display:flex; justify-content: center; align-items: center;margin:5em;"> @@ -147,304 +52,7 @@ </div> </div> - <script type="text/javascript"> - const datasetFolder = "./dataset/" - const fileNamesList = ['10 1', '10 10', '10 11', '10 12', '10 13', '10 14', '10 15', '10 2', '10 3', '10 4', '10 5', '10 6', '10 7', '10 8', '10 9', '11 1', '11 10', '11 11', '11 12', '11 13', '11 14', '11 15', '11 2', '11 3', '11 4', '11 5', '11 6', '11 7', '11 8', '11 9', '12 1', '12 12', '12 2', '12 3', '12 4', '12 5', '12 6', '13 1', '13 10', '13 11', '13 12', '13 13', '13 14', '13 15', '13 2', '13 3', '13 4', '13 5', '13 6', '13 7', '13 8', '13 9', '14 1', '14 10', '14 11', '14 12', '14 13', '14 14', '14 15', '14 2', '14 3', '14 4', '14 5', '14 6', '14 7', '14 8', '14 9', '15 1', '15 10', '15 11', '15 12', '15 13', '15 14', '15 15', '15 2', '15 3', '15 4', '15 5', '15 6', '15 7', '15 8', '15 9', '16 1', '16 10', '16 11', '16 12', '16 13', '16 14', '16 15', '16 16', '16 2', '16 3', '16 4', '16 5', '16 6', '16 7', '16 8', '16 9', '17 1', '17 10', '17 11', '17 12', '17 13', '17 14', '17 15', '17 16', '17 17', '17 2', '17 3', '17 4', '17 5', '17 6', '17 7', '17 8', '17 9', '18 1', '18 18', '18 2', '18 3', '18 4', '18 5', '18 6', '18 7', '19 1', '19 19', '20 1', '20 20', '21 1', '21 21', '22 1', '22 22', '23 1', '23 23', '24 1', '24 24', '25 1', '25 25', '26 1', '26 26', '27 1', '27 27', '28 1', '28 28', '29 1', '29 29', '3 1', '3 3', '30 1', '30 30', '31 1', '31 31', '32 1', '32 32', '33 1', '33 33', '34 1', '34 34', '35 1', '35 35', '36 1', '36 36', '37 1', '37 37', '38 1', '38 38', '39 1', '39 39', '4 1', '4 4', '40 1', '40 40', '41 1', '41 41', '42 1', '42 42', '43 1', '43 43', '44 1', '44 44', '45 1', '45 45', '46 1', '46 46', '47 1', '47 47', '48 1', '48 48', '49 1', '5 1', '5 10', '5 11', '5 12', '5 13', '5 14', '5 15', '5 2', '5 3', '5 4', '5 5', '5 6', '5 7', '5 8', '5 9', '6 1', '6 10', '6 11', '6 12', '6 13', '6 14', '6 15', '6 2', '6 3', '6 4', '6 5', '6 6', '6 7', '6 8', '6 9', '7 1', '7 10', '7 11', '7 12', '7 13', '7 14', '7 15', '7 2', '7 3', '7 4', '7 5', '7 6', '7 7', '7 8', '7 9', '8 1', '8 10', '8 11', '8 12', '8 13', '8 14', '8 15', '8 2', '8 3', '8 4', '8 5', '8 6', '8 7', '8 8', '8 9', '9 1', '9 10', '9 11', '9 12', '9 13', '9 14', '9 15', '9 2', '9 3', '9 4', '9 5', '9 6', '9 7', '9 8', '9 9']; - const dropdown = document.getElementById("keyDropdown"); - const mediaCheckbox = document.getElementById("mediaCheckbox"); - const distributionCheckbox = document.getElementById("distributionCheckbox"); - const animationSlider = document.getElementById("animationSlider"); - const animationTimeValue = document.getElementById("animationTimeValue"); - const barsSlider = document.getElementById("barsSlider"); - const barsCountValue = document.getElementById("barsCountValue"); - - var key = null; - var animationTime = 600; - var bins = 10; - var currentData = null; - var currentKey = null; - - async function checkFileExists(fileName) { - try { - const response = await fetch(datasetFolder + fileName + ".json", { method: 'HEAD' }); - return response.ok; - } catch (error) { - return false; - } - } - - async function getData(key) { - try { - const response = await fetch(datasetFolder + key + ".json"); - if (!response.ok) { - throw new Error("Error: " + response.status); - } - const data = await response.json(); - return data[key]; - } catch (error) { - console.error('Error:', error); - alert(error); - } - } - - async function crearHistogramas() { - if (currentKey !== key) { - currentData = await getData(key); - currentKey = key; - } - histograma(currentData[0], "div1", bins, "Iteraciones"); - histograma(currentData[1], "div2", bins, "Cantidad elementos"); - } - - function histograma(data, div, binNumber, label) { - const container = document.getElementById(div); - const width = container.clientWidth; - const height = container.clientHeight; - const heightFactor = 1.1; - const axisXOffset = 100; - const axisYOffset = 40; - - const bins = d3.bin() - .thresholds(binNumber) - .value((d) => d)(data); - - const x = d3.scaleLinear() - .domain([bins[0].x0, bins[bins.length - 1].x1]) - .range([axisXOffset, width - axisXOffset]); - - const yMax = d3.max(bins, d => d.length) * heightFactor; - - const y = d3.scaleLinear() - .domain([0, yMax]) - .range([height - axisYOffset, 0]); - - let svg = d3.select("#" + div).select("svg"); - if (svg.empty()) { - svg = d3.select("#" + div) - .append("svg") - .attr("width", width) - .attr("height", height) - .attr("viewBox", [0, 0, width, height]) - .attr("style", "max-width: 100%; height: auto;"); - } - - //Construir las barras - const bars = svg.selectAll("rect") - .data(bins, d => d.x0); - - bars.exit() - .transition() - .duration(0) - .ease(d3.easeCubicInOut) - .attr("y", height - axisYOffset) - .attr("height", 0) - .remove(); - - bars.transition() - .duration(animationTime) - .ease(d3.easeElasticOut.amplitude(2).period(1.5)) - .attr("x", (d) => x(d.x0) + 1) - .attr("width", (d) => Math.max(0, x(d.x1) - x(d.x0) - 3)) - .attr("y", (d) => y(d.length)) - .attr("height", (d) => y(0) - y(d.length)) - .attr("fill", "#055902"); - - bars.enter() - .append("rect") - .attr("x", (d) => x(d.x0) + 1) - .attr("width", (d) => Math.max(0, x(d.x1) - x(d.x0) - 3)) - .attr("y", height - axisYOffset) - .attr("height", 0) - .attr("fill", "#ff7f0e") - .transition() - .delay((d, i) => i * (animationTime / 10)) - .duration(animationTime) - .ease(d3.easeCubicInOut) - .attr("y", (d) => y(d.length)) - .attr("height", (d) => y(0) - y(d.length)) - .attr("fill", "#055902"); - - //Ejes x e y - svg.selectAll(".x-axis").remove(); - svg.append("g") - .attr("class", "x-axis") - .attr("transform", "translate(0," + (height - axisYOffset) + ")") - .call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0)) - .call((g) => g.selectAll("path, line").style("stroke", "#222222")) - .call((g) => g.selectAll("text").style("fill", "#222222")).style("font-size", "0.8em") - .call((g) => g.append("text") - .attr("x", width / 2) - .attr("y", 35) - .attr("fill", "#222222") - .attr("text-anchor", "middle") - .text(label)); - - svg.selectAll(".y-axis").remove(); - svg.append("g") - .attr("class", "y-axis") - .attr("transform", "translate(" + axisXOffset + ",0)") - .call(d3.axisLeft(y).ticks(height / 70)) - .call((g) => g.select(".domain").remove()) - .call((g) => g.selectAll("path, line").style("stroke", "#222222")) - .call((g) => g.selectAll("text").style("fill", "#222222")).style("font-size", "0.8em") - .call((g) => g.append("text") - .attr("x", -height / 2) - .attr("y", -70) - .attr("fill", "#222222") - .attr("text-anchor", "middle") - .attr("transform", "rotate(-90)") - .text("Simulaciones")); - - // Mostrar lÃnea de la media - svg.selectAll(".media") - .attr("y1", 0) - .transition() - .duration(animationTime) - .ease(d3.easeCubicInOut) - .style("opacity", 0) - .attr("y1", height - axisYOffset) - .remove(); - if (mediaCheckbox.checked) { - const average = d3.mean(data); - - svg.append("line") - .attr("class", "media") - .attr("x1", x(average)) - .attr("x2", x(average)) - .attr("y1", height - axisYOffset) - .attr("y2", height - axisYOffset) - .attr("stroke", "#1e90ff") - .attr("stroke-width", 4) - .transition() - .duration(animationTime) - .ease(d3.easeCubicInOut) - .attr("y1", 0); - - svg.append("text") - .attr("class", "media") - .attr("x", x(average) + 5) - .attr("y", 15) - .attr("fill", "#333333") - .style("font-size", "1em") - .style("font-weight", "bold") - .text("Media: " + average.toFixed(2)) - .style("opacity", 0) - .transition() - .duration(animationTime) - .ease(d3.easeCubicInOut) - .style("opacity", 1); - } - - // Mostrar curva de distribución - svg.selectAll(".distribution") - .transition() - .duration(animationTime) - .ease(d3.easeCubicInOut) - .attr("stroke-dashoffset", function () { return this.getTotalLength(); }) - .remove(); - if (distributionCheckbox.checked) { - const density = kde(epanechnikov(10), x.ticks(40), data.filter((_, index) => index % 30 === 0)); - - const yDist = d3.scaleLinear() - .domain([0, d3.max(density, d => d[1]) * heightFactor]) - .range([height - axisYOffset, 0]); - - const line = d3.line() - .curve(d3.curveBasis) - .x(d => x(d[0])) - .y(d => yDist(d[1])); - - svg.append("path") - .datum(density) - .attr("class", "distribution") - .attr("fill", "none") - .attr("stroke", "#222222") - .attr("stroke-width", 2) - .attr("stroke-linejoin", "round") - .attr("stroke-linecap", "round") - .attr("d", line) - .attr("stroke-dasharray", function () { return this.getTotalLength(); }) - .attr("stroke-dashoffset", function () { return this.getTotalLength(); }) - .transition() - .duration(animationTime) - .ease(d3.easeCubicInOut) - .attr("stroke-dashoffset", 0); - } - } - - //Funciones que se ejecutan al ocurrir eventos - function onDropdownChange() { - key = dropdown.value; - crearHistogramas(); - } - - function onCheckboxMediaChange() { - crearHistogramas(); - } - - function onCheckboxDistributionChange() { - crearHistogramas(); - } - - function onSliderChange() { - updateAnimationTime(parseInt(animationSlider.value)); - } - - function onBarsSliderChange() { - updateBarsCount(parseInt(barsSlider.value)); - crearHistogramas(); - } - - function updateAnimationTime(newAnimationTime) { - animationTime = newAnimationTime; - animationTimeValue.textContent = animationTime + " ms"; - } - - function updateBarsCount(newBins) { - bins = newBins; - barsCountValue.textContent = bins; - } - - //Funciones para calcular la distribución de probabilidad - function kde(kernel, thresholds, data) { - return thresholds.map(t => [t, d3.mean(data, d => kernel(t - d))]); - } - - function epanechnikov(bandwidth) { - return x => Math.abs(x /= bandwidth) <= 1 ? 0.75 * (1 - x * x) / bandwidth : 0; - } - - //Inicializar documento - async function iniciarWeb() { - /*const availableFiles = []; - for (const fileName of fileNamesList) { - if (await checkFileExists(fileName)) { - availableFiles.push(fileName); - } - } - availableFiles.sort((a, b) => a.split(" ")[0] - b.split(" ")[0] || a.split(" ")[1] - b.split(" ")[1]);*/ - - //Seleccionar ficheros del dataset, para que no se compruebe la existencia de todos - const availableFiles = ["10 5", "10 7", "10 9", "10 10", "10 11", "10 14", "14 7", "14 14", "16 9", "16 16", "22 22", "23 23", "28 28", "32 32", "35 35"]; - availableFiles.forEach(fileName => { - const option = document.createElement("option"); - option.value = fileName; - option.textContent = fileName; - dropdown.appendChild(option); - }); - - key = availableFiles[6]; - dropdown.value = key; - mediaCheckbox.checked = true; - distributionCheckbox.checked = false; - animationSlider.value = 600; - updateAnimationTime(600); - updateBarsCount(20); - barsSlider.value = 10; - crearHistogramas(); - } - iniciarWeb(); - </script> + <script src="script.js"></script> </body> </html> \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..7e5238f --- /dev/null +++ b/script.js @@ -0,0 +1,296 @@ +const datasetFolder = "./dataset/" + const fileNamesList = ['10 1', '10 10', '10 11', '10 12', '10 13', '10 14', '10 15', '10 2', '10 3', '10 4', '10 5', '10 6', '10 7', '10 8', '10 9', '11 1', '11 10', '11 11', '11 12', '11 13', '11 14', '11 15', '11 2', '11 3', '11 4', '11 5', '11 6', '11 7', '11 8', '11 9', '12 1', '12 12', '12 2', '12 3', '12 4', '12 5', '12 6', '13 1', '13 10', '13 11', '13 12', '13 13', '13 14', '13 15', '13 2', '13 3', '13 4', '13 5', '13 6', '13 7', '13 8', '13 9', '14 1', '14 10', '14 11', '14 12', '14 13', '14 14', '14 15', '14 2', '14 3', '14 4', '14 5', '14 6', '14 7', '14 8', '14 9', '15 1', '15 10', '15 11', '15 12', '15 13', '15 14', '15 15', '15 2', '15 3', '15 4', '15 5', '15 6', '15 7', '15 8', '15 9', '16 1', '16 10', '16 11', '16 12', '16 13', '16 14', '16 15', '16 16', '16 2', '16 3', '16 4', '16 5', '16 6', '16 7', '16 8', '16 9', '17 1', '17 10', '17 11', '17 12', '17 13', '17 14', '17 15', '17 16', '17 17', '17 2', '17 3', '17 4', '17 5', '17 6', '17 7', '17 8', '17 9', '18 1', '18 18', '18 2', '18 3', '18 4', '18 5', '18 6', '18 7', '19 1', '19 19', '20 1', '20 20', '21 1', '21 21', '22 1', '22 22', '23 1', '23 23', '24 1', '24 24', '25 1', '25 25', '26 1', '26 26', '27 1', '27 27', '28 1', '28 28', '29 1', '29 29', '3 1', '3 3', '30 1', '30 30', '31 1', '31 31', '32 1', '32 32', '33 1', '33 33', '34 1', '34 34', '35 1', '35 35', '36 1', '36 36', '37 1', '37 37', '38 1', '38 38', '39 1', '39 39', '4 1', '4 4', '40 1', '40 40', '41 1', '41 41', '42 1', '42 42', '43 1', '43 43', '44 1', '44 44', '45 1', '45 45', '46 1', '46 46', '47 1', '47 47', '48 1', '48 48', '49 1', '5 1', '5 10', '5 11', '5 12', '5 13', '5 14', '5 15', '5 2', '5 3', '5 4', '5 5', '5 6', '5 7', '5 8', '5 9', '6 1', '6 10', '6 11', '6 12', '6 13', '6 14', '6 15', '6 2', '6 3', '6 4', '6 5', '6 6', '6 7', '6 8', '6 9', '7 1', '7 10', '7 11', '7 12', '7 13', '7 14', '7 15', '7 2', '7 3', '7 4', '7 5', '7 6', '7 7', '7 8', '7 9', '8 1', '8 10', '8 11', '8 12', '8 13', '8 14', '8 15', '8 2', '8 3', '8 4', '8 5', '8 6', '8 7', '8 8', '8 9', '9 1', '9 10', '9 11', '9 12', '9 13', '9 14', '9 15', '9 2', '9 3', '9 4', '9 5', '9 6', '9 7', '9 8', '9 9']; + const dropdown = document.getElementById("keyDropdown"); + const mediaCheckbox = document.getElementById("mediaCheckbox"); + const distributionCheckbox = document.getElementById("distributionCheckbox"); + const animationSlider = document.getElementById("animationSlider"); + const animationTimeValue = document.getElementById("animationTimeValue"); + const barsSlider = document.getElementById("barsSlider"); + const barsCountValue = document.getElementById("barsCountValue"); + + var key = null; + var animationTime = 600; + var bins = 10; + var currentData = null; + var currentKey = null; + + async function checkFileExists(fileName) { + try { + const response = await fetch(datasetFolder + fileName + ".json", { method: 'HEAD' }); + return response.ok; + } catch (error) { + return false; + } + } + + async function getData(key) { + try { + const response = await fetch(datasetFolder + key + ".json"); + if (!response.ok) { + throw new Error("Error: " + response.status); + } + const data = await response.json(); + return data[key]; + } catch (error) { + console.error('Error:', error); + alert(error); + } + } + + async function crearHistogramas() { + if (currentKey !== key) { + currentData = await getData(key); + currentKey = key; + } + histograma(currentData[0], "div1", bins, "Iteraciones"); + histograma(currentData[1], "div2", bins, "Cantidad elementos"); + } + + function histograma(data, div, binNumber, label) { + const container = document.getElementById(div); + const width = container.clientWidth; + const height = container.clientHeight; + const heightFactor = 1.1; + const axisXOffset = 100; + const axisYOffset = 40; + + const bins = d3.bin() + .thresholds(binNumber) + .value((d) => d)(data); + + const x = d3.scaleLinear() + .domain([bins[0].x0, bins[bins.length - 1].x1]) + .range([axisXOffset, width - axisXOffset]); + + const yMax = d3.max(bins, d => d.length) * heightFactor; + + const y = d3.scaleLinear() + .domain([0, yMax]) + .range([height - axisYOffset, 0]); + + let svg = d3.select("#" + div).select("svg"); + if (svg.empty()) { + svg = d3.select("#" + div) + .append("svg") + .attr("width", width) + .attr("height", height) + .attr("viewBox", [0, 0, width, height]) + .attr("style", "max-width: 100%; height: auto;"); + } + + //Construir las barras + const bars = svg.selectAll("rect") + .data(bins, d => d.x0); + + bars.exit() + .transition() + .duration(0) + .ease(d3.easeCubicInOut) + .attr("y", height - axisYOffset) + .attr("height", 0) + .remove(); + + bars.transition() + .duration(animationTime) + .ease(d3.easeElasticOut.amplitude(2).period(1.5)) + .attr("x", (d) => x(d.x0) + 1) + .attr("width", (d) => Math.max(0, x(d.x1) - x(d.x0) - 3)) + .attr("y", (d) => y(d.length)) + .attr("height", (d) => y(0) - y(d.length)) + .attr("fill", "#055902"); + + bars.enter() + .append("rect") + .attr("x", (d) => x(d.x0) + 1) + .attr("width", (d) => Math.max(0, x(d.x1) - x(d.x0) - 3)) + .attr("y", height - axisYOffset) + .attr("height", 0) + .attr("fill", "#ff7f0e") + .transition() + .delay((d, i) => i * (animationTime / 10)) + .duration(animationTime) + .ease(d3.easeCubicInOut) + .attr("y", (d) => y(d.length)) + .attr("height", (d) => y(0) - y(d.length)) + .attr("fill", "#055902"); + + //Ejes x e y + svg.selectAll(".x-axis").remove(); + svg.append("g") + .attr("class", "x-axis") + .attr("transform", "translate(0," + (height - axisYOffset) + ")") + .call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0)) + .call((g) => g.selectAll("path, line").style("stroke", "#222222")) + .call((g) => g.selectAll("text").style("fill", "#222222")).style("font-size", "0.8em") + .call((g) => g.append("text") + .attr("x", width / 2) + .attr("y", 35) + .attr("fill", "#222222") + .attr("text-anchor", "middle") + .text(label)); + + svg.selectAll(".y-axis").remove(); + svg.append("g") + .attr("class", "y-axis") + .attr("transform", "translate(" + axisXOffset + ",0)") + .call(d3.axisLeft(y).ticks(height / 70)) + .call((g) => g.select(".domain").remove()) + .call((g) => g.selectAll("path, line").style("stroke", "#222222")) + .call((g) => g.selectAll("text").style("fill", "#222222")).style("font-size", "0.8em") + .call((g) => g.append("text") + .attr("x", -height / 2) + .attr("y", -70) + .attr("fill", "#222222") + .attr("text-anchor", "middle") + .attr("transform", "rotate(-90)") + .text("Simulaciones")); + + // Mostrar lÃnea de la media + svg.selectAll(".media") + .attr("y1", 0) + .transition() + .duration(animationTime) + .ease(d3.easeCubicInOut) + .style("opacity", 0) + .attr("y1", height - axisYOffset) + .remove(); + if (mediaCheckbox.checked) { + const average = d3.mean(data); + + svg.append("line") + .attr("class", "media") + .attr("x1", x(average)) + .attr("x2", x(average)) + .attr("y1", height - axisYOffset) + .attr("y2", height - axisYOffset) + .attr("stroke", "#1e90ff") + .attr("stroke-width", 4) + .transition() + .duration(animationTime) + .ease(d3.easeCubicInOut) + .attr("y1", 0); + + svg.append("text") + .attr("class", "media") + .attr("x", x(average) + 5) + .attr("y", 15) + .attr("fill", "#333333") + .style("font-size", "1em") + .style("font-weight", "bold") + .text("Media: " + average.toFixed(2)) + .style("opacity", 0) + .transition() + .duration(animationTime) + .ease(d3.easeCubicInOut) + .style("opacity", 1); + } + + // Mostrar curva de distribución + svg.selectAll(".distribution") + .transition() + .duration(animationTime) + .ease(d3.easeCubicInOut) + .attr("stroke-dashoffset", function () { return this.getTotalLength(); }) + .remove(); + if (distributionCheckbox.checked) { + const density = kde(epanechnikov(10), x.ticks(40), data.filter((_, index) => index % 30 === 0)); + + const yDist = d3.scaleLinear() + .domain([0, d3.max(density, d => d[1]) * heightFactor]) + .range([height - axisYOffset, 0]); + + const line = d3.line() + .curve(d3.curveBasis) + .x(d => x(d[0])) + .y(d => yDist(d[1])); + + svg.append("path") + .datum(density) + .attr("class", "distribution") + .attr("fill", "none") + .attr("stroke", "#222222") + .attr("stroke-width", 2) + .attr("stroke-linejoin", "round") + .attr("stroke-linecap", "round") + .attr("d", line) + .attr("stroke-dasharray", function () { return this.getTotalLength(); }) + .attr("stroke-dashoffset", function () { return this.getTotalLength(); }) + .transition() + .duration(animationTime) + .ease(d3.easeCubicInOut) + .attr("stroke-dashoffset", 0); + } + } + + //Funciones que se ejecutan al ocurrir eventos + function onDropdownChange() { + key = dropdown.value; + crearHistogramas(); + } + + function onCheckboxMediaChange() { + crearHistogramas(); + } + + function onCheckboxDistributionChange() { + crearHistogramas(); + } + + function onSliderChange() { + updateAnimationTime(parseInt(animationSlider.value)); + } + + function onBarsSliderChange() { + updateBarsCount(parseInt(barsSlider.value)); + crearHistogramas(); + } + + function updateAnimationTime(newAnimationTime) { + animationTime = newAnimationTime; + animationTimeValue.textContent = animationTime + " ms"; + } + + function updateBarsCount(newBins) { + bins = newBins; + barsCountValue.textContent = bins; + } + + //Funciones para calcular la distribución de probabilidad + function kde(kernel, thresholds, data) { + return thresholds.map(t => [t, d3.mean(data, d => kernel(t - d))]); + } + + function epanechnikov(bandwidth) { + return x => Math.abs(x /= bandwidth) <= 1 ? 0.75 * (1 - x * x) / bandwidth : 0; + } + + //Inicializar documento + async function iniciarWeb() { + /*const availableFiles = []; + for (const fileName of fileNamesList) { + if (await checkFileExists(fileName)) { + availableFiles.push(fileName); + } + } + availableFiles.sort((a, b) => a.split(" ")[0] - b.split(" ")[0] || a.split(" ")[1] - b.split(" ")[1]);*/ + + //Seleccionar ficheros del dataset, para que no se compruebe la existencia de todos + const availableFiles = ["10 5", "10 7", "10 9", "10 10", "10 11", "10 14", "14 7", "14 14", "16 9", "16 16", "22 22", "23 23", "28 28", "32 32", "35 35"]; + availableFiles.forEach(fileName => { + const option = document.createElement("option"); + option.value = fileName; + option.textContent = fileName; + dropdown.appendChild(option); + }); + + key = availableFiles[6]; + dropdown.value = key; + mediaCheckbox.checked = true; + distributionCheckbox.checked = false; + animationSlider.value = 600; + updateAnimationTime(600); + updateBarsCount(20); + barsSlider.value = 10; + crearHistogramas(); + } + iniciarWeb(); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..fcd0a48 --- /dev/null +++ b/styles.css @@ -0,0 +1,95 @@ +body { + font-family: Arial, sans-serif; + justify-content: center; + align-items: center; + height: 100%; + margin: 0; + background-color: #fff; + } + + .div-histograma { + border: 0em solid #222222; + width: 40em; + height: 20em; + text-align: center; + margin: 1em; + padding: 2em; + color: #f9f9f9; + font-weight: bold; + border-radius: 0em; + box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); + transition: transform 0.3s; + } + + .div-histograma:hover { + transform: scale(1.05); + } + + #div1 { + background-color: #fff; + } + + #div2 { + background-color: #fff; + } + + #dropdownDiv { + display: flex; + flex-direction: column; + align-items: flex-start; + margin: 1em; + padding: 1em; + border: 0em solid #222222; + border-radius: 0em; + box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); + background-color: #f9f9f9; + transition: transform 0.3s; + } + + #keyDropdown { + margin-top: 0.5em; + padding: 0.5em; + border-radius: 0em; + border: 0em solid #ccc; + font-size: 1em; + transition: transform 0.3s; + } + + #opcinesDiv1 { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + margin: 1em; + padding: 0em; + } + + #sliderAnimationContainer, + #sliderBarrasContainer, + #checkboxContainer, + #distributionCheckboxContainer { + width: max-content; + height: fit-content; + display: block; + margin: 1em 1em 1em 1em; + padding: 1em; + border: 0em solid #222222; + border-radius: 0em; + box-shadow: 0 1em 3em rgba(22, 22, 22, 0.6); + background-color: #f9f9f9; + transition: transform 0.3s; + } + + #dropdownDiv:hover, + #keyDropdown:hover, + #checkboxContainer:hover, + #sliderAnimationContainer:hover, + #sliderBarrasContainer:hover, + #distributionCheckboxContainer:hover { + transform: scale(1.05); + } + + label { + font-weight: bold; + margin-bottom: 0.5em; + } \ No newline at end of file -- GitLab