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
- ID:
composites_polygon - Category:
composite - Lane:
composites - Status:
supported - Source:
examples/c/composites/polygon.c - Approved adaptation starter:
yes - Python source:
examples/python/gallery/composites/polygon.py - Python adaptation: Available
- Browser support: Live in browser
- WebGPU live route:
examples/webgpu/live.html?id=composites_polygon - Browser capability tags:
composite,polygon-set,primitive,segment,panzoom - Validation:
smoke+screenshot
Tags
composite, polygon, polygon-set, holes, panzoom
Data
| Field | Value |
|---|---|
kind |
synthetic |
