# Troubleshooting Guide

Complete guide to common issues, error messages, and performance expectations for ruviz.

## Common Issues

### Performance Issues

**Q: My 1K point plot takes 250ms, is this normal?**

A: **Yes!** This is "cold start" performance. The time breakdown:
- Font initialization: 50-100ms (cosmic-text system setup)
- Canvas setup: 20-50ms (memory allocation, DPI scaling)
- Rendering pipeline: 50-100ms (Skia initialization, theme application)
- File I/O: 30-50ms (PNG encoding, disk write)
- **Actual plotting: < 10ms** ✅

**Key Insight**: The system is optimized for large datasets where this overhead is amortized. For 100K points, total time is only 34.6ms because the fixed overhead becomes negligible.

**Solutions**:
- For single plots: 250ms is acceptable for publication-quality output
- For multiple plots: Create larger datasets to amortize overhead
- Future: Batch API will enable < 10ms per plot after warmup

**Q: My 100K point plot is slow**

A: Measure the path you actually use. `render()` is the path that can use the parallel renderer:
```rust
let _image = Plot::new()
    .line(&x, &y)
    .render()?;
```

Expected performance on large datasets:
- 100K points: ~35ms (2.9x faster than target) ✅
- 1M points: ~87ms (5.7x faster than target) ✅

**Q: How do I know which backend is being used?**

A: `.auto_optimize()` stores an inferred backend name based on data size:
- < 1K points → Skia
- 1,000 to 99,999 points → Parallel when built with the `parallel` feature, otherwise Skia
- 100,000+ points → GPU when built with the `gpu` feature, otherwise DataShader

The `render()` and `save()` paths still use their own internal heuristics.
Those runtime heuristics are more conservative than the stored label for very
large datasets: automatic DataShader is reserved for aggregation-safe series
such as scatter and histogram.
You can also inspect or override the stored backend label:
```rust
use ruviz::core::BackendType;

let plot = Plot::new()
    .line(&x, &y)
    .backend(BackendType::Parallel);

assert_eq!(plot.get_backend_name(), "parallel");
```

### Rendering Issues

**Q: Fonts look different on different platforms**

A: ruviz uses system fonts by default. To ensure consistency:

**Option 1: Specify explicit fonts**
```rust
let theme = Theme::builder()
    .font("Arial")
    .font_size(14.0)
    .title_font_size(16.0)
    .build();

Plot::new()
    .theme(theme)
    .title("My Plot")
    .save("plot.png")?;
```

**Option 2: Use installed open fonts**
```rust
let theme = Theme::builder()
    .font("Open Sans")
    .font_size(14.0)
    .title_font_size(16.0)
    .build();

Plot::new()
    .theme(theme)
    .title("My Plot")
    .save("plot.png")?;
```

**Option 3: Prefer generic families for portability**
```rust
let theme = Theme::builder().font("sans-serif").build();

Plot::new()
    .theme(theme)
    .title("My Plot")
    .save("plot.png")?;
```

**Q: Text is rotated incorrectly**

A: This is a known limitation with complex Unicode. Workarounds:
- Use ASCII labels for axes when possible
- Simplify special characters in labels
- File a bug report with your specific Unicode case

**Q: Plot looks pixelated or low quality**

A: Increase DPI for publication-quality output:
```rust
Plot::new()
    .line(&x, &y)
    .dpi(300)  // IEEE publication standard
    .save("plot.png")?;
```

Standard DPI settings:
- 96 DPI: Screen display (default)
- 150 DPI: Presentations
- 300 DPI: IEEE publications, journals
- 600 DPI: High-resolution printing

For Typst text specifically:
- PNG output uses native 1x rasterized Typst text; higher DPI still increases overall output pixel density.
- SVG/PDF exports preserve vector text and are preferred for publication workflows.

### API Issues

**Q: Which backend should I use?**

A: Start with plain `save()` or `render()`. Use `.auto_optimize()` only when you want the stored backend label for diagnostics:

```rust
Plot::new()
    .line(&x, &y)
    .save("plot.png")?;
```

Backend decision overhead is negligible (< 142µs worst case).
For guaranteed GPU rendering, enable the `gpu` feature and call `.gpu(true)`.

**Q: Can I reuse a plot object?**

A: **No**. Plot uses a consuming API pattern. Each plot is rendered once via `.save()`:

```rust
// Wrong - plot is consumed by save()
let plot = Plot::new().line(&x, &y);
plot.save("plot1.png")?;  // Consumes plot
plot.save("plot2.png")?;  // ERROR: plot already consumed

// Correct - create separate Plot instances
Plot::new().line(&x, &y).save("plot1.png")?;
Plot::new().line(&x, &y).save("plot2.png")?;
```

**Q: How do I create multiple series on one plot?**

A: Chain `.line()`, `.scatter()`, or other plot methods:

```rust
let plot = Plot::new()
    .line(&x, &y1)
    .line(&x, &y2)
    .scatter(&x, &y3);

plot.save("multi_series.png")?;
```

**Q: Can I customize colors and styles?**

A: Yes, using themes and series customization:

```rust
// Use built-in themes
Plot::with_theme(Theme::publication())
    .line(&x, &y)
    .save("plot.png")?;

// Available themes: light(), dark(), minimal(), publication()
```

### Data Issues

**Q: My data has NaN or Inf values - will the plot work?**

A: No. Plotting will fail with invalid data. Filter first:

```rust
let x_clean: Vec<f64> = x.iter()
    .copied()
    .filter(|v| v.is_finite())
    .collect();
let y_clean: Vec<f64> = y.iter()
    .copied()
    .filter(|v| v.is_finite())
    .collect();

Plot::new()
    .line(&x_clean, &y_clean)
    .save("plot.png")?;
```

**Q: My x and y vectors have different lengths**

A: Plot will fail with `DataLengthMismatch`. Ensure equal lengths:

```rust
let min_len = x.len().min(y.len());
let x = &x[..min_len];
let y = &y[..min_len];

Plot::new().line(x, y).save("plot.png")?;
```

## Error Messages

### `EmptyDataSet`

**Cause**: Attempted to plot empty data vectors

**Example**:
```rust
let x: Vec<f64> = vec![];
let y: Vec<f64> = vec![];
Plot::new().line(&x, &y).save("plot.png")?;  // ERROR
```

**Solution**: Ensure your data vectors contain at least one point

```rust
if !x.is_empty() && !y.is_empty() {
    Plot::new().line(&x, &y).save("plot.png")?;
}
```

### `DataLengthMismatch`

**Cause**: x and y data have different lengths

**Example**:
```rust
let x = vec![1.0, 2.0, 3.0];
let y = vec![1.0, 2.0];  // Different length!
Plot::new().line(&x, &y).save("plot.png")?;  // ERROR
```

**Solution**: Ensure `x.len() == y.len()`

```rust
assert_eq!(x.len(), y.len(), "Data vectors must have equal length");
Plot::new().line(&x, &y).save("plot.png")?;
```

### `FontError`

**Cause**: Cannot find or load specified font

**Solution**:
1. Check font name spelling (case-sensitive)
2. Ensure font is installed on system
3. Try generic families like `sans-serif` or `serif`
4. Keep font choices consistent across build machines

```rust
let theme = Theme::builder().font("sans-serif").build();

Plot::new().theme(theme).title("My Plot").save("plot.png")?;
```

### `RenderError`

**Cause**: Internal rendering failure (canvas allocation, drawing operations)

**Common causes**:
- Insufficient memory for large canvas
- Invalid dimensions (width or height = 0)
- Disk write failure (permissions, disk full)

**Solution**:
- Check available memory for large plots
- Ensure output directory exists and is writable
- Verify dimensions are positive integers

### `InvalidDPI`

**Cause**: DPI value below minimum (72) or unreasonably high

**Solution**: Use standard DPI values:
```rust
Plot::new()
    .line(&x, &y)
    .dpi(300)  // Valid: 72-600 recommended
    .save("plot.png")?;
```

## Performance Expectations

### Verified Benchmark Results (Week 6)

| Dataset Size | Expected Time | Backend/Mode | vs Target |
|--------------|---------------|--------------|-----------|
| 100 points | 250ms (cold) | Skia | N/A (cold start) |
| 1K points | 250ms (cold) | Skia | N/A (cold start) |
| 10K points | 50ms | Auto-optimized | Excellent |
| 100K points | **34.6ms** | Auto-optimized | **2.9x faster** ✅ |
| 1M points | **87ms** | Auto-optimized | **5.7x faster** ✅ |

**Cold start**: First plot in application (font init overhead)
**Warm**: Subsequent plots (fonts cached)

### Throughput

**3.17 million elements/second** with auto-optimization enabled

### Auto-Optimization Overhead

Backend selection decision time: **< 142µs** (worst case)
- 100 points: 218 nanoseconds
- 1K points: 1.5 microseconds
- 100K points: 142 microseconds

**Conclusion**: Zero user-facing overhead

### Performance Best Practices

1. **Use `render()` for in-memory images and `save()` for PNG export**
2. **Batch multiple plots in single application** to amortize cold start
3. **Use appropriate plot types** (histogram faster than scatter for distributions)
4. **Profile before optimizing** - measure actual performance first

## Reporting Issues

When reporting performance or rendering issues, please include:

1. **ruviz version**
   ```bash
   cargo tree | grep ruviz
   ```

2. **Rust version**
   ```bash
   rustc --version
   ```

3. **Operating system and version**
   ```bash
   uname -a  # Linux/macOS
   # or System Information on Windows
   ```

4. **Dataset size**
   - Number of points
   - Data ranges (min/max values)

5. **Minimal reproducible example**
   ```rust
   use ruviz::prelude::*;

   fn main() -> Result<()> {
       let x = vec![/* your data */];
       let y = vec![/* your data */];

       Plot::new()
           .line(&x, &y)
           .save("plot.png")?;

       Ok(())
   }
   ```

6. **Expected vs actual behavior**
   - What did you expect to happen?
   - What actually happened?
   - Error messages (full stack trace if applicable)

### GitHub Issues

Report bugs and request features at:
https://github.com/Ameyanagi/ruviz/issues

### Performance Issues

For performance problems, also include:
- Timing measurements (use `std::time::Instant`)
- System specifications (CPU cores, RAM)
- Whether you used `render()` or `save()`, and whether `.gpu(true)` was enabled

## Architecture Notes (for Advanced Users)

### Why is cold start 250ms?

ruviz uses a **one-shot rendering API** where every `.save()` call initializes everything from scratch. This design ensures:
- Thread safety (no global state)
- Predictable behavior (no hidden state)
- Simple API (no context management required)

**Trade-off**: Fixed overhead dominates small dataset performance but becomes negligible for large datasets.

**Future work**: Batch rendering API will enable < 10ms per plot after initial warmup.

### Stored Backend Metadata

`auto_optimize()` stores these inferred backend labels:
- < 1,000 points → Skia (simple rendering pipeline)
- 1,000-100,000 points → Parallel when available, otherwise Skia
- ≥ 100,000 points → GPU when the `gpu` feature is enabled, otherwise DataShader

Thresholds are based on empirical benchmarking across diverse hardware.

### Memory Usage

Typical memory usage: < 2x data size
- 100K points (1.6MB data) → < 3.2MB peak memory
- Efficient buffer pooling reduces allocations
- Automatic cleanup on Plot drop

## FAQ

**Q: Is ruviz production-ready?**

A: Yes! Week 6 benchmarks show performance exceeding all targets by 2.9-5.7x. Property-based testing (Week 8) validates robustness.

**Q: Can I use ruviz in web applications (WASM)?**

A: Yes, experimentally. The core crate now builds for `wasm32-unknown-unknown`, and browser-facing
bindings live in `crates/ruviz-web` with a working demo in `demo/web`.

Current caveats:
- Native path-based helpers such as `.save(...)` and windowed interactive APIs remain desktop-only.
- Browser usage should prefer `render_png_bytes()`, `render_to_svg()`, and the `ruviz` session APIs.
- `ruviz` auto-registers a bundled browser fallback font for canvas sessions; register custom bytes through `ruviz::render::register_font_bytes(...)` when you need a different face.
- `web_runtime_capabilities()` can be used to detect worker, touch, and WebGPU availability before wiring browser-only features.

**Q: Does ruviz support interactive plots?**

A: Yes, experimentally. The standalone interactive window and `ruviz-gpui`
desktop adapter are supported on Linux, macOS, and Windows. Enable the
`interactive` feature and start from `examples/basic_interaction.rs`,
`examples/interactive_multi_series.rs`, `examples/interactive_scatter_clusters.rs`,
or `examples/interactive_heatmap.rs`. The current window controls are
mouse-wheel zoom, left-drag pan, right-click context menu, right-drag box zoom,
`Escape` reset, plus `Cmd/Ctrl+S` for PNG export and `Cmd/Ctrl+C` for image
copy. For GIF export examples instead of live windows, enable `animation` and
run `examples/animation_basic.rs` or `examples/animation_wave.rs`.

If the interactive window does not appear on macOS, keep `show_interactive(...)`
on a main/current-thread Tokio runtime and run it from a local GUI session rather
than a remote or headless shell.

If `interactive` or `ruviz-gpui` fails to build on Linux with missing GTK
symbols or `pkg-config` errors, install GTK3 development headers first:

```bash
sudo apt-get install libgtk-3-dev
```

If a GPUI example exits with `neither DISPLAY nor WAYLAND_DISPLAY is set`, the
binary was launched from a headless shell. Run it from a local Linux desktop
session with X11 or Wayland available.

**Q: How does ruviz compare to matplotlib/plotly?**

A: ruviz is **pure Rust**, with 2.9-5.7x faster rendering for large datasets. It prioritizes performance and type safety over extensive features.

**Q: Can I contribute to ruviz?**

A: Yes. See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines. We welcome bug reports, feature requests, and code contributions.
