3d-arena / src /routes /Vote.svelte
dylanebert's picture
dylanebert HF staff
random session username
be445b3
raw
history blame
6.68 kB
<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}