Skip to content

Partial Data Update

This example moves only part of a point visual's position array.

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_partial (build and run), or rerun ./build/examples/c/features/update_partial
Python Available; direct-engine adaptation python3 -m examples.python.gallery.features.update_partial
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 visual initially uploads six points with position, color, and diameter_px arrays. After one second, dvz_visual_set_data_range() replaces only two position items, moving the middle pair upward while colors and diameters stay unchanged. Partial updates are useful when a large dataset changes locally and reuploading every item would waste time.

Source

#!/usr/bin/env python3
"""Move part of a point visual with a retained data-range update."""

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()

    positions = np.array(
        [
            [-0.72, -0.30, 0.0],
            [-0.44, -0.30, 0.0],
            [-0.16, -0.30, 0.0],
            [+0.16, -0.30, 0.0],
            [+0.44, -0.30, 0.0],
            [+0.72, -0.30, 0.0],
        ],
        dtype=np.float32,
    )
    colors = ex.color_array(ex.TEXT, ex.CYAN, ex.GREEN, ex.GREEN, ex.CYAN, ex.TEXT)
    diameters = np.array([32.0, 36.0, 42.0, 42.0, 36.0, 32.0], dtype=np.float32)
    point = ex.add_points(scene, panel, positions, colors, diameters)

    state = {"updated": False}

    def on_frame(_view, _frame_index: int, elapsed: float) -> None:
        if state["updated"] or elapsed < 1.0:
            return
        moved_positions = np.array(
            [
                [-0.16, +0.34, 0.0],
                [+0.16, +0.34, 0.0],
            ],
            dtype=np.float32,
        )
        if dvz.dvz_visual_set_data_range(point, "position", 2, moved_positions) != 0:
            raise RuntimeError("dvz_visual_set_data_range() failed")
        state["updated"] = True

    ex.run_with_frame_callback(scene, figure, "Partial 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 moves only part of a retained point visual's position array.
 *
 * What to look for: the visual initially uploads six points with position, color, and diameter_px
 * arrays. After one second, dvz_visual_set_data_range() replaces only two position items, moving
 * the middle pair upward while colors and diameters stay unchanged. Partial updates are useful
 * when a large dataset changes locally and reuploading every item would waste time.
 *
 * Scenario: features_update_partial
 * Style: features, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c features/update_partial
 * Run:    ./build/examples/c/features/update_partial --live
 * Smoke:  ./build/examples/c/features/update_partial --png
 * Video:  ./build/examples/c/features/update_partial --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 6u



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

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



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

DvzScenarioSpec dvz_example_update_partial_scenario(void);



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

/**
 * Upload the initial point arrays.
 *
 * @param visual point visual
 * @return true when the upload succeeds
 */
static bool _upload_initial_points(DvzVisual* visual)
{
    if (visual == NULL)
        return false;

    vec3 positions[POINT_COUNT] = {
        {-0.72f, -0.30f, 0.0f}, {-0.44f, -0.30f, 0.0f}, {-0.16f, -0.30f, 0.0f},
        {+0.16f, -0.30f, 0.0f}, {+0.44f, -0.30f, 0.0f}, {+0.72f, -0.30f, 0.0f},
    };
    DvzColor colors[POINT_COUNT] = {
        dvz_color_rgba(201, 209, 217, 210),
        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_ACCENT_SECONDARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
        dvz_color_rgba(201, 209, 217, 210),
    };
    float diameters[POINT_COUNT] = {32.0f, 36.0f, 42.0f, 42.0f, 36.0f, 32.0f};

    DvzVisualDataUpdate updates[] = {
        {.attr_name = "position", .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},
    };
    return dvz_visual_set_data_many(visual, updates, 3) == 0;
}



/**
 * Move the middle two points with a retained data-range update.
 *
 * @param visual point visual
 * @return true when the update succeeds
 */
static bool _upload_partial_positions(DvzVisual* visual)
{
    if (visual == NULL)
        return false;

    vec3 moved_positions[2] = {
        {-0.16f, +0.34f, 0.0f},
        {+0.16f, +0.34f, 0.0f},
    };

    return dvz_visual_set_data_range(visual, "position", 2, moved_positions, 2) == 0;
}


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

    const bool ok =
        updated ? _upload_partial_positions(state->point) : _upload_initial_points(state->point);
    if (ok)
        state->updated = updated;
    return ok;
}



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

/**
 * Initialize the partial point-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;

    UpdatePartialState* state = (UpdatePartialState*)dvz_calloc(1, sizeof(UpdatePartialState));
    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_initial_points(state->point))
        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;
}



/**
 * Apply the retained range update 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;

    UpdatePartialState* state = (UpdatePartialState*)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 partial point-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 partial update scenario specification.
 *
 * @return scenario specification
 */
DvzScenarioSpec dvz_example_update_partial_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "features_update_partial",
        .title = "Partial Data Update",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .init = _scenario_init,
        .frame = _scenario_frame,
        .destroy = _scenario_destroy,
    };
}



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

/**
 * Run a minimal partial point-data update 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_partial_scenario();
    return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details

Data

Field Value
kind synthetic