Skip to content

Guide Spans

This example shows movable guide spans for highlighting data ranges.

Preview

Run And Adapt

Commands below assume a Datoviz source checkout and start at the repository root. Use your configured build environment; Python routes additionally require local bindings.

Route Availability Command or action
C Canonical native source just example-c features/guide_spans (build and run), or rerun ./build/examples/c/features/guide_spans
Python Available; direct-engine adaptation python3 -m examples.python.gallery.features.guide_spans
Browser Live WebGPU route Open live example

This example is approved as a starting point for user code and coding agents. Keep the object lifetimes and data shapes intact while adapting the data and styling.

What To Look For

The point visual uploads position, color, and diameter_px arrays, while the panel adds vertical and horizontal spans in data coordinates. Pointer events update each span's range around the current cursor data position. In the live preview, compare the shaded spans with nearby markers and ticks; spans are useful for highlighting windows of time, thresholds, or regions selected for closer analysis.

Source

#!/usr/bin/env python3
"""Vertical and horizontal guide spans over 2D markers."""

from __future__ import annotations

import ctypes

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


POINT_COUNT = 7


def _add_points(scene, panel) -> None:
    positions = np.array(
        [
            [0.5, 0.35, 0.0],
            [1.5, 0.82, 0.0],
            [2.5, 1.10, 0.0],
            [3.5, 0.62, 0.0],
            [4.5, 1.35, 0.0],
            [5.5, 1.58, 0.0],
            [6.5, 1.05, 0.0],
        ],
        dtype=np.float32,
    )
    colors = ex.color_array(ex.CYAN, ex.GREEN, ex.CYAN, ex.GREEN, ex.CYAN, ex.GREEN, ex.CYAN)
    diameters = np.array([25.0, 25.0, 25.0, 25.0, 38.0, 25.0, 25.0], dtype=np.float32)
    ex.add_points(scene, panel, positions, colors, diameters)


def _add_axes(panel) -> None:
    x_axis = dvz.dvz_panel_axis(panel, dvz.DVZ_DIM_X)
    y_axis = dvz.dvz_panel_axis(panel, dvz.DVZ_DIM_Y)
    if not x_axis or not y_axis:
        raise RuntimeError("dvz_panel_axis() failed")
    if dvz.dvz_axis_set_grid(x_axis, True) != 0:
        raise RuntimeError("dvz_axis_set_grid(X) failed")
    if dvz.dvz_axis_set_grid(y_axis, True) != 0:
        raise RuntimeError("dvz_axis_set_grid(Y) failed")
    if dvz.dvz_axis_set_label(x_axis, b"time") != 0:
        raise RuntimeError("dvz_axis_set_label(X) failed")
    if dvz.dvz_axis_set_label(y_axis, b"amplitude") != 0:
        raise RuntimeError("dvz_axis_set_label(Y) failed")


def _add_guides(panel) -> None:
    vdesc = dvz.dvz_guide_span_desc()
    vdesc.orientation = dvz.DVZ_GUIDE_ORIENTATION_VERTICAL
    vdesc.min_value = 1.2
    vdesc.max_value = 2.8
    vdesc.fill_color = dvz.DvzColor(76, 201, 240, 42)
    vdesc.outline_color = dvz.DvzColor(76, 201, 240, 170)
    vdesc.outline_width_px = 2.0
    vdesc.label = b"window"
    vspan = dvz.dvz_guide_span(panel, ctypes.byref(vdesc))
    if not vspan:
        raise RuntimeError("dvz_guide_span(vertical) failed")

    hdesc = dvz.dvz_guide_span_desc()
    hdesc.orientation = dvz.DVZ_GUIDE_ORIENTATION_HORIZONTAL
    hdesc.min_value = 0.72
    hdesc.max_value = 1.28
    hdesc.fill_color = dvz.DvzColor(255, 183, 3, 36)
    hdesc.outline_color = dvz.DvzColor(255, 183, 3, 170)
    hdesc.outline_width_px = 2.0
    hdesc.label = b"target band"
    hspan = dvz.dvz_guide_span(panel, ctypes.byref(hdesc))
    if not hspan:
        raise RuntimeError("dvz_guide_span(horizontal) failed")


def main() -> None:
    scene, figure, panel = ex.scene_panel()
    if dvz.dvz_panel_set_domain(panel, dvz.DVZ_DIM_X, 0.0, 7.0) != 0:
        raise RuntimeError("dvz_panel_set_domain(X) failed")
    if dvz.dvz_panel_set_domain(panel, dvz.DVZ_DIM_Y, 0.0, 2.0) != 0:
        raise RuntimeError("dvz_panel_set_domain(Y) failed")

    _add_guides(panel)
    _add_points(scene, panel)
    _add_axes(panel)

    def configure(view) -> None:
        ex.bind_panzoom(view, scene, panel, dvz.DVZ_DIM_MASK_XY)

    ex.run_with_view(scene, figure, "Guide Spans", configure)


if __name__ == "__main__":
    main()
/*
 * Copyright (c) 2021 Cyrille Rossant and contributors. All rights reserved.
 * Licensed under the MIT license. See LICENSE file in the project root for details.
 * SPDX-License-Identifier: MIT
 */

/* guide_spans - This example shows movable guide spans for highlighting data ranges.
 *
 * Scenario: features_guide_spans
 * Style: features, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c features/guide_spans
 * Run:    ./build/examples/c/features/guide_spans --live
 * Smoke:  ./build/examples/c/features/guide_spans --png
 *
 * What to look for: the point visual uploads position, color, and diameter_px arrays, while the
 * panel adds vertical and horizontal spans in data coordinates. Pointer events update each span's
 * range around the current cursor data position. In the live preview, compare the shaded spans with
 * nearby markers and ticks; spans are useful for highlighting windows of time, thresholds, or
 * regions selected for closer analysis.
 */



/*************************************************************************************************/
/*  Includes                                                                                     */
/*************************************************************************************************/

#include <stdbool.h>
#include <stdint.h>

#include "_alloc.h"
#include "_assertions.h"
#include "datoviz/scene.h"
#include "example_style.h"
#include "runner/scenario_runner.h"



/*************************************************************************************************/
/*  Forward declarations                                                                         */
/*************************************************************************************************/

DvzScenarioSpec dvz_example_guide_spans_scenario(void);



/*************************************************************************************************/
/*  Constants                                                                                    */
/*************************************************************************************************/

#define WIDTH  EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT
#define POINT_COUNT 7u



/*************************************************************************************************/
/*  Structs                                                                                      */
/*************************************************************************************************/

typedef struct GuideSpansState
{
    DvzPanel* panel;
    DvzGuideSpan* hspan;
    DvzGuideSpan* vspan;
} GuideSpansState;



/*************************************************************************************************/
/*  Helpers                                                                                      */
/*************************************************************************************************/

/**
 * Add a compact point series in data coordinates.
 *
 * @param scene scene owning the visual
 * @param panel panel receiving the visual
 * @return true when the points were added
 */
static bool _add_points(DvzScene* scene, DvzPanel* panel)
{
    ANN(scene);
    ANN(panel);

    const vec3 data_positions[POINT_COUNT] = {
        {0.5f, 0.35f, 0.0f}, {1.5f, 0.82f, 0.0f}, {2.5f, 1.10f, 0.0f}, {3.5f, 0.62f, 0.0f},
        {4.5f, 1.35f, 0.0f}, {5.5f, 1.58f, 0.0f}, {6.5f, 1.05f, 0.0f},
    };
    DvzColor colors[POINT_COUNT] = {{0}};
    float diameters[POINT_COUNT] = {0};

    for (uint32_t i = 0; i < POINT_COUNT; i++)
    {
        colors[i] = i % 2u == 0u
                        ? example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY)
                        : example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY);
        diameters[i] = i == 4u ? 38.0f : 25.0f;
    }

    DvzVisual* point = dvz_point(scene, 0);
    if (point == NULL)
        return false;

    DvzVisualDataUpdate updates[] = {
        {.attr_name = "position", .data = data_positions, .item_count = POINT_COUNT},
        {.attr_name = "color", .data = colors, .item_count = POINT_COUNT},
        {.attr_name = "diameter_px", .data = diameters, .item_count = POINT_COUNT},
    };
    if (dvz_visual_set_data_many(point, updates, 3) != 0)
        return false;
    if (dvz_visual_set_depth_test(point, false) != 0)
        return false;
    return dvz_panel_add_visual(panel, point, NULL) == 0;
}



/**
 * Configure axes for the guide-span example.
 *
 * @param panel target panel
 * @return true when axes were configured
 */
static bool _add_axes(DvzPanel* panel)
{
    ANN(panel);

    DvzAxis* x_axis = dvz_panel_axis(panel, DVZ_DIM_X);
    DvzAxis* y_axis = dvz_panel_axis(panel, DVZ_DIM_Y);
    if (x_axis == NULL || y_axis == NULL)
        return false;
    if (!example_graphite_cyan_apply_axis_style(x_axis, false, NULL))
        return false;
    if (!example_graphite_cyan_apply_axis_style(y_axis, true, NULL))
        return false;
    if (dvz_axis_set_grid(x_axis, true) != DVZ_OK || dvz_axis_set_grid(y_axis, true) != DVZ_OK)
        return false;
    return dvz_axis_set_label(x_axis, "time") == DVZ_OK &&
           dvz_axis_set_label(y_axis, "amplitude") == DVZ_OK;
}


/*************************************************************************************************/
/*  Scenario callbacks                                                                           */
/*************************************************************************************************/

/**
 * Initialize the guide-span feature example.
 *
 * @param ctx scenario context
 * @param out_user scenario state output
 * @return true on success
 */
static bool _scenario_init(DvzScenarioContext* ctx, void** out_user)
{
    if (ctx == NULL || out_user == NULL)
        return false;

    GuideSpansState* state = (GuideSpansState*)dvz_calloc(1, sizeof(*state));
    if (state == NULL)
        return false;

    ctx->figure = dvz_figure(ctx->scene, ctx->width, ctx->height, 0);
    if (ctx->figure == NULL)
        goto error;

    DvzPanel* panel = dvz_panel_full(ctx->figure);
    if (panel == NULL)
        goto error;
    state->panel = panel;
    example_graphite_cyan_set_panel_background(panel);

    if (dvz_panel_set_domain(panel, DVZ_DIM_X, 0.0, 7.0) != 0)
        goto error;
    if (dvz_panel_set_domain(panel, DVZ_DIM_Y, 0.0, 2.0) != 0)
        goto error;

    DvzGuideSpanDesc vdesc = dvz_guide_span_desc();
    vdesc.orientation = DVZ_GUIDE_ORIENTATION_VERTICAL;
    vdesc.min_value = 1.2;
    vdesc.max_value = 2.8;
    vdesc.fill_color = dvz_color_rgba(76, 201, 240, 42);
    vdesc.outline_color = dvz_color_rgba(76, 201, 240, 170);
    vdesc.outline_width_px = 2.0f;
    vdesc.label = "window";
    state->vspan = dvz_guide_span(panel, &vdesc);
    if (state->vspan == NULL)
        goto error;

    DvzGuideSpanDesc hdesc = dvz_guide_span_desc();
    hdesc.orientation = DVZ_GUIDE_ORIENTATION_HORIZONTAL;
    hdesc.min_value = 0.72;
    hdesc.max_value = 1.28;
    hdesc.fill_color = dvz_color_rgba(255, 183, 3, 36);
    hdesc.outline_color = dvz_color_rgba(255, 183, 3, 170);
    hdesc.outline_width_px = 2.0f;
    hdesc.label = "target band";
    state->hspan = dvz_guide_span(panel, &hdesc);
    if (state->hspan == NULL)
        goto error;

    if (!_add_points(ctx->scene, panel) || !_add_axes(panel))
        goto error;
    if (dvz_scenario_panzoom(ctx, panel, NULL, DVZ_DIM_MASK_XY) == NULL)
        goto error;

    *out_user = state;
    return true;

error:
    dvz_free(state);
    return false;
}



/**
 * Center both guide spans around the current pointer position.
 *
 * @param ctx scenario context
 * @param event scenario event
 * @param user scenario state
 */
static void _scenario_event(DvzScenarioContext* ctx, const DvzScenarioEvent* event, void* user)
{
    if (ctx == NULL || event == NULL || user == NULL || event->kind != DVZ_SCENARIO_EVENT_POINTER)
        return;

    const DvzScenarioPointerEvent* pointer = &event->content.pointer;
    if (
        pointer->type != DVZ_SCENARIO_POINTER_MOVE &&
        pointer->type != DVZ_SCENARIO_POINTER_DRAG)
        return;

    GuideSpansState* state = (GuideSpansState*)user;
    double data[2] = {0};
    if (!dvz_panel_position_to_data(
            state->panel, DVZ_PANEL_COORD_FIGURE_PX,
            (const double[2]){pointer->x, pointer->y}, data))
    {
        return;
    }

    const double half_x = 0.65;
    const double half_y = 0.22;
    (void)dvz_guide_span_set_range(state->vspan, data[0] - half_x, data[0] + half_x);
    (void)dvz_guide_span_set_range(state->hspan, data[1] - half_y, data[1] + half_y);
}



/**
 * Destroy the guide-span example state.
 *
 * @param ctx scenario context
 * @param user scenario state
 */
static void _scenario_destroy(DvzScenarioContext* ctx, void* user)
{
    (void)ctx;
    dvz_free(user);
}



/**
 * Return the guide-span scenario specification.
 *
 * @return scenario specification
 */
DvzScenarioSpec dvz_example_guide_spans_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "features_guide_spans",
        .title = "Guide Spans",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .requirements = DVZ_SCENARIO_REQ_CONTROLLER | DVZ_SCENARIO_REQ_PANZOOM,
        .init = _scenario_init,
        .event = _scenario_event,
        .destroy = _scenario_destroy,
    };
}



/*************************************************************************************************/
/*  Functions                                                                                    */
/*************************************************************************************************/

/**
 * Run the guide-span feature example through the native scenario runner.
 *
 * @param argc command-line argument count
 * @param argv command-line argument vector
 * @return process exit code
 */
#ifndef DVZ_EXAMPLE_NO_MAIN
int main(int argc, char** argv)
{
    DvzScenarioSpec spec = dvz_example_guide_spans_scenario();
    return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details

Data

Field Value
kind synthetic