Graph Topology Visualizer

Displays DSP graph structure with blocks arranged by topological depth.

Overview

GraphTopologyVisualizer renders a static snapshot of a DSP graph, showing:

  • Blocks colored by category (generator, effector, modulator, I/O)
  • Audio connections as solid bezier curves
  • Modulation connections as dashed lines with parameter labels
  • Block names as text labels

Creating a Visualizer

With Default Configuration

#![allow(unused)]
fn main() {
use bbx_draw::GraphTopologyVisualizer;
use bbx_dsp::{blocks::OscillatorBlock, graph::GraphBuilder, waveform::Waveform};

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);
builder.add(OscillatorBlock::new(440.0, Waveform::Sine, None));

let topology = builder.capture_topology();
let visualizer = GraphTopologyVisualizer::new(topology);
}

With Custom Configuration

#![allow(unused)]
fn main() {
use bbx_draw::{GraphTopologyVisualizer, config::GraphTopologyConfig};

let config = GraphTopologyConfig {
    block_width: 150.0,
    block_height: 60.0,
    show_arrows: false,
    ..Default::default()
};

let visualizer = GraphTopologyVisualizer::with_config(topology, config);
}

Configuration Options

GraphTopologyConfig

OptionTypeDefaultDescription
block_widthf32120.0Width of block rectangles
block_heightf3250.0Height of block rectangles
horizontal_spacingf3280.0Space between depth columns
vertical_spacingf3230.0Space between blocks in column

Colors

OptionTypeDefaultDescription
generator_colorRgbBlueGenerator block fill
effector_colorRgbGreenEffector block fill
modulator_colorRgbPurpleModulator block fill
io_colorRgbOrangeI/O block fill
audio_connection_colorRgbGrayAudio connection lines
modulation_connection_colorRgbPinkModulation connection lines
text_colorRgbWhiteBlock label text

Connections

OptionTypeDefaultDescription
audio_connection_weightf322.0Audio line thickness
modulation_connection_weightf321.5Modulation line thickness
show_arrowsbooltrueShow directional arrows
arrow_sizef328.0Arrow head size
dash_lengthf328.0Modulation dash length
dash_gapf324.0Gap between dashes

Layout Algorithm

Blocks are positioned using topological depth:

  1. Source blocks (no inputs) have depth 0
  2. Each block's depth is max(input depths) + 1
  3. Blocks are arranged left-to-right by depth
  4. Blocks at the same depth are stacked vertically

This ensures signal flow reads left-to-right.

Updating Topology

For dynamic graphs, update the topology at runtime:

#![allow(unused)]
fn main() {
// After modifying the graph
let new_topology = builder.capture_topology();
visualizer.set_topology(new_topology);
}

Example

use bbx_draw::{GraphTopologyVisualizer, Visualizer};
use bbx_dsp::{blocks::{GainBlock, OscillatorBlock}, graph::GraphBuilder, waveform::Waveform};
use nannou::prelude::*;

struct Model {
    visualizer: GraphTopologyVisualizer,
}

fn model(app: &App) -> Model {
    app.new_window().view(view).build().unwrap();

    let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);
    let osc = builder.add(OscillatorBlock::new(440.0, Waveform::Sine, None));
    let gain = builder.add(GainBlock::new(-6.0, None));
    builder.connect(osc, 0, gain, 0);

    let topology = builder.capture_topology();

    Model {
        visualizer: GraphTopologyVisualizer::new(topology),
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.visualizer.update();
}

fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();
    draw.background().color(BLACK);
    model.visualizer.draw(&draw, app.window_rect());
    draw.to_frame(app, &frame).unwrap();
}

fn main() {
    nannou::app(model).update(update).run();
}