Skip to content

Polygon Composite

This example compares one holed polygon with a multi-region polygon set.

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 composites/polygon (build and run), or rerun ./build/examples/c/composites/polygon
Python Available python3 -m examples.python.gallery.composites.polygon
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 left shape uses outer and hole coordinate rings plus fill, stroke, width, join, and ID settings; the right shape uses three regions with per-region IDs and styles. Compare the transparent fills, hole, and beveled versus rounded joins to see how polygon composites support maps, masks, regions of interest, and segmented spatial data.

Source

#!/usr/bin/env python3
"""Semantic polygon composite with a hole and styled polygon set."""

from __future__ import annotations

import ctypes

import numpy as np

import datoviz as dvz

from examples.python.gallery import common as ex


def _polygon_desc(points: np.ndarray) -> tuple[dvz.DvzPolygonDesc, np.ndarray]:
    points = np.ascontiguousarray(points, dtype=np.float64)
    desc = dvz.dvz_polygon_desc()
    desc.outer.xy = ctypes.c_void_p(points.ctypes.data)
    desc.outer.count = len(points)
    return desc, points


def _set_equal_view2d(panel) -> None:
    padding = dvz.DvzPanelReserve()
    padding.left_px = 24.0
    padding.right_px = 24.0
    padding.top_px = 18.0
    padding.bottom_px = 18.0
    if dvz.dvz_panel_set_padding(panel, ctypes.byref(padding)) != 0:
        raise RuntimeError("dvz_panel_set_padding() failed")

    desc = dvz.dvz_panel_view2d_desc()
    desc.mode = dvz.DVZ_PANEL_VIEW2D_CONTAIN
    desc.aspect = dvz.DVZ_PANEL_VIEW2D_ASPECT_EQUAL
    desc.padding = 0.05
    desc.domain_x[:] = (-2.18, +3.06)
    desc.domain_y[:] = (-0.88, +0.88)
    desc.has_domain_x = True
    desc.has_domain_y = True
    if dvz.dvz_panel_set_view2d(panel, ctypes.byref(desc)) != 0:
        raise RuntimeError("dvz_panel_set_view2d() failed")


def _attach_composite(panel, composite, *, z_layer: int = 0) -> None:
    if not composite:
        raise RuntimeError("composite creation failed")
    attach = dvz.dvz_visual_attach_desc()
    attach.coord_space = dvz.DVZ_VISUAL_COORD_DATA
    attach.z_layer = z_layer
    if dvz.dvz_panel_add_composite(panel, composite, ctypes.byref(attach)) != 0:
        raise RuntimeError("dvz_panel_add_composite() failed")


def _add_holed_polygon(scene, panel) -> None:
    outer = np.array(
        [
            [-2.12, +0.00],
            [-1.88, -0.58],
            [-1.30, -0.82],
            [-0.72, -0.58],
            [-0.48, +0.00],
            [-0.72, +0.58],
            [-1.30, +0.82],
            [-1.88, +0.58],
        ],
        dtype=np.float64,
    )
    hole = np.array(
        [
            [-1.66, +0.00],
            [-1.52, -0.26],
            [-1.22, -0.26],
            [-1.08, +0.00],
            [-1.22, +0.26],
            [-1.52, +0.26],
        ],
        dtype=np.float64,
    )

    polygon = dvz.dvz_polygon(scene, 0)
    if not polygon:
        raise RuntimeError("dvz_polygon() failed")

    desc, outer = _polygon_desc(outer)
    if dvz.dvz_polygon_set_geometry(polygon, ctypes.byref(desc)) != 0:
        raise RuntimeError("dvz_polygon_set_geometry() failed")
    if dvz.dvz_polygon_set_hole(polygon, 0, ctypes.c_void_p(hole.ctypes.data), len(hole)) != 0:
        raise RuntimeError("dvz_polygon_set_hole() failed")
    if dvz.dvz_polygon_set_id(polygon, 10) != 0:
        raise RuntimeError("dvz_polygon_set_id() failed")
    if dvz.dvz_polygon_set_fill_color(polygon, dvz.DvzColor(36, 151, 178, 210)) != 0:
        raise RuntimeError("dvz_polygon_set_fill_color() failed")
    if dvz.dvz_polygon_set_stroke_color(polygon, dvz.DvzColor(214, 240, 255, 255)) != 0:
        raise RuntimeError("dvz_polygon_set_stroke_color() failed")
    if dvz.dvz_polygon_set_stroke_width_px(polygon, 8.0) != 0:
        raise RuntimeError("dvz_polygon_set_stroke_width_px() failed")
    if dvz.dvz_polygon_set_stroke_join(polygon, dvz.DVZ_PATH_JOIN_ROUND, 4.0) != 0:
        raise RuntimeError("dvz_polygon_set_stroke_join() failed")

    _attach_composite(panel, dvz.dvz_polygon_composite(polygon, 0))


def _add_polygon_set(scene, panel) -> None:
    regions = [
        np.array(
            [
                [+0.30, +0.74],
                [+0.49, +0.24],
                [+1.03, +0.24],
                [+0.60, -0.06],
                [+0.77, -0.58],
                [+0.30, -0.26],
                [-0.17, -0.58],
                [+0.00, -0.06],
                [-0.43, +0.24],
                [+0.11, +0.24],
            ],
            dtype=np.float64,
        ),
        np.array(
            [[+1.12, -0.82], [+1.92, -0.82], [+1.92, -0.10], [+1.12, -0.10]],
            dtype=np.float64,
        ),
        np.array(
            [[+2.20, +0.10], [+3.00, +0.10], [+3.00, +0.82], [+2.20, +0.82]],
            dtype=np.float64,
        ),
    ]

    polygons = dvz.dvz_polygons(scene, 0)
    if not polygons:
        raise RuntimeError("dvz_polygons() failed")

    for region in regions:
        desc, _region = _polygon_desc(region)
        if dvz.dvz_polygons_add_region(polygons, ctypes.byref(desc)) == 0xFFFFFFFF:
            raise RuntimeError("dvz_polygons_add_region() failed")

    ids = (ctypes.c_uint64 * 3)(21, 22, 23)
    if dvz.dvz_polygons_set_region_ids(polygons, 0, 3, ids) != 0:
        raise RuntimeError("dvz_polygons_set_region_ids() failed")

    fills = (dvz.DvzColor * 3)(
        dvz.DvzColor(231, 98, 82, 220),
        dvz.DvzColor(240, 189, 72, 220),
        dvz.DvzColor(92, 189, 132, 220),
    )
    strokes = (dvz.DvzColor * 3)(
        dvz.DvzColor(85, 42, 38, 255),
        dvz.DvzColor(88, 68, 26, 255),
        dvz.DvzColor(26, 74, 54, 255),
    )
    widths = (ctypes.c_float * 3)(5.0, 7.0, 5.0)

    if dvz.dvz_polygons_set_region_fill_colors(polygons, 0, 3, fills) != 0:
        raise RuntimeError("dvz_polygons_set_region_fill_colors() failed")
    if dvz.dvz_polygons_set_region_stroke_colors(polygons, 0, 3, strokes) != 0:
        raise RuntimeError("dvz_polygons_set_region_stroke_colors() failed")
    if dvz.dvz_polygons_set_region_stroke_widths_px(polygons, 0, 3, widths) != 0:
        raise RuntimeError("dvz_polygons_set_region_stroke_widths_px() failed")
    if dvz.dvz_polygons_set_stroke_join(polygons, dvz.DVZ_PATH_JOIN_BEVEL, 4.0) != 0:
        raise RuntimeError("dvz_polygons_set_stroke_join() failed")

    _attach_composite(panel, dvz.dvz_polygons_composite(polygons, 0), z_layer=2)


def _build_scene():
    scene, figure, panel = ex.scene_panel()
    _set_equal_view2d(panel)
    _add_holed_polygon(scene, panel)
    _add_polygon_set(scene, panel)
    return scene, figure, panel


def _configure_view(view, scene, panel) -> None:
    desc = dvz.dvz_panzoom_desc()
    desc.controller_flags = dvz.DVZ_PANZOOM_FLAGS_KEEP_ASPECT
    controller = dvz.dvz_panzoom(scene, ctypes.byref(desc))
    if not controller:
        raise RuntimeError("dvz_panzoom() failed")
    if dvz.dvz_view_bind_controller(view, panel, controller, dvz.DVZ_DIM_MASK_XY) != 0:
        raise RuntimeError("dvz_view_bind_controller() failed")


def main() -> None:
    scene, figure, panel = _build_scene()

    def configure(view) -> None:
        _configure_view(view, scene, panel)

    ex.run_with_view(scene, figure, "Polygon Composite", configure)


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

/* polygon - This example compares one holed polygon with a multi-region polygon set.
 *
 * What to look for: the left shape uses outer and hole coordinate rings plus fill, stroke, width,
 * join, and ID settings; the right shape uses three regions with per-region IDs and styles.
 * Compare the transparent fills, hole, and beveled versus rounded joins to see how polygon
 * composites support maps, masks, regions of interest, and segmented spatial data.
 *
 * Scenario: composites_polygon
 * Style: feature composite, graphite_cyan, 1280x720 window target
 *
 * Build:  just example-c composites/polygon
 * Run:    ./build/examples/c/composites/polygon --live
 * Smoke:  ./build/examples/c/composites/polygon --png
 */



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

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

#include "_assertions.h"
#include "datoviz/controller/panzoom.h"
#include "datoviz/scene.h"
#include "example_common.h"
#include "example_style.h"
#include "runner/scenario_runner.h"



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

DvzScenarioSpec dvz_composite_polygon_scenario(void);



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

#define WIDTH  EXAMPLE_WINDOW_WIDTH
#define HEIGHT EXAMPLE_WINDOW_HEIGHT



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

/**
 * Configure the panel used by the polygon composite example.
 *
 * @param panel target panel
 * @return whether setup succeeded
 */
static bool _configure_panel(DvzPanel* panel)
{
    ANN(panel);
    if (dvz_panel_set_padding(
            panel, &(DvzPanelReserve){
                       .left_px = 24.0f, .right_px = 24.0f, .bottom_px = 18.0f,
                       .top_px = 18.0f}) != DVZ_OK)
        return false;
    return example_configure_equal_aspect_panel(
        panel, (DvzDataDomain){.min = -2.18, .max = +3.06},
        (DvzDataDomain){.min = -0.88, .max = +0.88}, 0.05);
}



/**
 * Add one semantic polygon with a visible hole.
 *
 * @param scene scene owning the polygon
 * @param panel panel receiving the composite
 * @return whether the polygon was created and attached
 */
static bool _add_holed_polygon(DvzScene* scene, DvzPanel* panel)
{
    ANN(scene);
    ANN(panel);

    static const dvec2 outer[8] = {
        {-2.12, +0.00}, {-1.88, -0.58}, {-1.30, -0.82}, {-0.72, -0.58},
        {-0.48, +0.00}, {-0.72, +0.58}, {-1.30, +0.82}, {-1.88, +0.58},
    };
    static const dvec2 hole[6] = {
        {-1.66, +0.00}, {-1.52, -0.26}, {-1.22, -0.26},
        {-1.08, +0.00}, {-1.22, +0.26}, {-1.52, +0.26},
    };

    DvzPolygon* polygon = dvz_polygon(scene, 0);
    if (polygon == NULL)
        return false;
    int rc = dvz_polygon_set_geometry(
        polygon, &(DvzPolygonDesc){DVZ_STRUCT_INIT_FIELDS(DvzPolygonDesc),
                                   .outer = {.xy = outer, .count = DVZ_ARRAY_COUNT(outer)}});
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_hole(polygon, 0, hole, DVZ_ARRAY_COUNT(hole));
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_id(polygon, 10);
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_fill_color(polygon, (DvzColor){36, 151, 178, 210});
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_stroke_color(polygon, (DvzColor){214, 240, 255, 255});
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_stroke_width_px(polygon, 8.0f);
    if (rc != 0)
        return false;
    rc = dvz_polygon_set_stroke_join(polygon, DVZ_PATH_JOIN_ROUND, 4.0f);
    if (rc != 0)
        return false;

    DvzComposite* composite = dvz_polygon_composite(polygon, 0);
    if (composite == NULL)
        return false;

    DvzVisualAttachDesc attach = dvz_visual_attach_desc();
    attach.coord_space = DVZ_VISUAL_COORD_DATA;
    rc = dvz_panel_add_composite(panel, composite, &attach);
    return rc == 0;
}



/**
 * Add a small semantic polygon set with per-region styling.
 *
 * @param scene scene owning the polygon set
 * @param panel panel receiving the composite
 * @return whether the polygon set was created and attached
 */
static bool _add_polygon_set(DvzScene* scene, DvzPanel* panel)
{
    ANN(scene);
    ANN(panel);

    static const dvec2 left[10] = {
        {+0.30, +0.74}, {+0.49, +0.24}, {+1.03, +0.24}, {+0.60, -0.06}, {+0.77, -0.58},
        {+0.30, -0.26}, {-0.17, -0.58}, {+0.00, -0.06}, {-0.43, +0.24}, {+0.11, +0.24},
    };
    static const dvec2 middle[4] = {
        {+1.12, -0.82},
        {+1.92, -0.82},
        {+1.92, -0.10},
        {+1.12, -0.10},
    };
    static const dvec2 right[4] = {
        {+2.20, +0.10},
        {+3.00, +0.10},
        {+3.00, +0.82},
        {+2.20, +0.82},
    };

    DvzPolygons* set = dvz_polygons(scene, 0);
    if (set == NULL)
        return false;

    const uint32_t a = dvz_polygons_add_region(
        set, &(DvzPolygonDesc){DVZ_STRUCT_INIT_FIELDS(DvzPolygonDesc),
                               .outer = {.xy = left, .count = DVZ_ARRAY_COUNT(left)}});
    const uint32_t b = dvz_polygons_add_region(
        set, &(DvzPolygonDesc){DVZ_STRUCT_INIT_FIELDS(DvzPolygonDesc),
                               .outer = {.xy = middle, .count = DVZ_ARRAY_COUNT(middle)}});
    const uint32_t c = dvz_polygons_add_region(
        set, &(DvzPolygonDesc){DVZ_STRUCT_INIT_FIELDS(DvzPolygonDesc),
                               .outer = {.xy = right, .count = DVZ_ARRAY_COUNT(right)}});
    if (a == UINT32_MAX || b == UINT32_MAX || c == UINT32_MAX)
        return false;

    const uint64_t ids[3] = {21, 22, 23};
    int rc = dvz_polygons_set_region_ids(set, 0, 3, ids);
    if (rc != 0)
        return false;

    const DvzColor fill[3] = {
        {231, 98, 82, 220},
        {240, 189, 72, 220},
        {92, 189, 132, 220},
    };
    const DvzColor stroke[3] = {
        {85, 42, 38, 255},
        {88, 68, 26, 255},
        {26, 74, 54, 255},
    };
    const float widths[3] = {5.0f, 7.0f, 5.0f};
    rc = dvz_polygons_set_region_fill_colors(set, 0, 3, fill);
    if (rc != 0)
        return false;
    rc = dvz_polygons_set_region_stroke_colors(set, 0, 3, stroke);
    if (rc != 0)
        return false;
    rc = dvz_polygons_set_region_stroke_widths_px(set, 0, 3, widths);
    if (rc != 0)
        return false;
    rc = dvz_polygons_set_stroke_join(set, DVZ_PATH_JOIN_BEVEL, 4.0f);
    if (rc != 0)
        return false;

    DvzComposite* composite = dvz_polygons_composite(set, 0);
    if (composite == NULL)
        return false;

    DvzVisualAttachDesc attach = dvz_visual_attach_desc();
    attach.z_layer = 2;
    attach.coord_space = DVZ_VISUAL_COORD_DATA;
    rc = dvz_panel_add_composite(panel, composite, &attach);
    return rc == 0;
}



static bool _scenario_init(DvzScenarioContext* ctx, void** out_user)
{
    if (ctx == NULL)
        return false;
    if (out_user != NULL)
        *out_user = NULL;

    bool ok = false;
    ctx->figure = dvz_figure(ctx->scene, ctx->width, ctx->height, 0);
    EXAMPLE_CHECK(ctx->figure != NULL, "dvz_figure() failed");

    DvzPanel* panel = dvz_panel(ctx->figure, &(DvzPanelDesc){0.0f, 0.0f, 1.0f, 1.0f});
    EXAMPLE_CHECK(panel != NULL, "dvz_panel() failed");
    EXAMPLE_CHECK(_configure_panel(panel), "panel configuration failed");
    EXAMPLE_CHECK(_add_holed_polygon(ctx->scene, panel), "holed polygon setup failed");
    EXAMPLE_CHECK(_add_polygon_set(ctx->scene, panel), "polygon set setup failed");

    DvzPanzoomDesc panzoom_desc = dvz_panzoom_desc();
    panzoom_desc.controller_flags = DVZ_PANZOOM_FLAGS_KEEP_ASPECT;
    DvzPanzoom* panzoom = dvz_scenario_panzoom(ctx, panel, &panzoom_desc, DVZ_DIM_MASK_XY);
    EXAMPLE_CHECK(panzoom != NULL, "failed to create or bind panzoom controller");
    (void)panzoom;

    ok = true;
cleanup:
    return ok;
}



DvzScenarioSpec dvz_composite_polygon_scenario(void)
{
    return (DvzScenarioSpec){
        .id = "composites_polygon",
        .title = "Polygon Composite",
        .width = WIDTH,
        .height = HEIGHT,
        .fps = 60.0,
        .requirements = DVZ_SCENARIO_REQ_CONTROLLER | DVZ_SCENARIO_REQ_PANZOOM,
        .init = _scenario_init,
    };
}



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

#ifndef DVZ_EXAMPLE_NO_MAIN
int main(int argc, char** argv)
{
    DvzScenarioSpec spec = dvz_composite_polygon_scenario();
    return dvz_scenario_run_native_cli(&spec, argc, argv) == 0 ? 0 : 1;
}
#endif
Example details

Tags

composite, polygon, polygon-set, holes, panzoom

Data

Field Value
kind synthetic