feat: implement performance view
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m10s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m10s
This commit is contained in:
parent
c28ef550a9
commit
e0e1743b77
@ -52,7 +52,6 @@
|
|||||||
let mouseDown: null | [number, number] = null;
|
let mouseDown: null | [number, number] = null;
|
||||||
let mouseDownId = -1;
|
let mouseDownId = -1;
|
||||||
let boxSelection = false;
|
let boxSelection = false;
|
||||||
let loaded = false;
|
|
||||||
const cameraDown = [0, 0];
|
const cameraDown = [0, 0];
|
||||||
let cameraPosition: [number, number, number] = [0, 0, 4];
|
let cameraPosition: [number, number, number] = [0, 0, 4];
|
||||||
let addMenuPosition: [number, number] | null = null;
|
let addMenuPosition: [number, number] | null = null;
|
||||||
@ -783,7 +782,7 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
if (!event.dataTransfer) return;
|
if (!event.dataTransfer) return;
|
||||||
const nodeId: NodeId = event.dataTransfer.getData("data/node-id");
|
const nodeId = event.dataTransfer.getData("data/node-id") as NodeId;
|
||||||
|
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
let mx = event.clientX - rect.x;
|
let mx = event.clientX - rect.x;
|
||||||
@ -805,7 +804,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pos = projectScreenToWorld(mx, my);
|
const pos = projectScreenToWorld(mx, my);
|
||||||
graph.load([nodeId]).then(() => {
|
graph.registry.load([nodeId]).then(() => {
|
||||||
graph.createNode({
|
graph.createNode({
|
||||||
type: nodeId,
|
type: nodeId,
|
||||||
props,
|
props,
|
||||||
|
@ -114,3 +114,15 @@ export function withSubComponents<A, B extends Record<string, any>>(
|
|||||||
});
|
});
|
||||||
return component as A & B;
|
return component as A & B;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function humanizeNumber(number: number): string {
|
||||||
|
const suffixes = ["", "K", "M", "B", "T"];
|
||||||
|
if (number < 1000) {
|
||||||
|
return number.toString();
|
||||||
|
}
|
||||||
|
const numLength = Math.floor(Math.log10(number)) + 1;
|
||||||
|
const baseIndex = Math.floor((numLength - 1) / 3);
|
||||||
|
const base = Math.pow(10, baseIndex * 3);
|
||||||
|
const rounded = Math.round(number / base * 10) / 10;
|
||||||
|
return rounded + suffixes[baseIndex];
|
||||||
|
}
|
||||||
|
68
app/src/lib/performance/Monitor.svelte
Normal file
68
app/src/lib/performance/Monitor.svelte
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let points: number[];
|
||||||
|
|
||||||
|
$: max = Math.max(...points);
|
||||||
|
$: min = Math.min(...points);
|
||||||
|
function constructPath() {
|
||||||
|
return points
|
||||||
|
.map((point, i) => {
|
||||||
|
const x = (i / (points.length - 1)) * 100;
|
||||||
|
const y = 100 - ((point - min) / (max - min)) * 100;
|
||||||
|
return `${x},${y}`;
|
||||||
|
})
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
<p>Runtime Execution</p>
|
||||||
|
<span class="min">{min}ms</span>
|
||||||
|
<span class="max">{max}ms</span>
|
||||||
|
<svg preserveAspectRatio="none" viewBox="0 0 100 100">
|
||||||
|
<polyline vector-effect="non-scaling-stroke" points={constructPath()} />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
span {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.max {
|
||||||
|
top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.min {
|
||||||
|
bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
border-bottom: solid thin var(--outline);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0px;
|
||||||
|
top: 3px;
|
||||||
|
left: 5px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.5;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
height: 124px;
|
||||||
|
margin: 24px 0px;
|
||||||
|
border-top: solid thin var(--outline);
|
||||||
|
border-bottom: solid thin var(--outline);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
polyline {
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--layer-3);
|
||||||
|
opacity: 0.5;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,17 +1,122 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PerformanceData } from ".";
|
import { browser } from "$app/environment";
|
||||||
|
import Monitor from "./Monitor.svelte";
|
||||||
|
import { humanizeNumber } from "$lib/helpers";
|
||||||
|
|
||||||
|
type PerformanceData = {
|
||||||
|
total: Record<string, number>;
|
||||||
|
runs: Record<string, number[]>[];
|
||||||
|
};
|
||||||
|
|
||||||
export let data: PerformanceData;
|
export let data: PerformanceData;
|
||||||
|
export let viewer: PerformanceData;
|
||||||
|
|
||||||
function getPerformanceData() {
|
function getPerformanceData() {
|
||||||
return Object.entries(data.total).sort((a, b) => b[1] - a[1]);
|
return Object.entries(data.total)
|
||||||
|
.sort((a, b) => b[1] - a[1])
|
||||||
|
.filter(([key]) => !key.startsWith("node/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodePerformanceData() {
|
||||||
|
return Object.entries(data.total)
|
||||||
|
.filter(([key]) => key.startsWith("node/"))
|
||||||
|
.sort((a, b) => b[1] - a[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewerPerformanceData() {
|
||||||
|
return Object.entries(viewer.total)
|
||||||
|
.filter(([key]) => key !== "total-vertices" && key !== "total-faces")
|
||||||
|
.sort((a, b) => b[1] - a[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructPoints(key: keyof (typeof data.runs)[0]) {
|
||||||
|
return data.runs
|
||||||
|
.map((run, i) => {
|
||||||
|
return run[key][0];
|
||||||
|
})
|
||||||
|
.slice(-100);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if data.runs.length !== 0}
|
{#key data}
|
||||||
{#each getPerformanceData() as [key, value]}
|
{#if browser}
|
||||||
<p>{key}: {Math.floor(value * 100) / 100}ms</p>
|
<Monitor points={constructPoints("total")} />
|
||||||
{/each}
|
{/if}
|
||||||
{:else}
|
|
||||||
<p>No runs available</p>
|
<div class="px-4">
|
||||||
{/if}
|
{#if data.runs.length !== 0}
|
||||||
|
<h3>General</h3>
|
||||||
|
<table>
|
||||||
|
{#each getPerformanceData() as [key, value]}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{Math.floor(value * 100) / 100}<span>ms</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{key}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<h3>Nodes</h3>
|
||||||
|
{#each getNodePerformanceData() as [key, value]}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{Math.floor(value * 100) / 100}<span>ms</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{key.split("/").slice(-1).join("/")}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if viewer.runs.length}
|
||||||
|
<h3>Viewer</h3>
|
||||||
|
<tr>
|
||||||
|
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-vertices"])}</td>
|
||||||
|
<td>Vertices</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{humanizeNumber(viewer.runs.at(-1)?.["total-faces"])}</td>
|
||||||
|
<td>Faces</td>
|
||||||
|
</tr>
|
||||||
|
{#each getViewerPerformanceData() as [key, value]}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{Math.floor(value * 100) / 100}<span>ms</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{key.split("/").slice(-1).join("/")}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</table>
|
||||||
|
{:else}
|
||||||
|
<p>No runs available</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
opacity: 0.3;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-block: 5px;
|
||||||
|
}
|
||||||
|
tr > td:nth-child(1) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
tr > td:nth-child(2) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,68 +1,2 @@
|
|||||||
import { readable, type Readable } from "svelte/store";
|
export * from "./store";
|
||||||
|
|
||||||
export type PerformanceData = {
|
|
||||||
total: Record<string, number>;
|
|
||||||
runs: Record<string, number[]>[];
|
|
||||||
}
|
|
||||||
export interface PerformanceStore extends Readable<PerformanceData> {
|
|
||||||
startRun(): void;
|
|
||||||
stopRun(): void;
|
|
||||||
addPoint(name: string, value?: number): void;
|
|
||||||
get: () => PerformanceData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPerformanceStore(): PerformanceStore {
|
|
||||||
|
|
||||||
let data: PerformanceData = { total: {}, runs: [] };
|
|
||||||
|
|
||||||
let currentRun: Record<string, number[]> | undefined;
|
|
||||||
|
|
||||||
let set: (v: PerformanceData) => void;
|
|
||||||
|
|
||||||
const { subscribe } = readable<PerformanceData>({ total: {}, runs: [] }, (_set) => {
|
|
||||||
set = _set;
|
|
||||||
});
|
|
||||||
|
|
||||||
function startRun() {
|
|
||||||
currentRun = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopRun() {
|
|
||||||
if (currentRun) {
|
|
||||||
// Calculate total
|
|
||||||
Object.keys(currentRun).forEach((name) => {
|
|
||||||
if (!currentRun?.[name]?.length) return;
|
|
||||||
let runTotal = currentRun[name].reduce((a, b) => a + b, 0) / currentRun[name].length;
|
|
||||||
if (!data.total[name]) {
|
|
||||||
data.total[name] = runTotal;
|
|
||||||
} else {
|
|
||||||
data.total[name] = (data.total[name] + runTotal) / 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
data.runs.push(currentRun);
|
|
||||||
currentRun = undefined;
|
|
||||||
if (set) set(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPoint(name: string, value: number) {
|
|
||||||
if (!currentRun) return;
|
|
||||||
currentRun[name] = currentRun[name] || [];
|
|
||||||
currentRun[name].push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function get() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe,
|
|
||||||
startRun,
|
|
||||||
stopRun,
|
|
||||||
addPoint,
|
|
||||||
get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { default as PerformanceViewer } from "./PerformanceViewer.svelte";
|
export { default as PerformanceViewer } from "./PerformanceViewer.svelte";
|
||||||
|
67
app/src/lib/performance/store.ts
Normal file
67
app/src/lib/performance/store.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { readable, type Readable } from "svelte/store";
|
||||||
|
|
||||||
|
export type PerformanceData = {
|
||||||
|
total: Record<string, number>;
|
||||||
|
runs: Record<string, number[]>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerformanceStore extends Readable<PerformanceData> {
|
||||||
|
startRun(): void;
|
||||||
|
stopRun(): void;
|
||||||
|
addPoint(name: string, value?: number): void;
|
||||||
|
get: () => PerformanceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPerformanceStore(): PerformanceStore {
|
||||||
|
|
||||||
|
let data: PerformanceData = { total: {}, runs: [] };
|
||||||
|
|
||||||
|
let currentRun: Record<string, number[]> | undefined;
|
||||||
|
|
||||||
|
let set: (v: PerformanceData) => void;
|
||||||
|
|
||||||
|
const { subscribe } = readable<PerformanceData>({ total: {}, runs: [] }, (_set) => {
|
||||||
|
set = _set;
|
||||||
|
});
|
||||||
|
|
||||||
|
function startRun() {
|
||||||
|
currentRun = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRun() {
|
||||||
|
if (currentRun) {
|
||||||
|
// Calculate total
|
||||||
|
Object.keys(currentRun).forEach((name) => {
|
||||||
|
if (!currentRun?.[name]?.length) return;
|
||||||
|
let runTotal = currentRun[name].reduce((a, b) => a + b, 0) / currentRun[name].length;
|
||||||
|
if (!data.total[name]) {
|
||||||
|
data.total[name] = runTotal;
|
||||||
|
} else {
|
||||||
|
data.total[name] = (data.total[name] + runTotal) / 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
data.runs.push(currentRun);
|
||||||
|
currentRun = undefined;
|
||||||
|
if (set) set(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPoint(name: string, value: number) {
|
||||||
|
if (!currentRun) return;
|
||||||
|
currentRun[name] = currentRun[name] || [];
|
||||||
|
currentRun[name].push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
startRun,
|
||||||
|
stopRun,
|
||||||
|
addPoint,
|
||||||
|
get
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { T } from "@threlte/core";
|
import { T, useTask } from "@threlte/core";
|
||||||
import {
|
import {
|
||||||
MeshLineGeometry,
|
MeshLineGeometry,
|
||||||
MeshLineMaterial,
|
MeshLineMaterial,
|
||||||
@ -7,9 +7,12 @@
|
|||||||
useTexture,
|
useTexture,
|
||||||
} from "@threlte/extras";
|
} from "@threlte/extras";
|
||||||
import {
|
import {
|
||||||
|
type Group,
|
||||||
type BufferGeometry,
|
type BufferGeometry,
|
||||||
type PerspectiveCamera,
|
type PerspectiveCamera,
|
||||||
type Vector3,
|
Vector3,
|
||||||
|
type Vector3Tuple,
|
||||||
|
Box3,
|
||||||
} from "three";
|
} from "three";
|
||||||
import type { OrbitControls as OrbitControlsType } from "three/addons/controls/OrbitControls.js";
|
import type { OrbitControls as OrbitControlsType } from "three/addons/controls/OrbitControls.js";
|
||||||
import { OrbitControls } from "@threlte/extras";
|
import { OrbitControls } from "@threlte/extras";
|
||||||
@ -18,19 +21,21 @@
|
|||||||
|
|
||||||
export let geometries: BufferGeometry[];
|
export let geometries: BufferGeometry[];
|
||||||
export let lines: Vector3[][];
|
export let lines: Vector3[][];
|
||||||
|
let geos: Group;
|
||||||
|
|
||||||
export let camera: PerspectiveCamera;
|
let camera: PerspectiveCamera;
|
||||||
export let controls: OrbitControlsType;
|
let controls: OrbitControlsType;
|
||||||
|
export let centerCamera: boolean = true;
|
||||||
|
|
||||||
const matcap = useTexture("/matcap_green.jpg");
|
const matcap = useTexture("/matcap_green.jpg");
|
||||||
|
|
||||||
const cameraTransform = localStore<{ camera: number[]; target: number[] }>(
|
const cameraTransform = localStore<{
|
||||||
"nodes.camera.transform",
|
camera: Vector3Tuple;
|
||||||
{
|
target: Vector3Tuple;
|
||||||
camera: [0, 0, 10],
|
}>("nodes.camera.transform", {
|
||||||
target: [0, 0, 0],
|
camera: [0, 0, 10],
|
||||||
},
|
target: [0, 0, 0],
|
||||||
);
|
});
|
||||||
|
|
||||||
function saveCameraState() {
|
function saveCameraState() {
|
||||||
if (!camera) return;
|
if (!camera) return;
|
||||||
@ -41,12 +46,49 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPosition(geo: BufferGeometry, i: number) {
|
function getPosition(geo: BufferGeometry, i: number) {
|
||||||
const pos = [
|
return [
|
||||||
geo.attributes.position.array[i],
|
geo.attributes.position.array[i],
|
||||||
geo.attributes.position.array[i + 1],
|
geo.attributes.position.array[i + 1],
|
||||||
geo.attributes.position.array[i + 2],
|
geo.attributes.position.array[i + 2],
|
||||||
];
|
] as Vector3Tuple;
|
||||||
return pos;
|
}
|
||||||
|
|
||||||
|
let cameraTarget: Vector3;
|
||||||
|
let duration = 0;
|
||||||
|
let totalDuration = 5;
|
||||||
|
const { start, stop, started } = useTask((delta) => {
|
||||||
|
duration += delta;
|
||||||
|
if (!cameraTarget) {
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// This function will be executed on every frame
|
||||||
|
if (duration >= totalDuration) {
|
||||||
|
controls.target.copy(cameraTarget);
|
||||||
|
stop();
|
||||||
|
controls.update();
|
||||||
|
} else {
|
||||||
|
const t = duration / totalDuration;
|
||||||
|
controls.target.lerp(cameraTarget, t);
|
||||||
|
controls.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stop();
|
||||||
|
|
||||||
|
$: if (geometries && geos && centerCamera) {
|
||||||
|
const aabb = new Box3();
|
||||||
|
aabb.setFromObject(geos);
|
||||||
|
const newCenter = aabb.getCenter(new Vector3());
|
||||||
|
if (
|
||||||
|
newCenter &&
|
||||||
|
newCenter.x !== 0 &&
|
||||||
|
newCenter.y !== 0 &&
|
||||||
|
newCenter.z !== 0
|
||||||
|
) {
|
||||||
|
cameraTarget = newCenter;
|
||||||
|
duration = 0;
|
||||||
|
start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -70,30 +112,32 @@
|
|||||||
<T.DirectionalLight position={[0, 10, 10]} />
|
<T.DirectionalLight position={[0, 10, 10]} />
|
||||||
<T.AmbientLight intensity={2} />
|
<T.AmbientLight intensity={2} />
|
||||||
|
|
||||||
{#each geometries as geo}
|
<T.Group bind:ref={geos}>
|
||||||
{#if $AppSettings.showIndices}
|
{#each geometries as geo}
|
||||||
{#each geo.attributes.position.array as _, i}
|
{#if $AppSettings.showIndices}
|
||||||
{#if i % 3 === 0}
|
{#each geo.attributes.position.array as _, i}
|
||||||
<Text text={i / 3} fontSize={0.25} position={getPosition(geo, i)} />
|
{#if i % 3 === 0}
|
||||||
{/if}
|
<Text fontSize={0.25} position={getPosition(geo, i)} />
|
||||||
{/each}
|
{/if}
|
||||||
{/if}
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if $AppSettings.showVertices}
|
{#if $AppSettings.showVertices}
|
||||||
<T.Points visible={true}>
|
<T.Points visible={true}>
|
||||||
<T is={geo} />
|
<T is={geo} />
|
||||||
<T.PointsMaterial size={0.25} />
|
<T.PointsMaterial size={0.25} />
|
||||||
</T.Points>
|
</T.Points>
|
||||||
{/if}
|
{/if}
|
||||||
{#await matcap then value}
|
{#await matcap then value}
|
||||||
<T.Mesh geometry={geo}>
|
<T.Mesh geometry={geo}>
|
||||||
<T.MeshMatcapMaterial matcap={value} wireframe={$AppSettings.wireframe} />
|
<T.MeshMatcapMaterial
|
||||||
{#if false}
|
matcap={value}
|
||||||
<T.MeshStandardMaterial color="green" depthTest={true} />
|
wireframe={$AppSettings.wireframe}
|
||||||
{/if}
|
/>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
{/await}
|
{/await}
|
||||||
{/each}
|
{/each}
|
||||||
|
</T.Group>
|
||||||
|
|
||||||
{#if $AppSettings.showStemLines && lines}
|
{#if $AppSettings.showStemLines && lines}
|
||||||
{#each lines as line}
|
{#each lines as line}
|
||||||
|
@ -1,36 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Canvas } from "@threlte/core";
|
import { Canvas } from "@threlte/core";
|
||||||
import Scene from "./Scene.svelte";
|
import Scene from "./Scene.svelte";
|
||||||
import {
|
import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
|
||||||
BufferGeometry,
|
|
||||||
Float32BufferAttribute,
|
|
||||||
PerspectiveCamera,
|
|
||||||
Vector3,
|
|
||||||
} from "three";
|
|
||||||
import { decodeFloat } from "@nodes/utils";
|
import { decodeFloat } from "@nodes/utils";
|
||||||
import type { OrbitControls } from "three/examples/jsm/Addons.js";
|
import type { PerformanceStore } from "$lib/performance";
|
||||||
|
|
||||||
export let result: Int32Array;
|
export let result: Int32Array;
|
||||||
|
|
||||||
let camera: PerspectiveCamera;
|
|
||||||
let controls: OrbitControls;
|
|
||||||
let center: Vector3;
|
|
||||||
export let centerCamera: boolean = true;
|
export let centerCamera: boolean = true;
|
||||||
|
export let perf: PerformanceStore;
|
||||||
|
|
||||||
let geometries: BufferGeometry[] = [];
|
let geometries: BufferGeometry[] = [];
|
||||||
let lines: Vector3[][] = [];
|
let lines: Vector3[][] = [];
|
||||||
|
|
||||||
|
let totalVertices = 0;
|
||||||
|
let totalFaces = 0;
|
||||||
|
|
||||||
function createGeometryFromEncodedData(
|
function createGeometryFromEncodedData(
|
||||||
encodedData: Int32Array,
|
encodedData: Int32Array,
|
||||||
|
geometry = new BufferGeometry(),
|
||||||
): BufferGeometry {
|
): BufferGeometry {
|
||||||
const geometry = new BufferGeometry();
|
|
||||||
|
|
||||||
// Extract data from the encoded array
|
// Extract data from the encoded array
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const geometryType = encodedData[index++];
|
const geometryType = encodedData[index++];
|
||||||
const vertexCount = encodedData[index++];
|
const vertexCount = encodedData[index++];
|
||||||
const faceCount = encodedData[index++];
|
const faceCount = encodedData[index++];
|
||||||
|
|
||||||
|
totalVertices += vertexCount;
|
||||||
|
totalFaces += faceCount;
|
||||||
|
|
||||||
// Indices
|
// Indices
|
||||||
const indicesEnd = index + faceCount * 3;
|
const indicesEnd = index + faceCount * 3;
|
||||||
const indices = encodedData.subarray(index, indicesEnd);
|
const indices = encodedData.subarray(index, indicesEnd);
|
||||||
@ -119,8 +117,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: if (result) {
|
$: if (result) {
|
||||||
const inputs = parse_args(result);
|
perf?.startRun();
|
||||||
|
|
||||||
|
let a = performance.now();
|
||||||
|
const inputs = parse_args(result);
|
||||||
|
let b = performance.now();
|
||||||
|
perf?.addPoint("parse-args", b - a);
|
||||||
|
|
||||||
|
totalVertices = 0;
|
||||||
|
totalFaces = 0;
|
||||||
|
|
||||||
|
a = performance.now();
|
||||||
lines = inputs
|
lines = inputs
|
||||||
.map((input) => {
|
.map((input) => {
|
||||||
if (input[0] === 0) {
|
if (input[0] === 0) {
|
||||||
@ -128,26 +135,27 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean) as Vector3[][];
|
.filter(Boolean) as Vector3[][];
|
||||||
|
b = performance.now();
|
||||||
|
perf?.addPoint("create-lines", b - a);
|
||||||
|
|
||||||
center = new Vector3();
|
a = performance.now();
|
||||||
|
|
||||||
geometries = inputs
|
geometries = inputs
|
||||||
.map((input) => {
|
.map((input, i) => {
|
||||||
if (input[0] === 1) {
|
if (input[0] === 1) {
|
||||||
const geo = createGeometryFromEncodedData(input);
|
return createGeometryFromEncodedData(input, geometries[i]);
|
||||||
geo?.computeBoundingSphere();
|
|
||||||
if (geo.boundingSphere) {
|
|
||||||
center.add(geo.boundingSphere.center);
|
|
||||||
}
|
|
||||||
return geo;
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean) as BufferGeometry[];
|
.filter(Boolean) as BufferGeometry[];
|
||||||
|
b = performance.now();
|
||||||
|
perf?.addPoint("create-geometries", b - a);
|
||||||
|
|
||||||
center = center.divideScalar(geometries.length);
|
perf?.addPoint("total-vertices", totalVertices);
|
||||||
|
perf?.addPoint("total-faces", totalFaces);
|
||||||
|
|
||||||
|
perf?.stopRun();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Canvas>
|
<Canvas>
|
||||||
<Scene bind:camera bind:controls {geometries} {lines} />
|
<Scene {geometries} {lines} {centerCamera} />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
@ -5,7 +5,7 @@ export const AppSettings = localStore("node-settings", {
|
|||||||
showGrid: true,
|
showGrid: true,
|
||||||
showNodeGrid: true,
|
showNodeGrid: true,
|
||||||
snapToGrid: true,
|
snapToGrid: true,
|
||||||
wireframes: false,
|
wireframe: false,
|
||||||
showIndices: false,
|
showIndices: false,
|
||||||
showVertices: false,
|
showVertices: false,
|
||||||
centerCamera: true,
|
centerCamera: true,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { MemoryRuntimeExecutor } from "./runtime-executor";
|
import { MemoryRuntimeExecutor } from "./runtime-executor";
|
||||||
import { RemoteNodeRegistry } from "./node-registry-client";
|
import { RemoteNodeRegistry } from "./node-registry-client";
|
||||||
import type { Graph } from "@nodes/types";
|
import type { Graph } from "@nodes/types";
|
||||||
import { createPerformanceStore } from "./performance";
|
import { createPerformanceStore } from "./performance/store";
|
||||||
|
|
||||||
const nodeRegistry = new RemoteNodeRegistry("");
|
const nodeRegistry = new RemoteNodeRegistry("");
|
||||||
const executor = new MemoryRuntimeExecutor(nodeRegistry);
|
const executor = new MemoryRuntimeExecutor(nodeRegistry);
|
||||||
|
@ -24,11 +24,14 @@
|
|||||||
import Panel from "$lib/settings/Panel.svelte";
|
import Panel from "$lib/settings/Panel.svelte";
|
||||||
import GraphSettings from "$lib/settings/panels/GraphSettings.svelte";
|
import GraphSettings from "$lib/settings/panels/GraphSettings.svelte";
|
||||||
import NestedSettings from "$lib/settings/panels/NestedSettings.svelte";
|
import NestedSettings from "$lib/settings/panels/NestedSettings.svelte";
|
||||||
|
import { createPerformanceStore } from "$lib/performance";
|
||||||
|
import { type PerformanceData } from "$lib/performance/store";
|
||||||
|
|
||||||
const nodeRegistry = new RemoteNodeRegistry("");
|
const nodeRegistry = new RemoteNodeRegistry("");
|
||||||
const workerRuntime = new WorkerRuntimeExecutor();
|
const workerRuntime = new WorkerRuntimeExecutor();
|
||||||
|
|
||||||
let performanceData: PerformanceData;
|
let performanceData: PerformanceData;
|
||||||
|
let viewerPerformance = createPerformanceStore();
|
||||||
|
|
||||||
globalThis.decode = decodeNestedArray;
|
globalThis.decode = decodeNestedArray;
|
||||||
globalThis.encode = encodeNestedArray;
|
globalThis.encode = encodeNestedArray;
|
||||||
@ -51,15 +54,38 @@
|
|||||||
let graphSettings = writable<Record<string, any>>({});
|
let graphSettings = writable<Record<string, any>>({});
|
||||||
let graphSettingTypes = {};
|
let graphSettingTypes = {};
|
||||||
|
|
||||||
async function handleResult(event: CustomEvent<Graph>) {
|
let isWorking = false;
|
||||||
const settings = $graphSettings;
|
|
||||||
if (!settings) return;
|
let unfinished:
|
||||||
|
| {
|
||||||
|
graph: Graph;
|
||||||
|
settings: Record<string, any>;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
async function handleResult(_graph: Graph, _settings: Record<string, any>) {
|
||||||
|
if (!_settings) return;
|
||||||
|
if (isWorking) {
|
||||||
|
unfinished = {
|
||||||
|
graph: _graph,
|
||||||
|
settings: _settings,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isWorking = true;
|
||||||
try {
|
try {
|
||||||
res = await workerRuntime.execute(event.detail, settings);
|
res = await workerRuntime.execute(_graph, _settings);
|
||||||
performanceData = await workerRuntime.getPerformanceData();
|
performanceData = await workerRuntime.getPerformanceData();
|
||||||
|
isWorking = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("errors", error);
|
console.log("errors", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unfinished) {
|
||||||
|
let d = unfinished;
|
||||||
|
unfinished = undefined;
|
||||||
|
handleResult(d.graph, d.settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (AppSettings) {
|
$: if (AppSettings) {
|
||||||
@ -69,7 +95,7 @@
|
|||||||
};
|
};
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
||||||
graph = templates.tree($AppSettings.amount, $AppSettings.amount);
|
graph = templates.tree($AppSettings.amount);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +108,11 @@
|
|||||||
<header></header>
|
<header></header>
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
<Viewer centerCamera={$AppSettings.centerCamera} result={res} />
|
<Viewer
|
||||||
|
centerCamera={$AppSettings.centerCamera}
|
||||||
|
result={res}
|
||||||
|
perf={viewerPerformance}
|
||||||
|
/>
|
||||||
</Grid.Cell>
|
</Grid.Cell>
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
{#key graph}
|
{#key graph}
|
||||||
@ -96,7 +126,7 @@
|
|||||||
snapToGrid={$AppSettings?.snapToGrid}
|
snapToGrid={$AppSettings?.snapToGrid}
|
||||||
bind:settings={graphSettings}
|
bind:settings={graphSettings}
|
||||||
bind:settingTypes={graphSettingTypes}
|
bind:settingTypes={graphSettingTypes}
|
||||||
on:result={handleResult}
|
on:result={(ev) => handleResult(ev.detail, $graphSettings)}
|
||||||
on:save={handleSave}
|
on:save={handleSave}
|
||||||
/>
|
/>
|
||||||
<Settings>
|
<Settings>
|
||||||
@ -116,7 +146,10 @@
|
|||||||
icon="i-tabler-brand-speedtest"
|
icon="i-tabler-brand-speedtest"
|
||||||
>
|
>
|
||||||
{#if performanceData}
|
{#if performanceData}
|
||||||
<PerformanceViewer data={performanceData} />
|
<PerformanceViewer
|
||||||
|
data={performanceData}
|
||||||
|
viewer={$viewerPerformance}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel
|
<Panel
|
||||||
|
@ -102,54 +102,26 @@ importers:
|
|||||||
specifier: ^1.5.1
|
specifier: ^1.5.1
|
||||||
version: 1.5.1(@types/node@20.12.7)(jsdom@24.0.0)(sass@1.75.0)
|
version: 1.5.1(@types/node@20.12.7)(jsdom@24.0.0)(sass@1.75.0)
|
||||||
|
|
||||||
nodes/max/plantarium/array: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/array/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/box: {}
|
nodes/max/plantarium/box: {}
|
||||||
|
|
||||||
nodes/max/plantarium/box/pkg: {}
|
nodes/max/plantarium/branch: {}
|
||||||
|
|
||||||
nodes/max/plantarium/branches: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/branches/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/float: {}
|
nodes/max/plantarium/float: {}
|
||||||
|
|
||||||
nodes/max/plantarium/float/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/math: {}
|
nodes/max/plantarium/math: {}
|
||||||
|
|
||||||
nodes/max/plantarium/math/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/noise: {}
|
nodes/max/plantarium/noise: {}
|
||||||
|
|
||||||
nodes/max/plantarium/noise/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/output: {}
|
nodes/max/plantarium/output: {}
|
||||||
|
|
||||||
nodes/max/plantarium/output/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/random: {}
|
nodes/max/plantarium/random: {}
|
||||||
|
|
||||||
nodes/max/plantarium/random/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/stem: {}
|
nodes/max/plantarium/stem: {}
|
||||||
|
|
||||||
nodes/max/plantarium/stem/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/sum: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/sum/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/triangle: {}
|
nodes/max/plantarium/triangle: {}
|
||||||
|
|
||||||
nodes/max/plantarium/triangle/pkg: {}
|
|
||||||
|
|
||||||
nodes/max/plantarium/vec3: {}
|
nodes/max/plantarium/vec3: {}
|
||||||
|
|
||||||
nodes/max/plantarium/vec3/pkg: {}
|
|
||||||
|
|
||||||
packages/types:
|
packages/types:
|
||||||
dependencies:
|
dependencies:
|
||||||
zod:
|
zod:
|
||||||
|
Loading…
Reference in New Issue
Block a user