Skip to content

Multi Window

This example drives two independent native GLFW windows from one Datoviz app.

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 runtime/multi_window (build and run), or rerun ./build/examples/c/runtime/multi_window
Python Available; direct-engine adaptation python3 -m examples.python.gallery.runtime.multi_window
Browser Native only multiple native GLFW windows are native-only

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 overview and detail figures each get their own point-position, point-color, and point-diameter arrays, then the same app presents them in two standard-size, cascaded windows. Compare the two live windows rather than one screenshot: they prove that separate figures and views can share a process while keeping their own scene content.

This workflow is useful for scientific tools that need a coarse overview beside a focused detail view without starting a second renderer.

Source

#!/usr/bin/env python3
"""Two native Datoviz windows driven by one app."""

from __future__ import annotations

import ctypes

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


WIDTH = ex.WIDTH
HEIGHT = ex.HEIGHT
POINT_COUNT = 8
FIRST_WINDOW_X = 64
FIRST_WINDOW_Y = 96
SECOND_WINDOW_OFFSET_X = 96
SECOND_WINDOW_OFFSET_Y = 64


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.add_visual(panel, point)


def _overview_data():
    positions = np.array(
        [
            [-0.78, -0.40, 0.0],
            [-0.55, +0.20, 0.0],
            [-0.32, -0.08, 0.0],
            [-0.10, +0.48, 0.0],
            [+0.16, -0.26, 0.0],
            [+0.38, +0.36, 0.0],
            [+0.62, -0.02, 0.0],
            [+0.82, +0.30, 0.0],
        ],
        dtype=np.float32,
    )
    colors = ex.color_array(
        ex.CYAN, ex.BLUE, ex.YELLOW, ex.TEXT, ex.GREEN, ex.WHITE, ex.CYAN, ex.BLUE
    )
    diameters = np.array(
        [26.0, 34.0, 42.0, 54.0, 46.0, 38.0, 30.0, 24.0], dtype=np.float32
    )
    return positions, colors, diameters


def _detail_data():
    positions = np.array(
        [
            [-0.42, -0.35, 0.0],
            [-0.34, +0.10, 0.0],
            [-0.20, +0.38, 0.0],
            [-0.02, -0.08, 0.0],
            [+0.14, +0.52, 0.0],
            [+0.30, -0.28, 0.0],
            [+0.48, +0.16, 0.0],
            [+0.62, +0.42, 0.0],
        ],
        dtype=np.float32,
    )
    colors = ex.color_array(
        ex.YELLOW, ex.CYAN, ex.TEXT, ex.BLUE, ex.GREEN, ex.WHITE, ex.YELLOW, ex.CYAN
    )
    diameters = np.array(
        [36.0, 44.0, 58.0, 50.0, 42.0, 34.0, 28.0, 24.0], dtype=np.float32
    )
    return positions, colors, diameters


def _build_scene():
    scene = dvz.dvz_scene()
    if not scene:
        raise RuntimeError("dvz_scene() failed")

    overview = dvz.dvz_figure(scene, WIDTH, HEIGHT, 0)
    detail = dvz.dvz_figure(scene, WIDTH, HEIGHT, 0)
    if not overview or not detail:
        raise RuntimeError("dvz_figure() failed")

    overview_panel = dvz.dvz_panel_full(overview)
    detail_panel = dvz.dvz_panel_full(detail)
    if not overview_panel or not detail_panel:
        raise RuntimeError("dvz_panel_full() failed")
    dvz.dvz_panel_set_background_color(overview_panel, ex.BG)
    dvz.dvz_panel_set_background_color(detail_panel, ex.BG)

    _add_points(scene, overview_panel, *_overview_data())
    _add_points(scene, detail_panel, *_detail_data())
    return scene, overview, detail, overview_panel, detail_panel


def _positioned_view(app, figure, title: bytes, x: int, y: int):
    desc = dvz.dvz_view_desc(dvz.DVZ_VIEW_WINDOW)
    desc.size_policy = dvz.DVZ_VIEW_SIZE_HOST_LOGICAL_PX
    desc.size_width = float(WIDTH)
    desc.size_height = float(HEIGHT)
    desc.title = title
    desc.has_position = True
    desc.x = x
    desc.y = y
    view = dvz.dvz_view(app, figure, ctypes.byref(desc))
    if not view:
        raise RuntimeError("dvz_view() failed")
    return view


def _configure_view(view, scene, panel) -> None:
    ex.bind_panzoom(view, scene, panel, dvz.DVZ_DIM_MASK_XY)


def main() -> None:
    scene, overview, detail, overview_panel, detail_panel = _build_scene()
    app = dvz.dvz_app(scene)
    if not app:
        dvz.dvz_scene_destroy(scene)
        raise RuntimeError("dvz_app() failed")

    try:
        overview_view = _positioned_view(
            app, overview, b"multi_window overview", FIRST_WINDOW_X, FIRST_WINDOW_Y
        )
        detail_view = _positioned_view(
            app,
            detail,
            b"multi_window detail",
            FIRST_WINDOW_X + SECOND_WINDOW_OFFSET_X,
            FIRST_WINDOW_Y + SECOND_WINDOW_OFFSET_Y,
        )
        _configure_view(overview_view, scene, overview_panel)
        _configure_view(detail_view, scene, detail_panel)
        ex.run_app(app, overview_view)
    finally:
        dvz.dvz_app_destroy(app)
        dvz.dvz_scene_destroy(scene)


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

/* multi_window - This example drives two independent native GLFW windows from one Datoviz app.
 *
 * What to look for: the overview and detail figures each get their own point-position,
 * point-color, and point-diameter arrays, then the same app presents them in two standard-size,
 * cascaded windows. Compare the two live windows rather than one screenshot: they prove that
 * separate figures and views can share a process while keeping their own scene content.
 *
 * This workflow is useful for scientific tools that need a coarse overview beside a focused
 * detail view without starting a second renderer.
 *
 * Scenario: runtime_multi_window
 * Style: runtime, native app, multi-window
 *
 * Build:  just example-c runtime/multi_window
 * Run:    ./build/examples/c/runtime/multi_window
 * Smoke:  ./build/examples/c/runtime/multi_window 2
 */



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

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

#include "datoviz/app.h"
#include "datoviz/scene.h"
#include "example_common.h"
#include "example_style.h"



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

#define WIDTH  EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT
#define POINT_COUNT 8u
#define FIRST_WINDOW_X         64
#define FIRST_WINDOW_Y         96
#define SECOND_WINDOW_OFFSET_X 96
#define SECOND_WINDOW_OFFSET_Y 64



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

/**
 * Add one point cloud visual to a panel.
 *
 * @param scene scene owning the visual
 * @param panel destination panel
 * @param positions point positions
 * @param colors point colors
 * @param diameters point diameters in pixels
 * @return true on success
 */
static bool _add_points(
    DvzScene* scene, DvzPanel* panel, vec3* positions, DvzColor* colors, float* diameters)
{
    DvzVisual* point = dvz_point(scene, 0);
    if (point == NULL)
        return false;

    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},
    };
    if (dvz_visual_set_data_many(point, updates, 3) != 0)
        return false;

    return dvz_panel_add_visual(panel, point, NULL) == 0;
}


/**
 * Create one positioned native GLFW view.
 *
 * @param app app driving the view
 * @param figure figure rendered in the view
 * @param title native window title
 * @param x initial monitor-space X position
 * @param y initial monitor-space Y position
 * @return view handle, or NULL
 */
static DvzView*
_positioned_view(DvzApp* app, DvzFigure* figure, const char* title, int32_t x, int32_t y)
{
    DvzViewDesc desc = dvz_view_desc(DVZ_VIEW_WINDOW);
    desc.size_policy = DVZ_VIEW_SIZE_HOST_LOGICAL_PX;
    desc.size_width = WIDTH;
    desc.size_height = HEIGHT;
    desc.title = title;
    desc.has_position = true;
    desc.x = x;
    desc.y = y;
    return dvz_view(app, figure, &desc);
}



/*************************************************************************************************/
/*  Main                                                                                         */
/*************************************************************************************************/

int main(int argc, char** argv)
{
    int ret = 1;
    DvzScene* scene = NULL;
    DvzApp* app = NULL;

    scene = dvz_scene();
    EXAMPLE_CHECK(scene != NULL, "dvz_scene() failed");

    DvzFigure* overview = dvz_figure(scene, WIDTH, HEIGHT, 0);
    DvzFigure* detail = dvz_figure(scene, WIDTH, HEIGHT, 0);
    DvzPanel* overview_panel = overview != NULL ? dvz_panel_full(overview) : NULL;
    DvzPanel* detail_panel = detail != NULL ? dvz_panel_full(detail) : NULL;
    EXAMPLE_CHECK(
        overview != NULL && detail != NULL && overview_panel != NULL && detail_panel != NULL,
        "failed to create figures and panels");

    example_graphite_cyan_set_panel_background(overview_panel);
    example_graphite_cyan_set_panel_background(detail_panel);

    vec3 overview_positions[POINT_COUNT] = {
        {-0.78f, -0.40f, 0.0f}, {-0.55f, +0.20f, 0.0f}, {-0.32f, -0.08f, 0.0f},
        {-0.10f, +0.48f, 0.0f}, {+0.16f, -0.26f, 0.0f}, {+0.38f, +0.36f, 0.0f},
        {+0.62f, -0.02f, 0.0f}, {+0.82f, +0.30f, 0.0f},
    };
    DvzColor overview_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_GRID),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_MINOR_TICK),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY),
    };
    float overview_diameters[POINT_COUNT] = {26.0f, 34.0f, 42.0f, 54.0f, 46.0f, 38.0f, 30.0f,
                                             24.0f};
    EXAMPLE_CHECK(
        _add_points(
            scene, overview_panel, overview_positions, overview_colors, overview_diameters),
        "failed to create overview points");

    vec3 detail_positions[POINT_COUNT] = {
        {-0.42f, -0.35f, 0.0f}, {-0.34f, +0.10f, 0.0f}, {-0.20f, +0.38f, 0.0f},
        {-0.02f, -0.08f, 0.0f}, {+0.14f, +0.52f, 0.0f}, {+0.30f, -0.28f, 0.0f},
        {+0.48f, +0.16f, 0.0f}, {+0.62f, +0.42f, 0.0f},
    };
    DvzColor detail_colors[POINT_COUNT] = {
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_TEXT),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_SECONDARY),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_GRID),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_MINOR_TICK),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_WARNING),
        example_graphite_cyan_color(EXAMPLE_STYLE_COLOR_ACCENT_PRIMARY),
    };
    float detail_diameters[POINT_COUNT] = {36.0f, 44.0f, 58.0f, 50.0f, 42.0f, 34.0f, 28.0f,
                                           24.0f};
    EXAMPLE_CHECK(
        _add_points(scene, detail_panel, detail_positions, detail_colors, detail_diameters),
        "failed to create detail points");

    app = dvz_app(scene);
    EXAMPLE_CHECK(app != NULL, "dvz_app() failed (no GPU or display?)");

    DvzView* overview_view =
        _positioned_view(app, overview, "multi_window overview", FIRST_WINDOW_X, FIRST_WINDOW_Y);
    DvzView* detail_view = _positioned_view(
        app, detail, "multi_window detail", FIRST_WINDOW_X + SECOND_WINDOW_OFFSET_X,
        FIRST_WINDOW_Y + SECOND_WINDOW_OFFSET_Y);
    EXAMPLE_CHECK(
        overview_view != NULL && detail_view != NULL,
        "dvz_view_window() failed (GLFW unavailable?)");

    EXAMPLE_CHECK(
        dvz_view_panzoom(overview_view, overview_panel, NULL) != NULL,
        "overview dvz_view_panzoom() failed");
    EXAMPLE_CHECK(
        dvz_view_panzoom(detail_view, detail_panel, NULL) != NULL,
        "detail dvz_view_panzoom() failed");

    dvz_app_run(app, example_frame_count(argc, argv));
    ret = 0;

cleanup:
    if (app != NULL)
        dvz_app_destroy(app);
    if (scene != NULL)
        dvz_scene_destroy(scene);
    return ret;
}
Example details
  • ID: runtime_multi_window
  • Category: runtime
  • Lane: runtime
  • Status: supported
  • Source: examples/c/runtime/multi_window.c
  • Approved adaptation starter: yes
  • Python source: examples/python/gallery/runtime/multi_window.py
  • Python adaptation: Available; direct-engine adaptation
  • Browser support: Native only
  • Browser note: multiple native GLFW windows are native-only
  • Browser capability tags: native-view, multi-window
  • Validation: smoke+interaction

Data

Field Value
kind synthetic