|
<script lang="ts"> |
|
import { onMount, onDestroy } from "svelte"; |
|
import type { IViewer } from "./viewers/IViewer"; |
|
import { createViewer } from "./viewers/ViewerFactory"; |
|
import { ArrowLeft, Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte"; |
|
import type { Config } from "./utils/Config"; |
|
import { getConfig } from "./utils/getConfig"; |
|
|
|
interface Scene { |
|
name: string; |
|
url: string; |
|
thumbnail: string; |
|
} |
|
|
|
export let modelName: string; |
|
export let scene: Scene; |
|
export let onBack: () => void; |
|
|
|
let container: HTMLDivElement; |
|
let canvas: HTMLCanvasElement; |
|
let overlay: HTMLDivElement; |
|
let loadingBarFill: HTMLDivElement; |
|
|
|
let viewer: IViewer; |
|
let displayName: string; |
|
|
|
async function loadScene() { |
|
overlay.style.display = "flex"; |
|
displayName = (await getConfig(modelName)).DisplayName || modelName; |
|
viewer = await createViewer(scene.url, canvas, (progress) => { |
|
loadingBarFill.style.width = `${progress * 100}%`; |
|
}); |
|
window.addEventListener("resize", handleResize); |
|
window.addEventListener("keydown", handleKeyDown); |
|
handleResize(); |
|
overlay.style.display = "none"; |
|
} |
|
|
|
function handleResize() { |
|
if (!canvas || !container) return; |
|
requestAnimationFrame(() => { |
|
canvas.width = container.clientWidth; |
|
canvas.height = container.clientHeight; |
|
}); |
|
} |
|
|
|
function handleKeyDown(e: KeyboardEvent) { |
|
if (e.code === "KeyP") { |
|
capture(); |
|
} |
|
} |
|
|
|
async function capture() { |
|
const data = await viewer.capture(); |
|
if (!data) { |
|
console.error("Failed to capture screenshot"); |
|
return; |
|
} |
|
const a = document.createElement("a"); |
|
a.href = data; |
|
a.download = `${scene.name}.png`; |
|
a.click(); |
|
} |
|
|
|
onMount(loadScene); |
|
|
|
onDestroy(() => { |
|
viewer?.dispose(); |
|
if (typeof window !== "undefined") { |
|
window.removeEventListener("resize", handleResize); |
|
window.removeEventListener("keydown", handleKeyDown); |
|
} |
|
}); |
|
</script> |
|
|
|
<div class="header"> |
|
<div class="back" aria-label="Back" aria-hidden="true" on:click={onBack}> |
|
<ArrowLeft size={24} /> |
|
</div> |
|
<div class="spacer" /> |
|
<button class="title-button" on:click={loadScene}> |
|
|
|
|
|
<h2> |
|
{#if displayName} |
|
<span class="muted" on:click={onBack}>{displayName}/</span>{scene.name.length > 16 |
|
? `${scene.name.slice(0, 16)}...` |
|
: scene.name} |
|
{:else} |
|
Loading... |
|
{/if} |
|
</h2> |
|
</button> |
|
<div class="desktop-spacer" /> |
|
</div> |
|
<div class="canvas-container" bind:this={container}> |
|
<div bind:this={overlay} class="loading-overlay"> |
|
<div class="loading-bar"> |
|
<div bind:this={loadingBarFill} class="loading-bar-fill" /> |
|
</div> |
|
</div> |
|
<canvas class="viewer-canvas" bind:this={canvas} width={800} height={600}> </canvas> |
|
<div class="stats"> |
|
{#if viewer} |
|
<p>vertex count: {viewer.vertexCount}</p> |
|
{/if} |
|
</div> |
|
{#if viewer && !viewer.topoOnly} |
|
<div class="mode-toggle"> |
|
<label> |
|
<input |
|
type="radio" |
|
name="modeB" |
|
value="default" |
|
checked |
|
on:change={() => viewer.setRenderMode("default")} |
|
/> |
|
<Cube class="mode-toggle-icon" /> |
|
</label> |
|
<label> |
|
<input |
|
type="radio" |
|
name="modeB" |
|
value="wireframe" |
|
on:change={() => viewer.setRenderMode("wireframe")} |
|
/> |
|
<WatsonHealth3DPrintMesh class="mode-toggle-icon" /> |
|
</label> |
|
</div> |
|
{/if} |
|
</div> |
|
|