Annotation Readout¶
This example shows a text annotation anchored to one data point.
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/annotation_readout (build and run), or rerun ./build/examples/c/features/annotation_readout |
| Python | Available; direct-engine adaptation | python3 -m examples.python.gallery.features.annotation_readout |
| 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 for a small signal-like scatter plot. One highlighted sample is paired with a label annotation at the same data coordinate, using placement settings to keep the text offset from the marker. Compare the emphasized marker with the muted neighbors and the label placement relative to the axes; this is the pattern used for callouts, readouts, and selected observations in scientific plots.
Source¶
#!/usr/bin/env python3
"""Retained text annotation anchored to one highlighted data point."""
from __future__ import annotations
import ctypes
import numpy as np
import datoviz as dvz
from examples.python.gallery import common as ex
POINT_COUNT = 96
READOUT_INDEX = 61
def _point_data():
t = np.linspace(0.0, 1.0, POINT_COUNT, dtype=np.float32)
x = 10.0 * t
y = 0.45 * np.sin(2.0 * np.pi * 1.7 * t)
y += 0.22 * np.cos(2.0 * np.pi * 4.0 * t + 0.2)
positions = np.column_stack((x, y, np.zeros(POINT_COUNT, dtype=np.float32))).astype(np.float32)
colors = np.tile(
np.array([[ex.CYAN.r, ex.CYAN.g, ex.CYAN.b, 190]], dtype=np.uint8), (POINT_COUNT, 1)
)
diameters = np.full(POINT_COUNT, 8.0, dtype=np.float32)
colors[READOUT_INDEX] = (ex.YELLOW.r, ex.YELLOW.g, ex.YELLOW.b, 255)
diameters[READOUT_INDEX] = 16.0
return positions, colors, diameters
def _add_points(scene, panel, positions, colors, diameters) -> None:
point = dvz.dvz_point(scene, 0)
if not point:
raise RuntimeError("dvz_point() failed")
if dvz.dvz_visual_set_data_many(
point,
{
"position": positions,
"color": colors,
"diameter_px": diameters,
},
) != 0:
raise RuntimeError("dvz_visual_set_data_many(point) failed")
ex.set_filled_point_style(point)
ex.add_visual(panel, point)
def _add_readout(panel, position) -> None:
style = dvz.dvz_text_style()
style.size_px = 24.0
style.renderer = dvz.DVZ_TEXT_RENDERER_MSDF_ATLAS
style.color[:] = (ex.TEXT.r, ex.TEXT.g, ex.TEXT.b, ex.TEXT.a)
placement = dvz.dvz_text_placement()
placement.mode = dvz.DVZ_TEXT_PLACEMENT_DATA
placement.position[:] = (float(position[0]), float(position[1]), float(position[2]))
placement.offset[:] = (28.0, -24.0)
placement.text_anchor[:] = (0.0, 0.5)
placement.has_text_anchor = True
placement.depth_test = False
desc = dvz.dvz_label_desc()
desc.text = f"peak x {position[0]:.2f} y {position[1]:.2f}".encode()
annotation = dvz.dvz_annotation_label(panel, ctypes.byref(desc))
if not annotation:
raise RuntimeError("dvz_annotation_label() failed")
if dvz.dvz_annotation_set_style(annotation, ctypes.byref(style)) != 0:
raise RuntimeError("dvz_annotation_set_style() failed")
if dvz.dvz_annotation_set_placement(annotation, ctypes.byref(placement)) != 0:
raise RuntimeError("dvz_annotation_set_placement() failed")
def main() -> None:
scene, figure, panel = ex.scene_panel()
if dvz.dvz_panel_set_domain(panel, dvz.DVZ_DIM_X, 0.0, 10.0) != 0:
raise RuntimeError("dvz_panel_set_domain(X) failed")
if dvz.dvz_panel_set_domain(panel, dvz.DVZ_DIM_Y, -1.0, 1.0) != 0:
raise RuntimeError("dvz_panel_set_domain(Y) failed")
positions, colors, diameters = _point_data()
_add_points(scene, panel, positions, colors, diameters)
_add_readout(panel, positions[READOUT_INDEX])
ex.run(scene, figure, "Annotation Readout")
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
*/
/* annotation_readout - This example shows a retained text annotation anchored to one data point.
*
* Scenario: features_annotation_readout
* Style: features, graphite_cyan, 1280x720 window target
*
* Build: just example-c features/annotation_readout
* Run: ./build/examples/c/features/annotation_readout --live
* Smoke: ./build/examples/c/features/annotation_readout --png
*
* What to look for: the point visual uploads position, color, and diameter_px arrays for a small
* signal-like scatter plot. One highlighted sample is paired with a label annotation at the same
* data coordinate, using placement settings to keep the text offset from the marker. Compare the
* emphasized marker with the muted neighbors and the label placement relative to the axes; this is
* the pattern used for callouts, readouts, and selected observations in scientific plots.
*/
/*************************************************************************************************/
/* Includes */
/*************************************************************************************************/
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "_assertions.h"
#include "_compat.h"
#include "datoviz/scene.h"
#include "example_style.h"
#include "runner/scenario_runner.h"
DvzScenarioSpec dvz_example_annotation_readout_scenario(void);
/*************************************************************************************************/
/* Constants */
/*************************************************************************************************/
#define WIDTH EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT
#define POINT_COUNT 96u
static const float TAU = 6.28318530718f;
static const uint32_t READOUT_INDEX = 61u;
/*************************************************************************************************/
/* Helpers */
/*************************************************************************************************/
/**
* Fill deterministic point positions in data coordinates.
*
* @param positions output data-space positions
* @param colors output point colors
* @param diameters output point diameters
*/
static void _fill_points(
vec3 positions[POINT_COUNT], DvzColor colors[POINT_COUNT], float diameters[POINT_COUNT])
{
ANN(positions);
ANN(colors);
ANN(diameters);
const DvzColor muted = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_GRID);
const DvzColor accent = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY);
for (uint32_t i = 0; i < POINT_COUNT; i++)
{
const float t = POINT_COUNT > 1u ? (float)i / (float)(POINT_COUNT - 1u) : 0.0f;
const float x = 10.0f * t;
const float y = 0.45f * sinf(TAU * 1.7f * t) + 0.22f * cosf(TAU * 4.0f * t + 0.2f);
positions[i][0] = x;
positions[i][1] = y;
positions[i][2] = 0.0f;
colors[i] = muted;
colors[i].a = 190u;
diameters[i] = 8.0f;
}
colors[READOUT_INDEX] = accent;
colors[READOUT_INDEX].a = 255u;
diameters[READOUT_INDEX] = 16.0f;
}
/**
* Add one point visual used as an anchor target for the annotation.
*
* @param scene scene owning the visual
* @param panel panel receiving the visual
* @param data_positions data-space positions
* @param colors point colors
* @param diameters point diameters
* @return true when the visual was added
*/
static bool _add_points(
DvzScene* scene, DvzPanel* panel, vec3 data_positions[POINT_COUNT],
const DvzColor colors[POINT_COUNT], const float diameters[POINT_COUNT])
{
ANN(scene);
ANN(panel);
ANN(data_positions);
ANN(colors);
ANN(diameters);
DvzVisual* points = dvz_point(scene, 0);
if (points == 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(points, updates, 3) != 0)
return false;
DvzPointStyleDesc style = dvz_point_style_desc();
style.aspect = DVZ_SHAPE_ASPECT_FILLED;
style.stroke_width_px = 0.0f;
if (dvz_point_set_style(points, &style) != 0)
return false;
return dvz_panel_add_visual(
panel, points,
&(DvzVisualAttachDesc){DVZ_STRUCT_INIT_FIELDS(DvzVisualAttachDesc),
.coord_space = DVZ_VISUAL_COORD_DATA}) == 0;
}
/**
* Add one retained label annotation at the highlighted data point.
*
* @param panel panel receiving the annotation
* @param position highlighted data-space position
* @return created annotation, or NULL on failure
*/
static DvzAnnotation* _add_readout(DvzPanel* panel, const vec3 position)
{
ANN(panel);
DvzColor text = example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT);
DvzTextStyle style = dvz_text_style();
style.size_px = 24.0f;
style.renderer = DVZ_TEXT_RENDERER_MSDF_ATLAS;
style.color[0] = text.r;
style.color[1] = text.g;
style.color[2] = text.b;
style.color[3] = 255u;
DvzTextPlacement placement = dvz_text_placement();
placement.mode = DVZ_TEXT_PLACEMENT_DATA;
placement.position[0] = position[0];
placement.position[1] = position[1];
placement.position[2] = position[2];
placement.offset[0] = 28.0f;
placement.offset[1] = -24.0f;
placement.text_anchor[0] = 0.0f;
placement.text_anchor[1] = 0.5f;
placement.has_text_anchor = true;
placement.depth_test = false;
char label[128] = {0};
int n = dvz_snprintf(label, sizeof(label), "peak x %.2f y %.2f", position[0], position[1]);
if (n <= 0 || (size_t)n >= sizeof(label))
return NULL;
DvzAnnotation* annotation = dvz_annotation_label(
panel, &(DvzLabelDesc){DVZ_STRUCT_INIT_FIELDS(DvzLabelDesc), .text = label});
if (annotation == NULL)
return NULL;
if (dvz_annotation_set_style(annotation, &style) != 0 ||
dvz_annotation_set_placement(annotation, &placement) != 0)
{
dvz_annotation_destroy(annotation);
return NULL;
}
return annotation;
}
/*************************************************************************************************/
/* Scenario callbacks */
/*************************************************************************************************/
/**
* Initialize the deterministic retained annotation readout 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 data_positions[POINT_COUNT] = {{0}};
DvzColor colors[POINT_COUNT] = {{0}};
float diameters[POINT_COUNT] = {0};
_fill_points(data_positions, colors, diameters);
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 (dvz_panel_set_domain(panel, DVZ_DIM_X, 0.0, 10.0) != 0)
return false;
if (dvz_panel_set_domain(panel, DVZ_DIM_Y, -1.0, 1.0) != 0)
return false;
if (!_add_points(ctx->scene, panel, data_positions, colors, diameters))
return false;
DvzAnnotation* readout = _add_readout(panel, data_positions[READOUT_INDEX]);
return readout != NULL;
}
/**
* Return the annotation readout scenario specification.
*
* @return scenario specification
*/
DvzScenarioSpec dvz_example_annotation_readout_scenario(void)
{
return (DvzScenarioSpec){
.id = "features_annotation_readout",
.title = "Annotation Readout",
.width = WIDTH,
.height = HEIGHT,
.fps = 60.0,
.requirements = DVZ_SCENARIO_REQ_POINT_VISUAL | DVZ_SCENARIO_REQ_TEXT_VISUAL,
.init = _scenario_init,
};
}
/*************************************************************************************************/
/* Functions */
/*************************************************************************************************/
/**
* Run the deterministic retained annotation readout feature proof 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_annotation_readout_scenario();
return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details
- ID:
features_annotation_readout - Category:
feature - Lane:
features - Status:
supported - Source:
examples/c/features/annotation_readout.c - Approved adaptation starter:
yes - Python source:
examples/python/gallery/features/annotation_readout.py - Python adaptation: Available; direct-engine adaptation
- Browser support: Live in browser
- WebGPU live route:
examples/webgpu/live.html?id=features_annotation_readout - Browser capability tags:
annotation,readout,text,point - Validation:
smoke+screenshot
Data
| Field | Value |
|---|---|
kind |
synthetic |
