d3-shape
DefinitelyTyped icon, indicating that this package has TypeScript declarations provided by the separate @types/d3-shape package

3.2.0 • Public • Published

d3-shape

Visualizations typically consist of discrete graphical marks, such as symbols, arcs, lines and areas. While the rectangles of a bar chart may be easy enough to generate directly using SVG or Canvas, other shapes are complex, such as rounded annular sectors and centripetal Catmull–Rom splines. This module provides a variety of shape generators for your convenience.

As with other aspects of D3, these shapes are driven by data: each shape generator exposes accessors that control how the input data are mapped to a visual representation. For example, you might define a line generator for a time series by scaling fields of your data to fit the chart:

const line = d3.line()
    .x(d => x(d.date))
    .y(d => y(d.value));

This line generator can then be used to compute the d attribute of an SVG path element:

path.datum(data).attr("d", line);

Or you can use it to render to a Canvas 2D context:

line.context(context)(data);

For more, read Introducing d3-shape.

Installing

If you use npm, npm install d3-shape. You can also download the latest release on GitHub. For vanilla HTML in modern browsers, import d3-shape from jsDelivr:

<script type="module">

import {line} from "https://cdn.jsdelivr.net/npm/d3-shape@3/+esm";

const l = line();

</script>

For legacy environments, you can load d3-shape’s UMD bundle; a d3 global is exported:

<script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-shape@3"></script>
<script>

const l = d3.line();

</script>

API Reference

Note: all the methods that accept arrays also accept iterables and convert them to arrays internally.

Arcs

Pie ChartDonut Chart

The arc generator produces a circular or annular sector, as in a pie or donut chart. If the absolute difference between the start and end angles (the angular span) is greater than τ, the arc generator will produce a complete circle or annulus. If it is less than τ, the arc’s angular length will be equal to the absolute difference between the two angles (going clockwise if the signed difference is positive and anticlockwise if it is negative). If the absolute difference is less than τ, the arc may have rounded corners and angular padding. Arcs are always centered at ⟨0,0⟩; use a transform (see: SVG, Canvas) to move the arc to a different position.

See also the pie generator, which computes the necessary angles to represent an array of data as a pie or donut chart; these angles can then be passed to an arc generator.

# d3.arc() · Source

Constructs a new arc generator with the default settings.

# arc(arguments…) · Source

Generates an arc for the given arguments. The arguments are arbitrary; they are simply propagated to the arc generator’s accessor functions along with the this object. For example, with the default settings, an object with radii and angles is expected:

const arc = d3.arc();

arc({
  innerRadius: 0,
  outerRadius: 100,
  startAngle: 0,
  endAngle: Math.PI / 2
}); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

If the radii and angles are instead defined as constants, you can generate an arc without any arguments:

const arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI / 2);

arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"

If the arc generator has a context, then the arc is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.

# arc.centroid(arguments…) · Source

Computes the midpoint [x, y] of the center line of the arc that would be generated by the given arguments. The arguments are arbitrary; they are simply propagated to the arc generator’s accessor functions along with the this object. To be consistent with the generated arc, the accessors must be deterministic, i.e., return the same value given the same arguments. The midpoint is defined as (startAngle + endAngle) / 2 and (innerRadius + outerRadius) / 2. For example:

Circular Sector CentroidsAnnular Sector Centroids

Note that this is not the geometric center of the arc, which may be outside the arc; this method is merely a convenience for positioning labels.

# arc.innerRadius([radius]) · Source

If radius is specified, sets the inner radius to the specified function or number and returns this arc generator. If radius is not specified, returns the current inner radius accessor, which defaults to:

function innerRadius(d) {
  return d.innerRadius;
}

Specifying the inner radius as a function is useful for constructing a stacked polar bar chart, often in conjunction with a sqrt scale. More commonly, a constant inner radius is used for a donut or pie chart. If the outer radius is smaller than the inner radius, the inner and outer radii are swapped. A negative value is treated as zero.

# arc.outerRadius([radius]) · Source

If radius is specified, sets the outer radius to the specified function or number and returns this arc generator. If radius is not specified, returns the current outer radius accessor, which defaults to:

function outerRadius(d) {
  return d.outerRadius;
}

Specifying the outer radius as a function is useful for constructing a coxcomb or polar bar chart, often in conjunction with a sqrt scale. More commonly, a constant outer radius is used for a pie or donut chart. If the outer radius is smaller than the inner radius, the inner and outer radii are swapped. A negative value is treated as zero.

# arc.cornerRadius([radius]) · Source

If radius is specified, sets the corner radius to the specified function or number and returns this arc generator. If radius is not specified, returns the current corner radius accessor, which defaults to:

function cornerRadius() {
  return 0;
}

If the corner radius is greater than zero, the corners of the arc are rounded using circles of the given radius. For a circular sector, the two outer corners are rounded; for an annular sector, all four corners are rounded. The corner circles are shown in this diagram:

Rounded Circular SectorsRounded Annular Sectors

The corner radius may not be larger than (outerRadius - innerRadius) / 2. In addition, for arcs whose angular span is less than π, the corner radius may be reduced as two adjacent rounded corners intersect. This is occurs more often with the inner corners. See the arc corners animation for illustration.

# arc.startAngle([angle]) · Source

If angle is specified, sets the start angle to the specified function or number and returns this arc generator. If angle is not specified, returns the current start angle accessor, which defaults to:

function startAngle(d) {
  return d.startAngle;
}

The angle is specified in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise. If |endAngle - startAngle| ≥ τ, a complete circle or annulus is generated rather than a sector.

# arc.endAngle([angle]) · Source

If angle is specified, sets the end angle to the specified function or number and returns this arc generator. If angle is not specified, returns the current end angle accessor, which defaults to:

function endAngle(d) {
  return d.endAngle;
}

The angle is specified in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise. If |endAngle - startAngle| ≥ τ, a complete circle or annulus is generated rather than a sector.

# arc.padAngle([angle]) · Source

If angle is specified, sets the pad angle to the specified function or number and returns this arc generator. If angle is not specified, returns the current pad angle accessor, which defaults to:

function padAngle() {
  return d && d.padAngle;
}

The pad angle is converted to a fixed linear distance separating adjacent arcs, defined as padRadius * padAngle. This distance is subtracted equally from the start and end of the arc. If the arc forms a complete circle or annulus, as when |endAngle - startAngle| ≥ τ, the pad angle is ignored.

If the inner radius or angular span is small relative to the pad angle, it may not be possible to maintain parallel edges between adjacent arcs. In this case, the inner edge of the arc may collapse to a point, similar to a circular sector. For this reason, padding is typically only applied to annular sectors (i.e., when innerRadius is positive), as shown in this diagram:

Padded Circular SectorsPadded Annular Sectors

The recommended minimum inner radius when using padding is outerRadius * padAngle / sin(θ), where θ is the angular span of the smallest arc before padding. For example, if the outer radius is 200 pixels and the pad angle is 0.02 radians, a reasonable θ is 0.04 radians, and a reasonable inner radius is 100 pixels. See the arc padding animation for illustration.

Often, the pad angle is not set directly on the arc generator, but is instead computed by the pie generator so as to ensure that the area of padded arcs is proportional to their value; see pie.padAngle. See the pie padding animation for illustration. If you apply a constant pad angle to the arc generator directly, it tends to subtract disproportionately from smaller arcs, introducing distortion.

# arc.padRadius([radius]) · Source

If radius is specified, sets the pad radius to the specified function or number and returns this arc generator. If radius is not specified, returns the current pad radius accessor, which defaults to null, indicating that the pad radius should be automatically computed as sqrt(innerRadius * innerRadius + outerRadius * outerRadius). The pad radius determines the fixed linear distance separating adjacent arcs, defined as padRadius * padAngle.

# arc.context([context]) · Source

If context is specified, sets the context and returns this arc generator. If context is not specified, returns the current context, which defaults to null. If the context is not null, then the generated arc is rendered to this context as a sequence of path method calls. Otherwise, a path data string representing the generated arc is returned.

# arc.digits([digits]) · Source

If digits is specified, sets the maximum number of digits after the decimal separator and returns this arc generator. If digits is not specified, returns the current maximum fraction digits, which defaults to 3. This option only applies when the associated context is null, as when this arc generator is used to produce path data.

Pies

The pie generator does not produce a shape directly, but instead computes the necessary angles to represent a tabular dataset as a pie or donut chart; these angles can then be passed to an arc generator.

# d3.pie() · Source

Constructs a new pie generator with the default settings.

# pie(data[, arguments…]) · Source

Generates a pie for the given array of data, returning an array of objects representing each datum’s arc angles. Any additional arguments are arbitrary; they are simply propagated to the pie generator’s accessor functions along with the this object. The length of the returned array is the same as data, and each element i in the returned array corresponds to the element i in the input data. Each object in the returned array has the following properties:

  • data - the input datum; the corresponding element in the input data array.
  • value - the numeric value of the arc.
  • index - the zero-based sorted index of the arc.
  • startAngle - the start angle of the arc.
  • endAngle - the end angle of the arc.
  • padAngle - the pad angle of the arc.

This representation is designed to work with the arc generator’s default startAngle, endAngle and padAngle accessors. The angular units are arbitrary, but if you plan to use the pie generator in conjunction with an arc generator, you should specify angles in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise.

Given a small dataset of numbers, here is how to compute the arc angles to render this data as a pie chart:

const data = [1, 1, 2, 3, 5, 8, 13, 21];
const arcs = d3.pie()(data);

The first pair of parens, pie(), constructs a default pie generator. The second, pie()(data), invokes this generator on the dataset, returning an array of objects:

[
  {"data":  1, "value":  1, "index": 6, "startAngle": 6.050474740247008, "endAngle": 6.166830023713296, "padAngle": 0},
  {"data":  1, "value":  1, "index": 7, "startAngle": 6.166830023713296, "endAngle": 6.283185307179584, "padAngle": 0},
  {"data":  2, "value":  2, "index": 5, "startAngle": 5.817764173314431, "endAngle": 6.050474740247008, "padAngle": 0},
  {"data":  3, "value":  3, "index": 4, "startAngle": 5.468698322915565, "endAngle": 5.817764173314431, "padAngle": 0},
  {"data":  5, "value":  5, "index": 3, "startAngle": 4.886921905584122, "endAngle": 5.468698322915565, "padAngle": 0},
  {"data":  8, "value":  8, "index": 2, "startAngle": 3.956079637853813, "endAngle": 4.886921905584122, "padAngle": 0},
  {"data": 13, "value": 13, "index": 1, "startAngle": 2.443460952792061, "endAngle": 3.956079637853813, "padAngle": 0},
  {"data": 21, "value": 21, "index": 0, "startAngle": 0.000000000000000, "endAngle": 2.443460952792061, "padAngle": 0}
]

Note that the returned array is in the same order as the data, even though this pie chart is sorted by descending value, starting with the arc for the last datum (value 21) at 12 o’clock.

# pie.value([value]) · Source

If value is specified, sets the value accessor to the specified function or number and returns this pie generator. If value is not specified, returns the current value accessor, which defaults to:

function value(d) {
  return d;
}

When a pie is generated, the value accessor will be invoked for each element in the input data array, being passed the element d, the index i, and the array data as three arguments. The default value accessor assumes that the input data are numbers, or that they are coercible to numbers using valueOf. If your data are not simply numbers, then you should specify an accessor that returns the corresponding numeric value for a given datum. For example:

const data = [
  {"number":  4, "name": "Locke"},
  {"number":  8, "name": "Reyes"},
  {"number": 15, "name": "Ford"},
  {"number": 16, "name": "Jarrah"},
  {"number": 23, "name": "Shephard"},
  {"number": 42, "name": "Kwon"}
];

const arcs = d3.pie()
    .value(d => d.number)
    (data);

This is similar to mapping your data to values before invoking the pie generator:

const arcs = d3.pie()(data.map(d => d.number));

The benefit of an accessor is that the input data remains associated with the returned objects, thereby making it easier to access other fields of the data, for example to set the color or to add text labels.

# pie.sort([compare]) · Source

If compare is specified, sets the data comparator to the specified function and returns this pie generator. If compare is not specified, returns the current data comparator, which defaults to null. If both the data comparator and the value comparator are null, then arcs are positioned in the original input order. Otherwise, the data is sorted according to the data comparator, and the resulting order is used. Setting the data comparator implicitly sets the value comparator to null.

The compare function takes two arguments a and b, each elements from the input data array. If the arc for a should be before the arc for b, then the comparator must return a number less than zero; if the arc for a should be after the arc for b, then the comparator must return a number greater than zero; returning zero means that the relative order of a and b is unspecified. For example, to sort arcs by their associated name:

pie.sort((a, b) => a.name.localeCompare(b.name));

Sorting does not affect the order of the generated arc array which is always in the same order as the input data array; it merely affects the computed angles of each arc. The first arc starts at the start angle and the last arc ends at the end angle.

# pie.sortValues([compare]) · Source

If compare is specified, sets the value comparator to the specified function and returns this pie generator. If compare is not specified, returns the current value comparator, which defaults to descending value. The default value comparator is implemented as:

function compare(a, b) {
  return b - a;
}

If both the data comparator and the value comparator are null, then arcs are positioned in the original input order. Otherwise, the data is sorted according to the data comparator, and the resulting order is used. Setting the value comparator implicitly sets the data comparator to null.

The value comparator is similar to the data comparator, except the two arguments a and b are values derived from the input data array using the value accessor, not the data elements. If the arc for a should be before the arc for b, then the comparator must return a number less than zero; if the arc for a should be after the arc for b, then the comparator must return a number greater than zero; returning zero means that the relative order of a and b is unspecified. For example, to sort arcs by ascending value:

pie.sortValues((a, b) => a - b);

Sorting does not affect the order of the generated arc array which is always in the same order as the input data array; it merely affects the computed angles of each arc. The first arc starts at the start angle and the last arc ends at the end angle.

# pie.startAngle([angle]) · Source

If angle is specified, sets the overall start angle of the pie to the specified function or number and returns this pie generator. If angle is not specified, returns the current start angle accessor, which defaults to:

function startAngle() {
  return 0;
}

The start angle here means the overall start angle of the pie, i.e., the start angle of the first arc. The start angle accessor is invoked once, being passed the same arguments and this context as the pie generator. The units of angle are arbitrary, but if you plan to use the pie generator in conjunction with an arc generator, you should specify an angle in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise.

# pie.endAngle([angle]) · Source

If angle is specified, sets the overall end angle of the pie to the specified function or number and returns this pie generator. If angle is not specified, returns the current end angle accessor, which defaults to:

function endAngle() {
  return 2 * Math.PI;
}

The end angle here means the overall end angle of the pie, i.e., the end angle of the last arc. The end angle accessor is invoked once, being passed the same arguments and this context as the pie generator. The units of angle are arbitrary, but if you plan to use the pie generator in conjunction with an arc generator, you should specify an angle in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise.

The value of the end angle is constrained to startAngle ± τ, such that |endAngle - startAngle| ≤ τ.

# pie.padAngle([angle]) · Source

If angle is specified, sets the pad angle to the specified function or number and returns this pie generator. If angle is not specified, returns the current pad angle accessor, which defaults to:

function padAngle() {
  return 0;
}

The pad angle here means the angular separation between each adjacent arc. The total amount of padding reserved is the specified angle times the number of elements in the input data array, and at most |endAngle - startAngle|; the remaining space is then divided proportionally by value such that the relative area of each arc is preserved. See the pie padding animation for illustration. The pad angle accessor is invoked once, being passed the same arguments and this context as the pie generator. The units of angle are arbitrary, but if you plan to use the pie generator in conjunction with an arc generator, you should specify an angle in radians.

Lines

Line Chart

The line generator produces a spline or polyline, as in a line chart. Lines also appear in many other visualization types, such as the links in hierarchical edge bundling.

# d3.line([x][, y]) · Source, Examples

Constructs a new line generator with the default settings. If x or y are specified, sets the corresponding accessors to the specified function or number and returns this line generator.

# line(data) · Source, Examples

Generates a line for the given array of data. Depending on this line generator’s associated curve, the given input data may need to be sorted by x-value before being passed to the line generator. If the line generator has a context, then the line is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.

# line.x([x]) · Source, Examples

If x is specified, sets the x accessor to the specified function or number and returns this line generator. If x is not specified, returns the current x accessor, which defaults to:

function x(d) {
  return d[0];
}

When a line is generated, the x accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. The default x accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering, then you should specify a custom accessor. For example, if x is a time scale and y is a linear scale:

const data = [
  {date: new Date(2007, 3, 24), value: 93.24},
  {date: new Date(2007, 3, 25), value: 95.35},
  {date: new Date(2007, 3, 26), value: 98.84},
  {date: new Date(2007, 3, 27), value: 99.92},
  {date: new Date(2007, 3, 30), value: 99.80},
  {date: new Date(2007, 4,  1), value: 99.47},
  
];

const line = d3.line()
    .x(d => x(d.date))
    .y(d => y(d.value));

# line.y([y]) · Source, Examples

If y is specified, sets the y accessor to the specified function or number and returns this line generator. If y is not specified, returns the current y accessor, which defaults to:

function y(d) {
  return d[1];
}

When a line is generated, the y accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. The default y accessor assumes that the input data are two-element arrays of numbers. See line.x for more information.

# line.defined([defined]) · Source, Examples

If defined is specified, sets the defined accessor to the specified function or boolean and returns this line generator. If defined is not specified, returns the current defined accessor, which defaults to:

function defined() {
  return true;
}

The default accessor thus assumes that the input data is always defined. When a line is generated, the defined accessor will be invoked for each element in the input data array, being passed the element d, the index i, and the array data as three arguments. If the given element is defined (i.e., if the defined accessor returns a truthy value for this element), the x and y accessors will subsequently be evaluated and the point will be added to the current line segment. Otherwise, the element will be skipped, the current line segment will be ended, and a new line segment will be generated for the next defined point. As a result, the generated line may have several discrete segments. For example:

Line with Missing Data

Note that if a line segment consists of only a single point, it may appear invisible unless rendered with rounded or square line caps. In addition, some curves such as curveCardinalOpen only render a visible segment if it contains multiple points.

# line.curve([curve]) · Source, Examples

If curve is specified, sets the curve factory and returns this line generator. If curve is not specified, returns the current curve factory, which defaults to curveLinear.

# line.context([context]) · Source, Examples

If context is specified, sets the context and returns this line generator. If context is not specified, returns the current context, which defaults to null. If the context is not null, then the generated line is rendered to this context as a sequence of path method calls. Otherwise, a path data string representing the generated line is returned.

# line.digits([digits]) · Source

If digits is specified, sets the maximum number of digits after the decimal separator and returns this line generator. If digits is not specified, returns the current maximum fraction digits, which defaults to 3. This option only applies when the associated context is null, as when this line generator is used to produce path data.

# d3.lineRadial() · Source, Examples

Radial Line

Constructs a new radial line generator with the default settings. A radial line generator is equivalent to the standard Cartesian line generator, except the x and y accessors are replaced with angle and radius accessors. Radial lines are always positioned relative to ⟨0,0⟩; use a transform (see: SVG, Canvas) to change the origin.

# lineRadial(data) · Source, Examples

Equivalent to line.

# lineRadial.angle([angle]) · Source, Examples

Equivalent to line.x, except the accessor returns the angle in radians, with 0 at -y (12 o’clock).

# lineRadial.radius([radius]) · Source, Examples

Equivalent to line.y, except the accessor returns the radius: the distance from the origin ⟨0,0⟩.

# lineRadial.defined([defined])

Equivalent to line.defined.

# lineRadial.curve([curve]) · Source, Examples

Equivalent to line.curve. Note that curveMonotoneX or curveMonotoneY are not recommended for radial lines because they assume that the data is monotonic in x or y, which is typically untrue of radial lines.

# lineRadial.context([context])

Equivalent to line.context.

Areas

Area ChartStacked Area ChartDifference Chart

The area generator produces an area, as in an area chart. An area is defined by two bounding lines, either splines or polylines. Typically, the two lines share the same x-values (x0 = x1), differing only in y-value (y0 and y1); most commonly, y0 is defined as a constant representing zero. The first line (the topline) is defined by x1 and y1 and is rendered first; the second line (the baseline) is defined by x0 and y0 and is rendered second, with the points in reverse order. With a curveLinear curve, this produces a clockwise polygon.

# d3.area([x][, y0][, y1]) · Source

Constructs a new area generator with the default settings. If x, y0 or y1 are specified, sets the corresponding accessors to the specified function or number and returns this area generator.

# area(data) · Source

Generates an area for the given array of data. Depending on this area generator’s associated curve, the given input data may need to be sorted by x-value before being passed to the area generator. If the area generator has a context, then the area is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.

# area.x([x]) · Source

If x is specified, sets x0 to x and x1 to null and returns this area generator. If x is not specified, returns the current x0 accessor.

# area.x0([x]) · Source

If x is specified, sets the x0 accessor to the specified function or number and returns this area generator. If x is not specified, returns the current x0 accessor, which defaults to:

function x(d) {
  return d[0];
}

When an area is generated, the x0 accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. The default x0 accessor assumes that the input data are two-element arrays of numbers. If your data are in a different format, or if you wish to transform the data before rendering, then you should specify a custom accessor. For example, if x is a time scale and y is a linear scale:

const data = [
  {date: new Date(2007, 3, 24), value: 93.24},
  {date: new Date(2007, 3, 25), value: 95.35},
  {date: new Date(2007, 3, 26), value: 98.84},
  {date: new Date(2007, 3, 27), value: 99.92},
  {date: new Date(2007, 3, 30), value: 99.80},
  {date: new Date(2007, 4,  1), value: 99.47},
  
];

const area = d3.area()
    .x(d => x(d.date))
    .y1(d => y(d.value))
    .y0(y(0));

# area.x1([x]) · Source

If x is specified, sets the x1 accessor to the specified function or number and returns this area generator. If x is not specified, returns the current x1 accessor, which defaults to null, indicating that the previously-computed x0 value should be reused for the x1 value.

When an area is generated, the x1 accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. See area.x0 for more information.

# area.y([y]) · Source

If y is specified, sets y0 to y and y1 to null and returns this area generator. If y is not specified, returns the current y0 accessor.

# area.y0([y]) · Source

If y is specified, sets the y0 accessor to the specified function or number and returns this area generator. If y is not specified, returns the current y0 accessor, which defaults to:

function y() {
  return 0;
}

When an area is generated, the y0 accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. See area.x0 for more information.

# area.y1([y]) · Source

If y is specified, sets the y1 accessor to the specified function or number and returns this area generator. If y is not specified, returns the current y1 accessor, which defaults to:

function y(d) {
  return d[1];
}

A null accessor is also allowed, indicating that the previously-computed y0 value should be reused for the y1 value. When an area is generated, the y1 accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. See area.x0 for more information.

# area.defined([defined]) · Source

If defined is specified, sets the defined accessor to the specified function or boolean and returns this area generator. If defined is not specified, returns the current defined accessor, which defaults to:

function defined() {
  return true;
}

The default accessor thus assumes that the input data is always defined. When an area is generated, the defined accessor will be invoked for each element in the input data array, being passed the element d, the index i, and the array data as three arguments. If the given element is defined (i.e., if the defined accessor returns a truthy value for this element), the x0, x1, y0 and y1 accessors will subsequently be evaluated and the point will be added to the current area segment. Otherwise, the element will be skipped, the current area segment will be ended, and a new area segment will be generated for the next defined point. As a result, the generated area may have several discrete segments. For example:

Area with Missing Data

Note that if an area segment consists of only a single point, it may appear invisible unless rendered with rounded or square line caps. In addition, some curves such as curveCardinalOpen only render a visible segment if it contains multiple points.

# area.curve([curve]) · Source

If curve is specified, sets the curve factory and returns this area generator. If curve is not specified, returns the current curve factory, which defaults to curveLinear.

# area.context([context]) · Source

If context is specified, sets the context and returns this area generator. If context is not specified, returns the current context, which defaults to null. If the context is not null, then the generated area is rendered to this context as a sequence of path method calls. Otherwise, a path data string representing the generated area is returned.

# area.digits([digits]) · Source

If digits is specified, sets the maximum number of digits after the decimal separator and returns this area generator. If digits is not specified, returns the current maximum fraction digits, which defaults to 3. This option only applies when the associated context is null, as when this area generator is used to produce path data.

# area.lineX0() · Source
# area.lineY0() · Source

Returns a new line generator that has this area generator’s current defined accessor, curve and context. The line’s x-accessor is this area’s x0-accessor, and the line’s y-accessor is this area’s y0-accessor.

# area.lineX1() · Source

Returns a new line generator that has this area generator’s current defined accessor, curve and context. The line’s x-accessor is this area’s x1-accessor, and the line’s y-accessor is this area’s y0-accessor.

# area.lineY1() · Source

Returns a new line generator that has this area generator’s current defined accessor, curve and context. The line’s x-accessor is this area’s x0-accessor, and the line’s y-accessor is this area’s y1-accessor.

# d3.areaRadial() · Source

Radial Area

Constructs a new radial area generator with the default settings. A radial area generator is equivalent to the standard Cartesian area generator, except the x and y accessors are replaced with angle and radius accessors. Radial areas are always positioned relative to ⟨0,0⟩; use a transform (see: SVG, Canvas) to change the origin.

# areaRadial(data)

Equivalent to area.

# areaRadial.angle([angle]) · Source

Equivalent to area.x, except the accessor returns the angle in radians, with 0 at -y (12 o’clock).

# areaRadial.startAngle([angle]) · Source

Equivalent to area.x0, except the accessor returns the angle in radians, with 0 at -y (12 o’clock). Note: typically angle is used instead of setting separate start and end angles.

# areaRadial.endAngle([angle]) · Source

Equivalent to area.x1, except the accessor returns the angle in radians, with 0 at -y (12 o’clock). Note: typically angle is used instead of setting separate start and end angles.

# areaRadial.radius([radius]) · Source

Equivalent to area.y, except the accessor returns the radius: the distance from the origin ⟨0,0⟩.

# areaRadial.innerRadius([radius]) · Source

Equivalent to area.y0, except the accessor returns the radius: the distance from the origin ⟨0,0⟩.

# areaRadial.outerRadius([radius]) · Source

Equivalent to area.y1, except the accessor returns the radius: the distance from the origin ⟨0,0⟩.

# areaRadial.defined([defined])

Equivalent to area.defined.

# areaRadial.curve([curve]) · Source

Equivalent to area.curve. Note that curveMonotoneX or curveMonotoneY are not recommended for radial areas because they assume that the data is monotonic in x or y, which is typically untrue of radial areas.

# areaRadial.context([context])

Equivalent to line.context.

# areaRadial.lineStartAngle() · Source
# areaRadial.lineInnerRadius() · Source

Returns a new radial line generator that has this radial area generator’s current defined accessor, curve and context. The line’s angle accessor is this area’s start angle accessor, and the line’s radius accessor is this area’s inner radius accessor.

# areaRadial.lineEndAngle() · Source

Returns a new radial line generator that has this radial area generator’s current defined accessor, curve and context. The line’s angle accessor is this area’s end angle accessor, and the line’s radius accessor is this area’s inner radius accessor.

# areaRadial.lineOuterRadius() · Source

Returns a new radial line generator that has this radial area generator’s current defined accessor, curve and context. The line’s angle accessor is this area’s start angle accessor, and the line’s radius accessor is this area’s outer radius accessor.

Curves

While lines are defined as a sequence of two-dimensional [x, y] points, and areas are similarly defined by a topline and a baseline, there remains the task of transforming this discrete representation into a continuous shape: i.e., how to interpolate between the points. A variety of curves are provided for this purpose.

Curves are typically not constructed or used directly, instead being passed to line.curve and area.curve. For example:

const line = d3.line(d => d.date, d => d.value)
    .curve(d3.curveCatmullRom.alpha(0.5));

# d3.curveBasis(context) · Source

basis

Produces a cubic basis spline using the specified control points. The first and last points are triplicated such that the spline starts at the first point and ends at the last point, and is tangent to the line between the first and second points, and to the line between the penultimate and last points.

# d3.curveBasisClosed(context) · Source

basisClosed

Produces a closed cubic basis spline using the specified control points. When a line segment ends, the first three control points are repeated, producing a closed loop with C2 continuity.

# d3.curveBasisOpen(context) · Source

basisOpen

Produces a cubic basis spline using the specified control points. Unlike basis, the first and last points are not repeated, and thus the curve typically does not intersect these points.

# d3.curveBumpX(context) · Source

bumpX

Produces a Bézier curve between each pair of points, with horizontal tangents at each point.

# d3.curveBumpY(context) · Source

bumpY

Produces a Bézier curve between each pair of points, with vertical tangents at each point.

# d3.curveBundle(context) · Source

bundle

Produces a straightened cubic basis spline using the specified control points, with the spline straightened according to the curve’s beta, which defaults to 0.85. This curve is typically used in hierarchical edge bundling to disambiguate connections, as proposed by Danny Holten in Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data. This curve does not implement curve.areaStart and curve.areaEnd; it is intended to work with d3.line, not d3.area.

# bundle.beta(beta) · Source

Returns a bundle curve with the specified beta in the range [0, 1], representing the bundle strength. If beta equals zero, a straight line between the first and last point is produced; if beta equals one, a standard basis spline is produced. For example:

const line = d3.line().curve(d3.curveBundle.beta(0.5));

# d3.curveCardinal(context) · Source

cardinal

Produces a cubic cardinal spline using the specified control points, with one-sided differences used for the first and last piece. The default tension is 0.

# d3.curveCardinalClosed(context) · Source

cardinalClosed

Produces a closed cubic cardinal spline using the specified control points. When a line segment ends, the first three control points are repeated, producing a closed loop. The default tension is 0.

# d3.curveCardinalOpen(context) · Source

cardinalOpen

Produces a cubic cardinal spline using the specified control points. Unlike curveCardinal, one-sided differences are not used for the first and last piece, and thus the curve starts at the second point and ends at the penultimate point. The default tension is 0.

# cardinal.tension(tension) · Source

Returns a cardinal curve with the specified tension in the range [0, 1]. The tension determines the length of the tangents: a tension of one yields all zero tangents, equivalent to curveLinear; a tension of zero produces a uniform Catmull–Rom spline. For example:

const line = d3.line().curve(d3.curveCardinal.tension(0.5));

# d3.curveCatmullRom(context) · Source

catmullRom

Produces a cubic Catmull–Rom spline using the specified control points and the parameter alpha, which defaults to 0.5, as proposed by Yuksel et al. in On the Parameterization of Catmull–Rom Curves, with one-sided differences used for the first and last piece.

# d3.curveCatmullRomClosed(context) · Source

catmullRomClosed

Produces a closed cubic Catmull–Rom spline using the specified control points and the parameter alpha, which defaults to 0.5, as proposed by Yuksel et al. When a line segment ends, the first three control points are repeated, producing a closed loop.

# d3.curveCatmullRomOpen(context) · Source

catmullRomOpen

Produces a cubic Catmull–Rom spline using the specified control points and the parameter alpha, which defaults to 0.5, as proposed by Yuksel et al. Unlike curveCatmullRom, one-sided differences are not used for the first and last piece, and thus the curve starts at the second point and ends at the penultimate point.

# catmullRom.alpha(alpha) · Source

Returns a cubic Catmull–Rom curve with the specified alpha in the range [0, 1]. If alpha is zero, produces a uniform spline, equivalent to curveCardinal with a tension of zero; if alpha is one, produces a chordal spline; if alpha is 0.5, produces a centripetal spline. Centripetal splines are recommended to avoid self-intersections and overshoot. For example:

const line = d3.line().curve(d3.curveCatmullRom.alpha(0.5));

# d3.curveLinear(context) · Source

linear

Produces a polyline through the specified points.

# d3.curveLinearClosed(context) · Source

linearClosed

Produces a closed polyline through the specified points by repeating the first point when the line segment ends.

# d3.curveMonotoneX(context) · Source

monotoneX

Produces a cubic spline that preserves monotonicity in y, assuming monotonicity in x, as proposed by Steffen in A simple method for monotonic interpolation in one dimension: “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”

# d3.curveMonotoneY(context) · Source

monotoneY

Produces a cubic spline that preserves monotonicity in x, assuming monotonicity in y, as proposed by Steffen in A simple method for monotonic interpolation in one dimension: “a smooth curve with continuous first-order derivatives that passes through any given set of data points without spurious oscillations. Local extrema can occur only at grid points where they are given by the data, but not in between two adjacent grid points.”

# d3.curveNatural(context) · Source

natural

Produces a natural cubic spline with the second derivative of the spline set to zero at the endpoints.

# d3.curveStep(context) · Source

step

Produces a piecewise constant function (a step function) consisting of alternating horizontal and vertical lines. The y-value changes at the midpoint of each pair of adjacent x-values.

# d3.curveStepAfter(context) · Source

stepAfter

Produces a piecewise constant function (a step function) consisting of alternating horizontal and vertical lines. The y-value changes after the x-value.

# d3.curveStepBefore(context) · Source

stepBefore

Produces a piecewise constant function (a step function) consisting of alternating horizontal and vertical lines. The y-value changes before the x-value.

Custom Curves

Curves are typically not used directly, instead being passed to line.curve and area.curve. However, you can define your own curve implementation should none of the built-in curves satisfy your needs using the following interface. You can also use this low-level interface with a built-in curve type as an alternative to the line and area generators.

# curve.areaStart() · Source

Indicates the start of a new area segment. Each area segment consists of exactly two line segments: the topline, followed by the baseline, with the baseline points in reverse order.

# curve.areaEnd() · Source

Indicates the end of the current area segment.

# curve.lineStart() · Source

Indicates the start of a new line segment. Zero or more points will follow.

# curve.lineEnd() · Source

Indicates the end of the current line segment.

# curve.point(x, y) · Source

Indicates a new point in the current line segment with the given x- and y-values.

Links

Tidy Tree

The link shape generates a smooth cubic Bézier curve from a source point to a target point. The tangents of the curve at the start and end are either vertical, horizontal or radial.

# d3.link(curve) · Source

Returns a new link generator using the specified curve. For example, to visualize links in a tree diagram rooted on the top edge of the display, you might say:

const link = d3.link(d3.curveBumpY)
    .x(d => d.x)
    .y(d => d.y);

# d3.linkVertical() · Source

Shorthand for d3.link with d3.curveBumpY; suitable for visualizing links in a tree diagram rooted on the top edge of the display. Equivalent to:

const link = d3.link(d3.curveBumpY);

# d3.linkHorizontal() · Source

Shorthand for d3.link with d3.curveBumpX; suitable for visualizing links in a tree diagram rooted on the left edge of the display. Equivalent to:

const link = d3.link(d3.curveBumpX);

# link(arguments…) · Source

Generates a link for the given arguments. The arguments are arbitrary; they are simply propagated to the link generator’s accessor functions along with the this object. For example, with the default settings, an object expected:

link({
  source: [100, 100],
  target: [300, 300]
});

# link.source([source]) · Source

If source is specified, sets the source accessor to the specified function and returns this link generator. If source is not specified, returns the current source accessor, which defaults to:

function source(d) {
  return d.source;
}

# link.target([target]) · Source

If target is specified, sets the target accessor to the specified function and returns this link generator. If target is not specified, returns the current target accessor, which defaults to:

function target(d) {
  return d.target;
}

# link.x([x]) · Source

If x is specified, sets the x-accessor to the specified function or number and returns this link generator. If x is not specified, returns the current x-accessor, which defaults to:

function x(d) {
  return d[0];
}

# link.y([y]) · Source

If y is specified, sets the y-accessor to the specified function or number and returns this link generator. If y is not specified, returns the current y-accessor, which defaults to:

function y(d) {
  return d[1];
}

# link.context([context]) · Source

If context is specified, sets the context and returns this link generator. If context is not specified, returns the current context, which defaults to null. If the context is not null, then the generated link is rendered to this context as a sequence of path method calls. Otherwise, a path data string representing the generated link is returned. See also d3-path.

# link.digits([digits]) · Source

If digits is specified, sets the maximum number of digits after the decimal separator and returns this link generator. If digits is not specified, returns the current maximum fraction digits, which defaults to 3. This option only applies when the associated context is null, as when this link generator is used to produce path data.

# d3.linkRadial() · Source

Returns a new link generator with radial tangents. For example, to visualize links in a tree diagram rooted in the center of the display, you might say:

const link = d3.linkRadial()
    .angle(d => d.x)
    .radius(d => d.y);

# linkRadial.angle([angle]) · Source

Equivalent to link.x, except the accessor returns the angle in radians, with 0 at -y (12 o’clock).

# linkRadial.radius([radius]) · Source

Equivalent to link.y, except the accessor returns the radius: the distance from the origin ⟨0,0⟩.

Symbols

Symbols provide a categorical shape encoding as is commonly used in scatterplots. Symbols are always centered at ⟨0,0⟩; use a transform (see: SVG, Canvas) to move the symbol to a different position.

# d3.symbol([type][, size]) · Source, Examples

Constructs a new symbol generator of the specified type and size. If not specified, type defaults to a circle, and size defaults to 64.

# symbol(arguments…) · Source

Generates a symbol for the given arguments. The arguments are arbitrary; they are simply propagated to the symbol generator’s accessor functions along with the this object. For example, with the default settings, no arguments are needed to produce a circle with area 64 square pixels. If the symbol generator has a context, then the symbol is rendered to this context as a sequence of path method calls and this function returns void. Otherwise, a path data string is returned.

# symbol.type([type]) · Source

If type is specified, sets the symbol type to the specified function or symbol type and returns this symbol generator. If type is a function, the symbol generator’s arguments and this are passed through. (See selection.attr if you are using d3-selection.) If type is not specified, returns the current symbol type accessor, which defaults to:

function type() {
  return circle;
}

See symbolsFill and symbolsStroke for built-in symbol types. To implement a custom symbol type, pass an object that implements symbolType.draw.

# symbol.size([size]) · Source

If size is specified, sets the size to the specified function or number and returns this symbol generator. If size is a function, the symbol generator’s arguments and this are passed through. (See selection.attr if you are using d3-selection.) If size is not specified, returns the current size accessor, which defaults to:

function size() {
  return 64;
}

Specifying the size as a function is useful for constructing a scatterplot with a size encoding. If you wish to scale the symbol to fit a given bounding box, rather than by area, try SVG’s getBBox.

# symbol.context([context]) · Source

If context is specified, sets the context and returns this symbol generator. If context is not specified, returns the current context, which defaults to null. If the context is not null, then the generated symbol is rendered to this context as a sequence of path method calls. Otherwise, a path data string representing the generated symbol is returned.

# symbol.digits([digits]) · Source

If digits is specified, sets the maximum number of digits after the decimal separator and returns this symbol generator. If digits is not specified, returns the current maximum fraction digits, which defaults to 3. This option only applies when the associated context is null, as when this symbol generator is used to produce path data.

# d3.symbolsFill · Source

An array containing a set of symbol types designed for filling: circle, cross, diamond, square, star, triangle, and wye. Useful for constructing the range of an ordinal scale should you wish to use a shape encoding for categorical data.

# d3.symbolsStroke · Source

An array containing a set of symbol types designed for stroking: circle, plus, times, triangle2, asterisk, square2, and diamond2. Useful for constructing the range of an ordinal scale should you wish to use a shape encoding for categorical data.

# d3.symbolAsterisk · Source

The asterisk symbol type; intended for stroking.

# d3.symbolCircle · Source

The circle symbol type; intended for either filling or stroking.

# d3.symbolCross · Source

The Greek cross symbol type, with arms of equal length; intended for filling.

# d3.symbolDiamond · Source

The rhombus symbol type; intended for filling.

# d3.symbolDiamond2 · Source

The rotated square symbol type; intended for stroking.

# d3.symbolPlus · Source

The plus symbol type; intended for stroking.

# d3.symbolSquare · Source

The square symbol type; intended for filling.

# d3.symbolSquare2 · Source

The square2 symbol type; intended for stroking.

# d3.symbolStar · Source

The pentagonal star (pentagram) symbol type; intended for filling.

# d3.symbolTriangle · Source

The up-pointing triangle symbol type; intended for filling.

# d3.symbolTriangle2 · Source

The up-pointing triangle symbol type; intended for stroking.

# d3.symbolWye · Source

The Y-shape symbol type; intended for filling.

# d3.symbolTimes · Source

The X-shape symbol type; intended for stroking.

# d3.pointRadial(angle, radius) · Source, Examples

Returns the point [x, y] for the given angle in radians, with 0 at -y (12 o’clock) and positive angles proceeding clockwise, and the given radius.

Custom Symbol Types

Symbol types are typically not used directly, instead being passed to symbol.type. However, you can define your own symbol type implementation should none of the built-in types satisfy your needs using the following interface. You can also use this low-level interface with a built-in symbol type as an alternative to the symbol generator.

# symbolType.draw(context, size)

Renders this symbol type to the specified context with the specified size in square pixels. The context implements the CanvasPathMethods interface. (Note that this is a subset of the CanvasRenderingContext2D interface!)

Stacks

Stacked Bar ChartStreamgraph

Some shape types can be stacked, placing one shape adjacent to another. For example, a bar chart of monthly sales might be broken down into a multi-series bar chart by product category, stacking bars vertically. This is equivalent to subdividing a bar chart by an ordinal dimension (such as product category) and applying a color encoding.

Stacked charts can show overall value and per-category value simultaneously; however, it is typically harder to compare across categories, as only the bottom layer of the stack is aligned. So, chose the stack order carefully, and consider a streamgraph. (See also grouped charts.)

Like the pie generator, the stack generator does not produce a shape directly. Instead it computes positions which you can then pass to an area generator or use directly, say to position bars.

# d3.stack() · Source

Constructs a new stack generator with the default settings.

# stack(data[, arguments…]) · Source

Generates a stack for the given array of data, returning an array representing each series. Any additional arguments are arbitrary; they are simply propagated to accessors along with the this object.

The series are determined by the keys accessor; each series i in the returned array corresponds to the ith key. Each series is an array of points, where each point j corresponds to the jth element in the input data. Lastly, each point is represented as an array [y0, y1] where y0 is the lower value (baseline) and y1 is the upper value (topline); the difference between y0 and y1 corresponds to the computed value for this point. The key for each series is available as series.key, and the index as series.index. The input data element for each point is available as point.data.

For example, consider the following table representing monthly sales of fruits:

Month Apples Bananas Cherries Durians
1/2015 3840 1920 960 400
2/2015 1600 1440 960 400
3/2015 640 960 640 400
4/2015 320 480 640 400

This might be represented in JavaScript as an array of objects:

const data = [
  {month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, durians: 400},
  {month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, durians: 400},
  {month: new Date(2015, 2, 1), apples:  640, bananas:  960, cherries: 640, durians: 400},
  {month: new Date(2015, 3, 1), apples:  320, bananas:  480, cherries: 640, durians: 400}
];

To produce a stack for this data:

const stack = d3.stack()
    .keys(["apples", "bananas", "cherries", "durians"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);

const series = stack(data);

The resulting array has one element per series. Each series has one point per month, and each point has a lower and upper value defining the baseline and topline:

[
  [[   0, 3840], [   0, 1600], [   0,  640], [   0,  320]], // apples
  [[3840, 5760], [1600, 3040], [ 640, 1600], [ 320,  800]], // bananas
  [[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
  [[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // durians
]

Each series in then typically passed to an area generator to render an area chart, or used to construct rectangles for a bar chart.

# stack.keys([keys]) · Source

If keys is specified, sets the keys accessor to the specified function or array and returns this stack generator. If keys is not specified, returns the current keys accessor, which defaults to the empty array. A series (layer) is generated for each key. Keys are typically strings, but they may be arbitrary values. The series’ key is passed to the value accessor, along with each data point, to compute the point’s value.

# stack.value([value]) · Source

If value is specified, sets the value accessor to the specified function or number and returns this stack generator. If value is not specified, returns the current value accessor, which defaults to:

function value(d, key) {
  return d[key];
}

Thus, by default the stack generator assumes that the input data is an array of objects, with each object exposing named properties with numeric values; see stack for an example.

# stack.order([order]) · Source

If order is specified, sets the order accessor to the specified function or array and returns this stack generator. If order is not specified, returns the current order accessor, which defaults to stackOrderNone; this uses the order given by the key accessor. See stack orders for the built-in orders.

If order is a function, it is passed the generated series array and must return an array of numeric indexes representing the stack order. For example, the default order is defined as:

function orderNone(series) {
  let n = series.length;
  const o = new Array(n);
  while (--n >= 0) o[n] = n;
  return o;
}

The stack order is computed prior to the offset; thus, the lower value for all points is zero at the time the order is computed. The index attribute for each series is also not set until after the order is computed.

# stack.offset([offset]) · Source

If offset is specified, sets the offset accessor to the specified function and returns this stack generator. If offset is not specified, returns the current offset acccesor, which defaults to stackOffsetNone; this uses a zero baseline. See stack offsets for the built-in offsets.

The offset function is passed the generated series array and the order index array; it is then responsible for updating the lower and upper values in the series array. For example, the default offset is defined as:

function offsetNone(series, order) {
  if (!((n = series.length) > 1)) return;
  for (let i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
    s0 = s1, s1 = series[order[i]];
    for (let j = 0; j < m; ++j) {
      s1[j][1] += s1[j][0] = s0[j][1];
    }
  }
}

Stack Orders

Stack orders are typically not used directly, but are instead passed to stack.order.

# d3.stackOrderAppearance(series) · Source

Returns a series order such that the earliest series (according to the maximum value) is at the bottom.

# d3.stackOrderAscending(series) · Source

Returns a series order such that the smallest series (according to the sum of values) is at the bottom.

# d3.stackOrderDescending(series) · Source

Returns a series order such that the largest series (according to the sum of values) is at the bottom.

# d3.stackOrderInsideOut(series) · Source

Returns a series order such that the earliest series (according to the maximum value) are on the inside and the later series are on the outside. This order is recommended for streamgraphs in conjunction with the wiggle offset. See Stacked Graphs—Geometry & Aesthetics by Byron & Wattenberg for more information.

# d3.stackOrderNone(series) · Source

Returns the given series order [0, 1, … n - 1] where n is the number of elements in series. Thus, the stack order is given by the key accessor.

# d3.stackOrderReverse(series) · Source

Returns the reverse of the given series order [n - 1, n - 2, … 0] where n is the number of elements in series. Thus, the stack order is given by the reverse of the key accessor.

Stack Offsets

Stack offsets are typically not used directly, but are instead passed to stack.offset.

# d3.stackOffsetExpand(series, order) · Source

Applies a zero baseline and normalizes the values for each point such that the topline is always one.

# d3.stackOffsetDiverging(series, order) · Source

Positive values are stacked above zero, negative values are stacked below zero, and zero values are stacked at zero.

# d3.stackOffsetNone(series, order) · Source

Applies a zero baseline.

# d3.stackOffsetSilhouette(series, order) · Source

Shifts the baseline down such that the center of the streamgraph is always at zero.

# d3.stackOffsetWiggle(series, order) · Source

Shifts the baseline so as to minimize the weighted wiggle of layers. This offset is recommended for streamgraphs in conjunction with the inside-out order. See Stacked Graphs—Geometry & Aesthetics by Bryon & Wattenberg for more information.

Package Sidebar

Install

npm i d3-shape

Weekly Downloads

5,318,063

Version

3.2.0

License

ISC

Unpacked Size

247 kB

Total Files

65

Last publish

Collaborators

  • mbostock
  • recifs