CoolFace
Apppublic

lerobot/robot-learning-tutorial

sourceHugging Faceupdated 1y agoView on Hugging Face
508likes
ChartRendererRefactored.svelte243 linesDownload Raw Back to renderers
1<script>2  import { onMount, onDestroy } from 'svelte';3  import { SVGManager } from './core/svg-manager.js';4  import { GridRenderer } from './core/grid-renderer.js';5  import { PathRenderer } from './core/path-renderer.js';6  import { InteractionManager } from './core/interaction-manager.js';7  import { ChartTransforms } from './utils/chart-transforms.js';8  import { trackioSampler } from '../core/adaptive-sampler.js';9  10  // Props - same as original ChartRenderer11  export let metricData = {};12  export let rawMetricData = {};13  export let colorForRun = (name) => '#999';14  export let variant = 'classic';15  export let logScaleX = false;16  export let smoothing = false;17  export let normalizeLoss = true;18  export let metricKey = '';19  export let titleText = '';20  export let hostEl = null;21  export let width = 800;22  export let height = 150;23  export let margin = { top: 10, right: 12, bottom: 46, left: 44 };24  export let onHover = null;25  export let onLeave = null;26  27  // Internal state28  let container;29  let svgManager;30  let gridRenderer;31  let pathRenderer;32  let interactionManager;33  let cleanup;34  35  // Sampling state36  let sampledData = {};37  let samplingInfo = {};38  let needsSampling = false;39  40  // Computed values41  $: innerHeight = height - margin.top - margin.bottom;42  43  // Reactive rendering when data or props change44  $: {45    if (container && svgManager) {46      // List all dependencies to trigger render when any change47      void metricData;48      void metricKey;49      void variant;50      void logScaleX;51      void normalizeLoss;52      void smoothing;53      render();54    }55  }56  57  /**58   * Initialize all managers and renderers59   */60  function initializeManagers() {61    if (!container) return;62    63    // Create SVG manager with configuration64    svgManager = new SVGManager(container, { width, height, margin });65    svgManager.ensureSvg();66    svgManager.initializeScales(logScaleX);67    68    // Create specialized renderers69    gridRenderer = new GridRenderer(svgManager);70    pathRenderer = new PathRenderer(svgManager);71    interactionManager = new InteractionManager(svgManager, pathRenderer);72    73    console.log('๐Ÿ“Š Chart managers initialized');74  }75  76  /**77   * Apply adaptive sampling to large datasets78   */79  function applySampling() {80    // Check if any run has more than 400 points81    const runSizes = Object.keys(metricData).map(run => (metricData[run] || []).length);82    const maxSize = Math.max(0, ...runSizes);83    needsSampling = maxSize > 400;84    85    if (needsSampling) {86      console.log(`๐ŸŽฏ Large dataset detected (${maxSize} points), applying adaptive sampling`);87      const result = trackioSampler.sampleMetricData(metricData, 'smart');88      sampledData = result.sampledData;89      samplingInfo = result.samplingInfo;90      91      // Log sampling stats92      Object.keys(samplingInfo).forEach(run => {93        const info = samplingInfo[run];94        console.log(`๐Ÿ“Š ${run}: ${info.originalLength} โ†’ ${info.sampledLength} points (${(info.compressionRatio * 100).toFixed(1)}% retained)`);95      });96    } else {97      sampledData = metricData;98      samplingInfo = {};99    }100  }101 102  /**103   * Main render function - orchestrates all rendering104   */105  function render() {106    if (!svgManager) return;107    108    // Apply sampling if needed109    applySampling();110    111    // Use sampled data for rendering112    const dataToRender = needsSampling ? sampledData : metricData;113    114    // Validate and clean data115    const cleanedData = ChartTransforms.validateData(dataToRender);116    const processedData = ChartTransforms.processMetricData(cleanedData, metricKey, normalizeLoss);117    118    if (!processedData.hasData) {119      const { root } = svgManager.getGroups();120      root.style('display', 'none');121      return;122    }123    124    const { root } = svgManager.getGroups();125    root.style('display', null);126    127    // Update scales based on log scale setting128    svgManager.initializeScales(logScaleX);129    130    // Setup scales and domains131    const { stepIndex } = ChartTransforms.setupScales(svgManager, processedData, logScaleX);132    const normalizeY = ChartTransforms.createNormalizeFunction(processedData, normalizeLoss);133    134    // Update lineGen with normalization135    const { line: lineGen, y: yScale } = svgManager.getScales();136    lineGen.y(d => yScale(normalizeY(d.value)));137    138    // Update layout and render axes139    const { innerWidth, xTicksForced, yTicksForced } = svgManager.updateLayout(processedData.hoverSteps, logScaleX);140    141    // Render grid142    gridRenderer.renderGrid(xTicksForced, yTicksForced, processedData.hoverSteps, variant);143    144    // Render data series145    pathRenderer.renderSeries(146      processedData.runs, 147      cleanedData, 148      rawMetricData, 149      colorForRun, 150      smoothing, 151      logScaleX, 152      stepIndex, 153      normalizeY154    );155    156    // Setup interactions157    interactionManager.setupHoverInteractions(158      processedData.hoverSteps,159      stepIndex,160      processedData.runs.map(r => ({ 161        run: r, 162        color: colorForRun(r), 163        values: (cleanedData[r] || []).slice().sort((a, b) => a.step - b.step) 164      })),165      normalizeY,166      processedData.isAccuracy,167      innerWidth,168      logScaleX,169      onHover,170      onLeave171    );172  }173  174  /**175   * Public API: Show hover line at specific step176   */177  export function showHoverLine(step) {178    if (!interactionManager) return;179    180    // Use sampled data for interactions as well181    const dataToRender = needsSampling ? sampledData : metricData;182    const cleanedData = ChartTransforms.validateData(dataToRender);183    const processedData = ChartTransforms.processMetricData(cleanedData, metricKey, normalizeLoss);184    const { stepIndex } = ChartTransforms.setupScales(svgManager, processedData, logScaleX);185    186    interactionManager.showHoverLine(step, processedData.hoverSteps, stepIndex, logScaleX);187  }188  189  /**190   * Public API: Hide hover line191   */192  export function hideHoverLine() {193    if (interactionManager) {194      interactionManager.hideHoverLine();195    }196  }197  198  /**199   * Setup resize observer and lifecycle200   */201  onMount(() => {202    initializeManagers();203    render();204    205    // Debounced resize handling for better mobile performance206    let resizeTimeout;207    const debouncedRender = () => {208      if (resizeTimeout) clearTimeout(resizeTimeout);209      resizeTimeout = setTimeout(() => {210        render();211      }, 100);212    };213    214    const ro = window.ResizeObserver ? new ResizeObserver(debouncedRender) : null;215    if (ro && container) ro.observe(container);216    217    // Listen for orientation changes on mobile218    const handleOrientationChange = () => {219      setTimeout(() => {220        render();221      }, 300);222    };223    224    window.addEventListener('orientationchange', handleOrientationChange);225    window.addEventListener('resize', debouncedRender);226    227    cleanup = () => { 228      if (ro) ro.disconnect();229      if (resizeTimeout) clearTimeout(resizeTimeout);230      window.removeEventListener('orientationchange', handleOrientationChange);231      window.removeEventListener('resize', debouncedRender);232      if (svgManager) svgManager.destroy();233      if (interactionManager) interactionManager.destroy();234    };235  });236  237  onDestroy(() => {238    cleanup && cleanup();239  });240</script>241 242<div bind:this={container} style="width: 100%; height: 100%; min-width: 200px; overflow: hidden;"></div>243