Skip to content

Visual Data Update

This example replaces all arrays on one point visual.

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/update_visual_data (build and run), or rerun ./build/examples/c/features/update_visual_data
Python Available; direct-engine adaptation python3 -m examples.python.gallery.features.update_visual_data
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 initial upload draws seven small gray points in a line. After one second, the example replaces the full position, color, and diameter_px arrays with a colored arc of larger points. This shows the simple full-data update path, which is useful when the whole dataset changes but the same visual, panel attachment, and style should remain in place.

Source

#!/usr/bin/env python3
"""Replace all point arrays on an existing visual."""

from __future__ import annotations

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


POINT_COUNT = 7


def _point_arrays(updated: bool):
    initial_positions = np.array(
        [
            [-0.72, -0.32, 0.0],
            [-0.48, -0.32, 0.0],
            [-0.24, -0.32, 0.0],
            [+0.00, -0.32, 0.0],
            [+0.24, -0.32, 0.0],
            [+0.48, -0.32, 0.0],
            [+0.72, -0.32, 0.0],
        ],
        dtype=np.float32,
    )
    updated_positions = np.array(
        [
            [-0.72, +0.18, 0.0],
            [-0.48, -0.02, 0.0],
            [-0.24, +0.30, 0.0],
            [+0.00, +0.04, 0.0],
            [+0.24, +0.30, 0.0],
            [+0.48, -0.02, 0.0],
            [+0.72, +0.18, 0.0],
        ],
        dtype=np.float32,
    )
    initial_colors = ex.color_array(*(ex.TEXT for _ in range(POINT_COUNT)))
    updated_colors = ex.color_array(ex.CYAN, ex.GREEN, ex.YELLOW, ex.TEXT, ex.YELLOW, ex.GREEN, ex.CYAN)
    initial_diameters = np.full(POINT_COUNT, 18.0, dtype=np.float32)
    updated_diameters = np.array([26.0, 34.0, 44.0, 58.0, 44.0, 34.0, 26.0], dtype=np.float32)

    if updated:
        return updated_positions, updated_colors, updated_diameters
    return initial_positions, initial_colors, initial_diameters


def _upload(point, updated: bool) -> None:
    positions, colors, diameters = _point_arrays(updated)
    if dvz.dvz_visual_set_data_many(
        point,
        {
            "position": positions,
            "color": colors,
            "diameter_px": diameters,
        },
    ) != 0:
        raise RuntimeError("dvz_visual_set_data_many() failed")


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

    point = dvz.dvz_point(scene, 0)
    if not point:
        raise RuntimeError("dvz_point() failed")
    _upload(point, False)
    ex.set_filled_point_style(point)
    dvz.dvz_visual_set_depth_test(point, False)
    ex.add_visual(panel, point)

    state = {"updated": False}

    def on_frame(_view, _frame_index: int, elapsed: float) -> None:
        if not state["updated"] and elapsed >= 1.0:
            _upload(point, True)
            state["updated"] = True

    ex.run_with_frame_callback(scene, figure, "Visual Data Update", on_frame)


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

/* This example replaces all arrays on one retained point visual.
 *
 * What to look for: the initial upload draws seven small gray points in a line. After one second,
 * the example replaces the full position, color, and diameter_px arrays with a colored arc of
 * larger points. This shows the simple full-data update path, which is useful when the whole
 * dataset changes but the same visual, panel attachment, and style should remain in place.
 *
 * Scenario: features_update_visual_data
 * Style: features, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c features/update_visual_data
 * Run:    ./build/examples/c/features/update_visual_data --live
 * Smoke:  ./build/examples/c/features/update_visual_data --png
 * Video:  ./build/examples/c/features/update_visual_data --offscreen-record 120
 */



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

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

#include "_alloc.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 POINT_COUNT 7u



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

typedef struct UpdateVisualDataState
{
    DvzVisual* point;
    bool updated;
} UpdateVisualDataState;



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

DvzScenarioSpec dvz_example_update_visual_data_scenario(void);



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

/**
 * Upload either the initial or replacement point arrays to one retained point visual.
 *
 * @param visual point visual
 * @param updated whether to upload the replacement data
 * @return true when the upload succeeds
 */
static bool _upload_points(DvzVisual* visual, bool updated)
{
    if (visual == NULL)
        return false;

    const vec3 initial_positions[POINT_COUNT] = {
        {-0.72f, -0.40f, 0.0f}, {-0.48f, -0.40f, 0.0f}, {-0.24f, -0.40f, 0.0f},
        {+0.00f, -0.40f, 0.0f}, {+0.24f, -0.40f, 0.0f}, {+0.48f, -0.40f, 0.0f},
        {+0.72f, -0.40f, 0.0f},
    };
    const vec3 updated_positions[POINT_COUNT] = {
        {-0.72f, +0.24f, 0.0f}, {-0.48f, +0.02f, 0.0f}, {-0.24f, +0.42f, 0.0f},
        {+0.00f, +0.10f, 0.0f}, {+0.24f, +0.42f, 0.0f}, {+0.48f, +0.02f, 0.0f},
        {+0.72f, +0.24f, 0.0f},
    };
    DvzColor initial_colors[POINT_COUNT] = {
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
    };
    DvzColor updated_colors[POINT_COUNT] = {
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
    };
    const float initial_diameters[POINT_COUNT] = {26.0f, 26.0f, 26.0f, 26.0f, 26.0f, 26.0f, 26.0f};
    const float updated_diameters[POINT_COUNT] = {34.0f, 42.0f, 54.0f, 68.0f, 54.0f, 42.0f, 34.0f};

    for (uint32_t i = 0; i < POINT_COUNT; i++)
        initial_colors[i].a = 210u;

    DvzVisualDataUpdate updates[] = {
        {
            .attr_name = "position",
            .data = updated ? updated_positions : initial_positions,
            .item_count = POINT_COUNT,
        },
        {
            .attr_name = "color",
            .data = updated ? updated_colors : initial_colors,
            .item_count = POINT_COUNT,
        },
        {
            .attr_name = "diameter_px",
            .data = updated ? updated_diameters : initial_diameters,
            .item_count = POINT_COUNT,
        },
    };
    return dvz_visual_set_data_many(visual, updates, 3) == 0;
}


/**
 * Apply one idempotent state of the retained full-data update.
 *
 * @param state scenario state
 * @param updated whether the replacement data should be active
 * @return true when the state is already active or the upload succeeds
 */
static bool _set_update_state(UpdateVisualDataState* state, bool updated)
{
    if (state == NULL)
        return false;
    if (state->updated == updated)
        return true;

    const bool ok = _upload_points(state->point, updated);
    if (ok)
        state->updated = updated;
    return ok;
}



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

/**
 * Initialize the retained visual data update 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 || out_user == NULL)
        return false;

    UpdateVisualDataState* state =
        (UpdateVisualDataState*)dvz_calloc(1, sizeof(UpdateVisualDataState));
    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;
    example_graphite_cyan_set_panel_background(panel);

    state->point = dvz_point(ctx->scene, 0);
    if (state->point == NULL)
        goto error;
    if (!_upload_points(state->point, false))
        goto error;

    DvzPointStyleDesc style = dvz_point_style_desc();
    style.aspect = DVZ_SHAPE_ASPECT_FILLED;
    style.stroke_width_px = 0.0f;
    if (dvz_point_set_style(state->point, &style) != 0)
        goto error;

    if (dvz_visual_set_depth_test(state->point, false) != 0)
        goto error;

    if (dvz_panel_add_visual(panel, state->point, NULL) != 0)
        goto error;

    *out_user = state;
    return true;

error:
    dvz_free(state);
    return false;
}



/**
 * Replace retained point data once after one second of scenario time.
 *
 * @param ctx scenario context
 * @param user scenario state
 */
static void _scenario_frame(DvzScenarioContext* ctx, void* user)
{
    if (ctx == NULL || user == NULL)
        return;

    UpdateVisualDataState* state = (UpdateVisualDataState*)user;
    if (ctx->preview_mode && ctx->preview_segment_id != NULL)
    {
        (void)_set_update_state(state, strcmp(ctx->preview_segment_id, "after") == 0);
        return;
    }

    const double time = ctx->preview_mode ? dvz_scenario_preview_time(ctx) : ctx->time;
    if (time >= 1.0)
        (void)_set_update_state(state, true);
}



/**
 * Destroy the retained visual data update scenario.
 *
 * @param ctx scenario context
 * @param user scenario state
 */
static void _scenario_destroy(DvzScenarioContext* ctx, void* user)
{
    (void)ctx;
    dvz_free(user);
}



/**
 * Return the retained visual data update scenario specification.
 *
 * @return scenario specification
 */
DvzScenarioSpec dvz_example_update_visual_data_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "features_update_visual_data",
        .title = "Visual Data Update",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .init = _scenario_init,
        .frame = _scenario_frame,
        .destroy = _scenario_destroy,
    };
}



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

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

Data

Field Value
kind synthetic