frontend: expand cosmos runtime tuning and diagnostics
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Graph, type GraphConfig } from "@cosmos.gl/graph";
|
||||
import { cosmosRuntimeConfig } from "./cosmos_config";
|
||||
import { cosmosBackgroundCss, cosmosRuntimeConfig } from "./cosmos_config";
|
||||
import {
|
||||
computeLayoutMetrics,
|
||||
type GraphLayoutMetrics,
|
||||
@@ -194,28 +194,68 @@ export const TripleGraphView = memo(function TripleGraphView({ model }: TripleGr
|
||||
};
|
||||
|
||||
const config: GraphConfig = {
|
||||
backgroundColor: "#05070a",
|
||||
backgroundColor: cosmosRuntimeConfig.backgroundColor,
|
||||
spaceSize: cosmosRuntimeConfig.spaceSize,
|
||||
enableSimulation: cosmosRuntimeConfig.enableSimulation,
|
||||
enableDrag: true,
|
||||
enableZoom: true,
|
||||
fitViewOnInit: false,
|
||||
pointDefaultColor: cosmosRuntimeConfig.pointDefaultColor,
|
||||
pointGreyoutColor: cosmosRuntimeConfig.pointGreyoutColor,
|
||||
pointGreyoutOpacity: cosmosRuntimeConfig.pointGreyoutOpacity,
|
||||
pointDefaultSize: cosmosRuntimeConfig.pointDefaultSize,
|
||||
pointOpacity: cosmosRuntimeConfig.pointOpacity,
|
||||
pointSizeScale: cosmosRuntimeConfig.pointSizeScale,
|
||||
hoveredPointCursor: cosmosRuntimeConfig.hoveredPointCursor,
|
||||
hoveredLinkCursor: cosmosRuntimeConfig.hoveredLinkCursor,
|
||||
renderHoveredPointRing: cosmosRuntimeConfig.renderHoveredPointRing,
|
||||
hoveredPointRingColor: cosmosRuntimeConfig.hoveredPointRingColor,
|
||||
focusedPointRingColor: cosmosRuntimeConfig.focusedPointRingColor,
|
||||
renderLinks: cosmosRuntimeConfig.renderLinks,
|
||||
linkDefaultColor: cosmosRuntimeConfig.linkDefaultColor,
|
||||
linkOpacity: cosmosRuntimeConfig.linkOpacity,
|
||||
linkGreyoutOpacity: cosmosRuntimeConfig.linkGreyoutOpacity,
|
||||
linkDefaultWidth: cosmosRuntimeConfig.linkDefaultWidth,
|
||||
hoveredLinkColor: cosmosRuntimeConfig.hoveredLinkColor,
|
||||
hoveredLinkWidthIncrease: cosmosRuntimeConfig.hoveredLinkWidthIncrease,
|
||||
linkWidthScale: cosmosRuntimeConfig.linkWidthScale,
|
||||
scaleLinksOnZoom: cosmosRuntimeConfig.scaleLinksOnZoom,
|
||||
enableDrag: cosmosRuntimeConfig.enableDrag,
|
||||
enableZoom: cosmosRuntimeConfig.enableZoom,
|
||||
enableSimulationDuringZoom: cosmosRuntimeConfig.enableSimulationDuringZoom,
|
||||
fitViewOnInit: cosmosRuntimeConfig.fitViewOnInit,
|
||||
fitViewDelay: cosmosRuntimeConfig.fitViewDelay,
|
||||
fitViewPadding: cosmosRuntimeConfig.fitViewPadding,
|
||||
rescalePositions: false,
|
||||
fitViewDuration: cosmosRuntimeConfig.fitViewDuration,
|
||||
initialZoomLevel: cosmosRuntimeConfig.initialZoomLevel,
|
||||
pointSamplingDistance: cosmosRuntimeConfig.pointSamplingDistance,
|
||||
rescalePositions: cosmosRuntimeConfig.rescalePositions,
|
||||
curvedLinks: cosmosRuntimeConfig.curvedLinks,
|
||||
curvedLinkSegments: cosmosRuntimeConfig.curvedLinkSegments,
|
||||
curvedLinkWeight: cosmosRuntimeConfig.curvedLinkWeight,
|
||||
curvedLinkControlPointDistance: cosmosRuntimeConfig.curvedLinkControlPointDistance,
|
||||
linkDefaultArrows: cosmosRuntimeConfig.linkDefaultArrows,
|
||||
linkArrowsSizeScale: cosmosRuntimeConfig.linkArrowsSizeScale,
|
||||
linkVisibilityDistanceRange: cosmosRuntimeConfig.linkVisibilityDistanceRange,
|
||||
linkVisibilityMinTransparency: cosmosRuntimeConfig.linkVisibilityMinTransparency,
|
||||
useClassicQuadtree: cosmosRuntimeConfig.useClassicQuadtree,
|
||||
simulationDecay: cosmosRuntimeConfig.simulationDecay,
|
||||
simulationGravity: cosmosRuntimeConfig.simulationGravity,
|
||||
simulationCenter: cosmosRuntimeConfig.simulationCenter,
|
||||
simulationRepulsion: cosmosRuntimeConfig.simulationRepulsion,
|
||||
simulationRepulsionTheta: cosmosRuntimeConfig.simulationRepulsionTheta,
|
||||
simulationRepulsionQuadtreeLevels:
|
||||
cosmosRuntimeConfig.simulationRepulsionQuadtreeLevels,
|
||||
simulationLinkSpring: cosmosRuntimeConfig.simulationLinkSpring,
|
||||
simulationLinkDistance: cosmosRuntimeConfig.simulationLinkDistance,
|
||||
simulationLinkDistRandomVariationRange:
|
||||
cosmosRuntimeConfig.simulationLinkDistRandomVariationRange,
|
||||
simulationRepulsionFromMouse: cosmosRuntimeConfig.simulationRepulsionFromMouse,
|
||||
enableRightClickRepulsion: cosmosRuntimeConfig.enableRightClickRepulsion,
|
||||
simulationFriction: cosmosRuntimeConfig.simulationFriction,
|
||||
renderHoveredPointRing: true,
|
||||
hoveredPointRingColor: "#35d6ff",
|
||||
hoveredPointCursor: "pointer",
|
||||
hoveredLinkCursor: "pointer",
|
||||
hoveredLinkColor: "#ffd166",
|
||||
hoveredLinkWidthIncrease: 2.5,
|
||||
simulationCluster: cosmosRuntimeConfig.simulationCluster,
|
||||
randomSeed: cosmosRuntimeConfig.randomSeed,
|
||||
showFPSMonitor: cosmosRuntimeConfig.showFPSMonitor,
|
||||
pixelRatio: cosmosRuntimeConfig.pixelRatio,
|
||||
scalePointsOnZoom: cosmosRuntimeConfig.scalePointsOnZoom,
|
||||
attribution: cosmosRuntimeConfig.attribution,
|
||||
onSimulationStart: () => {
|
||||
reportLayout("simulation-start", "running", 1);
|
||||
},
|
||||
@@ -320,7 +360,14 @@ export const TripleGraphView = memo(function TripleGraphView({ model }: TripleGr
|
||||
}, [activeDetail]);
|
||||
|
||||
return (
|
||||
<div style={{ position: "relative", flex: 1, minHeight: 0, background: "#05070a" }}>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
background: cosmosBackgroundCss,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
|
||||
@@ -468,7 +515,18 @@ function applyGraphModel(graph: Graph, model: TripleGraphModel): void {
|
||||
graph.setLinkWidths(model.linkWidths);
|
||||
graph.render(0);
|
||||
requestAnimationFrame(() => {
|
||||
graph.fitViewByPointPositions(Array.from(model.pointPositions), 0, cosmosRuntimeConfig.fitViewPadding);
|
||||
if (typeof cosmosRuntimeConfig.initialZoomLevel === "number") {
|
||||
graph.setZoomLevel(
|
||||
cosmosRuntimeConfig.initialZoomLevel,
|
||||
cosmosRuntimeConfig.fitViewDuration,
|
||||
);
|
||||
} else {
|
||||
graph.fitViewByPointPositions(
|
||||
Array.from(model.pointPositions),
|
||||
cosmosRuntimeConfig.fitViewDuration,
|
||||
cosmosRuntimeConfig.fitViewPadding,
|
||||
);
|
||||
}
|
||||
if (cosmosRuntimeConfig.enableSimulation) {
|
||||
graph.start(1);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
type CosmosColor = string | [number, number, number, number];
|
||||
|
||||
function parseBoolean(value: string | undefined, fallback: boolean): boolean {
|
||||
if (value === undefined) return fallback;
|
||||
const normalized = value.trim().toLowerCase();
|
||||
@@ -12,17 +14,167 @@ function parseNumber(value: string | undefined, fallback: number): number {
|
||||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
function parseOptionalNumber(value: string | undefined): number | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.length === 0) return undefined;
|
||||
const parsed = Number(trimmed);
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function parseOptionalString(value: string | undefined): string | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
function parseNumberList(value: string | undefined, fallback: number[]): number[] {
|
||||
return parseOptionalNumberList(value) ?? fallback;
|
||||
}
|
||||
|
||||
function parseOptionalNumberList(value: string | undefined): number[] | undefined {
|
||||
const raw = parseOptionalString(value);
|
||||
if (!raw) return undefined;
|
||||
const normalized = raw.startsWith("[") && raw.endsWith("]") ? raw.slice(1, -1) : raw;
|
||||
const parts = normalized
|
||||
.split(",")
|
||||
.map((entry) => entry.trim())
|
||||
.filter((entry) => entry.length > 0);
|
||||
if (parts.length === 0) return undefined;
|
||||
const parsed = parts.map((entry) => Number(entry));
|
||||
return parsed.every((entry) => Number.isFinite(entry)) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function parseColor(value: string | undefined, fallback: CosmosColor): CosmosColor {
|
||||
return parseOptionalColor(value) ?? fallback;
|
||||
}
|
||||
|
||||
function parseOptionalColor(value: string | undefined): CosmosColor | undefined {
|
||||
const raw = parseOptionalString(value);
|
||||
if (!raw) return undefined;
|
||||
const rgba = parseOptionalNumberList(raw);
|
||||
if (rgba && rgba.length === 4) {
|
||||
return [rgba[0], rgba[1], rgba[2], rgba[3]];
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function parseOptionalSeed(value: string | undefined): number | string | undefined {
|
||||
const raw = parseOptionalString(value);
|
||||
if (!raw) return undefined;
|
||||
const numeric = Number(raw);
|
||||
return Number.isFinite(numeric) ? numeric : raw;
|
||||
}
|
||||
|
||||
function toCssColor(color: CosmosColor): string {
|
||||
if (typeof color === "string") return color;
|
||||
return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3] / 255})`;
|
||||
}
|
||||
|
||||
export const cosmosRuntimeConfig = {
|
||||
enableSimulation: parseBoolean(import.meta.env.VITE_COSMOS_ENABLE_SIMULATION, true),
|
||||
debugLayout: parseBoolean(import.meta.env.VITE_COSMOS_DEBUG_LAYOUT, false),
|
||||
backgroundColor: parseColor(import.meta.env.VITE_COSMOS_BACKGROUND_COLOR, "#05070a"),
|
||||
spaceSize: parseNumber(import.meta.env.VITE_COSMOS_SPACE_SIZE, 4096),
|
||||
pointDefaultColor: parseOptionalColor(import.meta.env.VITE_COSMOS_POINT_DEFAULT_COLOR),
|
||||
pointGreyoutColor: parseOptionalColor(import.meta.env.VITE_COSMOS_POINT_GREYOUT_COLOR),
|
||||
pointGreyoutOpacity: parseOptionalNumber(import.meta.env.VITE_COSMOS_POINT_GREYOUT_OPACITY),
|
||||
pointDefaultSize: parseNumber(import.meta.env.VITE_COSMOS_POINT_DEFAULT_SIZE, 4),
|
||||
pointOpacity: parseNumber(import.meta.env.VITE_COSMOS_POINT_OPACITY, 1),
|
||||
pointSizeScale: parseNumber(import.meta.env.VITE_COSMOS_POINT_SIZE_SCALE, 1),
|
||||
hoveredPointCursor: parseOptionalString(import.meta.env.VITE_COSMOS_HOVERED_POINT_CURSOR) ?? "pointer",
|
||||
hoveredLinkCursor: parseOptionalString(import.meta.env.VITE_COSMOS_HOVERED_LINK_CURSOR) ?? "pointer",
|
||||
renderHoveredPointRing: parseBoolean(
|
||||
import.meta.env.VITE_COSMOS_RENDER_HOVERED_POINT_RING,
|
||||
true,
|
||||
),
|
||||
hoveredPointRingColor: parseColor(
|
||||
import.meta.env.VITE_COSMOS_HOVERED_POINT_RING_COLOR,
|
||||
"#35d6ff",
|
||||
),
|
||||
focusedPointRingColor: parseColor(
|
||||
import.meta.env.VITE_COSMOS_FOCUSED_POINT_RING_COLOR,
|
||||
"white",
|
||||
),
|
||||
renderLinks: parseBoolean(import.meta.env.VITE_COSMOS_RENDER_LINKS, true),
|
||||
linkDefaultColor: parseOptionalColor(import.meta.env.VITE_COSMOS_LINK_DEFAULT_COLOR),
|
||||
linkOpacity: parseNumber(import.meta.env.VITE_COSMOS_LINK_OPACITY, 1),
|
||||
linkGreyoutOpacity: parseNumber(import.meta.env.VITE_COSMOS_LINK_GREYOUT_OPACITY, 0.1),
|
||||
linkDefaultWidth: parseNumber(import.meta.env.VITE_COSMOS_LINK_DEFAULT_WIDTH, 1),
|
||||
hoveredLinkColor: parseColor(import.meta.env.VITE_COSMOS_HOVERED_LINK_COLOR, "#ffd166"),
|
||||
hoveredLinkWidthIncrease: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_HOVERED_LINK_WIDTH_INCREASE,
|
||||
2.5,
|
||||
),
|
||||
linkWidthScale: parseNumber(import.meta.env.VITE_COSMOS_LINK_WIDTH_SCALE, 1),
|
||||
scaleLinksOnZoom: parseBoolean(import.meta.env.VITE_COSMOS_SCALE_LINKS_ON_ZOOM, false),
|
||||
curvedLinks: parseBoolean(import.meta.env.VITE_COSMOS_CURVED_LINKS, true),
|
||||
fitViewPadding: parseNumber(import.meta.env.VITE_COSMOS_FIT_VIEW_PADDING, 0.12),
|
||||
curvedLinkSegments: parseNumber(import.meta.env.VITE_COSMOS_CURVED_LINK_SEGMENTS, 19),
|
||||
curvedLinkWeight: parseNumber(import.meta.env.VITE_COSMOS_CURVED_LINK_WEIGHT, 0.8),
|
||||
curvedLinkControlPointDistance: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_CURVED_LINK_CONTROL_POINT_DISTANCE,
|
||||
0.5,
|
||||
),
|
||||
linkDefaultArrows: parseBoolean(import.meta.env.VITE_COSMOS_LINK_DEFAULT_ARROWS, false),
|
||||
linkArrowsSizeScale: parseNumber(import.meta.env.VITE_COSMOS_LINK_ARROWS_SIZE_SCALE, 1),
|
||||
linkVisibilityDistanceRange: parseNumberList(
|
||||
import.meta.env.VITE_COSMOS_LINK_VISIBILITY_DISTANCE_RANGE,
|
||||
[50, 150],
|
||||
),
|
||||
linkVisibilityMinTransparency: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_LINK_VISIBILITY_MIN_TRANSPARENCY,
|
||||
0.25,
|
||||
),
|
||||
useClassicQuadtree: parseBoolean(import.meta.env.VITE_COSMOS_USE_CLASSIC_QUADTREE, false),
|
||||
simulationDecay: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_DECAY, 5000),
|
||||
simulationGravity: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_GRAVITY, 0),
|
||||
simulationCenter: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_CENTER, 0.05),
|
||||
simulationRepulsion: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_REPULSION, 0.5),
|
||||
simulationRepulsionTheta: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_SIMULATION_REPULSION_THETA,
|
||||
1.15,
|
||||
),
|
||||
simulationRepulsionQuadtreeLevels: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_SIMULATION_REPULSION_QUADTREE_LEVELS,
|
||||
12,
|
||||
),
|
||||
simulationLinkSpring: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_LINK_SPRING, 1),
|
||||
simulationLinkDistance: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_LINK_DISTANCE, 10),
|
||||
simulationLinkDistRandomVariationRange: parseNumberList(
|
||||
import.meta.env.VITE_COSMOS_SIMULATION_LINK_DISTANCE_RANDOM_VARIATION_RANGE,
|
||||
[1, 1.2],
|
||||
),
|
||||
simulationRepulsionFromMouse: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_SIMULATION_REPULSION_FROM_MOUSE,
|
||||
2,
|
||||
),
|
||||
enableRightClickRepulsion: parseBoolean(
|
||||
import.meta.env.VITE_COSMOS_ENABLE_RIGHT_CLICK_REPULSION,
|
||||
false,
|
||||
),
|
||||
simulationFriction: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_FRICTION, 0.1),
|
||||
} as const;
|
||||
simulationCluster: parseNumber(import.meta.env.VITE_COSMOS_SIMULATION_CLUSTER, 0.1),
|
||||
showFPSMonitor: parseBoolean(import.meta.env.VITE_COSMOS_SHOW_FPS_MONITOR, false),
|
||||
pixelRatio: parseNumber(import.meta.env.VITE_COSMOS_PIXEL_RATIO, 2),
|
||||
scalePointsOnZoom: parseBoolean(import.meta.env.VITE_COSMOS_SCALE_POINTS_ON_ZOOM, false),
|
||||
initialZoomLevel: parseOptionalNumber(import.meta.env.VITE_COSMOS_INITIAL_ZOOM_LEVEL),
|
||||
enableZoom: parseBoolean(import.meta.env.VITE_COSMOS_ENABLE_ZOOM, true),
|
||||
enableSimulationDuringZoom: parseBoolean(
|
||||
import.meta.env.VITE_COSMOS_ENABLE_SIMULATION_DURING_ZOOM,
|
||||
false,
|
||||
),
|
||||
enableDrag: parseBoolean(import.meta.env.VITE_COSMOS_ENABLE_DRAG, true),
|
||||
fitViewOnInit: parseBoolean(import.meta.env.VITE_COSMOS_FIT_VIEW_ON_INIT, false),
|
||||
fitViewDelay: parseNumber(import.meta.env.VITE_COSMOS_FIT_VIEW_DELAY, 250),
|
||||
fitViewPadding: parseNumber(import.meta.env.VITE_COSMOS_FIT_VIEW_PADDING, 0.12),
|
||||
fitViewDuration: parseNumber(import.meta.env.VITE_COSMOS_FIT_VIEW_DURATION, 250),
|
||||
randomSeed: parseOptionalSeed(import.meta.env.VITE_COSMOS_RANDOM_SEED),
|
||||
pointSamplingDistance: parseNumber(
|
||||
import.meta.env.VITE_COSMOS_POINT_SAMPLING_DISTANCE,
|
||||
150,
|
||||
),
|
||||
rescalePositions: parseBoolean(import.meta.env.VITE_COSMOS_RESCALE_POSITIONS, false),
|
||||
attribution: parseOptionalString(import.meta.env.VITE_COSMOS_ATTRIBUTION),
|
||||
};
|
||||
|
||||
export const cosmosBackgroundCss = toCssColor(cosmosRuntimeConfig.backgroundColor);
|
||||
|
||||
@@ -213,12 +213,23 @@ export class Renderer {
|
||||
const count = xs.length;
|
||||
const edgeCount = edges.length / 2;
|
||||
this.nodeCount = count;
|
||||
console.log("[renderer.init] start", {
|
||||
nodes: count,
|
||||
edges: edgeCount,
|
||||
route_line_vertices: routeLineVertices ? routeLineVertices.length / 2 : 0,
|
||||
});
|
||||
|
||||
// Build quadtree (spatially sorts the array)
|
||||
const spatialStart = performance.now();
|
||||
const { sorted, leaves, order } = buildSpatialIndex(xs, ys);
|
||||
this.leaves = leaves;
|
||||
this.sorted = sorted;
|
||||
this.sortedToOriginal = order;
|
||||
console.log("[renderer.init] spatial index built", {
|
||||
nodes: count,
|
||||
leaves: leaves.length,
|
||||
spatial_ms: Math.round(performance.now() - spatialStart),
|
||||
});
|
||||
|
||||
// Pre-allocate arrays for render loop (zero-allocation rendering)
|
||||
this.visibleLeafIndices = new Uint32Array(leaves.length);
|
||||
@@ -226,12 +237,18 @@ export class Renderer {
|
||||
this.countsArray = new Int32Array(leaves.length);
|
||||
|
||||
// Upload sorted particles to GPU as STATIC VBO (never changes)
|
||||
const uploadNodesStart = performance.now();
|
||||
gl.bindVertexArray(this.vao);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.nodeVbo);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, sorted, gl.STATIC_DRAW);
|
||||
gl.bindVertexArray(null);
|
||||
console.log("[renderer.init] node buffer uploaded", {
|
||||
upload_ms: Math.round(performance.now() - uploadNodesStart),
|
||||
sorted_bytes: sorted.byteLength,
|
||||
});
|
||||
|
||||
// Build vertex ID → original input index mapping
|
||||
const mapsStart = performance.now();
|
||||
const vertexIdToOriginal = new Map<number, number>();
|
||||
for (let i = 0; i < count; i++) {
|
||||
vertexIdToOriginal.set(vertexIds[i], i);
|
||||
@@ -250,6 +267,10 @@ export class Renderer {
|
||||
vertexIdToSortedIndex.set(vertexIds[i], originalToSorted[i]);
|
||||
}
|
||||
this.vertexIdToSortedIndex = vertexIdToSortedIndex;
|
||||
console.log("[renderer.init] index maps built", {
|
||||
maps_ms: Math.round(performance.now() - mapsStart),
|
||||
vertex_id_map_size: vertexIdToSortedIndex.size,
|
||||
});
|
||||
|
||||
this.useRawLineSegments = routeLineVertices !== null && routeLineVertices.length > 0;
|
||||
this.rawLineVertexCount = this.useRawLineSegments && routeLineVertices ? routeLineVertices.length / 2 : 0;
|
||||
@@ -257,14 +278,20 @@ export class Renderer {
|
||||
this.edgeCount = edgeCount;
|
||||
this.leafEdgeStarts = new Uint32Array(0);
|
||||
this.leafEdgeCounts = new Uint32Array(0);
|
||||
const uploadRoutesStart = performance.now();
|
||||
gl.bindVertexArray(this.lineVao);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.lineVbo);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, routeLineVertices, gl.STATIC_DRAW);
|
||||
gl.bindVertexArray(null);
|
||||
console.log("[renderer.init] raw line segments uploaded", {
|
||||
upload_ms: Math.round(performance.now() - uploadRoutesStart),
|
||||
total_ms: Math.round(performance.now() - t0),
|
||||
});
|
||||
return performance.now() - t0;
|
||||
}
|
||||
|
||||
// Remap edges from vertex IDs to sorted indices
|
||||
const remapEdgesStart = performance.now();
|
||||
const lineIndices = new Uint32Array(edgeCount * 2);
|
||||
let validEdges = 0;
|
||||
for (let i = 0; i < edgeCount; i++) {
|
||||
@@ -278,9 +305,15 @@ export class Renderer {
|
||||
validEdges++;
|
||||
}
|
||||
this.edgeCount = validEdges;
|
||||
console.log("[renderer.init] edges remapped", {
|
||||
remap_ms: Math.round(performance.now() - remapEdgesStart),
|
||||
valid_edges: validEdges,
|
||||
line_indices_bytes: lineIndices.byteLength,
|
||||
});
|
||||
|
||||
// Build per-leaf edge index for efficient visible-only edge drawing
|
||||
// Find which leaf each sorted index belongs to
|
||||
const edgeIndexStart = performance.now();
|
||||
const nodeToLeaf = new Uint32Array(count);
|
||||
for (let li = 0; li < leaves.length; li++) {
|
||||
const lf = leaves[li];
|
||||
@@ -314,11 +347,22 @@ export class Renderer {
|
||||
|
||||
this.leafEdgeStarts = leafEdgeOffsets;
|
||||
this.leafEdgeCounts = leafEdgeCounts;
|
||||
console.log("[renderer.init] leaf edge index built", {
|
||||
leaf_index_ms: Math.round(performance.now() - edgeIndexStart),
|
||||
leaves: leaves.length,
|
||||
sorted_edge_indices_bytes: sortedEdgeIndices.byteLength,
|
||||
});
|
||||
|
||||
// Upload sorted edges to GPU
|
||||
const uploadEdgesStart = performance.now();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.linesIbo);
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, sortedEdgeIndices, gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
||||
console.log("[renderer.init] edge buffer uploaded", {
|
||||
upload_ms: Math.round(performance.now() - uploadEdgesStart),
|
||||
total_ms: Math.round(performance.now() - t0),
|
||||
valid_edges: validEdges,
|
||||
});
|
||||
|
||||
return performance.now() - t0;
|
||||
}
|
||||
|
||||
49
frontend/src/vite-env.d.ts
vendored
49
frontend/src/vite-env.d.ts
vendored
@@ -4,16 +4,65 @@ interface ImportMetaEnv {
|
||||
readonly VITE_BACKEND_URL?: string;
|
||||
readonly VITE_COSMOS_ENABLE_SIMULATION?: string;
|
||||
readonly VITE_COSMOS_DEBUG_LAYOUT?: string;
|
||||
readonly VITE_COSMOS_BACKGROUND_COLOR?: string;
|
||||
readonly VITE_COSMOS_SPACE_SIZE?: string;
|
||||
readonly VITE_COSMOS_POINT_DEFAULT_COLOR?: string;
|
||||
readonly VITE_COSMOS_POINT_GREYOUT_COLOR?: string;
|
||||
readonly VITE_COSMOS_POINT_GREYOUT_OPACITY?: string;
|
||||
readonly VITE_COSMOS_POINT_DEFAULT_SIZE?: string;
|
||||
readonly VITE_COSMOS_POINT_OPACITY?: string;
|
||||
readonly VITE_COSMOS_POINT_SIZE_SCALE?: string;
|
||||
readonly VITE_COSMOS_HOVERED_POINT_CURSOR?: string;
|
||||
readonly VITE_COSMOS_HOVERED_LINK_CURSOR?: string;
|
||||
readonly VITE_COSMOS_RENDER_HOVERED_POINT_RING?: string;
|
||||
readonly VITE_COSMOS_HOVERED_POINT_RING_COLOR?: string;
|
||||
readonly VITE_COSMOS_FOCUSED_POINT_RING_COLOR?: string;
|
||||
readonly VITE_COSMOS_RENDER_LINKS?: string;
|
||||
readonly VITE_COSMOS_LINK_DEFAULT_COLOR?: string;
|
||||
readonly VITE_COSMOS_LINK_OPACITY?: string;
|
||||
readonly VITE_COSMOS_LINK_GREYOUT_OPACITY?: string;
|
||||
readonly VITE_COSMOS_LINK_DEFAULT_WIDTH?: string;
|
||||
readonly VITE_COSMOS_HOVERED_LINK_COLOR?: string;
|
||||
readonly VITE_COSMOS_HOVERED_LINK_WIDTH_INCREASE?: string;
|
||||
readonly VITE_COSMOS_LINK_WIDTH_SCALE?: string;
|
||||
readonly VITE_COSMOS_SCALE_LINKS_ON_ZOOM?: string;
|
||||
readonly VITE_COSMOS_CURVED_LINKS?: string;
|
||||
readonly VITE_COSMOS_CURVED_LINK_SEGMENTS?: string;
|
||||
readonly VITE_COSMOS_CURVED_LINK_WEIGHT?: string;
|
||||
readonly VITE_COSMOS_CURVED_LINK_CONTROL_POINT_DISTANCE?: string;
|
||||
readonly VITE_COSMOS_LINK_DEFAULT_ARROWS?: string;
|
||||
readonly VITE_COSMOS_LINK_ARROWS_SIZE_SCALE?: string;
|
||||
readonly VITE_COSMOS_LINK_VISIBILITY_DISTANCE_RANGE?: string;
|
||||
readonly VITE_COSMOS_LINK_VISIBILITY_MIN_TRANSPARENCY?: string;
|
||||
readonly VITE_COSMOS_USE_CLASSIC_QUADTREE?: string;
|
||||
readonly VITE_COSMOS_FIT_VIEW_PADDING?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_DECAY?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_GRAVITY?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_CENTER?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_REPULSION?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_REPULSION_THETA?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_REPULSION_QUADTREE_LEVELS?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_LINK_SPRING?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_LINK_DISTANCE?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_LINK_DISTANCE_RANDOM_VARIATION_RANGE?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_REPULSION_FROM_MOUSE?: string;
|
||||
readonly VITE_COSMOS_ENABLE_RIGHT_CLICK_REPULSION?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_FRICTION?: string;
|
||||
readonly VITE_COSMOS_SIMULATION_CLUSTER?: string;
|
||||
readonly VITE_COSMOS_SHOW_FPS_MONITOR?: string;
|
||||
readonly VITE_COSMOS_PIXEL_RATIO?: string;
|
||||
readonly VITE_COSMOS_SCALE_POINTS_ON_ZOOM?: string;
|
||||
readonly VITE_COSMOS_INITIAL_ZOOM_LEVEL?: string;
|
||||
readonly VITE_COSMOS_ENABLE_ZOOM?: string;
|
||||
readonly VITE_COSMOS_ENABLE_SIMULATION_DURING_ZOOM?: string;
|
||||
readonly VITE_COSMOS_ENABLE_DRAG?: string;
|
||||
readonly VITE_COSMOS_FIT_VIEW_ON_INIT?: string;
|
||||
readonly VITE_COSMOS_FIT_VIEW_DELAY?: string;
|
||||
readonly VITE_COSMOS_FIT_VIEW_DURATION?: string;
|
||||
readonly VITE_COSMOS_RANDOM_SEED?: string;
|
||||
readonly VITE_COSMOS_POINT_SAMPLING_DISTANCE?: string;
|
||||
readonly VITE_COSMOS_RESCALE_POSITIONS?: string;
|
||||
readonly VITE_COSMOS_ATTRIBUTION?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
Reference in New Issue
Block a user