Image¶
This example displays a generated scalar field as a 2D image.
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/image (build and run), or rerun ./build/examples/c/visuals/image |
| Python | Available; manually maintained direct-engine example | python3 -m examples.python.gallery.visuals.image |
| 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 FIELD_WIDTH by FIELD_HEIGHT value array is attached as a sampled field, while four corner positions and texture coordinates place it on a panel. The colormap scale maps values from 0 to 1, so compare the smooth bands and local extrema in the image to see how dense measurements such as microscopy tiles, detector frames, or model grids become a texture-backed visual.
Source¶
#!/usr/bin/env python3
"""Generated scalar field displayed as a sampled-field image."""
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()
width, height = 320, 240
x = np.linspace(0.0, 1.0, width, dtype=np.float32)
y = np.linspace(0.0, 1.0, height, dtype=np.float32)
u, v = np.meshgrid(x, y)
field_values = (
0.18
+ 0.22 * u
+ 0.16 * v
+ 0.12 * np.sin(2.0 * np.pi * (2.4 * u + 0.35 * v))
+ 0.10 * np.cos(2.0 * np.pi * (0.70 * u - 3.2 * v))
).astype(np.float32)
field_values = np.clip(field_values, 0.0, 1.0)
scale = ex.continuous_scale(scene, b"python_visual_image")
field = dvz.dvz_sampled_field_from_array(scene, field_values)
ex.add_image(scene, panel, field, scale=scale)
ex.run_with_view(
scene,
figure,
"Image",
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
*/
/* image - This example displays a generated scalar field as a 2D image.
*
* What to look for: the FIELD_WIDTH by FIELD_HEIGHT value array is attached as a sampled field,
* while four corner positions and texture coordinates place it on a panel. The colormap scale maps
* values from 0 to 1, so compare the smooth bands and local extrema in the image to see how dense
* measurements such as microscopy tiles, detector frames, or model grids become a texture-backed
* visual.
*
* Scenario: visuals_image
* Style: visuals, graphite_cyan, 1280x720 window target
*
* Build: just example-c visuals/image
* Run: ./build/examples/c/visuals/image --live
* Smoke: ./build/examples/c/visuals/image --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 FIELD_WIDTH 320u
#define FIELD_HEIGHT 240u
static const float TAU = 6.28318530718f;
/*************************************************************************************************/
/* Forward declarations */
/*************************************************************************************************/
DvzScenarioSpec dvz_visual_image_scenario(void);
/*************************************************************************************************/
/* Structs */
/*************************************************************************************************/
typedef struct ImageVisualState
{
float* values;
} ImageVisualState;
/*************************************************************************************************/
/* Helpers */
/*************************************************************************************************/
/**
* Clamp a scalar to the unit interval.
*
* @param value input value
* @return clamped value
*/
static float _clamp01(float value)
{
if (value < 0.0f)
return 0.0f;
if (value > 1.0f)
return 1.0f;
return value;
}
/**
* Return a deterministic sampled-field value.
*
* @param x normalized X coordinate
* @param y normalized Y coordinate
* @return normalized scalar sample
*/
static float _sample_field(float x, float y)
{
float value = 0.12f;
value += 0.15f * x + 0.08f * y;
value += 0.08f * sinf(TAU * (2.4f * x + 0.35f * y));
value += 0.06f * cosf(TAU * (0.70f * x - 3.2f * y));
const float ridge = sinf(TAU * (1.15f * x + 0.18f * sinf(TAU * y)));
value += 0.21f * expf(-20.0f * (ridge - 0.12f) * (ridge - 0.12f));
const float centers[7][3] = {
{0.16f, 0.23f, 0.060f}, {0.28f, 0.70f, 0.047f}, {0.44f, 0.42f, 0.036f},
{0.60f, 0.78f, 0.052f}, {0.72f, 0.52f, 0.043f}, {0.82f, 0.25f, 0.055f},
{0.88f, 0.68f, 0.038f},
};
for (uint32_t i = 0; i < 7u; i++)
{
const float dx = x - centers[i][0];
const float dy = y - centers[i][1];
const float sigma = centers[i][2];
const float d2 = (dx * dx + 1.25f * dy * dy) / (2.0f * sigma * sigma);
value += (0.18f + 0.05f * (float)(i % 3u)) * expf(-d2);
}
const float marker_x = x - 0.67f;
const float marker_y = y - 0.46f;
value += 0.30f * expf(-(marker_x * marker_x + marker_y * marker_y) / (2.0f * 0.075f * 0.075f));
return _clamp01(value);
}
/**
* Fill the scalar sampled field.
*
* @param values output normalized scalar field
*/
static void _fill_field(float* values)
{
ANN(values);
for (uint32_t y = 0; y < FIELD_HEIGHT; y++)
{
for (uint32_t x = 0; x < FIELD_WIDTH; x++)
{
const float u = FIELD_WIDTH > 1u ? (float)x / (float)(FIELD_WIDTH - 1u) : 0.0f;
const float v = FIELD_HEIGHT > 1u ? (float)y / (float)(FIELD_HEIGHT - 1u) : 0.0f;
values[y * FIELD_WIDTH + x] = _sample_field(u, v);
}
}
}
/**
* Create the graphite/cyan/amber scalar image scale.
*
* @param scene scene owning the scale
* @return created scale, or NULL on failure
*/
static DvzScale* _add_image_scale(DvzScene* scene)
{
ANN(scene);
DvzScale* scale = dvz_scale(
scene,
&(DvzScaleDesc){DVZ_STRUCT_INIT_FIELDS(DvzScaleDesc), .kind = DVZ_SCALE_CONTINUOUS});
if (scale == NULL)
return NULL;
dvz_scale_set_domain(scale, 0.0, 1.0);
DvzColormap* colormap = dvz_colormap(scene, NULL);
if (colormap == NULL)
return NULL;
DvzColormapStop stops[6] = {
{.position = 0.00, .rgba = {14, 17, 23, 255}},
{.position = 0.18, .rgba = {22, 42, 62, 255}},
{.position = 0.40, .rgba = {35, 124, 165, 255}},
{.position = 0.64, .rgba = {76, 201, 240, 255}},
{.position = 0.84, .rgba = {128, 255, 219, 255}},
{.position = 1.00, .rgba = {255, 183, 3, 255}},
};
dvz_colormap_set_stops(colormap, stops, 6);
dvz_scale_set_colormap(scale, colormap);
return scale;
}
/**
* Add the sampled-field image visual to the panel.
*
* @param scene scene owning the visual and field
* @param panel panel receiving the image
* @param scale scalar color scale
* @param values scalar field values
* @return true when the image was added
*/
static bool _add_image(DvzScene* scene, DvzPanel* panel, DvzScale* scale, float* values)
{
ANN(scene);
ANN(panel);
ANN(scale);
ANN(values);
vec3 positions[4] = {
{-0.88f, -0.66f, 0.0f},
{-0.88f, +0.66f, 0.0f},
{+0.88f, -0.66f, 0.0f},
{+0.88f, +0.66f, 0.0f},
};
vec2 texcoords[4] = {
{0.0f, 0.0f},
{0.0f, 1.0f},
{1.0f, 0.0f},
{1.0f, 1.0f},
};
DvzVisual* image = dvz_image(scene, 0);
if (image == NULL)
return false;
if (dvz_visual_set_data(image, "position", positions, 4) != 0)
return false;
if (dvz_visual_set_data(image, "texcoords", texcoords, 4) != 0)
return false;
if (dvz_visual_set_scale(image, "color", scale) != 0)
return false;
DvzSampledField* field = dvz_sampled_field(
scene, &(DvzSampledFieldDesc){
DVZ_STRUCT_INIT_FIELDS(DvzSampledFieldDesc),
.dim = DVZ_FIELD_DIM_2D,
.format = DVZ_FIELD_FORMAT_R32_FLOAT,
.semantic = DVZ_FIELD_SEMANTIC_SCALAR,
.width = FIELD_WIDTH,
.height = FIELD_HEIGHT,
.depth = 1,
});
if (field == NULL)
return false;
if (dvz_sampled_field_set_data(
field, &(DvzFieldDataView){
DVZ_STRUCT_INIT_FIELDS(DvzFieldDataView),
.data = values,
.bytes_per_row = FIELD_WIDTH * sizeof(float),
.rows_per_image = FIELD_HEIGHT,
}) != DVZ_OK)
{
return false;
}
if (dvz_visual_set_field(image, "field", field) != DVZ_OK)
return false;
if (dvz_visual_set_depth_test(image, false) != 0)
return false;
return dvz_panel_add_visual(panel, image, NULL) == 0;
}
/*************************************************************************************************/
/* Scenario callbacks */
/*************************************************************************************************/
/**
* Initialize the retained image 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;
ImageVisualState* state = (ImageVisualState*)dvz_malloc(sizeof(*state));
if (state == NULL)
return false;
state->values = NULL;
state->values = (float*)dvz_malloc(FIELD_WIDTH * FIELD_HEIGHT * sizeof(*state->values));
if (state->values == NULL)
{
dvz_free(state);
return false;
}
_fill_field(state->values);
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);
DvzScale* scale = _add_image_scale(ctx->scene);
if (scale == NULL)
goto error;
if (!_add_image(ctx->scene, panel, scale, state->values))
goto error;
if (dvz_scenario_panzoom(ctx, panel, NULL, DVZ_DIM_MASK_XY) == NULL)
goto error;
if (out_user != NULL)
*out_user = state;
return true;
error:
dvz_free(state->values);
dvz_free(state);
return false;
}
/**
* Destroy the retained image visual scenario state.
*
* @param ctx scenario context
* @param user scenario state
*/
static void _scenario_destroy(DvzScenarioContext* ctx, void* user)
{
(void)ctx;
ImageVisualState* state = (ImageVisualState*)user;
if (state == NULL)
return;
dvz_free(state->values);
dvz_free(state);
}
/**
* Return the image visual scenario specification.
*
* @return scenario specification
*/
DvzScenarioSpec dvz_visual_image_scenario(void)
{
return (DvzScenarioSpec){
.id = "visuals_image",
.title = "Image",
.width = WIDTH,
.height = HEIGHT,
.fps = 60.0,
.init = _scenario_init,
.destroy = _scenario_destroy,
};
}
/*************************************************************************************************/
/* Functions */
/*************************************************************************************************/
/**
* Run the retained image 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_image_scenario();
return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details
- ID:
visuals_image - Category:
visual - Lane:
visuals - Status:
supported - Source:
examples/c/visuals/image.c - Approved adaptation starter:
yes - Reference: Image visual family
- Python source:
examples/python/gallery/visuals/image.py - Python adaptation: Available; manually maintained direct-engine example
- Browser support: Live in browser
- WebGPU live route:
examples/webgpu/live.html?id=visuals_image - Browser capability tags:
image,sampled-field,panzoom - Validation:
smoke+screenshot
Tags
image, sampled-field, panzoom, capture
Data
| Field | Value |
|---|---|
kind |
synthetic |
