Skip to content

Segment

This example draws independent line segments with different cap styles.

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 visuals/segment (build and run), or rerun ./build/examples/c/visuals/segment
Python Available; manually maintained direct-engine example python3 -m examples.python.gallery.visuals.segment
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

Start and end position arrays define each segment, while color and width arrays encode per-segment styling. The three horizontal bands reuse the same data with different cap settings, making it easy to compare butt, square, and round endpoints for uncertainty bars, links, ticks, and measurement overlays.

Source

#!/usr/bin/env python3
"""Independent stroked line segments with rounded caps."""

from __future__ import annotations

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


def main() -> None:
    scene, figure, panel = ex.scene_panel()

    count = 32
    t = np.linspace(0.0, 1.0, count, dtype=np.float32)
    a = 2.0 * np.pi * t
    starts = np.column_stack([0.18 * np.cos(a), 0.18 * np.sin(a), np.zeros(count)]).astype(
        np.float32
    )
    ends = np.column_stack([0.78 * np.cos(a), 0.55 * np.sin(a), np.zeros(count)]).astype(np.float32)
    colors = np.column_stack(
        [
            np.full(count, 34, dtype=np.uint8),
            (150 + 80 * t).astype(np.uint8),
            np.full(count, 238, dtype=np.uint8),
            np.full(count, 235, dtype=np.uint8),
        ]
    )
    widths = np.linspace(2.0, 8.0, count, dtype=np.float32)

    segment = dvz.dvz_segment(scene, 0)
    if not segment:
        raise RuntimeError("dvz_segment() failed")
    dvz.dvz_visual_set_data_many(
        segment,
        {
            "position_start": starts,
            "position_end": ends,
            "color": colors,
            "stroke_width_px": widths,
        },
    )
    dvz.dvz_segment_set_caps(segment, dvz.DVZ_SEGMENT_CAP_ROUND, dvz.DVZ_SEGMENT_CAP_ROUND)
    ex.add_visual(panel, segment)

    ex.run_with_view(
        scene,
        figure,
        "Segment",
        lambda view: ex.bind_panzoom(view, scene, panel, dvz.DVZ_DIM_MASK_XY),
    )


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
 */

/* segment - This example draws independent line segments with different cap styles.
 *
 * What to look for: start and end position arrays define each segment, while color and width
 * arrays encode per-segment styling. The three horizontal bands reuse the same data with different
 * cap settings, making it easy to compare butt, square, and round endpoints for uncertainty bars,
 * links, ticks, and measurement overlays.
 *
 * Scenario: visuals_segment
 * Style: visuals, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c visuals/segment
 * Run:    ./build/examples/c/visuals/segment --live
 * Smoke:  ./build/examples/c/visuals/segment --png
 */



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

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

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



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

#define WIDTH  EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT
#define SEGMENT_COUNT     24u
#define SEGMENTS_PER_BAND 8u

static const float TAU = 6.28318530718f;



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

DvzScenarioSpec dvz_visual_segment_scenario(void);



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

/**
 * Blend two color channels.
 *
 * @param a first channel
 * @param b second channel
 * @param t blend factor in [0, 1]
 * @return blended channel
 */
static uint8_t _mix_channel(uint8_t a, uint8_t b, float t)
{
    return (uint8_t)((1.0f - t) * (float)a + t * (float)b + 0.5f);
}



/**
 * Return one deterministic segment color.
 *
 * @param index segment index
 * @return segment color
 */
static DvzColor _segment_color(uint32_t index)
{
    DvzColor primary = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY);
    DvzColor secondary = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY);
    DvzColor warning = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING);

    const uint32_t phase = index % 8u;
    const float t = (float)phase / 7.0f;
    DvzColor a = index % 3u == 2u ? warning : primary;
    DvzColor b = index % 3u == 0u ? secondary : primary;
    return dvz_color_rgba(
        _mix_channel(a.r, b.r, t), _mix_channel(a.g, b.g, t), _mix_channel(a.b, b.b, t), 232);
}



/**
 * Fill independent endpoint pairs arranged in three separated bands.
 *
 * @param starts output segment starts
 * @param ends output segment ends
 * @param colors output segment colors
 * @param widths output segment stroke widths
 */
static void _fill_segments(
    vec3 starts[SEGMENT_COUNT], vec3 ends[SEGMENT_COUNT], DvzColor colors[SEGMENT_COUNT],
    float widths[SEGMENT_COUNT])
{
    for (uint32_t i = 0; i < SEGMENT_COUNT; i++)
    {
        const uint32_t band = i / 8u;
        const uint32_t col = i % 8u;
        const float t = (float)col / 7.0f;
        const float cx = -0.82f + 1.64f * t;
        const float cy = 0.52f - 0.52f * (float)band;
        const float angle = (0.10f + 0.11f * (float)band + 0.07f * (float)(col % 3u)) * TAU;
        const float length = 0.13f + 0.08f * (float)((col + 2u * band) % 4u);
        const float dx = 0.5f * length * cosf(angle);
        const float dy = 0.5f * length * sinf(angle);

        starts[i][0] = cx - dx;
        starts[i][1] = cy - dy;
        starts[i][2] = 0.0f;
        ends[i][0] = cx + dx;
        ends[i][1] = cy + dy;
        ends[i][2] = 0.0f;
        colors[i] = _segment_color(i);
        widths[i] = 2.0f + 5.0f * (float)((i + band) % 5u);
    }
}


/**
 * Add one band of segments with a shared endpoint cap style.
 *
 * @param scene scene owning the visual
 * @param panel panel receiving the visual
 * @param starts all segment starts
 * @param ends all segment ends
 * @param colors all segment colors
 * @param widths all segment stroke widths
 * @param band zero-based cap band
 * @param cap endpoint cap style
 * @return true on success
 */
static bool _add_segment_band(
    DvzScene* scene, DvzPanel* panel, const vec3 starts[SEGMENT_COUNT],
    const vec3 ends[SEGMENT_COUNT], const DvzColor colors[SEGMENT_COUNT],
    const float widths[SEGMENT_COUNT], uint32_t band, DvzSegmentCap cap)
{
    ANN(scene);
    ANN(panel);
    ANN(starts);
    ANN(ends);
    ANN(colors);
    ANN(widths);

    if (band >= 3u)
        return false;

    const uint32_t offset = band * SEGMENTS_PER_BAND;
    DvzVisual* visual = dvz_segment(scene, 0);
    if (visual == NULL)
        return false;

    DvzVisualDataUpdate updates[] = {
        {.attr_name = "position_start", .data = &starts[offset], .item_count = SEGMENTS_PER_BAND},
        {.attr_name = "position_end", .data = &ends[offset], .item_count = SEGMENTS_PER_BAND},
        {.attr_name = "color", .data = &colors[offset], .item_count = SEGMENTS_PER_BAND},
        {.attr_name = "stroke_width_px", .data = &widths[offset], .item_count = SEGMENTS_PER_BAND},
    };
    if (dvz_visual_set_data_many(visual, updates, 4) != 0)
        return false;
    if (dvz_segment_set_caps(visual, cap, cap) != 0)
        return false;
    return dvz_panel_add_visual(panel, visual, NULL) == 0;
}



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

/**
 * Initialize the retained segment visual scenario.
 *
 * @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)
        return false;
    if (out_user != NULL)
        *out_user = NULL;

    vec3 starts[SEGMENT_COUNT] = {{0}};
    vec3 ends[SEGMENT_COUNT] = {{0}};
    DvzColor colors[SEGMENT_COUNT] = {{0}};
    float widths[SEGMENT_COUNT] = {0};
    _fill_segments(starts, ends, colors, widths);

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

    DvzPanel* panel = dvz_panel_full(ctx->figure);
    if (panel == NULL)
        return false;
    example_graphite_cyan_set_panel_background(panel);

    if (!_add_segment_band(
            ctx->scene, panel, (const vec3*)starts, (const vec3*)ends, colors, widths, 0,
            DVZ_SEGMENT_CAP_BUTT))
        return false;
    if (!_add_segment_band(
            ctx->scene, panel, (const vec3*)starts, (const vec3*)ends, colors, widths, 1,
            DVZ_SEGMENT_CAP_SQUARE))
        return false;
    if (!_add_segment_band(
            ctx->scene, panel, (const vec3*)starts, (const vec3*)ends, colors, widths, 2,
            DVZ_SEGMENT_CAP_ROUND))
        return false;

    DvzPanzoom* panzoom = dvz_scenario_panzoom(ctx, panel, NULL, DVZ_DIM_MASK_XY);
    return panzoom != NULL;
}



/**
 * Return the segment visual scenario specification.
 *
 * @return scenario specification
 */
DvzScenarioSpec dvz_visual_segment_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "visuals_segment",
        .title = "Segment",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .init = _scenario_init,
    };
}



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

/**
 * Run the retained segment visual 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_visual_segment_scenario();
    return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details

Tags

segment, panzoom, capture

Data

Field Value
kind synthetic