Skip to content

Point

This example renders a 2D scatter plot with per-point color and diameter.

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

Generated positions place the samples, scalar values are mapped through a color scale, and diameters vary each point's screen-space size. Compare dense and sparse parts of the cloud to see how a beginner scatter plot can show location, magnitude, and emphasis with three simple arrays.

Source

#!/usr/bin/env python3
"""2D point visual with scalar-derived color and per-point diameter."""

from __future__ import annotations

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


POINT_COUNT = 960
TAU = 2.0 * np.pi


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

    i = np.arange(POINT_COUNT, dtype=np.float32)
    arm = np.mod(i, 6.0)
    local = np.floor(i / 6.0) / (POINT_COUNT / 6.0)
    theta = TAU * (2.10 * local + arm / 6.0)
    radius = 0.10 + 0.82 * np.sqrt(local)
    ripple = 0.040 * np.sin(TAU * (3.0 * local + 0.13 * arm))

    positions = np.zeros((POINT_COUNT, 3), dtype=np.float32)
    positions[:, 0] = (radius + ripple) * np.cos(theta)
    positions[:, 1] = 0.84 * (radius - 0.5 * ripple) * np.sin(theta)

    band = 0.5 + 0.5 * np.sin(TAU * (i / (POINT_COUNT - 1) + 0.08 * arm))
    values = np.clip(0.12 + 0.76 * (0.25 + 0.75 * np.sqrt(local)) + 0.12 * band, 0.0, 1.0)
    colors = np.column_stack(
        [
            (40 + 80 * values).astype(np.uint8),
            (100 + 130 * values).astype(np.uint8),
            (180 + 60 * (1.0 - values)).astype(np.uint8),
            np.full(POINT_COUNT, 230, dtype=np.uint8),
        ]
    )
    diameters = (10.0 + 11.0 * band + 5.0 * (1.0 - local)).astype(np.float32)

    point = dvz.dvz_point(scene, 0)
    if not point:
        raise RuntimeError("dvz_point() failed")
    dvz.dvz_visual_set_data_many(
        point,
        {
            "position": positions,
            "color": colors,
            "diameter_px": diameters,
        },
    )
    ex.set_filled_point_style(point)
    dvz.dvz_visual_set_depth_test(point, False)
    dvz.dvz_visual_set_alpha_mode(point, dvz.DVZ_ALPHA_BLENDED)
    ex.add_visual(panel, point)

    ex.run_with_view(
        scene,
        figure,
        "Point",
        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
 */

/* point - This example renders a 2D scatter plot with per-point color and diameter.
 *
 * What to look for: generated positions place the samples, scalar values are mapped through a
 * color scale, and diameters vary each point's screen-space size. Compare dense and sparse parts
 * of the cloud to see how a beginner scatter plot can show location, magnitude, and emphasis with
 * three simple arrays.
 *
 * Scenario: visuals_point
 * Style: visuals, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c visuals/point
 * Run:    ./build/examples/c/visuals/point --live
 * Smoke:  ./build/examples/c/visuals/point --png
 */



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

#include <math.h>
#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"



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

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

static const float TAU = 6.28318530718f;



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

DvzScenarioSpec dvz_visual_point_scenario(void);



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

typedef struct PointVisualState
{
    vec3* positions;
    float* values;
    float* diameters;
} PointVisualState;



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

/**
 * Fill one deterministic compact point cloud.
 *
 * @param positions output point positions
 * @param values output point scalar values
 * @param diameters output point diameters in pixels
 */
static void _fill_points(
    vec3 positions[POINT_COUNT], float values[POINT_COUNT], float diameters[POINT_COUNT])
{
    ANN(positions);
    ANN(values);
    ANN(diameters);

    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 arm = (float)(i % 6u);
        const float local = (float)(i / 6u) / (float)(POINT_COUNT / 6u);
        const float theta = TAU * (2.10f * local + arm / 6.0f);
        const float radius = 0.10f + 0.82f * sqrtf(local);
        const float ripple = 0.040f * sinf(TAU * (3.0f * local + 0.13f * arm));

        positions[i][0] = (radius + ripple) * cosf(theta);
        positions[i][1] = 0.84f * (radius - 0.5f * ripple) * sinf(theta);
        positions[i][2] = 0.0f;

        const float band = 0.5f + 0.5f * sinf(TAU * (t + 0.08f * arm));
        const float mix = 0.25f + 0.75f * sqrtf(local);
        values[i] = fminf(1.0f, 0.12f + 0.76f * mix + 0.12f * band);

        diameters[i] = 10.0f + 11.0f * band + 5.0f * (1.0f - local);
    }
}



/**
 * Upload the point arrays to one retained visual.
 *
 * @param visual point visual
 * @param positions point positions
 * @param values point scalar values
 * @param diameters point diameters
 * @return true when all uploads succeed
 */
static bool _upload_points(
    DvzVisual* visual, vec3 positions[POINT_COUNT], float values[POINT_COUNT],
    float diameters[POINT_COUNT])
{
    ANN(visual);
    ANN(positions);
    ANN(values);
    ANN(diameters);

    DvzVisualDataUpdate updates[] = {
        {.attr_name = "position", .data = positions, .item_count = POINT_COUNT},
        {.attr_name = "color", .data = values, .item_count = POINT_COUNT},
        {.attr_name = "diameter_px", .data = diameters, .item_count = POINT_COUNT},
    };
    return dvz_visual_set_data_many(visual, updates, 3) == 0;
}



/**
 * Free the retained point buffers.
 *
 * @param state point visual state
 */
static void _free_state(PointVisualState* state)
{
    if (state == NULL)
        return;
    dvz_free(state->diameters);
    dvz_free(state->values);
    dvz_free(state->positions);
    dvz_free(state);
}



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

/**
 * Initialize the retained 2D point 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;

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

    state->positions = (vec3*)dvz_calloc(POINT_COUNT, sizeof(*state->positions));
    state->values = (float*)dvz_calloc(POINT_COUNT, sizeof(*state->values));
    state->diameters = (float*)dvz_calloc(POINT_COUNT, sizeof(*state->diameters));
    if (state->positions == NULL || state->values == NULL || state->diameters == NULL)
        goto error;

    _fill_points(state->positions, state->values, state->diameters);

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

    DvzVisual* point = dvz_point(ctx->scene, 0);
    if (point == NULL)
        goto error;

    int rc = dvz_visual_set_attr_format(point, "color", DVZ_VISUAL_ATTR_FORMAT_SCALAR_F32);
    if (rc != 0)
        goto error;

    DvzScale* scale = example_graphite_cyan_color_scale(ctx->scene, 0.0, 1.0);
    if (scale == NULL)
        goto error;

    rc = dvz_visual_set_scale(point, "color", scale);
    if (rc != 0)
        goto error;

    if (!_upload_points(point, state->positions, state->values, state->diameters))
        goto error;

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

    rc = dvz_visual_set_depth_test(point, false);
    if (rc != 0)
        goto error;

    rc = dvz_visual_set_alpha_mode(point, DVZ_ALPHA_BLENDED);
    if (rc != 0)
        goto error;

    rc = dvz_panel_add_visual(panel, point, NULL);
    if (rc != 0)
        goto error;

    DvzPanzoom* panzoom = dvz_scenario_panzoom(ctx, panel, NULL, DVZ_DIM_MASK_XY);
    if (panzoom == NULL)
        goto error;

    if (out_user != NULL)
        *out_user = state;
    return true;

error:
    _free_state(state);
    return false;
}



/**
 * Destroy the retained 2D point visual scenario state.
 *
 * @param ctx scenario context
 * @param user scenario state
 */
static void _scenario_destroy(DvzScenarioContext* ctx, void* user)
{
    (void)ctx;
    _free_state((PointVisualState*)user);
}



/**
 * Return the point visual scenario specification.
 *
 * @return scenario specification
 */
DvzScenarioSpec dvz_visual_point_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "visuals_point",
        .title = "Point",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .init = _scenario_init,
        .destroy = _scenario_destroy,
    };
}



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

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

Tags

point, panzoom, capture

Data

Field Value
kind synthetic