|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<title>ARC‐Converted Dataset Visualizer (Upload Local Folder)</title> |
|
<style> |
|
body { |
|
font-family: sans-serif; |
|
margin: 16px; |
|
} |
|
.selector-area { |
|
margin-bottom: 1rem; |
|
} |
|
.grid-canvas { |
|
margin: 4px; |
|
border: 1px solid #ccc; |
|
} |
|
.example-container { |
|
display: inline-block; |
|
margin: 0 16px 16px 0; |
|
vertical-align: top; |
|
} |
|
.puzzle-display { |
|
margin-top: 1rem; |
|
} |
|
.puzzle-id { |
|
font-weight: bold; |
|
margin-bottom: 0.5rem; |
|
} |
|
#groupList, #puzzleList { |
|
margin: 1rem 0; |
|
} |
|
.group-item, .puzzle-item { |
|
cursor: pointer; |
|
margin: 4px 8px 4px 0; |
|
padding: 2px 6px; |
|
border: 1px solid #aaa; |
|
display: inline-block; |
|
} |
|
.group-item:hover, .puzzle-item:hover { |
|
background: #eef; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>ARC‐Converted Dataset Visualizer (Local Directory)</h1> |
|
|
|
<div class="selector-area"> |
|
|
|
<label>Upload ARC Folder:</label> |
|
<input type="file" id="folderInput" |
|
webkitdirectory mozdirectory multiple |
|
onchange="onFolderSelected(event)" /> |
|
<br><br> |
|
|
|
|
|
<label>Set:</label> |
|
<select id="setSelect" disabled> |
|
<option value="train">train</option> |
|
<option value="test">test</option> |
|
</select> |
|
|
|
<label> Subset:</label> |
|
<select id="subsetSelect" disabled> |
|
<option value="all">all</option> |
|
</select> |
|
|
|
<button id="loadBtn" disabled>Load</button> |
|
</div> |
|
|
|
<div> |
|
<div id="groupList"></div> |
|
<div id="puzzleList"></div> |
|
<div class="puzzle-display" id="puzzleView"></div> |
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
<script src="assets/npyjs.js"></script> |
|
|
|
<script> |
|
|
|
|
|
|
|
|
|
|
|
let filesByPath = {}; |
|
|
|
|
|
let inputsArr, labelsArr; |
|
let puzzleIndicesArr, groupIndicesArr, puzzleIdentifiersArr; |
|
let identifiersJson; |
|
|
|
|
|
let seqLen = 0; |
|
let gridSize = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function onFolderSelected(event) { |
|
filesByPath = {}; |
|
const fileList = event.target.files; |
|
if (!fileList || fileList.length === 0) { |
|
alert("No files selected!"); |
|
return; |
|
} |
|
|
|
|
|
const paths = []; |
|
for (let i = 0; i < fileList.length; i++) { |
|
|
|
const file = fileList[i]; |
|
const relPath = file.webkitRelativePath || file.mozRelativePath || file.name; |
|
paths.push(relPath); |
|
} |
|
|
|
|
|
const idPath = paths.find(p => p.endsWith("identifiers.json")); |
|
if (!idPath) { |
|
alert("Error: No 'identifiers.json' found in the uploaded folder."); |
|
return; |
|
} |
|
|
|
|
|
|
|
|
|
let topDir = ""; |
|
const lastSlash = idPath.lastIndexOf("/"); |
|
if (lastSlash >= 0) { |
|
topDir = idPath.substring(0, lastSlash); |
|
} |
|
|
|
|
|
|
|
|
|
for (let i = 0; i < fileList.length; i++) { |
|
const file = fileList[i]; |
|
let relPath = file.webkitRelativePath || file.mozRelativePath || file.name; |
|
|
|
if (topDir && relPath.startsWith(topDir + "/")) { |
|
relPath = relPath.substring(topDir.length + 1); |
|
} |
|
filesByPath[relPath] = file; |
|
} |
|
|
|
|
|
document.getElementById("setSelect").disabled = false; |
|
document.getElementById("subsetSelect").disabled = false; |
|
document.getElementById("loadBtn").disabled = false; |
|
} |
|
|
|
|
|
document.getElementById("loadBtn").addEventListener("click", async () => { |
|
document.getElementById("groupList").innerHTML = ""; |
|
document.getElementById("puzzleList").innerHTML = ""; |
|
document.getElementById("puzzleView").innerHTML = ""; |
|
|
|
const setName = document.getElementById("setSelect").value; |
|
const subsetName = document.getElementById("subsetSelect").value; |
|
|
|
try { |
|
await loadDataset(setName, subsetName); |
|
buildGroupList(); |
|
} catch (err) { |
|
console.error(err); |
|
alert("Error while loading dataset: " + err); |
|
} |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
async function loadDataset(setName, subsetName) { |
|
const prefix = `${setName}/${subsetName}__`; |
|
|
|
const inputsPath = prefix + "inputs.npy"; |
|
const labelsPath = prefix + "labels.npy"; |
|
const pIdxPath = prefix + "puzzle_indices.npy"; |
|
const gIdxPath = prefix + "group_indices.npy"; |
|
const pIdsPath = prefix + "puzzle_identifiers.npy"; |
|
const identifiersPath = "identifiers.json"; |
|
|
|
|
|
const needed = [inputsPath, labelsPath, pIdxPath, gIdxPath, pIdsPath, identifiersPath]; |
|
for (const f of needed) { |
|
if (!filesByPath[f]) { |
|
throw new Error(`Missing file: ${f}`); |
|
} |
|
} |
|
|
|
|
|
const inputsNpy = await parseNpy(filesByPath[inputsPath]); |
|
const labelsNpy = await parseNpy(filesByPath[labelsPath]); |
|
const puzzleIndicesNpy= await parseNpy(filesByPath[pIdxPath]); |
|
const groupIndicesNpy = await parseNpy(filesByPath[gIdxPath]); |
|
const puzzleIdsNpy = await parseNpy(filesByPath[pIdsPath]); |
|
|
|
inputsArr = inputsNpy.data; |
|
labelsArr = labelsNpy.data; |
|
puzzleIndicesArr = puzzleIndicesNpy.data; |
|
groupIndicesArr = groupIndicesNpy.data; |
|
puzzleIdentifiersArr = puzzleIdsNpy.data; |
|
|
|
|
|
seqLen = inputsNpy.shape[1]; |
|
gridSize = Math.sqrt(seqLen); |
|
|
|
|
|
identifiersJson = await readJsonFile(filesByPath[identifiersPath]); |
|
} |
|
|
|
|
|
|
|
|
|
function parseNpy(file) { |
|
return new Promise((resolve, reject) => { |
|
const reader = new FileReader(); |
|
reader.onload = async () => { |
|
try { |
|
const arrayBuffer = reader.result; |
|
const npy = new npyjs(); |
|
resolve(await npy.parse(arrayBuffer)); |
|
} catch (err) { |
|
reject(err); |
|
} |
|
}; |
|
reader.onerror = err => reject(err); |
|
reader.readAsArrayBuffer(file); |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
function readJsonFile(file) { |
|
return new Promise((resolve, reject) => { |
|
const reader = new FileReader(); |
|
reader.onload = () => { |
|
try { |
|
const obj = JSON.parse(reader.result); |
|
resolve(obj); |
|
} catch (err) { |
|
reject(err); |
|
} |
|
}; |
|
reader.onerror = (err) => reject(err); |
|
reader.readAsText(file); |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
function buildGroupList() { |
|
document.getElementById("groupList").innerHTML = "<h3>Groups</h3>"; |
|
const groupListDiv = document.getElementById("groupList"); |
|
|
|
const nGroups = groupIndicesArr.length - 1; |
|
for (let g = 0; g < nGroups; g++) { |
|
const div = document.createElement("span"); |
|
div.className = "group-item"; |
|
div.textContent = `Group ${g}`; |
|
div.onclick = () => onSelectGroup(g); |
|
groupListDiv.appendChild(div); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
function onSelectGroup(groupIndex) { |
|
document.getElementById("puzzleList").innerHTML = ""; |
|
document.getElementById("puzzleView").innerHTML = ""; |
|
|
|
const puzzleListDiv = document.getElementById("puzzleList"); |
|
puzzleListDiv.innerHTML = `<h4>Puzzles in Group ${groupIndex}</h4>`; |
|
|
|
const firstPuzzle = groupIndicesArr[groupIndex]; |
|
const lastPuzzle = groupIndicesArr[groupIndex + 1]; |
|
|
|
for (let p = firstPuzzle; p < lastPuzzle; p++) { |
|
const puzzleIntId = puzzleIdentifiersArr[p]; |
|
const puzzleStrId = (puzzleIntId < identifiersJson.length) |
|
? identifiersJson[puzzleIntId] |
|
: "<unknown>"; |
|
|
|
const div = document.createElement("span"); |
|
div.className = "puzzle-item"; |
|
div.textContent = `Puzzle #${p} [ID=${puzzleIntId}: ${puzzleStrId}]`; |
|
div.onclick = () => onSelectPuzzle(p); |
|
puzzleListDiv.appendChild(div); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
function onSelectPuzzle(puzzleIndex) { |
|
const puzzleView = document.getElementById("puzzleView"); |
|
puzzleView.innerHTML = ""; |
|
|
|
|
|
const puzzleIntId = puzzleIdentifiersArr[puzzleIndex]; |
|
const puzzleStrId = (puzzleIntId < identifiersJson.length) |
|
? identifiersJson[puzzleIntId] |
|
: "<unknown>"; |
|
|
|
const titleDiv = document.createElement("div"); |
|
titleDiv.className = "puzzle-id"; |
|
titleDiv.textContent = `Puzzle #${puzzleIndex} — ID: ${puzzleStrId}`; |
|
puzzleView.appendChild(titleDiv); |
|
|
|
|
|
const firstExample = puzzleIndicesArr[puzzleIndex]; |
|
const lastExample = puzzleIndicesArr[puzzleIndex + 1]; |
|
|
|
for (let e = firstExample; e < lastExample; e++) { |
|
const inputSeq = slice1D(inputsArr, e*seqLen, (e+1)*seqLen); |
|
const outputSeq = slice1D(labelsArr, e*seqLen, (e+1)*seqLen); |
|
|
|
const inputGrid = decodeGrid(inputSeq); |
|
const outputGrid = decodeGrid(outputSeq); |
|
|
|
const exDiv = document.createElement("div"); |
|
exDiv.className = "example-container"; |
|
exDiv.appendChild(document.createTextNode(`Example ${e}`)); |
|
exDiv.appendChild(document.createElement("br")); |
|
|
|
exDiv.appendChild(renderGrid(inputGrid)); |
|
exDiv.appendChild(renderGrid(outputGrid)); |
|
|
|
puzzleView.appendChild(exDiv); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
function slice1D(arr, start, end) { |
|
const result = new Uint32Array(end - start); |
|
for (let i = start; i < end; i++) { |
|
result[i - start] = Number(arr[i]); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
function decodeGrid(seq) { |
|
const grid = []; |
|
let idx = 0; |
|
for (let r = 0; r < gridSize; r++) { |
|
const row = []; |
|
for (let c = 0; c < gridSize; c++) { |
|
row.push(seq[idx]); |
|
idx++; |
|
} |
|
grid.push(row); |
|
} |
|
return grid; |
|
} |
|
|
|
|
|
|
|
|
|
function renderGrid(grid2d) { |
|
const rows = grid2d.length; |
|
const cols = grid2d[0].length; |
|
const scale = 10; |
|
|
|
const canvas = document.createElement("canvas"); |
|
canvas.width = cols * scale; |
|
canvas.height = rows * scale; |
|
canvas.className = "grid-canvas"; |
|
const ctx = canvas.getContext("2d"); |
|
|
|
for (let r = 0; r < rows; r++) { |
|
for (let c = 0; c < cols; c++) { |
|
const val = grid2d[r][c]; |
|
ctx.fillStyle = indexToColor(val); |
|
ctx.fillRect(c * scale, r * scale, scale, scale); |
|
} |
|
} |
|
return canvas; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function indexToColor(value) { |
|
if (value === 0) return "#FFFFFF"; |
|
if (value === 1) return "#DDDDDD"; |
|
|
|
|
|
const colorIdx = value - 2; |
|
const palette = [ |
|
"#000000", |
|
"#FF0000", |
|
"#00FF00", |
|
"#0000FF", |
|
"#FFFF00", |
|
"#FFA500", |
|
"#800080", |
|
"#00FFFF", |
|
"#FFC0CB", |
|
"#808080" |
|
]; |
|
if (colorIdx >= 0 && colorIdx < palette.length) { |
|
return palette[colorIdx]; |
|
} |
|
return "#FFFFFF"; |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
|