# Delaunay marks ^0.5.1​

Given set of points in x and y, the Delaunay marks compute the Delaunay triangulation, its dual the Voronoi tessellation, and the convex hull.

The voronoi mark computes the region closest to each point (its Voronoi cell). The cell can be empty if another point shares the exact same coordinates. Together, the cells cover the entire plot. Voronoi diagrams can group related points with color, for example.

Fork
js
``````Plot.plot({
color: {legend: true},
marks: [
Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species", fillOpacity: 0.2, stroke: "white"}),
Plot.frame(),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````
``````Plot.plot({
color: {legend: true},
marks: [
Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species", fillOpacity: 0.2, stroke: "white"}),
Plot.frame(),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````

Each cell is associated with a particular data point, and channels such as stroke, fill, fillOpacity, strokeOpacity, href, etc., work as they do on other marks, such as dots.

To show the local density of a scatterplot, one can draw the whole boundary at once with voronoiMesh. Whereas the voronoi mark will draw shared cell boundaries twice, the mesh will draw them only once.

Fork
js
``````Plot.plot({
marks: [
Plot.voronoiMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````
``````Plot.plot({
marks: [
Plot.voronoiMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````

The boundary between two neighboring Voronoi cells is a line segment defined by equal distance from their two respective points. The construction of the Voronoi diagram involves the computation of the Delaunay graph, which defines these neighbors. Use delaunayMesh to draw the graph.

Fork
js
``````Plot.plot({
marks: [
Plot.delaunayMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", z: "species", stroke: "species", strokeOpacity: 0.5}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````
``````Plot.plot({
marks: [
Plot.delaunayMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", z: "species", stroke: "species", strokeOpacity: 0.5}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species"})
]
})``````

As shown above, the Delaunay graph is computed separately for each color; specifying z, stroke, or fill creates independent series.

Another derivative of the Delaunay graph is the convex hull of a set of points: the polygon with the minimum perimeter that contains all the points. The hull mark will draw this hull.

Fork
js
``````Plot.plot({
marks: [
Plot.hull(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species", fillOpacity: 0.2}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "species"})
]
})``````
``````Plot.plot({
marks: [
Plot.hull(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "species", fillOpacity: 0.2}),
Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "species"})
]
})``````

Using independent series is not recommended in the case of the voronoi and voronoiMesh marks as it will result in an unreadable chart due to overlapping Voronoi diagrams, but it can be useful to color the links of the Delaunay graph based on some property of data, such as the body mass of penguins below.

Fork
js
``````Plot.plot({
color: {legend: true},
marks: [
Plot.delaunayLink(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "body_mass_g", strokeWidth: 1.5})
]
})``````
``````Plot.plot({
color: {legend: true},
marks: [
Plot.delaunayLink(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "body_mass_g", strokeWidth: 1.5})
]
})``````

CAUTION

The link color is driven by one arbitrary extremity of each edge; this might change in the future.

The Delaunay marks can be one-dimensional, too.

Fork
js
``````Plot.plot({
marks: [
Plot.voronoi(penguins, {x: "body_mass_g", fill: "species"}),
Plot.voronoiMesh(penguins, {x: "body_mass_g", stroke: "white", strokeOpacity: 1})
]
})``````
``````Plot.plot({
marks: [
Plot.voronoi(penguins, {x: "body_mass_g", fill: "species"}),
Plot.voronoiMesh(penguins, {x: "body_mass_g", stroke: "white", strokeOpacity: 1})
]
})``````

The Delaunay marks also work with Plot’s projection system, as in this Voronoi diagram showing the distribution of Walmart stores in the contiguous United States.

Fork
js
``````Plot.plot({
projection: "albers",
marks: [
Plot.geo(nation),
Plot.dot(walmarts, {x: "longitude", y: "latitude", fill: "currentColor", r: 1}),
Plot.voronoiMesh(walmarts, {x: "longitude", y: "latitude"})
]
})``````
``````Plot.plot({
projection: "albers",
marks: [
Plot.geo(nation),
Plot.dot(walmarts, {x: "longitude", y: "latitude", fill: "currentColor", r: 1}),
Plot.voronoiMesh(walmarts, {x: "longitude", y: "latitude"})
]
})``````

CAUTION

Distances between projected points are not exactly proportional to the corresponding distances on the sphere. This creates a discrepancy between the planar Voronoi diagram and its spherical counterpart. For greater accuracy, use d3-geo-voronoi with the geo mark.

js
``Plot.delaunayLink(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``
``Plot.delaunayLink(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``

Draws links for each edge of the Delaunay triangulation of the points given by the x and y channels. Supports the same options as the link mark, except that x1, y1, x2, and y2 are derived automatically from x and y. When an aesthetic channel is specified (such as stroke or strokeWidth), the link inherits the corresponding channel value from one of its two endpoints arbitrarily.

If a z channel is specified, the input points are grouped by z, and separate Delaunay triangulations are constructed for each group.

## delaunayMesh(data, options) ​

js
``Plot.delaunayMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``
``Plot.delaunayMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``

Draws a mesh of the Delaunay triangulation of the points given by the x and y channels. The stroke option defaults to currentColor, and the strokeOpacity defaults to 0.2. The fill option is not supported. When an aesthetic channel is specified (such as stroke or strokeWidth), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily.

If a z channel is specified, the input points are grouped by z, and separate Delaunay triangulations are constructed for each group.

## hull(data, options) ​

js
``Plot.hull(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``
``Plot.hull(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``

Draws a convex hull around the points given by the x and y channels. The stroke option defaults to currentColor and the fill option defaults to none. When an aesthetic channel is specified (such as stroke or strokeWidth), the hull inherits the corresponding channel value from one of its constituent points arbitrarily.

If a z channel is specified, the input points are grouped by z, and separate convex hulls are constructed for each group. If the z channel is not specified, it defaults to either the fill channel, if any, or the stroke channel, if any.

## voronoi(data, options) ​

js
``Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``
``Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``

Draws polygons for each cell of the Voronoi tessellation of the points given by the x and y channels.

If a z channel is specified, the input points are grouped by z, and separate Voronoi tessellations are constructed for each group.

## voronoiMesh(data, options) ​

js
``Plot.voronoiMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``
``Plot.voronoiMesh(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm"})``

Draws a mesh for the cell boundaries of the Voronoi tessellation of the points given by the x and y channels. The stroke option defaults to currentColor, and the strokeOpacity defaults to 0.2. The fill option is not supported. When an aesthetic channel is specified (such as stroke or strokeWidth), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily.

If a z channel is specified, the input points are grouped by z, and separate Voronoi tessellations are constructed for each group.

Resources
Observable