diff --git a/data_processing.js b/data_processing.js index e88a897..972f38b 100644 --- a/data_processing.js +++ b/data_processing.js @@ -311,11 +311,10 @@ async function loadDataAndDetermineParams(normalizedCores, params) { } function saveUpdatedCores(format) { - if (!window.sortedCoresData) { + if (!window.finalSaveData) { alert("No data available to save."); return; } - // Save data as JSON or CSV if (format === "json") { diff --git a/drawCanvas.js b/drawCanvas.js index 964f56c..c603d8a 100644 --- a/drawCanvas.js +++ b/drawCanvas.js @@ -1695,7 +1695,7 @@ async function createVirtualGrid( startingY ); - await drawVirtualGridFromWSI(imageSrc, sortedCoresData, 12); + await drawVirtualGridFromWSI(imageSrc, sortedCoresData, 64); } else { updateGridSpacingInVirtualGridForSVS( horizontalSpacing, @@ -1719,31 +1719,6 @@ async function createVirtualGrid( } } -// async function createImageForCore(svsImageURL, core, coreSize = 64) { -// const coreWidth = core.currentRadius * 2; -// const coreHeight = core.currentRadius * 2; - -// const tileParams = { -// tileX: core.x - core.currentRadius, -// tileY: core.y - core.currentRadius, -// tileWidth: coreWidth, -// tileHeight: coreHeight, -// tileSize: 128, -// }; - -// const imageResp = await getRegionFromWSI(svsImageURL, tileParams, 1); -// const blob = await imageResp.blob(); -// const img = new Image(coreSize, coreSize); -// return new Promise((resolve, reject) => { -// img.onload = function () { -// URL.revokeObjectURL(img.src); // Free memory -// resolve(img); -// }; -// img.onerror = reject; -// img.src = URL.createObjectURL(blob); -// }); -// } - // Move the initiateDownload function outside of createImageForCore async function initiateDownload( svsImageURL, @@ -1774,11 +1749,9 @@ async function initiateDownload( document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); - debugger + debugger; } - - // Create an array to store all the core containers const coreContainers = []; @@ -1807,8 +1780,6 @@ function populateAndEditMetadataForm(rowValue, colValue) { // Get the form element const form = document.getElementById("editMetadataForm"); - const virtualGrid = document.getElementById("VirtualGrid"); - // Clear existing form contents form.innerHTML = ""; form.className = "space-y-4"; @@ -1816,45 +1787,74 @@ function populateAndEditMetadataForm(rowValue, colValue) { // Dynamically create form elements for each metadata property for (const key in metadataObj) { const value = metadataObj[key]; - + // Determine input type based on the value type let inputType = "text"; // Default input type if (typeof value === "number") { inputType = "number"; } else if (typeof value === "boolean") { inputType = "checkbox"; - } // For more specific cases, like radio buttons, additional logic would be needed - - // Create a div wrapper for styling - const div = document.createElement("div"); - div.className = "flex flex-col justify-start"; - - // Create a label for the input - const label = document.createElement("label"); - label.setAttribute("for", key); - label.textContent = key + ": "; - label.className = "mb-2 text-sm font-medium text-gray-900"; - div.appendChild(label); - - // Create an input element - const input = document.createElement("input"); - input.type = inputType; - input.name = key; - input.id = key; - input.className = - "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"; - + } + if (inputType === "checkbox") { + // Create the checkbox container div + const checkboxContainer = document.createElement("div"); + checkboxContainer.className = "custom-checkbox"; + + // Create the hidden checkbox input + const input = document.createElement("input"); + input.type = "checkbox"; + input.id = key; + input.name = key; input.checked = value; + + // Create the label element for the checkbox + const label = document.createElement("label"); + label.setAttribute("for", key); + label.className = "custom-checkbox-label"; + label.textContent = `${key}: `; + + // Create the custom checkmark span + const checkmark = document.createElement("span"); + checkmark.className = "checkmark"; + + // Append the hidden checkbox and checkmark to the checkbox container + checkboxContainer.appendChild(input); + checkboxContainer.appendChild(checkmark); + + // Append the checkbox container to the label + label.appendChild(checkboxContainer); + + // Append the label to the form + form.appendChild(label); } else { + // Create a label for non-checkbox inputs + const label = document.createElement("label"); + label.setAttribute("for", key); + label.textContent = key + ": "; + label.className = "mb-2 text-sm font-medium text-gray-900"; + + // Create the text or number input + const input = document.createElement("input"); + input.type = inputType; + input.name = key; + input.id = key; input.value = value; + input.className = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"; + + // Create a wrapper div for non-checkbox inputs + const inputDiv = document.createElement("div"); + inputDiv.className = "flex flex-col mb-4"; + + // Append the label and input to the wrapper div + inputDiv.appendChild(label); + inputDiv.appendChild(input); + + // Append the wrapper div to the form + form.appendChild(inputDiv); } - - div.appendChild(input); - - form.appendChild(div); } - + // Create a submit button const submitButton = document.createElement("input"); submitButton.type = "submit"; @@ -1900,7 +1900,7 @@ async function createImageForCore(svsImageURL, core, coreSize = 64) { tileY: core.y - core.currentRadius, tileWidth: coreWidth, tileHeight: coreHeight, - tileSize: 128, + tileSize: coreSize, }; const imageResp = await getRegionFromWSI(svsImageURL, tileParams, 1); @@ -1922,15 +1922,23 @@ async function createImageForCore(svsImageURL, core, coreSize = 64) { // Double-click event for initiating download container.ondblclick = () => { - const fileName = `core_${core.row + 1}_${core.col + 1}.jpg`; // Construct file name + const fileName = `core_${core.row + 1}_${core.col + 1}.png`; // Construct file name initiateDownload(svsImageURL, core, coreWidth, coreHeight, fileName); }; container.onclick = () => { // Select the core - core.isSelected = true; populateAndEditMetadataForm(core.row + 1, core.col + 1); + + // Remove the active class from all cores + coreContainers.forEach((container) => { + container.classList.remove("active"); + }); + + // Add the active class to the selected core + container.classList.add("active"); + }; // Append children to the container diff --git a/index2.html b/index2.html index 6a824fe..07e3ea5 100644 --- a/index2.html +++ b/index2.html @@ -479,16 +479,6 @@

Configure Parameters

-
- - -
- - @@ -623,6 +613,15 @@

Edit Metadata

+ +
+ + +
diff --git a/main2.js b/main2.js index 3972c88..45406f6 100644 --- a/main2.js +++ b/main2.js @@ -183,7 +183,6 @@ const handleImageLoad = (file, processCallback) => { window.uploadedImageFileType = "simple"; } else if (file && file.name.endsWith(".svs")) { - updateImagePreview( originalImageContainer.src, originalImageContainer.width, @@ -1174,57 +1173,67 @@ document.querySelectorAll("input[type='number']").forEach((e) => { }); async function downloadAllCores(cores) { - const svsImageURL = document.getElementById("imageUrlInput").value - ? document.getElementById("imageUrlInput").value - : document.getElementById("fileInput").files.length > 0 - ? document.getElementById("fileInput").files[0] - : window.boxFileInfo - ? URL.createObjectURL(window.boxFile) - : "path/to/default/image.jpg"; - - const JSZip = window.JSZip || require('jszip'); + ? document.getElementById("imageUrlInput").value + : document.getElementById("fileInput").files.length > 0 + ? document.getElementById("fileInput").files[0] + : window.boxFileInfo + ? URL.createObjectURL(window.boxFile) + : "path/to/default/image.jpg"; + + const JSZip = window.JSZip || require("jszip"); const zip = new JSZip(); // Show progress overlay - const overlay = document.getElementById('progressOverlay'); - const progressBar = document.getElementById('progressBar'); - const progressText = document.getElementById('progressText'); - overlay.style.display = 'flex'; - progressBar.style.width = '0%'; - progressText.innerText = 'Starting download...'; - - await Promise.all(cores.map(async (core, index) => { - const fullResTileParams = { - tileX: core.x - core.currentRadius, - tileY: core.y - core.currentRadius, - tileWidth: core.currentRadius * 2, - tileHeight: core.currentRadius * 2, - tileSize: core.currentRadius * 2, - }; + const overlay = document.getElementById("progressOverlay"); + const progressBar = document.getElementById("progressBar"); + const progressText = document.getElementById("progressText"); + overlay.style.display = "flex"; + progressBar.style.width = "0%"; + progressText.innerText = "Starting download..."; + + await Promise.all( + cores.map(async (core, index) => { + const fullResTileParams = { + tileX: core.x - core.currentRadius, + tileY: core.y - core.currentRadius, + tileWidth: core.currentRadius * 2, + tileHeight: core.currentRadius * 2, + tileSize: core.currentRadius * 2, + }; - try { - const fullSizeImageResp = await getRegionFromWSI(svsImageURL, fullResTileParams); - const blob = await fullSizeImageResp.blob(); - // Log the size of each blob - console.log(`Blob ${index + 1} size: ${blob.size} bytes`); + try { + const fullSizeImageResp = await getRegionFromWSI( + svsImageURL, + fullResTileParams + ); + const blob = await fullSizeImageResp.blob(); + // Log the size of each blob + console.log(`Blob ${index + 1} size: ${blob.size} bytes`); - zip.file(`$Core_${core.row}_${core.col}.png`, blob); + zip.file(`core_${core.row}_${core.col}.png`, blob); - // Update progress - const progress = ((index + 1) / cores.length) * 100; - progressBar.style.width = `${progress}%`; - progressText.innerText = `Downloading... ${progress.toFixed(2)}%`; - } catch (error) { - console.error("Error fetching or adding an image to the zip:", error); - } - })); + // Update progress + const progress = ((index + 1) / cores.length) * 100; + progressBar.style.width = `${progress}%`; + progressText.innerText = `Downloading... ${progress.toFixed(2)}%`; + } catch (error) { + console.error("Error fetching or adding an image to the zip:", error); + } + }) + ); // Generate the zip file - zip.generateAsync({type:"blob"}) - .then(function(content) { + + zip + .generateAsync({ + type: "blob", + compression: "DEFLATE", + compressionOptions: { level: 9 }, // Highest compression + }) + .then(function (content) { // Use a temporary link to download the zip file - const downloadLink = document.createElement('a'); + const downloadLink = document.createElement("a"); downloadLink.href = URL.createObjectURL(content); downloadLink.download = "cores.zip"; document.body.appendChild(downloadLink); @@ -1232,9 +1241,9 @@ async function downloadAllCores(cores) { document.body.removeChild(downloadLink); // Hide progress overlay and reset progress bar - overlay.style.display = 'none'; - progressBar.style.width = '0%'; - progressText.innerText = 'Initializing...'; + overlay.style.display = "none"; + progressBar.style.width = "0%"; + progressText.innerText = "Initializing..."; }); } diff --git a/style2.css b/style2.css index cc475ef..8e3261b 100644 --- a/style2.css +++ b/style2.css @@ -952,6 +952,15 @@ svg { overflow: hidden; } +.image-container.active { + border: 2px solid var(--primary-color); /* Highlight border for active container */ + box-shadow: 0 0 8px rgba(0, 123, 255, 0.5); /* Optional: add a shadow for better visibility */ + background-color: #f0f8ff; /* Optional: slight background color change */ + transform: scale(1.05); + overflow: visible; + +} + .image-container:hover { transform: scale(1.05); z-index: 10; @@ -970,8 +979,24 @@ svg { justify-content: center; align-items: center; transition: opacity 0.3s; + font-size: 0.75rem; + white-space: nowrap; } +/* Adjust font size of image-overlay based on screen size */ +@media (max-width: 1024px) { + .image-overlay { + font-size: 0.65rem; + } +} + +@media (max-width: 576px) { + .image-overlay { + font-size: 0.45rem; + } +} + + .image-container:hover .image-overlay { display: flex; opacity: 1; @@ -1190,3 +1215,177 @@ svg { color: #555; font-size: 16px; } + +/* Base styles for the form */ +#editMetadataForm { + background-color: #f7f7f7; + padding: 20px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +/* Styling for div containers */ +#editMetadataForm div { + margin-bottom: 15px; +} + +/* Label styling */ +#editMetadataForm label { + display: block; + margin-bottom: 5px; + color: #333; + font-size: 16px; +} + +/* General input styling */ +#editMetadataForm input[type="text"], +#editMetadataForm input[type="number"], +#editMetadataForm input[type="checkbox"] { + width: 100%; + padding: 10px; + margin-top: 5px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; /* Ensures padding doesn't affect overall width */ +} + +/* Checkbox customization */ +#editMetadataForm input[type="checkbox"] { + width: auto; + margin-top: 0; +} + +/* Styling for the submit button */ +#editMetadataForm input[type="submit"] { + width: auto; + cursor: pointer; + border: none; + color: #fff; + background-color: #007bff; + padding: 10px 20px; + border-radius: 4px; + transition: background-color 0.3s ease; +} + +#editMetadataForm input[type="submit"]:hover, +#editMetadataForm input[type="submit"]:focus { + background-color: #0056b3; +} + +/* Additional responsiveness and interaction */ +@media (max-width: 768px) { + #editMetadataForm { + padding: 15px; + } +} + +:focus-visible { + outline: 2px solid #007bff; +} + +/* Custom checkbox styles */ +.custom-checkbox { + display: inline-block; + position: relative; + cursor: pointer; + user-select: none; +} + +/* Hide the default checkbox */ +.custom-checkbox input[type="checkbox"] { + visibility: hidden; + position: absolute; + left: 0; +} + +/* Create a custom box */ +.custom-checkbox .checkmark { + position: absolute; + top: 0; + left: 0; + height: 16px; + width: 16px; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 2px; +} + +/* On mouse-over, add a grey background color */ +.custom-checkbox:hover input ~ .checkmark { + background-color: #ccc; +} + +/* When the checkbox is checked */ +.custom-checkbox input:checked ~ .checkmark { + background-color: #2196F3; +} + +/* Create the checkmark/indicator (hidden when not checked) */ +.custom-checkbox .checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the checkmark when checked */ +.custom-checkbox input:checked ~ .checkmark:after { + display: block; +} + +/* Style the checkmark/indicator */ +.custom-checkbox .checkmark:after { + left: 5px; + top: 1px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 3px 3px 0; + transform: rotate(45deg); +} +/* Base styles for custom checkbox labels */ +.custom-checkbox-label { + display: block; /* Ensures that the label takes up the full width */ + position: relative; + margin-bottom: 12px; /* Space between each checkbox */ + cursor: pointer; + font-size: 16px; /* Set the font size for labels */ + user-select: none; /* Prevents text selection */ + line-height: 24px; /* Aligns the text vertically */ +} + +/* Hidden default checkbox */ +.custom-checkbox-label input[type="checkbox"] { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +/* Custom checkmark box */ +.custom-checkbox-label .checkmark { + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} + +/* Checkmark appearance on checked */ +.custom-checkbox-label input:checked ~ .checkmark:after { + display: block; +} + +/* Checkmark style */ +.custom-checkbox-label .checkmark:after { + left: 6px; + top: 2px; + width: 6px; + height: 11px; + border: solid white; + border-width: 0 3px 3px 0; + transform: rotate(45deg); +}