Spaces:
Running
Running
<script lang="ts"> | |
import { onMount, onDestroy } from "svelte"; | |
import { v4 as uuidv4 } from "uuid"; | |
import type { IViewer } from "./viewers/IViewer"; | |
import { createViewer } from "./viewers/ViewerFactory"; | |
interface Data { | |
input: string; | |
input_path: string; | |
model1: string; | |
model1_path: string; | |
model2: string; | |
model2_path: string; | |
} | |
let viewerA: IViewer; | |
let viewerB: IViewer; | |
let canvasA: HTMLCanvasElement; | |
let canvasB: HTMLCanvasElement; | |
let containerA: HTMLDivElement; | |
let containerB: HTMLDivElement; | |
let overlayA: HTMLDivElement; | |
let overlayB: HTMLDivElement; | |
let loadingBarFillA: HTMLDivElement; | |
let loadingBarFillB: HTMLDivElement; | |
let statusMessage: string = "Loading..."; | |
let errorMessage: string = ""; | |
let data: Data; | |
function getUsername() { | |
let storedUsername = sessionStorage.getItem("username"); | |
if (!storedUsername) { | |
storedUsername = uuidv4(); | |
sessionStorage.setItem("username", storedUsername); | |
} | |
return storedUsername; | |
} | |
async function fetchScenes() { | |
statusMessage = "Loading..."; | |
errorMessage = ""; | |
try { | |
const username = getUsername(); | |
console.log(`Fetching with username: ${username}`); | |
const url = `https://dylanebert-3d-arena-backend.hf.space/pair?username=${username}`; | |
const response = await fetch(url, { | |
method: "GET", | |
headers: { | |
Authorization: "Bearer " + import.meta.env.VITE_HF_TOKEN, | |
"Cache-Control": "no-cache", | |
}, | |
}); | |
const result = await response.json(); | |
if (result.input) { | |
data = result; | |
statusMessage = ""; | |
return true; | |
} else { | |
statusMessage = "Voting complete."; | |
return false; | |
} | |
} catch (error) { | |
errorMessage = "Failed to fetch pair."; | |
statusMessage = ""; | |
return false; | |
} | |
} | |
async function loadScenes() { | |
const success = await fetchScenes(); | |
if (!success) return; | |
overlayA.style.display = "flex"; | |
overlayB.style.display = "flex"; | |
const baseUrl = "https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/"; | |
const model1_path = `${baseUrl}${data.model1_path}`; | |
const model2_path = `${baseUrl}${data.model2_path}`; | |
try { | |
const promises = [ | |
createViewer(model1_path, canvasA, (progress) => { | |
loadingBarFillA.style.width = `${progress * 100}%`; | |
}), | |
createViewer(model2_path, canvasB, (progress) => { | |
loadingBarFillB.style.width = `${progress * 100}%`; | |
}), | |
]; | |
await Promise.all(promises); | |
window.addEventListener("resize", handleResize); | |
handleResize(); | |
} catch (error) { | |
errorMessage = "Failed to load scenes."; | |
} | |
overlayA.style.display = "none"; | |
overlayB.style.display = "none"; | |
} | |
async function vote(option: "A" | "B") { | |
statusMessage = "Processing vote..."; | |
errorMessage = ""; | |
const payload = { | |
username: "dylanebert", | |
input: data.input, | |
better: option == "A" ? data.model1 : data.model2, | |
worse: option == "A" ? data.model2 : data.model1, | |
}; | |
const url = `https://dylanebert-3d-arena-backend.hf.space/vote`; | |
try { | |
const response = await fetch(url, { | |
method: "POST", | |
headers: { | |
Authorization: "Bearer " + import.meta.env.VITE_HF_TOKEN, | |
"Cache-Control": "no-cache", | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify(payload), | |
}); | |
if (response.ok) { | |
const result = await response.json(); | |
console.log(result); | |
loadScenes(); | |
} else { | |
errorMessage = "Failed to process vote."; | |
} | |
} catch (error) { | |
errorMessage = "Failed to process vote."; | |
statusMessage = ""; | |
} | |
} | |
function handleResize() { | |
requestAnimationFrame(() => { | |
if (canvasA && containerA) { | |
const maxWidth = containerA.clientHeight; | |
const maxHeight = containerA.clientWidth; | |
canvasA.width = Math.min(containerA.clientWidth, maxWidth); | |
canvasA.height = Math.min(containerA.clientHeight, maxHeight); | |
} | |
if (canvasB && containerB) { | |
const maxWidth = containerB.clientHeight; | |
const maxHeight = containerB.clientWidth; | |
canvasB.width = Math.min(containerB.clientWidth, maxWidth); | |
canvasB.height = Math.min(containerB.clientHeight, maxHeight); | |
} | |
}); | |
} | |
onMount(loadScenes); | |
onDestroy(() => { | |
viewerA?.dispose(); | |
viewerB?.dispose(); | |
if (typeof window !== "undefined") { | |
window.removeEventListener("resize", handleResize); | |
} | |
}); | |
</script> | |
{#if errorMessage} | |
<p class="center-title muted" style="color: red;">{errorMessage}</p> | |
{:else if statusMessage} | |
<p class="center-title muted">{statusMessage}</p> | |
{:else} | |
<h2 class="center-title">Which is better?</h2> | |
<div class="voting-container"> | |
<div bind:this={containerA} class="voting-canvas-wrapper"> | |
<div bind:this={overlayA} class="loading-overlay"> | |
<div class="loading-bar"> | |
<div bind:this={loadingBarFillA} class="loading-bar-fill" /> | |
</div> | |
</div> | |
<canvas bind:this={canvasA} class="voting-canvas" id="canvas1"></canvas> | |
<button class="vote-button" on:click={() => vote("A")}>A is Better</button> | |
</div> | |
<div bind:this={containerB} class="voting-canvas-wrapper"> | |
<div bind:this={overlayB} class="loading-overlay"> | |
<div class="loading-bar"> | |
<div bind:this={loadingBarFillB} class="loading-bar-fill" /> | |
</div> | |
</div> | |
<canvas bind:this={canvasB} class="voting-canvas" id="canvas2"></canvas> | |
<button class="vote-button" on:click={() => vote("B")}>B is Better</button> | |
</div> | |
</div> | |
{/if} | |