Caps-Server/Public/grid.js

274 lines
7.7 KiB
JavaScript
Raw Normal View History

const capImageSize = 50;
const halfCapImageSize = capImageSize / 2;
const numberOfCaps = 1875;
const numberOfColumns = 40;
const numberOfRows = Math.ceil(numberOfCaps / numberOfColumns);
const effectiveHeight = Math.ceil(capImageSize*Math.cos(Math.PI/6));
function sourcePath(capId) {
return "thumbnails/" + `${capId}`.padStart(4, '0') + '.jpg'
}
function createImage(position) {
return '<img src="' + sourcePath(position) + '" loading="lazy"/>'
};
function setCapImageAtPosition(capId, position) {
elements[position].src = sourcePath(capId);
capPositions[position] = capId;
}
function getCapIdAtPosition(position) {
return capPositions[position];
}
2022-12-16 21:35:26 +01:00
const imageCanvas = document.getElementById("image-canvas");
var capPositions = Array.from(
{length: numberOfCaps},
(_, index) => index + 1
);
imageCanvas.innerHTML += capPositions.map(createImage).join("");
var selectedPosition = null;
const elements = imageCanvas.getElementsByTagName("img");
2022-12-16 21:35:26 +01:00
const selectedCapCircle = document.getElementById("selected-cap-circle");
imageCanvas.addEventListener("click", function(event) {
2022-12-20 00:44:11 +01:00
const yOffset = 50;
const xPosition = event.clientX - imageCanvas.getBoundingClientRect().left;
2022-12-20 00:56:56 +01:00
const yPosition = event.clientY - imageCanvas.getBoundingClientRect().top - yOffset;
var row = Math.floor(yPosition / effectiveHeight);
var rowIsEven = (row % 2 == 0)
const adjustedX = rowIsEven ? xPosition - halfCapImageSize : xPosition;
var column = Math.floor(adjustedX / capImageSize);
const rowRemainder = yPosition % effectiveHeight - halfCapImageSize;
const columnRemainder = adjustedX % capImageSize - halfCapImageSize;
const distanceToCenter = Math.sqrt(rowRemainder**2 + columnRemainder**2);
if (distanceToCenter > halfCapImageSize && rowRemainder < 6 - halfCapImageSize) {
// Move to the top left cap
row -= 1;
if (!rowIsEven && (columnRemainder < 0)) {
// Move to the top right cap
column -= 1;
}
if (rowIsEven && (columnRemainder > 0)) {
// Move to the top left cap
column += 1;
}
rowIsEven = !rowIsEven
}
if (row < 0 || row >= numberOfRows) {
return;
}
if (column < 0 || column >= numberOfColumns) {
return;
}
const currentPosition = row * numberOfColumns + column;
if (selectedPosition === null) {
selectedPosition = currentPosition;
const circlePositionX = rowIsEven ? column * capImageSize + halfCapImageSize : column * capImageSize;
2022-12-20 00:56:56 +01:00
const circlePositionY = row * effectiveHeight + yOffset;
selectedCapCircle.style.left = circlePositionX + "px";
selectedCapCircle.style.top = circlePositionY + "px";
selectedCapCircle.style.display = "block";
return;
}
selectedCapCircle.style.display = "none";
if (currentPosition === selectedPosition) {
2022-12-16 21:26:03 +01:00
selectedPosition = null;
return;
}
// Switch cap images
const currentCapId = getCapIdAtPosition(currentPosition);
const selectedCapId = getCapIdAtPosition(selectedPosition);
setCapImageAtPosition(selectedCapId, currentPosition);
setCapImageAtPosition(currentCapId, selectedPosition);
selectedPosition = null;
});
2022-12-20 00:44:11 +01:00
function averageColor(imageElement) {
var canvas = document.createElement('canvas');
var context = canvas.getContext && canvas.getContext('2d');
if (!context) {
return { r: 255, g: 0, b: 0 };
}
// Set the height and width equal
// to that of the canvas and the image
var height = canvas.height =
imageElement.naturalHeight ||
imageElement.offsetHeight ||
imageElement.height;
var width = canvas.width =
imageElement.naturalWidth ||
imageElement.offsetWidth ||
imageElement.width;
// Draw the image to the canvas
context.drawImage(imageElement, 0, 0);
// Get the data of the image
try {
var imgData = context.getImageData(0, 0, width, height);
} catch(e) {
/* security error, img on diff domain */alert('x');
return { r: 255, g: 0, b: 0 };
}
// Get the length of image data object
var length = imgData.data.length;
const pixelCount = height * width;
const halfX = width / 2;
const halfY = height / 2;
var rgb = { r: 0, g: 0, b: 0 };
var count = 0;
for (var i = 0; i < pixelCount; i += 1) {
// Exclude everything outside of circle
const x = i % width - halfX;
const y = Math.floor(i / width) - halfY;
const distanceToCenter = Math.sqrt(x**2 + y**2);
if (distanceToCenter > halfY) {
continue;
}
// Sum all values of the colour
const offset = 4 * i;
rgb.r += imgData.data[offset];
rgb.g += imgData.data[offset + 1];
rgb.b += imgData.data[offset + 2];
count += 1;
}
// Find the averages
rgb.r = Math.floor(rgb.r / count);
rgb.g = Math.floor(rgb.g / count);
rgb.b = Math.floor(rgb.b / count);
return rgb;
}
for (let i = 0; i < capPositions.length; i += 1) {
const element = elements[i];
element.onload = function() {
const rgb = averageColor(element);
const color = `rgb(${rgb.r},${rgb.g},${rgb.b})`;
element.style.backgroundColor = color;
}
}
var showColors = false;
function toggleColors() {
showColors = !showColors;
const padding = showColors ? "50px" : "0px";
for (let i = 0; i < capPositions.length; i += 1) {
elements[i].style.paddingLeft = padding;
}
}
function exportCapArrangement() {
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([JSON.stringify(capPositions)], {
type: "application/json"
}));
a.setAttribute("download", "caps.json");
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(a.href)
}
function resetCaps() {
// Set each cap in the data
for (let i = 0; i < capPositions.length; i++) {
setCapImageAtPosition(i+1, i);
}
}
function isInt(value) {
if (isNaN(value)) {
return false;
}
var x = parseFloat(value);
return (x | 0) === x;
}
function decodeCapFile(content) {
var caps = JSON.parse(content);
if (!Array.isArray(caps)) {
alert("Invalid file (not a JSON array)");
return;
}
const capCount = capPositions.length;
if (caps.length > capPositions.length) {
alert("Invalid file (Too many entries)");
return;
}
for (let i = 0; i < caps.length; i++) {
const cap = caps[i];
if (!Number.isInteger(cap)) {
alert("Invalid file (Not all integers)");
return;
}
if (cap < 1 || cap > capCount) {
alert("Invalid file (Not all valid cap counts)");
return;
}
// TODO: Check if each id is used once
}
// Set each cap in the data
for (let i = 0; i < caps.length; i++) {
const cap = caps[i];
setCapImageAtPosition(cap, i);
}
// Reset the remaining caps which may be missing from the loaded file
// since they are newer
for (let i = caps.length; i < capCount; i++) {
setCapImageAtPosition(i+1, i);
}
console.log("Cap image loaded")
}
function loadCapArrangementFile(e) {
console.log("File loaded")
var file = e.target.files[0];
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
document.getElementById('caps-upload').value = null;
decodeCapFile(contents);
};
reader.readAsText(file);
}
// Register when user selects a file
document.getElementById('caps-upload')
.addEventListener('change', loadCapArrangementFile, false);